rasti 4.2.0 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/rasti.js +22 -13
- package/dist/rasti.js.map +1 -1
- package/dist/rasti.min.js +1 -1
- package/dist/rasti.min.js.map +1 -1
- package/es/Component.js +4 -4
- package/es/Component.js.map +1 -1
- package/es/View.js +4 -4
- package/es/View.js.map +1 -1
- package/es/index.js +1 -1
- package/es/utils/assignOption.js +26 -0
- package/es/utils/assignOption.js.map +1 -0
- package/lib/Component.cjs +4 -4
- package/lib/Component.cjs.map +1 -1
- package/lib/View.cjs +4 -4
- package/lib/View.cjs.map +1 -1
- package/lib/index.cjs +1 -1
- package/lib/utils/assignOption.cjs +28 -0
- package/lib/utils/assignOption.cjs.map +1 -0
- package/package.json +1 -1
- package/src/Component.js +4 -4
- package/src/View.js +4 -4
- package/src/utils/assignOption.js +25 -0
- package/es/utils/defineOwn.js +0 -17
- package/es/utils/defineOwn.js.map +0 -1
- package/lib/utils/defineOwn.cjs +0 -19
- package/lib/utils/defineOwn.cjs.map +0 -1
- package/src/utils/defineOwn.js +0 -16
package/dist/rasti.min.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rasti.min.js","sources":["../src/utils/validateListener.js","../src/Emitter.js","../src/utils/getResult.js","../src/Model.js","../src/utils/defineOwn.js","../src/View.js","../src/core/SafeHTML.js","../src/core/Partial.js","../src/core/EventsManager.js","../src/core/Element.js","../src/utils/getAttributesDiff.js","../src/core/PathManager.js","../src/utils/syncNode.js","../src/utils/findComment.js","../src/core/Interpolation.js","../src/utils/deepFlat.js","../src/utils/parseHTML.js","../src/utils/getAttributesHTML.js","../src/utils/replaceNode.js","../src/Component.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","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","/**\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","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 * @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 * @static\n * @memberof module:Model\n * @name attributePrefix\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","/**\n * Define `key` on `target` as an own data property. Unlike an assignment, it does not go\n * through an accessor declared on the prototype, so it shadows a getter with no setter\n * instead of throwing.\n * @param {object} target Object to define the property on.\n * @param {string} key Property name.\n * @param {any} value Property value.\n * @return {object} The target object.\n * @module\n * @private\n */\nconst defineOwn = (target, key, value) => Object.defineProperty(target, key, {\n value, writable : true, enumerable : true, configurable : true\n});\n\nexport default defineOwn;\n","import Emitter from './Emitter.js';\nimport getResult from './utils/getResult.js';\nimport defineOwn from './utils/defineOwn.js';\nimport validateListener from './utils/validateListener.js';\nimport createDevelopmentErrorMessage from './utils/createDevelopmentErrorMessage.js';\nimport createProductionErrorMessage from './utils/createProductionErrorMessage.js';\nimport __DEV__ from './utils/dev.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 {string} 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>${View.sanitize(model.seconds)}</span>`;\n * }\n *\n * render() {\n * if (this.template) {\n * this.el.innerHTML = this.template(this.model);\n * }\n * return this;\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, as own properties so an option overrides\n // a getter declared by a subclass instead of being assigned through it.\n viewOptions.forEach(key => {\n if (key in options) {\n defineOwn(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 {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 {View} child\n * @return {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 {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 {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 const eventTypes = {};\n\n Object.keys(events).forEach(key => {\n const match = key.match(/^(\\w+)(?:\\s+(.+))*$/);\n\n if (!match) {\n const message = `Invalid event format: ${key}`;\n throw new Error(__DEV__ ? createDevelopmentErrorMessage(message) : createProductionErrorMessage(message));\n }\n // Extract type and selector from the event key.\n const [,type, selector] = match;\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 let node = event.target;\n // Traverse ancestors until reaching the view root (`this.el`).\n while (node) {\n if (node.matches) {\n // Iterate and run every individual listener if the selector matches.\n eventTypes[type].forEach(([selector, listener]) => {\n if ((node === this.el && !selector) || (node !== this.el && node.matches(selector))) {\n listener.call(this, event, this, node);\n }\n });\n }\n // Continue traversing ancestors until reaching the view root (`this.el`) or stopping propagation.\n node = node === this.el || event.cancelBubble ? null : node.parentElement;\n }\n };\n // Store the type and listener in the delegated event listeners array.\n this.delegatedEventListeners.push([type, typeListener]);\n // Add the event listener to the element.\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 {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 * `render` is the core function that your view should override, in order to populate its element (`this.el`), with the appropriate HTML. The convention is for `render` to always return `this`. \n * Views are low-level building blocks for creating user interfaces. For most use cases, we recommend using {@link #module_component Component} instead, which provides a more declarative template syntax, automatic DOM updates, and a more efficient render pipeline. \n * If you add any child views, you should call `this.destroyChildren` before re-rendering.\n * \n * @return {View} Returns `this` for chaining.\n * @example\n * class UserView extends View {\n * render() {\n * if (this.template) {\n * const model = this.model;\n * // Sanitize model attributes to prevent XSS attacks.\n * const safeData = {\n * name : View.sanitize(model.name),\n * email : View.sanitize(model.email),\n * bio : View.sanitize(model.bio)\n * };\n * this.el.innerHTML = this.template(safeData);\n * }\n * return this;\n * }\n * }\n */\n render() {\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 * Reset the unique ID counter to 0.\n * This is useful for server-side rendering scenarios where you want to ensure that\n * the generated unique IDs match those on the client, enabling seamless hydration of components.\n * This method is inherited by {@link #module_component Component}.\n * @static\n */\n static resetUid() {\n View.uid = 0;\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 * @memberof module:View\n * @name uid\n * @type {number}\n * @default 0\n */\nView.uid = 0;\n","/**\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","/**\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","/**\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","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","/**\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","/**\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","/**\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","/**\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","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","/**\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","/**\n * Parse HTML string to a DocumentFragment.\n *\n * The string is parsed as written. A component's markup already reaches here\n * without surrounding whitespace, and the content of an interpolation is the\n * rendered value itself, whose leading and trailing whitespace is significant.\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}`;\n return fragment.content;\n}\n","/**\n * Match a name that is legal for an HTML attribute. Excludes whitespace, quotes,\n * `>`, `/`, `=` and control characters, which would otherwise end the attribute\n * or the tag when the name is interpolated into markup. An empty name is excluded\n * as well, since it can't be serialized as a name.\n * @type {RegExp}\n * @private\n */\n// eslint-disable-next-line no-control-regex\nconst VALID_ATTRIBUTE_NAME = /^[^\\s\"'>/=\\u0000-\\u001F\\u007F-\\u009F]+$/;\n\n/**\n * Escape an attribute value. Values are serialized inside double quotes,\n * so escaping the double quote keeps the value from terminating the attribute.\n * @param {string} value Attribute value.\n * @return {string} Escaped attribute value.\n * @private\n */\nconst escapeAttributeValue = value => `${value}`.replace(/\"/g, '"');\n\n/**\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 // Skip invalid names, they can't be serialized as an attribute.\n if (!VALID_ATTRIBUTE_NAME.test(key)) return;\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}=\"${escapeAttributeValue(value)}\"`);\n }\n });\n\n return html.join(' ');\n}\n","let isChrome, moveBeforeSupported, preserveFocus, resetFocus;\n\n// Browser compatibility notes (as of 2025):\n// - Safari: Does not support moveBefore.\n// - Firefox: moveBefore preserves focus but loses scroll position.\n// - Chrome: moveBefore preserves scroll position but loses focus.\nif (typeof document !== 'undefined') {\n isChrome = !!navigator.userAgent.match(/Chrome/);\n moveBeforeSupported = !!Element.prototype.moveBefore;\n preserveFocus = !moveBeforeSupported || isChrome;\n // When using moveBefore, Chrome resets the focus but preserves the active element.\n // So we need to blur the active element before setting the focus again.\n resetFocus = moveBeforeSupported && isChrome;\n}\n\n/**\n * Replaces an existing DOM node with a new node, preserving internal DOM state.\n * Uses moveBefore if available, otherwise falls back to insertBefore.\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 const activeElement = preserveFocus &&\n document.activeElement &&\n newNode.contains(document.activeElement) ?\n document.activeElement : null;\n\n if (activeElement && resetFocus) activeElement.blur();\n\n oldNode.parentNode[moveBeforeSupported ? 'moveBefore' : 'insertBefore'](newNode, oldNode);\n oldNode.parentNode.removeChild(oldNode);\n\n if (activeElement && activeElement !== document.activeElement && newNode.contains(activeElement)) {\n activeElement.focus();\n }\n}\n","import View from './View.js';\nimport Model from './Model.js';\nimport SafeHTML from './core/SafeHTML.js';\nimport Partial from './core/Partial.js';\nimport EventsManager from './core/EventsManager.js';\nimport Element from './core/Element.js';\nimport Interpolation from './core/Interpolation.js';\nimport validateListener from './utils/validateListener.js';\nimport getResult from './utils/getResult.js';\nimport defineOwn from './utils/defineOwn.js';\nimport deepFlat from './utils/deepFlat.js';\nimport parseHTML from './utils/parseHTML.js';\nimport findComment from './utils/findComment.js';\nimport getAttributesHTML from './utils/getAttributesHTML.js';\nimport replaceNode from './utils/replaceNode.js';\nimport createDevelopmentErrorMessage from './utils/createDevelopmentErrorMessage.js';\nimport createProductionErrorMessage from './utils/createProductionErrorMessage.js';\nimport formatTemplateSource from './utils/formatTemplateSource.js';\nimport __DEV__ from './utils/dev.js';\n\n/**\n * Same as getResult, but pass context as argument to the expression.\n * Used to evaluate expressions in the context of a component.\n * @param {any} expression The expression to be evaluated.\n * @param {any} context The context to call the expression with.\n * @param {string} [meta] Optional metadata about the expression type for error messages.\n * @return {any} The result of the evaluated expression.\n * @private\n */\nconst getExpressionResult = (expression, context, meta) => {\n try {\n if (typeof expression !== 'function') return expression;\n // In development, detect uninstantiated Component classes and provide a helpful error.\n // This typically happens when a component tag is malformed and not properly expanded.\n if (__DEV__ && expression.prototype instanceof Component) {\n throw new Error(\n `Received uninstantiated Component class \"${expression.name || 'Anonymous'}\". ` +\n 'This usually happens when a component tag is malformed (e.g., missing closing tag or typo). ' +\n 'If that\\'s not the case, make sure to instantiate child components using a component tag, mount(), or new.'\n );\n }\n\n return expression.call(context, context);\n } catch (error) {\n if (meta && !error._rasti) {\n let message;\n\n if (__DEV__) {\n const formattedSource = formatTemplateSource(context.source, expression);\n message = createDevelopmentErrorMessage(\n `Error in ${context.constructor.name}#${context.uid} (${meta})\\n${error.message}\\n\\nTemplate source:\\n\\n${formattedSource}`\n );\n } else {\n message = createProductionErrorMessage(`Error in ${context.constructor.name}#${context.uid} expression`);\n }\n\n const enhancedError = new Error(message, { cause : error });\n enhancedError._rasti = true;\n\n throw enhancedError;\n }\n\n throw error;\n }\n};\n\n/**\n * Check if an element is a component root element.\n * Component root elements have the data attribute ending with '-1'.\n * @param {Element} el The element to check.\n * @return {boolean} True if the element is a component root element.\n * @private\n */\nconst isComponent = (el) => !!(el && el.dataset && el.dataset[Component.DATASET_ELEMENT] && el.dataset[Component.DATASET_ELEMENT].endsWith('-1'));\n\n/**\n * Check if an element contains (or is) a dynamic element.\n * @param {Element} el The element to check.\n * @return {boolean} True if the element contains (or is) a dynamic element.\n * @private\n */\nconst containsElement = (el) => !!(el && ((el.dataset && el.dataset[Component.DATASET_ELEMENT]) || (el.querySelector && el.querySelector(`[${Component.ATTRIBUTE_ELEMENT}]`))));\n\n/**\n * Generate string with placeholders for interpolated expressions.\n * @param {Array<string>} strings Array of strings.\n * @param {Array<any>} expressions Array of expressions.\n * @return {string} String with placeholders.\n * @private\n */\nconst addPlaceholders = (strings, expressions) =>\n strings.reduce((out, string, i) => {\n // Add string part.\n out.push(string);\n // Add expression placeholders.\n if (typeof expressions[i] !== 'undefined') {\n out.push(Component.PLACEHOLDER(i));\n }\n return out;\n }, []).join('');\n\n/**\n * Generate one dimensional array with strings and expressions.\n * @param main {string} The main template containing placeholders.\n * @param {Array<any>} expressions Array of expressions to replace placeholders.\n * @return {array} Array containing strings and expressions.\n * @private\n */\nconst splitPlaceholders = (main, expressions) => {\n const PH = Component.PLACEHOLDER('(\\\\d+)');\n const matchSinglePlaceholder = main.match(new RegExp(`^${PH}$`));\n if (matchSinglePlaceholder) return [expressions[parseInt(matchSinglePlaceholder[1], 10)]];\n\n const regExp = new RegExp(`${PH}`, 'g');\n const out = [];\n let lastIndex = 0;\n let match;\n // Generate one dimensional array with strings and expressions, \n // so all the components are added as children by the parent component.\n while ((match = regExp.exec(main)) !== null) {\n const before = main.slice(lastIndex, match.index);\n out.push(Component.markAsSafeHTML(before), expressions[parseInt(match[1], 10)]);\n lastIndex = match.index + match[0].length;\n }\n out.push(Component.markAsSafeHTML(main.slice(lastIndex)));\n\n return out;\n};\n\n/**\n * Expand attributes.\n * @param {Array<Array<any>>} attributes Array of attributes as key, value pairs.\n * @param {Function} getExpressionResult Function to render expressions.\n * @return {object}\n * @property {object} all All attributes.\n * @property {object} events Event listeners.\n * @property {object} attributes Attributes.\n * @private\n */\nconst expandAttributes = (attributes, getExpressionResult) => attributes.reduce((out, pair) => {\n const attribute = getExpressionResult(pair[0]);\n // Attribute without value. \n if (pair.length === 1) {\n if (typeof attribute === 'object') {\n // Expand objects as attributes.\n out = Object.assign(out, attribute);\n } else if (typeof attribute === 'string') {\n // Treat as boolean.\n out[attribute] = true;\n }\n } else {\n // Attribute with value.\n const value = pair[2] ? getExpressionResult(pair[1]) : pair[1];\n out[attribute] = value;\n }\n\n return out;\n}, {});\n\n/**\n * Expand events.\n * @param {object} attributes Attributes object.\n * @param {EventsManager} eventsManager Events manager.\n * @param {Function} getDataAttribute Function to get data attribute name with uid.\n * @return {object} Attributes object.\n * @private\n */\nconst expandEvents = (attributes, eventsManager, getDataAttribute) => {\n const out = {};\n Object.keys(attributes).forEach(key => {\n // Check if key is an event listener.\n const match = key.match(/on(([A-Z]{1}[a-z]+)+)/);\n\n if (match && match[1]) {\n const type = match[1].toLowerCase();\n const listener = attributes[key];\n if (listener) {\n const index = eventsManager.addListener(listener, type);\n // Add event listener index.\n out[getDataAttribute(type)] = index;\n }\n } else {\n // Add attribute.\n out[key] = attributes[key];\n }\n });\n return out;\n};\n\n/**\n * Replace component tags with expressions.\n * `<${Component} />` or `<${Component}></${Component}>` will be replaced \n * by a function that mounts the component.\n * Returns the template with component tags replaced by expressions placeholders \n * modifies the expressions array adding the mount functions.\n * @param main {string} The main template.\n * @param {Array<any>} expressions Array of expressions.\n * @param {boolean} skipNormalization Skip placeholder normalization (for recursive calls).\n * @return {string} The template with components tags replaced by expressions\n * placeholders.\n * @private\n */\nconst expandComponents = (main, expressions, skipNormalization = false) => {\n const PH = Component.PLACEHOLDER('(\\\\d+)');\n const componentRefMap = new Map();\n // Normalize component references to use first placeholder index.\n // Only on first call, not on recursive calls.\n if (!skipNormalization) {\n main = main.replace(\n new RegExp(PH, 'g'),\n (match, idx) => {\n const expression = expressions[idx];\n if (expression && expression.prototype instanceof Component) {\n if (componentRefMap.has(expression)) {\n return componentRefMap.get(expression);\n }\n componentRefMap.set(expression, match);\n }\n return match;\n }\n );\n }\n // Match component tags with backreference to ensure correct pairing.\n return main.replace(\n new RegExp(`<(${PH})([^>]*)/>|<(${PH})([^>]*)>([\\\\s\\\\S]*?)</\\\\4>`,'g'),\n (match, selfClosingTag, selfClosingIdx, selfClosingAttrs, openTag, openIdx, nonVoidAttrs, inner) => {\n let tag, attributesStr, innerList;\n\n if (openTag) {\n tag = expressions[openIdx];\n attributesStr = nonVoidAttrs;\n } else {\n tag = typeof selfClosingIdx !== 'undefined' ? expressions[selfClosingIdx] : selfClosingTag;\n attributesStr = selfClosingAttrs;\n }\n // No component found.\n if (!(tag.prototype instanceof Component)) return match;\n // Non void component.\n if (openTag) {\n // Process inner content same way as partial().\n // Recursively expand inner components.\n const innerTemplate = expandComponents(inner, expressions, true);\n // Parse partial elements to handle dynamic attributes and events.\n const parsedInner = parsePartialElements(innerTemplate, expressions);\n // Split into items.\n innerList = splitPlaceholders(parsedInner, expressions);\n }\n // Parse attributes.\n const attributes = parseAttributes(attributesStr, expressions);\n // Create mount function.\n const mount = function() {\n const options = expandAttributes(attributes, value => getExpressionResult(value, this, 'children options'));\n // Add `renderChildren` function to options.\n if (innerList) {\n // Evaluate items in parent context and create Partial.\n options.renderChildren = () => new Partial(innerList.map(item => getExpressionResult(item, this, 'children')));\n }\n // Mount component.\n return tag.mount(options);\n };\n // Add mount function to expression.\n expressions.push(mount);\n // Replace whole string with expression placeholder.\n return Component.PLACEHOLDER(expressions.length - 1);\n }\n );\n};\n\n/**\n * Replace elements in template.\n * @param {string} template Template string.\n * @param {Function} replacer Replacer function.\n * @return {string} Template string with replaced elements.\n * @private\n */\nconst replaceElements = (template, replacer) => {\n const PH = Component.PLACEHOLDER('(?:\\\\d+)');\n return template.replace(\n new RegExp(`<(${PH}|[a-z]+[1-6]?)(?:\\\\s*)((?:\"[^\"]*\"|'[^']*'|[^>])*?)(\\\\s*/?>)`, 'gi'),\n replacer\n );\n};\n\n/**\n * Parse all HTML elements in template and extract their attributes.\n * @param {string} template Template string with placeholders.\n * @param {Array} expressions Array of expressions.\n * @param {Array} elements Array to store element references.\n * @return {string} Template with parsed attributes.\n * @throws {SyntaxError} If the template does not have a single root element or is a container component.\n * @private\n */\nconst parseElements = (template, expressions, elements) => {\n const PH = Component.PLACEHOLDER('(?:\\\\d+)');\n // Check if template is a container (single placeholder, no tag).\n const containerMatch = template.match(new RegExp(`^\\\\s*${PH}\\\\s*$`));\n if (containerMatch) return template;\n\n // Validate that template has a root element.\n const rootElementMatch = template.match(new RegExp(`^\\\\s*<([a-z]+[1-6]?|${PH})([^>]*)>([\\\\s\\\\S]*?)</(\\\\1|${PH})>\\\\s*$|^\\\\s*<([a-z]+[1-6]?|${PH})([^>]*)/>\\\\s*$`));\n\n if (!rootElementMatch) {\n const message = __DEV__ ?\n createDevelopmentErrorMessage(\n 'Invalid component template structure.\\n' +\n 'The template must have a single root element or render a single component.\\n\\n' +\n 'Valid examples:\\n' +\n '- `<div>content</div>`\\n' +\n '- `<${MyComponent} />`\\n\\n' +\n 'Invalid examples:\\n' +\n '- `<div></div><div></div>` (multiple root elements)\\n' +\n '- `text <div></div>` (text outside root element)'\n ) :\n createProductionErrorMessage('Invalid component template');\n throw new Error(message);\n }\n\n let elementUid = 0;\n // Match all HTML elements including placeholders and self-closed elements.\n return replaceElements(rootElementMatch[0], (match, tag, attributesStr, ending) => {\n const isRoot = elementUid === 0;\n const currentElementUid = ++elementUid;\n // If there are no dynamic attributes, return original match.\n if (!isRoot && !attributesStr.match(new RegExp(PH))) {\n return match;\n }\n // Parse attributes.\n const parsedAttributes = parseAttributes(attributesStr, expressions);\n // Create element reference.\n const generateElementUid = componentUid => `${componentUid}-${currentElementUid}`;\n // Create function that returns attributes object.\n const getAttributes = function() {\n // Expand attributes and events.\n const attributes = expandEvents(\n expandAttributes(parsedAttributes, value => getExpressionResult(value, this, 'element attribute')),\n this.eventsManager,\n type => Component.ATTRIBUTE_EVENT(type, this.uid)\n );\n // Extend template attributes with `options.attributes`.\n if (isRoot && this.attributes) {\n Object.assign(attributes, getResult(this.attributes, this));\n }\n // Add data attribute for element identification.\n // First element gets the component uid, others get element uid.\n attributes[Component.ATTRIBUTE_ELEMENT] = generateElementUid(this.uid);\n\n return attributes;\n };\n\n const getSelector = function() {\n return `[${Component.ATTRIBUTE_ELEMENT}=\"${generateElementUid(this.uid)}\"]`;\n };\n const elementIndex = elements.length;\n // Add element reference to elements array.\n elements.push({\n getSelector,\n getAttributes,\n });\n // Add new expression to expressions array.\n expressions.push(function() {\n const element = this.template.elements[elementIndex];\n const attributes = element.getAttributes.call(this);\n element.previousAttributes = attributes;\n return Component.markAsSafeHTML(getAttributesHTML(attributes));\n });\n // Replace attributes with placeholder.\n const placeholder = Component.PLACEHOLDER(expressions.length - 1);\n // Preserve original tag ending (> or />)\n return `<${tag} ${placeholder}${ending}`;\n });\n};\n\n/**\n * Parse elements in partial template.\n * @param {string} template Template string with placeholders.\n * @param {Array} expressions Array of expressions.\n * @return {string} Template with parsed attributes.\n * @private\n */\nconst parsePartialElements = (template, expressions) => {\n const PH = Component.PLACEHOLDER('(?:\\\\d+)');\n // Match all HTML elements including placeholders and self-closed elements.\n return replaceElements(template, (match, tag, attributesStr, ending) => {\n // If there are no dynamic attributes, return original match.\n if (!attributesStr.match(new RegExp(PH))) {\n return match;\n }\n // Parse attributes.\n const parsedAttributes = parseAttributes(attributesStr, expressions);\n // Create function that returns attributes object.\n const getAttributes = function() {\n const attributes = expandEvents(\n expandAttributes(parsedAttributes, value => getExpressionResult(value, this, 'partial element attribute')),\n this.eventsManager,\n type => Component.ATTRIBUTE_EVENT(type, this.uid)\n );\n\n return attributes;\n };\n // Add new expression to expressions array.\n expressions.push(function() {\n const attributes = getAttributes.call(this);\n return Component.markAsSafeHTML(getAttributesHTML(attributes));\n });\n // Replace attributes with placeholder.\n const placeholder = Component.PLACEHOLDER(expressions.length - 1);\n // Preserve original tag ending (> or />)\n return `<${tag} ${placeholder}${ending}`;\n });\n};\n\n/**\n * Parse all interpolations in template text content.\n * @param {string} template Template string with placeholders.\n * @param {Array} expressions Array of expressions.\n * @param {Array} interpolations Array to store interpolation references.\n * @return {string} Template with interpolation markers.\n * @private\n */\nconst parseInterpolations = (template, expressions, interpolations) => {\n const PH = Component.PLACEHOLDER('(\\\\d+)');\n let interpolationUid = 0;\n // Match all expression placeholders.\n return template.replace(\n new RegExp(PH, 'g'),\n function(match, expressionIndex, offset) {\n // Check if this placeholder is inside an element tag (attribute).\n // `offset` is the index of the match in the original string.\n const beforeMatch = template.substring(0, offset);\n const lastOpenTag = beforeMatch.lastIndexOf('<');\n const lastCloseTag = beforeMatch.lastIndexOf('>');\n // If we're inside an element tag, don't process as interpolation.\n if (lastOpenTag > lastCloseTag) {\n return match;\n }\n\n const currentInterpolationUid = ++interpolationUid;\n\n function getStart() {\n return Component.MARKER_START(`${this.uid}-${currentInterpolationUid}`);\n }\n function getEnd() {\n return Component.MARKER_END(`${this.uid}-${currentInterpolationUid}`);\n }\n const interpolationIndex = interpolations.length;\n // Add interpolation reference to interpolations array.\n interpolations.push({\n getStart,\n getEnd,\n expression : expressions[expressionIndex]\n });\n // Add new expression to expressions array.\n expressions.push(function() {\n return this.template.interpolations[interpolationIndex];\n });\n // Replace with new placeholder.\n return Component.PLACEHOLDER(expressions.length - 1);\n }\n );\n};\n\n/**\n * Parse attributes string to extract dynamic attributes.\n * @param {string} attributesStr Attributes string from HTML element.\n * @param {Array} expressions Array of expressions.\n * @return {Array} Array of attribute pairs [key, value] or [key, value, hasQuotes].\n * @private\n */\nconst parseAttributes = (attributesStr, expressions) => {\n const PH = Component.PLACEHOLDER('(\\\\d+)');\n const attributes = [];\n // Parse attributes string with support for placeholders in both names and values.\n const regExp = new RegExp(`(?:${PH}|([\\\\w-]+))(?:=([\"']?)(?:${PH}|((?:.?(?![\"']?\\\\s+(?:\\\\S+)=|\\\\s*/>|\\\\s*[>\"']))+.))?\\\\3)?`, 'g');\n\n let attributeMatch;\n while ((attributeMatch = regExp.exec(attributesStr)) !== null) {\n const [, attributeIdx, attribute, quotes, valueIdx, value] = attributeMatch;\n\n const hasQuotes = !!quotes;\n\n let attr = typeof attributeIdx !== 'undefined' ? expressions[parseInt(attributeIdx, 10)] : attribute;\n let val = typeof valueIdx !== 'undefined' ? expressions[parseInt(valueIdx, 10)] : value;\n\n if (hasQuotes && typeof val === 'undefined') {\n val = '';\n }\n\n if (typeof val !== 'undefined') {\n attributes.push([attr, val, hasQuotes]);\n } else {\n attributes.push([attr]);\n }\n }\n\n return attributes;\n};\n\n/*\n * These option keys will be extended on the component instance.\n */\nconst componentOptions = ['key', 'state', 'onCreate', 'onChange', 'onHydrate', 'onBeforeRecycle', 'onRecycle', 'onBeforeUpdate', 'onUpdate'];\n\n/**\n * @lends module:Component\n */\nclass Component extends View {\n constructor(options = {}) {\n super(...arguments);\n this.componentOptions = [];\n // Extend \"this\" with options, as own properties so an option overrides\n // a getter declared by a subclass instead of being assigned through it.\n componentOptions.forEach(key => {\n if (key in options) {\n defineOwn(this, key, options[key]);\n this.componentOptions.push(key);\n }\n });\n // Extract props from options that aren't component or view options.\n const props = {};\n Object.keys(options).forEach(key => {\n if (this.viewOptions.indexOf(key) === -1 && this.componentOptions.indexOf(key) === -1) {\n props[key] = options[key];\n }\n });\n // Store props as Model for reactive updates.\n this.props = new Model(props);\n // Store options by default.\n this.options = options;\n // Bind `partial` method to `this`.\n this.partial = this.partial.bind(this);\n // Expose `partial` as `html`, unless a subclass defines its own `html`.\n if (!('html' in this)) this.html = this.partial;\n // Bind `onChange` method to `this`.\n this.onChange = this.onChange.bind(this);\n // Call lifecycle method.\n this.onCreate.apply(this, arguments);\n }\n\n /**\n * Get events object for automatic event delegation, based on data attributes.\n * @return {object} The events object.\n * @private\n */\n events() {\n const events = {};\n // Create events object.\n this.eventsManager.types.forEach(type => {\n const dataAttribute = Component.ATTRIBUTE_EVENT(type, this.uid);\n // Create a listener function that gets the listener index from the data attribute and calls the listener.\n const listener = function(event, component, matched) {\n // Get the listener index from the data attribute.\n const index = matched.getAttribute(dataAttribute);\n // Root element listener may not have a data attribute.\n if (index) {\n let currentListener = this.eventsManager.listeners[parseInt(index, 10)];\n if (typeof currentListener === 'string') currentListener = this[currentListener];\n validateListener(currentListener);\n // Call the listener.\n currentListener.call(this, event, component, matched);\n }\n };\n // Add an event listener to the events object for each event type, using the data attribute\n // as both a CSS selector and to store the listener's index.\n events[`${type} [${dataAttribute}]`] = listener;\n // Add an event listener to the events object for each event type that matches the root element.\n events[type] = listener;\n });\n\n return events;\n }\n\n /**\n * Override super method. We don't want to ensure an element on instantiation.\n * We will provide it later.\n * @private\n */\n ensureElement() {\n // Store data event listeners.\n this.eventsManager = new EventsManager();\n // Call template function.\n this.template = getResult(this.template, this);\n // If el is provided, delegate events.\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 // Check if the element has a parent node.\n if (!this.el.parentNode) {\n const message = __DEV__ ?\n createDevelopmentErrorMessage(\n `Hydration failed in ${this.constructor.name}#${this.uid}\\n` +\n 'The element must have a parent node for hydration to work.\\n' +\n 'Make sure the element is mounted in the DOM before hydrating.'\n ) :\n createProductionErrorMessage(`Hydration failed in ${this.constructor.name}#${this.uid}`);\n throw new Error(message);\n }\n // Render the component as a string to generate children components.\n this.toString();\n // Hydrate the component.\n this.hydrate(this.el.parentNode);\n }\n }\n\n /**\n * Tell if `Component` is a container.\n * In which case, it will not have an element by itself.\n * It will render a single expression which is expected to return a single component as child.\n * `this.el` will be a reference to that child component's element.\n * @return {boolean}\n * @private\n */\n isContainer() {\n return this.template.elements.length === 0 && this.template.interpolations.length === 1;\n }\n\n /**\n * Subscribes to a `change` event on a model or emitter object and invokes the `onChange` lifecycle method.\n * The subscription is automatically cleaned up when the component is destroyed.\n * By default, the component subscribes to changes on `this.model`, `this.state`, and `this.props`.\n * \n * @param {Object} model - The model or emitter object to listen to.\n * @param {string} [type='change'] - The event type to listen for.\n * @param {Function} [listener=this.onChange] - The callback to invoke when the event is emitted.\n * @returns {Component} The current component instance for chaining.\n */\n subscribe(model, type = 'change', listener = this.onChange) {\n // Check if model has `on` method.\n if (model.on) this.listenTo(model, type, listener);\n return this;\n }\n\n /**\n * Used internally on the render process.\n * Attach the `Component` to the dom element providing `this.el`, delegate events, \n * subscribe to model changes and call `onHydrate` lifecycle method.\n * @param parent {node} The parent node.\n * @return {Component} The component instance.\n * @private\n */\n hydrate(parent) {\n ['model', 'state', 'props'].forEach(key => {\n if (this[key]) this.subscribe(this[key]);\n });\n\n if (this.isContainer()) {\n // Don't hydrate interpolation markers for container components as we know they will have only one child.\n // Call hydrate on children.\n this.children[0].hydrate(parent);\n // Set the first element as the component's element.\n this.el = this.children[0].el;\n } else {\n // Search for every element in template using getSelector\n this.template.elements.forEach((element, index) => {\n if (index === 0) {\n element.hydrate(parent);\n this.el = element.ref;\n }\n else {\n element.hydrate(this.el);\n }\n });\n // Get references for interpolation marker comments\n this.template.interpolations.forEach(interpolation => interpolation.hydrate(this.el));\n this.children.forEach(child => child.hydrate(this.el));\n }\n // Delegate events.\n this.delegateEvents();\n // Call `onHydrate` lifecycle method.\n this.onHydrate.call(this);\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Used internally on the render process.\n * Reuse a `Component` by replacing the placeholder comment with the real nodes.\n * Calls `onBeforeRecycle` lifecycle method at the beginning, before any recycling operations occur.\n * @param parent {node} The parent node. If not provided, the node is already in the correct position and won't be moved.\n * @return {Component} The component instance.\n * @private\n */\n recycle(parent) {\n // Call `onBeforeRecycle` lifecycle method.\n this.onBeforeRecycle.call(this);\n // No parent means the node is already in the correct position. So we don't need to replace it.\n if (parent) {\n // Locate the placeholder comment and replace it with the real nodes\n const placeholder = findComment(parent, Component.MARKER_RECYCLED(this.uid), isComponent);\n replaceNode(placeholder, this.el);\n }\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Update the component's props.\n * Sets the props and calls the `onRecycle` lifecycle method.\n * @param props {object} The props to set on the component.\n * @return {Component} The component instance.\n * @private\n */\n updateProps(props) {\n // Set the props.\n this.props.set(props);\n // Call `onRecycle` lifecycle method.\n this.onRecycle.call(this);\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Get a `comment` marker with same data attribute as this component.\n * Used to replace the component when it is recycled.\n * @return {string} The recycle placeholder.\n * @private\n */\n getRecycledMarker() {\n return `<!--${Component.MARKER_RECYCLED(this.uid)}-->`;\n }\n\n /**\n * Tagged template helper method.\n * Used to create a partial template. \n * It will return a Partial object that preserves structure for position-based recycling.\n * Components will be added as children by the parent component. Template strings literals \n * will be marked as safe HTML to be rendered.\n * This method is bound to the component instance by default, and is also available as `this.html`.\n * @param {TemplateStringsArray} strings - Template strings.\n * @param {...any} expressions - Template expressions.\n * @return {Partial} Partial object containing strings and expressions.\n * @example\n * import { Component } from 'rasti';\n * // Create a Title component.\n * const Title = Component.create`\n * <h1>${({ props }) => props.renderChildren()}</h1>\n * `;\n * // Create Main component.\n * const Main = Component.create`\n * <main>\n * ${self => self.renderHeader()}\n * </main>\n * `.extend({\n * // Render header method.\n * // Use `partial` to render an HTML template adding children components.\n * renderHeader() {\n * return this.partial`\n * <header>\n * <${Title}>${({ model }) => model.title}</${Title}>\n * </header>\n * `;\n * }\n * });\n */\n partial(strings, ...expressions) {\n const items = splitPlaceholders(\n parsePartialElements(\n expandComponents(\n addPlaceholders(\n strings,\n expressions\n ).trim(),\n expressions\n ),\n expressions\n ),\n expressions\n ).map(item => getExpressionResult(item, this, 'partial'));\n\n return new Partial(items);\n }\n\n /**\n * Render a template part.\n * @param {any} part - The template part.\n * @param {function} addChild - The addChild function. It should handle children and return a HTML string.\n * @param {Array} items - The items array. It is used to store the items that are parsed.\n * @return {string} The rendered template part.\n * @private\n */\n renderTemplatePart(part, addChild, tracker) {\n const item = getExpressionResult(part, this, 'template part');\n\n if (typeof item === 'undefined' || item === null || item === false || item === true) {\n return '';\n }\n\n if (item instanceof SafeHTML) {\n return `${item}`;\n }\n\n if (item instanceof Component) {\n return `${addChild(item, tracker)}`;\n }\n\n if (item instanceof Partial) {\n // Single item.\n if (item.items.length === 1) return this.renderTemplatePart(item.items[0], addChild, tracker);\n // Several items.\n tracker.push();\n const out = item.items.map(subItem => {\n tracker.increment();\n return this.renderTemplatePart(subItem, addChild, tracker);\n }).join('');\n tracker.pop();\n return out;\n }\n // Handle arrays (user loops) - disable tracking.\n if (Array.isArray(item)) {\n tracker.pause();\n const out = deepFlat(item).map(subItem => this.renderTemplatePart(subItem, addChild, tracker)).join('');\n tracker.resume();\n return out;\n }\n // Interpolation: add markers and process maintaining tracking.\n if (item instanceof Interpolation) {\n // Reset the tracker.\n const tracker = item.tracker;\n tracker.reset();\n // Add Interpolation markers.\n const startMarker = this.isContainer() ? '' : `<!--${item.getStart()}-->`;\n const endMarker = this.isContainer() ? '' : `<!--${item.getEnd()}-->`;\n return `${startMarker}${this.renderTemplatePart(item.expression, addChild, tracker)}${endMarker}`;\n }\n\n return `${Component.sanitize(item)}`;\n }\n\n /**\n * Render the component as a string.\n * Used internally on the render process. \n * Use it for server-side rendering or static site generation.\n * @return {string} The rendered component.\n * @example\n * import { Component } from 'rasti';\n * const Button = Component.create`\n * <button class=\"button\">Click me</button>\n * `;\n * const App = Component.create`\n * <div>\n * <${Button}>Click me</${Button}>\n * </div>\n * `;\n * \n * const app = new App();\n * \n * console.log(app.toString());\n * // <div data-rst-el=\"r1-1\"><!--rst-s-r1-1--><button class=\"button\" data-rst-el=\"r2-1\">Click me</button><!--rst-e-r1-1--></div>\n * \n * console.log(`${app}`);\n * // <div data-rst-el=\"r1-1\"><!--rst-s-r1-1--><button class=\"button\" data-rst-el=\"r2-1\">Click me</button><!--rst-e-r1-1--></div>\n */\n toString() {\n // Normally there won't be any children, but if there are, destroy them.\n this.destroyChildren();\n // Normally there won't be any data event listeners, but if there are, clear them.\n this.eventsManager.reset();\n // Bind addChild method.\n const addChild = (component, tracker) => {\n tracker.track(component);\n return this.addChild(component);\n };\n // Render the template parts.\n return this.template.parts\n .map(part => this.renderTemplatePart(part, addChild))\n .join('');\n }\n\n /**\n * Render the `Component`.\n * \n * **First render (when `this.el` is not present):**\n * This is the initial render call. The component will be rendered as a string inside a `DocumentFragment` and hydrated, \n * making `this.el` available. `this.el` is the root DOM element of the component that can be applied to the DOM. \n * The `onHydrate` lifecycle method will be called. \n * \n * **Note:** Typically, you don't need to call `render()` directly for the first render. The static method `Component.mount()` \n * handles this process automatically, creating the component instance, rendering it, and appending it to the DOM.\n * \n * **Update render (when `this.el` is present):**\n * This indicates the component is being updated. The method will:\n * - Update only the attributes of the root element and child elements\n * - Update only the content of interpolations (the dynamic parts of the template)\n * - For container components (components that render a single child component), update the single interpolation\n * \n * The `onBeforeUpdate` lifecycle method will be called at the beginning, followed by the `onUpdate` lifecycle method at the end.\n * \n * **Child component handling:**\n * When rendering child components, they can be either recreated or recycled:\n * \n * - **Recreation:** A new component instance is created, running the constructor again. This happens when no matching component \n * is found for recycling.\n * \n * - **Recycling:** The same component instance is reused. Recycling happens in two ways:\n * - Components with a `key` are recycled if a previous child with the same key exists\n * - Unkeyed components are recycled if they have the same type and position in the template or partial\n * \n * When a component is recycled:\n * - The `onBeforeRecycle` lifecycle method is called when recycling starts\n * - The component's `this.props` is updated with the new props from the parent\n * - The `onRecycle` lifecycle method is called after props are updated\n * \n * A recycled component may not use props at all and remain unchanged, or it may be subscribed to a different model \n * (or even the same model as the parent) and update independently in subsequent render cycles.\n * \n * @return {Component} The component instance.\n */\n render() {\n // Prevent a last re render if view is already destroyed.\n if (this.destroyed) return this;\n // If `this.el` is not present, render the view as a string and hydrate it.\n if (!this.el) {\n const fragment = parseHTML(this);\n this.hydrate(fragment);\n return this;\n }\n // Call `onBeforeUpdate` lifecycle method.\n this.onBeforeUpdate.call(this);\n // Clear event listeners.\n this.eventsManager.reset();\n // Store previous children.\n const previousChildren = this.children;\n // Clear current children.\n this.children = [];\n // Store props to update.\n const propsQueue = [];\n // Update interpolations.\n this.template.interpolations.forEach(interpolation => {\n // Reset the tracker.\n const tracker = interpolation.tracker;\n tracker.reset();\n\n const nextChildren = [];\n const recycledChildren = [];\n\n // `addChild` handler is called from `renderTemplatePart` for every component.\n // It should handle children and return a HTML string.\n // In this case, where the component updates, it handles children recycling.\n const addChild = (component) => {\n let out = component;\n let found;\n // Check if child already exists by key.\n if (component.key) {\n found = previousChildren.find(prev => prev.key === component.key);\n } else {\n // Find by position and type using tracker.\n found = tracker.findRecyclable(component);\n }\n\n if (found) {\n // If child already exists, replace it html by its root element.\n out = found.getRecycledMarker();\n // Add child to recycled children.\n recycledChildren.push([found, component]);\n // Track the component.\n tracker.track(found);\n } else {\n // Add new component.\n nextChildren.push(component);\n // Track the component.\n tracker.track(component);\n }\n // Return the component or placeholder.\n return out;\n };\n // Render the interpolation content.\n const rendered = this.renderTemplatePart(interpolation.expression, addChild, tracker);\n\n const recycle = ([recycled, discarded], fragment) => {\n // Store props to update.\n propsQueue.push([recycled, discarded.props.toJSON()]);\n // Add child and recycle (move to new position if needed).\n this.addChild(recycled).recycle(fragment);\n // Destroy discarded component.\n discarded.destroy();\n };\n // Same single component in the same position. Don't move it.\n if (tracker.hasSingleComponent()) {\n recycle(recycledChildren[0], null);\n return;\n }\n // Parse the rendered content and get the fragment.\n const fragment = parseHTML(rendered);\n\n const handleComponents = (parent) => () => {\n // Add recycled components to children and move them to the new template in the fragment.\n recycledChildren.forEach(recycled => recycle(recycled, parent));\n // Add new children. Hydrate them and update the interpolation.\n nextChildren.forEach(child => this.addChild(child).hydrate(parent));\n };\n\n if (this.isContainer()) {\n interpolation.updateElement(this.el, fragment, handleComponents(this.el.parentNode));\n } else {\n interpolation.update(fragment, handleComponents(this.el));\n }\n });\n // Destroy unused children.\n previousChildren.forEach(prev => {\n if (this.children.indexOf(prev) < 0) prev.destroy();\n });\n // Update recycled children props.\n propsQueue.forEach(([recycled, props]) => {\n recycled.updateProps(props);\n });\n // If this component is a container, set el to the child element.\n // Otherwise, update elements attributes and delegate events.\n if (this.isContainer()) {\n this.el = this.children[0].el;\n } else {\n // Update elements attributes.\n this.template.elements.forEach(element => element.update());\n }\n // If there are pending event types, delegate events again.\n if (this.eventsManager.hasPendingTypes()) {\n this.delegateEvents();\n }\n // Call onUpdate lifecycle method.\n this.onUpdate.call(this);\n // Return this for chaining.\n return this;\n }\n\n /**\n * Lifecycle method. Called when the component is created, at the end of the constructor.\n * This method receives the same arguments passed to the constructor (options and any additional parameters).\n * It executes both on client and server.\n * Use this method to define models or state that will be used later in `onHydrate`.\n * @param {...*} args The constructor arguments (options and any additional parameters).\n */\n onCreate() {}\n\n /**\n * Lifecycle method. Called when model emits `change` event.\n * By default calls `render` method.\n * This method can be extended with custom logic.\n * Maybe comparing new attributes with previous ones and calling\n * render when needed.\n * @param model {Model} The model that emitted the event.\n * @param changed {object} Object containing keys and values that has changed.\n * @param [...args] {any} Any extra arguments passed to set method.\n */\n onChange() {\n this.render();\n }\n\n /**\n * Lifecycle method. Called when the component is rendered for the first time and hydrated in a DocumentFragment.\n * This method only executes on the client and only during the first render.\n * Use this method for client-only operations like making API requests or setting up browser-specific functionality.\n */\n onHydrate() {}\n\n /**\n * Lifecycle method. Called before the component is recycled and reused between renders.\n * This method is called at the beginning of the `recycle` method, before any recycling operations occur.\n * \n * A component is recycled when:\n * - It has a `key` and a previous child with the same key exists\n * - It doesn't have a `key` but has the same type and position in the template or partial\n * \n * Use this method to perform operations that need to happen before the component is recycled,\n * such as storing previous state or preparing for the recycling.\n */\n onBeforeRecycle() {}\n\n /**\n * Lifecycle method. Called when the component is recycled and reused between renders.\n * \n * A component is recycled when:\n * - It has a `key` and a previous child with the same key exists\n * - It doesn't have a `key` but has the same type and position in the template or partial\n * \n * During recycling, the component instance is reused and its props are updated with new values.\n * The component's element may be moved in the DOM if the new template structure differs from the previous one.\n */\n onRecycle() {}\n\n /**\n * Lifecycle method. Called before the component is updated or re-rendered.\n * This method is called at the beginning of the `render` method when the component's state, model, or props change and trigger a re-render.\n * Use this method to perform operations that need to happen before the component is updated,\n * such as saving previous state or preparing for the update.\n */\n onBeforeUpdate() {}\n\n /**\n * Lifecycle method. Called when the component is updated or re-rendered.\n * This method is called when the component's state, model, or props change and trigger a re-render.\n * Use this method to perform operations that need to happen on every update.\n */\n onUpdate() {}\n\n /**\n * Lifecycle method. Called when the component is destroyed.\n * Use this method to clean up resources, cancel timers, remove event listeners, etc.\n * @param {...*} args Options object or any arguments passed to `destroy` method.\n */\n onDestroy() {}\n\n /**\n * Mark a string as safe HTML to be rendered. \n * Normally you don't need to use this method, as Rasti will automatically mark string literals \n * as safe HTML when the component is {@link #module_component_create created} and when \n * using the {@link #module_component__partial Component.partial} method. \n * Be sure that the string is safe to be rendered, as it will be inserted into the DOM without any sanitization.\n * @static\n * @param {string} value \n * @return {SafeHTML} A safe HTML object.\n */\n static markAsSafeHTML(value) {\n return new SafeHTML(value);\n }\n\n /**\n * Helper method used to extend a `Component`, creating a subclass.\n * @static\n * @param {object|Function} object Object containing methods to be added to the new `Component` subclass. Also can be a function that receives the parent prototype and returns an object.\n */\n static extend(object) {\n const Current = this;\n\n class Component extends Current {}\n\n Object.assign(\n Component.prototype,\n typeof object === 'function' ? object(Current.prototype) : object\n );\n\n return Component;\n }\n\n /**\n * Mount the component into the DOM.\n * Creates a new component instance with the provided options and optionally mounts it into the DOM.\n * \n * **Mounting modes:**\n * - **Normal mount** (default): Renders the component as HTML and appends it to the provided element. Use this for client-side rendering.\n * - **Hydration mode**: Assumes the DOM already contains the component's HTML (from server-side rendering). \n * \n * If `el` is not provided, the component is instantiated but not mounted (the same as using `new Component(options)`). You can mount it later by calling `render()` and appending the element (`this.el`) to the DOM.\n * \n * @static\n * @param {object} [options={}] The component options. These will be passed to the constructor and can include \n * `model`, `state`, `props`, lifecycle methods, and any other component-specific options.\n * @param {node} [el] The DOM element where the component will be mounted. If provided, the component will be \n * rendered and appended to this element. If not provided, the component is created but not mounted.\n * @param {boolean} [hydrate=false] If `true`, enables hydration mode for server-side rendering. The component will \n * assume the DOM already contains its HTML structure and will only hydrate it.\n * If `false` (default), the component will be rendered from scratch and appended to `el`.\n * @return {Component} The component instance.\n * @example\n * import { Component, Model } from 'rasti';\n * \n * const Button = Component.create`\n * <button class=\"${({ props }) => props.className}\">\n * ${({ props }) => props.label}\n * </button>\n * `;\n * \n * // Normal mount: render and append to DOM.\n * const button = Button.mount({\n * label: 'Click me'\n * }, document.body);\n * \n * // Create without mounting (mount later).\n * const button2 = Button.mount({ className : 'secondary', label : 'Save' });\n * // Later, render and append it to the DOM.\n * document.body.appendChild(button2.render().el);\n * \n * // Hydration mode: hydrate existing server-rendered HTML\n * // Assuming document.body already contains the HTML structure of the button.\n * const hydratedButton = Button.mount({\n * className : 'primary',\n * label : 'Click me'\n * }, document.body, true);\n */\n static mount(options, el, hydrate) {\n // Instantiate component.\n const component = new this(options);\n // If `el` is passed, mount component.\n if (el) {\n if (hydrate) {\n // Hydrate existing DOM, only generate subcomponents calling `toString`.\n component.toString();\n } else {\n // Render the component and append it to the provided element.\n el.append(parseHTML(component));\n }\n // Hydrate in both cases.\n component.hydrate(el);\n }\n // Return component instance.\n return component;\n }\n\n /**\n * Takes a tagged template string or a function that returns another component, and returns a new `Component` class.\n * - The template outer tag and attributes will be used to create the view's root element.\n * - The template inner HTML will be used as the view's template.\n * ```javascript\n * const Button = Component.create`<button class=\"button\">Click me</button>`;\n * ```\n * - Template interpolations that are functions will be evaluated during the render process, receiving the view instance as an argument and being bound to it. If the function returns `null`, `undefined`, `false`, or an empty string, the interpolation won't render any content.\n * ```javascript\n * const Button = Component.create`\n * <button class=\"${({ props }) => props.className}\">\n * ${({ props }) => props.renderChildren()}\n * </button>\n * `;\n * ```\n * - Attach DOM event handlers per element using camel-cased attributes.\n * Event handlers are automatically bound to the component instance (`this`).\n * Internally, Rasti uses event delegation to the component's root element for performance.\n * \n * **Attribute Quoting:**\n * - **Quoted attributes** (`onClick=\"${handler}\"`) evaluate the expression first, useful for dynamic values\n * - **Unquoted attributes** (`onClick=${handler}`) pass the function reference directly\n * \n * **Listener Signature:** `(event, component, matched)`\n * - `event`: The native DOM event object\n * - `component`: The component instance (same as `this`)\n * - `matched`: The element that matched the event (useful for delegation)\n * \n * ```javascript\n * const Button = Component.create`\n * <button \n * onClick=${function(event, component, matched) {\n * // this === component\n * console.log('Button clicked:', matched);\n * }}\n * onMouseOver=\"${({ model }) => () => model.isHovered = true}\"\n * onMouseOut=\"${({ model }) => () => model.isHovered = false}\"\n * >\n * Click me\n * </button>\n * `;\n * ```\n * \n * If you need custom delegation (e.g., `{'click .selector': 'handler'}`), \n * you may override the `events` property as described in {@link #module_view__delegateevents View.delegateEvents}.\n * - Boolean attributes should be passed in the format `attribute=\"${() => true}\"`. `false` attributes won't be rendered. `true` attributes will be rendered without a value.\n * ```javascript\n * const Input = Component.create`\n * <input type=\"text\" disabled=${({ props }) => props.disabled} />\n * `;\n * ```\n * - If the interpolated function returns a component instance, it will be added as a child component.\n * - If the interpolated function returns an array, each item will be evaluated as above.\n * ```javascript\n * // Create a button component.\n * const Button = Component.create`\n * <button class=\"button\">\n * ${({ props }) => props.renderChildren()}\n * </button>\n * `;\n * // Create a navigation component. Add buttons as children. Iterate over items.\n * const Navigation = Component.create`\n * <nav>\n * ${({ props }) => props.items.map(\n * item => Button.mount({ renderChildren : () => item.label })\n * )}\n * </nav>\n * `;\n * // Create a header component. Add navigation as a child.\n * const Header = Component.create`\n * <header>\n * ${({ props }) => Navigation.mount({ items : props.items})}\n * </header>\n * `;\n * ```\n * - Child components can be added using a component tag.\n * ```javascript\n * // Create a button component.\n * const Button = Component.create`\n * <button class=\"button\">\n * ${({ props }) => props.renderChildren()}\n * </button>\n * `;\n * // Create a navigation component. Add buttons as children. Iterate over items.\n * const Navigation = Component.create`\n * <nav>\n * ${({ props, html }) => props.items.map(\n * item => html`<${Button}>${item.label}</${Button}>`\n * )}\n * </nav>\n * `;\n * // Create a header component. Add navigation as a child.\n * const Header = Component.create`\n * <header>\n * <${Navigation} items=\"${({ props }) => props.items}\" />\n * </header>\n * `;\n * ```\n * - If the tagged template contains only one expression that mounts a component, or the tags are references to a component, the component will be considered a <b>container</b>. It will render a single component as a child. `this.el` will be a reference to that child component's element.\n * ```javascript\n * // Create a button component.\n * const Button = Component.create`\n * <button class=\"${({ props }) => props.className}\">\n * ${({ props }) => props.renderChildren()}\n * </button>\n * `;\n * // Create a container that renders a Button component.\n * const ButtonOk = Component.create`\n * <${Button} className=\"ok\">Ok</${Button}>\n * `;\n * // Create a container that renders a Button component, using a function.\n * const ButtonCancel = Component.create(() => Button.mount({\n * className : 'cancel',\n * renderChildren : () => 'Cancel'\n * }));\n * ```\n * - Called on a subclass, the new component extends it, so the template can use its methods.\n * ```javascript\n * class ListBase extends Component {\n * renderItems() {\n * return this.props.items.map(item => this.html`<li>${item}</li>`);\n * }\n * }\n * const List = ListBase.create`\n * <ul>${(self) => self.renderItems()}</ul>\n * `;\n * ```\n * @static\n * @param {string|Function} strings - HTML template for the component or a function that mounts a sub component.\n * @param {...*} expressions - The expressions to be interpolated within the template.\n * @return {Component} The newly created component class.\n */\n static create(strings, ...expressions) {\n // Containers can be created using create as a functions instead of a tagged template.\n if (typeof strings === 'function') {\n expressions = [strings];\n strings = ['', ''];\n }\n // Store original template source for debugging (only in dev mode).\n const source = __DEV__ ? { strings, expressions : [...expressions] } : null;\n // Create elements, interpolations and parts arrays.\n const elements = [], interpolations = [];\n const parts = splitPlaceholders(\n parseInterpolations(\n parseElements(\n expandComponents(\n addPlaceholders(\n strings,\n expressions\n ).trim(),\n expressions\n ),\n expressions,\n elements\n ),\n expressions,\n interpolations\n ),\n expressions\n );\n // Create subclass for this component.\n return this.extend({\n source,\n template() {\n return {\n elements : elements.map(element => new Element({\n getSelector : element.getSelector.bind(this),\n getAttributes : element.getAttributes.bind(this)\n })),\n interpolations : interpolations.map(interpolation => new Interpolation({\n getStart : interpolation.getStart.bind(this),\n getEnd : interpolation.getEnd.bind(this),\n expression : interpolation.expression,\n shouldSkipFind : isComponent,\n shouldSkipSync : containsElement\n })),\n parts,\n };\n }\n });\n }\n}\n\n/*\n * Attributes used to identify elements and events.\n */\nComponent.ATTRIBUTE_ELEMENT = 'data-rst-el';\nComponent.ATTRIBUTE_EVENT = (type, uid) => `data-rst-on-${type}-${uid}`;\n\n/*\n * Dataset attribute used to identify elements.\n */\nComponent.DATASET_ELEMENT = 'rstEl';\n\n/*\n * Placeholders used to temporarily replace expressions in the template.\n */\nComponent.PLACEHOLDER = (idx) => `__RASTI_PLACEHOLDER_${idx}__`;\n\n/*\n * Markers used to identify interpolation and recycled components.\n */\nComponent.MARKER_RECYCLED = (uid) => `rst-r-${uid}`;\nComponent.MARKER_START = (uid) => `rst-s-${uid}`;\nComponent.MARKER_END = (uid) => `rst-e-${uid}`;\n\n/**\n * Components are a special kind of `View` that is designed to be easily composable, \n * making it simple to add child views and build complex user interfaces. \n * Unlike views, which are render-agnostic, components have a specific set of rendering \n * guidelines that allow for a more declarative development style. \n * Components are defined with the {@link #module_component_create Component.create} static method, which takes a tagged template string or a function that returns another component.\n * @module\n * @extends View\n * @param {object} options Object containing options. The following keys will be merged to `this`: model, state, key, onDestroy, onHydrate, onBeforeRecycle, onRecycle, onBeforeUpdate, onUpdate, onCreate, onChange. Any additional options not in the component or view options list will be automatically extracted as props and stored as `this.props`.\n * @property {string} [key] A unique key to identify the component. Components with keys are recycled when the same key is found in the previous render. Unkeyed components are recycled based on type and position.\n * @property {Model} [model] A `Model` or any emitter object containing data and business logic. The component will listen to `change` events and call `onChange` lifecycle method.\n * @property {Model} [state] A `Model` or any emitter object containing data and business logic, to be used as internal state. The component will listen to `change` events and call `onChange` lifecycle method.\n * @property {Model} [props] Automatically created from any options not merged to the component instance. Contains props passed from parent component as a `Model`. The component will listen to `change` events on props and call `onChange` lifecycle method. When a component with a `key` is recycled during parent re-render, new props are automatically updated and any changes trigger a re-render.\n * @property {Function} html Alias of {@link #module_component__partial partial}, bound to the component instance. Not set when a subclass defines its own `html`.\n * @see {@link #module_component_create Component.create}\n * @example\n * import { Component, Model } from 'rasti';\n * // Create Timer component.\n * const Timer = Component.create`\n * <div>\n * Seconds: <span>${({ model }) => model.seconds}</span>\n * </div>\n * `;\n * // Create model to store seconds.\n * const model = new Model({ seconds : 0 });\n * // Mount timer on body.\n * Timer.mount({ model }, document.body);\n * // Increment `model.seconds` every second.\n * setInterval(() => model.seconds++, 1000);\n */\nexport default Component.create`<div></div>`;\n"],"names":["validateListener","listener","TypeError","Emitter","on","type","this","listeners","push","off","once","wrapper","args","filter","fn","length","Object","keys","emit","slice","forEach","listenTo","emitter","listeningTo","stopListening","listenToOnce","item","getResult","expression","context","apply","Model","constructor","super","preinitialize","arguments","attributes","assign","defaults","parse","previous","defineAttribute","bind","key","defineProperty","attributePrefix","get","set","value","rest","attrs","changing","_changing","changed","changedKeys","_pending","pendingChange","data","toJSON","defineOwn","target","writable","enumerable","configurable","viewOptions","View","options","delegatedEventListeners","children","destroyQueue","ensureUid","ensureElement","$","selector","el","querySelector","$$","querySelectorAll","destroy","destroyChildren","undelegateEvents","onDestroy","destroyed","addChild","child","uid","tag","createElement","delegateEvents","document","setAttribute","removeElement","parentNode","removeChild","events","eventTypes","match","Error","typeListener","event","node","matches","call","cancelBubble","parentElement","addEventListener","removeEventListener","render","sanitize","replace","resetUid","SafeHTML","toString","Partial","items","EventsManager","types","Set","previousSize","addListener","add","reset","size","hasPendingTypes","SYNC_PROPS","Element$1","getSelector","getAttributes","previousAttributes","hydrate","parent","ref","update","remove","getAttributesDiff","attr","removeAttribute","indexOf","PathManager","paused","tracked","Map","positionStack","pop","increment","pause","resume","getPath","join","track","component","hasSingleComponent","currentPath","currentComponent","entries","next","previousPath","previousComponent","findRecyclable","candidate","syncNode","targetNode","sourceNode","nodeType","Node","TEXT_NODE","tagName","targetEl","sourceEl","srcAttrs","tgtAttrs","srcAttrNames","i","l","name","getAttribute","has","prop","syncAttributes","a","b","aChildren","childNodes","bChildren","aLength","isEqualNode","areChildNodesEqual","Array","from","replaceChildren","syncNodeContent","replaceWith","nodeValue","findComment","root","text","shouldSkip","startNode","firstChild","COMMENT_NODE","trim","ELEMENT_NODE","nextSibling","Interpolation","getStart","getEnd","shouldSkipFind","shouldSkipSync","tracker","startMark","endMark","fragment","handleComponents","divider","currentFirstElement","currentEmpty","currentSingleChildElement","insertBefore","createComment","range","createRange","setStartAfter","setEndAfter","deleteContents","updateElement","element","deepFlat","arr","reduce","acc","val","isArray","parseHTML","html","innerHTML","content","VALID_ATTRIBUTE_NAME","getAttributesHTML","test","escapeAttributeValue","isChrome","moveBeforeSupported","preserveFocus","resetFocus","navigator","userAgent","Element","prototype","moveBefore","getExpressionResult","meta","error","_rasti","message","enhancedError","cause","isComponent","dataset","Component","DATASET_ELEMENT","endsWith","containsElement","ATTRIBUTE_ELEMENT","addPlaceholders","strings","expressions","out","string","PLACEHOLDER","splitPlaceholders","main","PH","matchSinglePlaceholder","RegExp","parseInt","regExp","lastIndex","exec","before","index","markAsSafeHTML","expandAttributes","pair","attribute","expandEvents","eventsManager","getDataAttribute","toLowerCase","expandComponents","skipNormalization","componentRefMap","idx","selfClosingTag","selfClosingIdx","selfClosingAttrs","openTag","openIdx","nonVoidAttrs","inner","attributesStr","innerList","innerTemplate","parsedInner","parsePartialElements","parseAttributes","renderChildren","map","mount","replaceElements","template","replacer","parseElements","elements","rootElementMatch","elementUid","ending","isRoot","currentElementUid","parsedAttributes","generateElementUid","componentUid","elementIndex","ATTRIBUTE_EVENT","parseInterpolations","interpolations","interpolationUid","expressionIndex","offset","beforeMatch","substring","lastIndexOf","currentInterpolationUid","interpolationIndex","MARKER_START","MARKER_END","attributeMatch","attributeIdx","quotes","valueIdx","hasQuotes","componentOptions","props","partial","onChange","onCreate","dataAttribute","matched","currentListener","isContainer","subscribe","model","interpolation","onHydrate","recycle","onBeforeRecycle","oldNode","newNode","activeElement","contains","blur","focus","replaceNode","MARKER_RECYCLED","updateProps","onRecycle","getRecycledMarker","renderTemplatePart","part","subItem","startMarker","endMarker","parts","onBeforeUpdate","previousChildren","propsQueue","nextChildren","recycledChildren","rendered","found","find","prev","recycled","discarded","onUpdate","extend","object","Current","append","create","source","Component_default"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rasti.min.js","sources":["../src/utils/validateListener.js","../src/Emitter.js","../src/utils/getResult.js","../src/Model.js","../src/utils/assignOption.js","../src/View.js","../src/core/SafeHTML.js","../src/core/Partial.js","../src/core/EventsManager.js","../src/core/Element.js","../src/utils/getAttributesDiff.js","../src/core/PathManager.js","../src/utils/syncNode.js","../src/utils/findComment.js","../src/core/Interpolation.js","../src/utils/deepFlat.js","../src/utils/parseHTML.js","../src/utils/getAttributesHTML.js","../src/utils/replaceNode.js","../src/Component.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","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","/**\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","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 * @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 * @static\n * @memberof module:Model\n * @name attributePrefix\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","/**\n * Assign an option to `target`. A getter with no setter on the prototype would make the\n * assignment throw, so in that case `key` is defined as an own data property, which\n * shadows the getter. Any other accessor receives the value through its setter.\n * @param {object} target Object to assign the option to.\n * @param {string} key Property name.\n * @param {any} value Property value.\n * @return {object} The target object.\n * @module\n * @private\n */\nconst assignOption = (target, key, value) => {\n try {\n target[key] = value;\n } catch (error) {\n // Assigning to a getter with no setter throws a TypeError; anything else propagates.\n if (!(error instanceof TypeError)) throw error;\n Object.defineProperty(target, key, {\n value, writable : true, enumerable : true, configurable : true\n });\n }\n return target;\n};\n\nexport default assignOption;\n","import Emitter from './Emitter.js';\nimport getResult from './utils/getResult.js';\nimport assignOption from './utils/assignOption.js';\nimport validateListener from './utils/validateListener.js';\nimport createDevelopmentErrorMessage from './utils/createDevelopmentErrorMessage.js';\nimport createProductionErrorMessage from './utils/createProductionErrorMessage.js';\nimport __DEV__ from './utils/dev.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 {string} 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>${View.sanitize(model.seconds)}</span>`;\n * }\n *\n * render() {\n * if (this.template) {\n * this.el.innerHTML = this.template(this.model);\n * }\n * return this;\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. An option overrides a getter declared by a\n // subclass even when the getter has no setter.\n viewOptions.forEach(key => {\n if (key in options) {\n assignOption(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 {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 {View} child\n * @return {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 {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 {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 const eventTypes = {};\n\n Object.keys(events).forEach(key => {\n const match = key.match(/^(\\w+)(?:\\s+(.+))*$/);\n\n if (!match) {\n const message = `Invalid event format: ${key}`;\n throw new Error(__DEV__ ? createDevelopmentErrorMessage(message) : createProductionErrorMessage(message));\n }\n // Extract type and selector from the event key.\n const [,type, selector] = match;\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 let node = event.target;\n // Traverse ancestors until reaching the view root (`this.el`).\n while (node) {\n if (node.matches) {\n // Iterate and run every individual listener if the selector matches.\n eventTypes[type].forEach(([selector, listener]) => {\n if ((node === this.el && !selector) || (node !== this.el && node.matches(selector))) {\n listener.call(this, event, this, node);\n }\n });\n }\n // Continue traversing ancestors until reaching the view root (`this.el`) or stopping propagation.\n node = node === this.el || event.cancelBubble ? null : node.parentElement;\n }\n };\n // Store the type and listener in the delegated event listeners array.\n this.delegatedEventListeners.push([type, typeListener]);\n // Add the event listener to the element.\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 {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 * `render` is the core function that your view should override, in order to populate its element (`this.el`), with the appropriate HTML. The convention is for `render` to always return `this`. \n * Views are low-level building blocks for creating user interfaces. For most use cases, we recommend using {@link #module_component Component} instead, which provides a more declarative template syntax, automatic DOM updates, and a more efficient render pipeline. \n * If you add any child views, you should call `this.destroyChildren` before re-rendering.\n * \n * @return {View} Returns `this` for chaining.\n * @example\n * class UserView extends View {\n * render() {\n * if (this.template) {\n * const model = this.model;\n * // Sanitize model attributes to prevent XSS attacks.\n * const safeData = {\n * name : View.sanitize(model.name),\n * email : View.sanitize(model.email),\n * bio : View.sanitize(model.bio)\n * };\n * this.el.innerHTML = this.template(safeData);\n * }\n * return this;\n * }\n * }\n */\n render() {\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 * Reset the unique ID counter to 0.\n * This is useful for server-side rendering scenarios where you want to ensure that\n * the generated unique IDs match those on the client, enabling seamless hydration of components.\n * This method is inherited by {@link #module_component Component}.\n * @static\n */\n static resetUid() {\n View.uid = 0;\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 * @memberof module:View\n * @name uid\n * @type {number}\n * @default 0\n */\nView.uid = 0;\n","/**\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","/**\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","/**\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","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","/**\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","/**\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","/**\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","/**\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","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","/**\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","/**\n * Parse HTML string to a DocumentFragment.\n *\n * The string is parsed as written. A component's markup already reaches here\n * without surrounding whitespace, and the content of an interpolation is the\n * rendered value itself, whose leading and trailing whitespace is significant.\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}`;\n return fragment.content;\n}\n","/**\n * Match a name that is legal for an HTML attribute. Excludes whitespace, quotes,\n * `>`, `/`, `=` and control characters, which would otherwise end the attribute\n * or the tag when the name is interpolated into markup. An empty name is excluded\n * as well, since it can't be serialized as a name.\n * @type {RegExp}\n * @private\n */\n// eslint-disable-next-line no-control-regex\nconst VALID_ATTRIBUTE_NAME = /^[^\\s\"'>/=\\u0000-\\u001F\\u007F-\\u009F]+$/;\n\n/**\n * Escape an attribute value. Values are serialized inside double quotes,\n * so escaping the double quote keeps the value from terminating the attribute.\n * @param {string} value Attribute value.\n * @return {string} Escaped attribute value.\n * @private\n */\nconst escapeAttributeValue = value => `${value}`.replace(/\"/g, '"');\n\n/**\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 // Skip invalid names, they can't be serialized as an attribute.\n if (!VALID_ATTRIBUTE_NAME.test(key)) return;\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}=\"${escapeAttributeValue(value)}\"`);\n }\n });\n\n return html.join(' ');\n}\n","let isChrome, moveBeforeSupported, preserveFocus, resetFocus;\n\n// Browser compatibility notes (as of 2025):\n// - Safari: Does not support moveBefore.\n// - Firefox: moveBefore preserves focus but loses scroll position.\n// - Chrome: moveBefore preserves scroll position but loses focus.\nif (typeof document !== 'undefined') {\n isChrome = !!navigator.userAgent.match(/Chrome/);\n moveBeforeSupported = !!Element.prototype.moveBefore;\n preserveFocus = !moveBeforeSupported || isChrome;\n // When using moveBefore, Chrome resets the focus but preserves the active element.\n // So we need to blur the active element before setting the focus again.\n resetFocus = moveBeforeSupported && isChrome;\n}\n\n/**\n * Replaces an existing DOM node with a new node, preserving internal DOM state.\n * Uses moveBefore if available, otherwise falls back to insertBefore.\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 const activeElement = preserveFocus &&\n document.activeElement &&\n newNode.contains(document.activeElement) ?\n document.activeElement : null;\n\n if (activeElement && resetFocus) activeElement.blur();\n\n oldNode.parentNode[moveBeforeSupported ? 'moveBefore' : 'insertBefore'](newNode, oldNode);\n oldNode.parentNode.removeChild(oldNode);\n\n if (activeElement && activeElement !== document.activeElement && newNode.contains(activeElement)) {\n activeElement.focus();\n }\n}\n","import View from './View.js';\nimport Model from './Model.js';\nimport SafeHTML from './core/SafeHTML.js';\nimport Partial from './core/Partial.js';\nimport EventsManager from './core/EventsManager.js';\nimport Element from './core/Element.js';\nimport Interpolation from './core/Interpolation.js';\nimport validateListener from './utils/validateListener.js';\nimport getResult from './utils/getResult.js';\nimport assignOption from './utils/assignOption.js';\nimport deepFlat from './utils/deepFlat.js';\nimport parseHTML from './utils/parseHTML.js';\nimport findComment from './utils/findComment.js';\nimport getAttributesHTML from './utils/getAttributesHTML.js';\nimport replaceNode from './utils/replaceNode.js';\nimport createDevelopmentErrorMessage from './utils/createDevelopmentErrorMessage.js';\nimport createProductionErrorMessage from './utils/createProductionErrorMessage.js';\nimport formatTemplateSource from './utils/formatTemplateSource.js';\nimport __DEV__ from './utils/dev.js';\n\n/**\n * Same as getResult, but pass context as argument to the expression.\n * Used to evaluate expressions in the context of a component.\n * @param {any} expression The expression to be evaluated.\n * @param {any} context The context to call the expression with.\n * @param {string} [meta] Optional metadata about the expression type for error messages.\n * @return {any} The result of the evaluated expression.\n * @private\n */\nconst getExpressionResult = (expression, context, meta) => {\n try {\n if (typeof expression !== 'function') return expression;\n // In development, detect uninstantiated Component classes and provide a helpful error.\n // This typically happens when a component tag is malformed and not properly expanded.\n if (__DEV__ && expression.prototype instanceof Component) {\n throw new Error(\n `Received uninstantiated Component class \"${expression.name || 'Anonymous'}\". ` +\n 'This usually happens when a component tag is malformed (e.g., missing closing tag or typo). ' +\n 'If that\\'s not the case, make sure to instantiate child components using a component tag, mount(), or new.'\n );\n }\n\n return expression.call(context, context);\n } catch (error) {\n if (meta && !error._rasti) {\n let message;\n\n if (__DEV__) {\n const formattedSource = formatTemplateSource(context.source, expression);\n message = createDevelopmentErrorMessage(\n `Error in ${context.constructor.name}#${context.uid} (${meta})\\n${error.message}\\n\\nTemplate source:\\n\\n${formattedSource}`\n );\n } else {\n message = createProductionErrorMessage(`Error in ${context.constructor.name}#${context.uid} expression`);\n }\n\n const enhancedError = new Error(message, { cause : error });\n enhancedError._rasti = true;\n\n throw enhancedError;\n }\n\n throw error;\n }\n};\n\n/**\n * Check if an element is a component root element.\n * Component root elements have the data attribute ending with '-1'.\n * @param {Element} el The element to check.\n * @return {boolean} True if the element is a component root element.\n * @private\n */\nconst isComponent = (el) => !!(el && el.dataset && el.dataset[Component.DATASET_ELEMENT] && el.dataset[Component.DATASET_ELEMENT].endsWith('-1'));\n\n/**\n * Check if an element contains (or is) a dynamic element.\n * @param {Element} el The element to check.\n * @return {boolean} True if the element contains (or is) a dynamic element.\n * @private\n */\nconst containsElement = (el) => !!(el && ((el.dataset && el.dataset[Component.DATASET_ELEMENT]) || (el.querySelector && el.querySelector(`[${Component.ATTRIBUTE_ELEMENT}]`))));\n\n/**\n * Generate string with placeholders for interpolated expressions.\n * @param {Array<string>} strings Array of strings.\n * @param {Array<any>} expressions Array of expressions.\n * @return {string} String with placeholders.\n * @private\n */\nconst addPlaceholders = (strings, expressions) =>\n strings.reduce((out, string, i) => {\n // Add string part.\n out.push(string);\n // Add expression placeholders.\n if (typeof expressions[i] !== 'undefined') {\n out.push(Component.PLACEHOLDER(i));\n }\n return out;\n }, []).join('');\n\n/**\n * Generate one dimensional array with strings and expressions.\n * @param main {string} The main template containing placeholders.\n * @param {Array<any>} expressions Array of expressions to replace placeholders.\n * @return {array} Array containing strings and expressions.\n * @private\n */\nconst splitPlaceholders = (main, expressions) => {\n const PH = Component.PLACEHOLDER('(\\\\d+)');\n const matchSinglePlaceholder = main.match(new RegExp(`^${PH}$`));\n if (matchSinglePlaceholder) return [expressions[parseInt(matchSinglePlaceholder[1], 10)]];\n\n const regExp = new RegExp(`${PH}`, 'g');\n const out = [];\n let lastIndex = 0;\n let match;\n // Generate one dimensional array with strings and expressions, \n // so all the components are added as children by the parent component.\n while ((match = regExp.exec(main)) !== null) {\n const before = main.slice(lastIndex, match.index);\n out.push(Component.markAsSafeHTML(before), expressions[parseInt(match[1], 10)]);\n lastIndex = match.index + match[0].length;\n }\n out.push(Component.markAsSafeHTML(main.slice(lastIndex)));\n\n return out;\n};\n\n/**\n * Expand attributes.\n * @param {Array<Array<any>>} attributes Array of attributes as key, value pairs.\n * @param {Function} getExpressionResult Function to render expressions.\n * @return {object}\n * @property {object} all All attributes.\n * @property {object} events Event listeners.\n * @property {object} attributes Attributes.\n * @private\n */\nconst expandAttributes = (attributes, getExpressionResult) => attributes.reduce((out, pair) => {\n const attribute = getExpressionResult(pair[0]);\n // Attribute without value. \n if (pair.length === 1) {\n if (typeof attribute === 'object') {\n // Expand objects as attributes.\n out = Object.assign(out, attribute);\n } else if (typeof attribute === 'string') {\n // Treat as boolean.\n out[attribute] = true;\n }\n } else {\n // Attribute with value.\n const value = pair[2] ? getExpressionResult(pair[1]) : pair[1];\n out[attribute] = value;\n }\n\n return out;\n}, {});\n\n/**\n * Expand events.\n * @param {object} attributes Attributes object.\n * @param {EventsManager} eventsManager Events manager.\n * @param {Function} getDataAttribute Function to get data attribute name with uid.\n * @return {object} Attributes object.\n * @private\n */\nconst expandEvents = (attributes, eventsManager, getDataAttribute) => {\n const out = {};\n Object.keys(attributes).forEach(key => {\n // Check if key is an event listener.\n const match = key.match(/on(([A-Z]{1}[a-z]+)+)/);\n\n if (match && match[1]) {\n const type = match[1].toLowerCase();\n const listener = attributes[key];\n if (listener) {\n const index = eventsManager.addListener(listener, type);\n // Add event listener index.\n out[getDataAttribute(type)] = index;\n }\n } else {\n // Add attribute.\n out[key] = attributes[key];\n }\n });\n return out;\n};\n\n/**\n * Replace component tags with expressions.\n * `<${Component} />` or `<${Component}></${Component}>` will be replaced \n * by a function that mounts the component.\n * Returns the template with component tags replaced by expressions placeholders \n * modifies the expressions array adding the mount functions.\n * @param main {string} The main template.\n * @param {Array<any>} expressions Array of expressions.\n * @param {boolean} skipNormalization Skip placeholder normalization (for recursive calls).\n * @return {string} The template with components tags replaced by expressions\n * placeholders.\n * @private\n */\nconst expandComponents = (main, expressions, skipNormalization = false) => {\n const PH = Component.PLACEHOLDER('(\\\\d+)');\n const componentRefMap = new Map();\n // Normalize component references to use first placeholder index.\n // Only on first call, not on recursive calls.\n if (!skipNormalization) {\n main = main.replace(\n new RegExp(PH, 'g'),\n (match, idx) => {\n const expression = expressions[idx];\n if (expression && expression.prototype instanceof Component) {\n if (componentRefMap.has(expression)) {\n return componentRefMap.get(expression);\n }\n componentRefMap.set(expression, match);\n }\n return match;\n }\n );\n }\n // Match component tags with backreference to ensure correct pairing.\n return main.replace(\n new RegExp(`<(${PH})([^>]*)/>|<(${PH})([^>]*)>([\\\\s\\\\S]*?)</\\\\4>`,'g'),\n (match, selfClosingTag, selfClosingIdx, selfClosingAttrs, openTag, openIdx, nonVoidAttrs, inner) => {\n let tag, attributesStr, innerList;\n\n if (openTag) {\n tag = expressions[openIdx];\n attributesStr = nonVoidAttrs;\n } else {\n tag = typeof selfClosingIdx !== 'undefined' ? expressions[selfClosingIdx] : selfClosingTag;\n attributesStr = selfClosingAttrs;\n }\n // No component found.\n if (!(tag.prototype instanceof Component)) return match;\n // Non void component.\n if (openTag) {\n // Process inner content same way as partial().\n // Recursively expand inner components.\n const innerTemplate = expandComponents(inner, expressions, true);\n // Parse partial elements to handle dynamic attributes and events.\n const parsedInner = parsePartialElements(innerTemplate, expressions);\n // Split into items.\n innerList = splitPlaceholders(parsedInner, expressions);\n }\n // Parse attributes.\n const attributes = parseAttributes(attributesStr, expressions);\n // Create mount function.\n const mount = function() {\n const options = expandAttributes(attributes, value => getExpressionResult(value, this, 'children options'));\n // Add `renderChildren` function to options.\n if (innerList) {\n // Evaluate items in parent context and create Partial.\n options.renderChildren = () => new Partial(innerList.map(item => getExpressionResult(item, this, 'children')));\n }\n // Mount component.\n return tag.mount(options);\n };\n // Add mount function to expression.\n expressions.push(mount);\n // Replace whole string with expression placeholder.\n return Component.PLACEHOLDER(expressions.length - 1);\n }\n );\n};\n\n/**\n * Replace elements in template.\n * @param {string} template Template string.\n * @param {Function} replacer Replacer function.\n * @return {string} Template string with replaced elements.\n * @private\n */\nconst replaceElements = (template, replacer) => {\n const PH = Component.PLACEHOLDER('(?:\\\\d+)');\n return template.replace(\n new RegExp(`<(${PH}|[a-z]+[1-6]?)(?:\\\\s*)((?:\"[^\"]*\"|'[^']*'|[^>])*?)(\\\\s*/?>)`, 'gi'),\n replacer\n );\n};\n\n/**\n * Parse all HTML elements in template and extract their attributes.\n * @param {string} template Template string with placeholders.\n * @param {Array} expressions Array of expressions.\n * @param {Array} elements Array to store element references.\n * @return {string} Template with parsed attributes.\n * @throws {SyntaxError} If the template does not have a single root element or is a container component.\n * @private\n */\nconst parseElements = (template, expressions, elements) => {\n const PH = Component.PLACEHOLDER('(?:\\\\d+)');\n // Check if template is a container (single placeholder, no tag).\n const containerMatch = template.match(new RegExp(`^\\\\s*${PH}\\\\s*$`));\n if (containerMatch) return template;\n\n // Validate that template has a root element.\n const rootElementMatch = template.match(new RegExp(`^\\\\s*<([a-z]+[1-6]?|${PH})([^>]*)>([\\\\s\\\\S]*?)</(\\\\1|${PH})>\\\\s*$|^\\\\s*<([a-z]+[1-6]?|${PH})([^>]*)/>\\\\s*$`));\n\n if (!rootElementMatch) {\n const message = __DEV__ ?\n createDevelopmentErrorMessage(\n 'Invalid component template structure.\\n' +\n 'The template must have a single root element or render a single component.\\n\\n' +\n 'Valid examples:\\n' +\n '- `<div>content</div>`\\n' +\n '- `<${MyComponent} />`\\n\\n' +\n 'Invalid examples:\\n' +\n '- `<div></div><div></div>` (multiple root elements)\\n' +\n '- `text <div></div>` (text outside root element)'\n ) :\n createProductionErrorMessage('Invalid component template');\n throw new Error(message);\n }\n\n let elementUid = 0;\n // Match all HTML elements including placeholders and self-closed elements.\n return replaceElements(rootElementMatch[0], (match, tag, attributesStr, ending) => {\n const isRoot = elementUid === 0;\n const currentElementUid = ++elementUid;\n // If there are no dynamic attributes, return original match.\n if (!isRoot && !attributesStr.match(new RegExp(PH))) {\n return match;\n }\n // Parse attributes.\n const parsedAttributes = parseAttributes(attributesStr, expressions);\n // Create element reference.\n const generateElementUid = componentUid => `${componentUid}-${currentElementUid}`;\n // Create function that returns attributes object.\n const getAttributes = function() {\n // Expand attributes and events.\n const attributes = expandEvents(\n expandAttributes(parsedAttributes, value => getExpressionResult(value, this, 'element attribute')),\n this.eventsManager,\n type => Component.ATTRIBUTE_EVENT(type, this.uid)\n );\n // Extend template attributes with `options.attributes`.\n if (isRoot && this.attributes) {\n Object.assign(attributes, getResult(this.attributes, this));\n }\n // Add data attribute for element identification.\n // First element gets the component uid, others get element uid.\n attributes[Component.ATTRIBUTE_ELEMENT] = generateElementUid(this.uid);\n\n return attributes;\n };\n\n const getSelector = function() {\n return `[${Component.ATTRIBUTE_ELEMENT}=\"${generateElementUid(this.uid)}\"]`;\n };\n const elementIndex = elements.length;\n // Add element reference to elements array.\n elements.push({\n getSelector,\n getAttributes,\n });\n // Add new expression to expressions array.\n expressions.push(function() {\n const element = this.template.elements[elementIndex];\n const attributes = element.getAttributes.call(this);\n element.previousAttributes = attributes;\n return Component.markAsSafeHTML(getAttributesHTML(attributes));\n });\n // Replace attributes with placeholder.\n const placeholder = Component.PLACEHOLDER(expressions.length - 1);\n // Preserve original tag ending (> or />)\n return `<${tag} ${placeholder}${ending}`;\n });\n};\n\n/**\n * Parse elements in partial template.\n * @param {string} template Template string with placeholders.\n * @param {Array} expressions Array of expressions.\n * @return {string} Template with parsed attributes.\n * @private\n */\nconst parsePartialElements = (template, expressions) => {\n const PH = Component.PLACEHOLDER('(?:\\\\d+)');\n // Match all HTML elements including placeholders and self-closed elements.\n return replaceElements(template, (match, tag, attributesStr, ending) => {\n // If there are no dynamic attributes, return original match.\n if (!attributesStr.match(new RegExp(PH))) {\n return match;\n }\n // Parse attributes.\n const parsedAttributes = parseAttributes(attributesStr, expressions);\n // Create function that returns attributes object.\n const getAttributes = function() {\n const attributes = expandEvents(\n expandAttributes(parsedAttributes, value => getExpressionResult(value, this, 'partial element attribute')),\n this.eventsManager,\n type => Component.ATTRIBUTE_EVENT(type, this.uid)\n );\n\n return attributes;\n };\n // Add new expression to expressions array.\n expressions.push(function() {\n const attributes = getAttributes.call(this);\n return Component.markAsSafeHTML(getAttributesHTML(attributes));\n });\n // Replace attributes with placeholder.\n const placeholder = Component.PLACEHOLDER(expressions.length - 1);\n // Preserve original tag ending (> or />)\n return `<${tag} ${placeholder}${ending}`;\n });\n};\n\n/**\n * Parse all interpolations in template text content.\n * @param {string} template Template string with placeholders.\n * @param {Array} expressions Array of expressions.\n * @param {Array} interpolations Array to store interpolation references.\n * @return {string} Template with interpolation markers.\n * @private\n */\nconst parseInterpolations = (template, expressions, interpolations) => {\n const PH = Component.PLACEHOLDER('(\\\\d+)');\n let interpolationUid = 0;\n // Match all expression placeholders.\n return template.replace(\n new RegExp(PH, 'g'),\n function(match, expressionIndex, offset) {\n // Check if this placeholder is inside an element tag (attribute).\n // `offset` is the index of the match in the original string.\n const beforeMatch = template.substring(0, offset);\n const lastOpenTag = beforeMatch.lastIndexOf('<');\n const lastCloseTag = beforeMatch.lastIndexOf('>');\n // If we're inside an element tag, don't process as interpolation.\n if (lastOpenTag > lastCloseTag) {\n return match;\n }\n\n const currentInterpolationUid = ++interpolationUid;\n\n function getStart() {\n return Component.MARKER_START(`${this.uid}-${currentInterpolationUid}`);\n }\n function getEnd() {\n return Component.MARKER_END(`${this.uid}-${currentInterpolationUid}`);\n }\n const interpolationIndex = interpolations.length;\n // Add interpolation reference to interpolations array.\n interpolations.push({\n getStart,\n getEnd,\n expression : expressions[expressionIndex]\n });\n // Add new expression to expressions array.\n expressions.push(function() {\n return this.template.interpolations[interpolationIndex];\n });\n // Replace with new placeholder.\n return Component.PLACEHOLDER(expressions.length - 1);\n }\n );\n};\n\n/**\n * Parse attributes string to extract dynamic attributes.\n * @param {string} attributesStr Attributes string from HTML element.\n * @param {Array} expressions Array of expressions.\n * @return {Array} Array of attribute pairs [key, value] or [key, value, hasQuotes].\n * @private\n */\nconst parseAttributes = (attributesStr, expressions) => {\n const PH = Component.PLACEHOLDER('(\\\\d+)');\n const attributes = [];\n // Parse attributes string with support for placeholders in both names and values.\n const regExp = new RegExp(`(?:${PH}|([\\\\w-]+))(?:=([\"']?)(?:${PH}|((?:.?(?![\"']?\\\\s+(?:\\\\S+)=|\\\\s*/>|\\\\s*[>\"']))+.))?\\\\3)?`, 'g');\n\n let attributeMatch;\n while ((attributeMatch = regExp.exec(attributesStr)) !== null) {\n const [, attributeIdx, attribute, quotes, valueIdx, value] = attributeMatch;\n\n const hasQuotes = !!quotes;\n\n let attr = typeof attributeIdx !== 'undefined' ? expressions[parseInt(attributeIdx, 10)] : attribute;\n let val = typeof valueIdx !== 'undefined' ? expressions[parseInt(valueIdx, 10)] : value;\n\n if (hasQuotes && typeof val === 'undefined') {\n val = '';\n }\n\n if (typeof val !== 'undefined') {\n attributes.push([attr, val, hasQuotes]);\n } else {\n attributes.push([attr]);\n }\n }\n\n return attributes;\n};\n\n/*\n * These option keys will be extended on the component instance.\n */\nconst componentOptions = ['key', 'state', 'onCreate', 'onChange', 'onHydrate', 'onBeforeRecycle', 'onRecycle', 'onBeforeUpdate', 'onUpdate'];\n\n/**\n * @lends module:Component\n */\nclass Component extends View {\n constructor(options = {}) {\n super(...arguments);\n this.componentOptions = [];\n // Extend \"this\" with options. An option overrides a getter declared by a\n // subclass even when the getter has no setter.\n componentOptions.forEach(key => {\n if (key in options) {\n assignOption(this, key, options[key]);\n this.componentOptions.push(key);\n }\n });\n // Extract props from options that aren't component or view options.\n const props = {};\n Object.keys(options).forEach(key => {\n if (this.viewOptions.indexOf(key) === -1 && this.componentOptions.indexOf(key) === -1) {\n props[key] = options[key];\n }\n });\n // Store props as Model for reactive updates.\n this.props = new Model(props);\n // Store options by default.\n this.options = options;\n // Bind `partial` method to `this`.\n this.partial = this.partial.bind(this);\n // Expose `partial` as `html`, unless a subclass defines its own `html`.\n if (!('html' in this)) this.html = this.partial;\n // Bind `onChange` method to `this`.\n this.onChange = this.onChange.bind(this);\n // Call lifecycle method.\n this.onCreate.apply(this, arguments);\n }\n\n /**\n * Get events object for automatic event delegation, based on data attributes.\n * @return {object} The events object.\n * @private\n */\n events() {\n const events = {};\n // Create events object.\n this.eventsManager.types.forEach(type => {\n const dataAttribute = Component.ATTRIBUTE_EVENT(type, this.uid);\n // Create a listener function that gets the listener index from the data attribute and calls the listener.\n const listener = function(event, component, matched) {\n // Get the listener index from the data attribute.\n const index = matched.getAttribute(dataAttribute);\n // Root element listener may not have a data attribute.\n if (index) {\n let currentListener = this.eventsManager.listeners[parseInt(index, 10)];\n if (typeof currentListener === 'string') currentListener = this[currentListener];\n validateListener(currentListener);\n // Call the listener.\n currentListener.call(this, event, component, matched);\n }\n };\n // Add an event listener to the events object for each event type, using the data attribute\n // as both a CSS selector and to store the listener's index.\n events[`${type} [${dataAttribute}]`] = listener;\n // Add an event listener to the events object for each event type that matches the root element.\n events[type] = listener;\n });\n\n return events;\n }\n\n /**\n * Override super method. We don't want to ensure an element on instantiation.\n * We will provide it later.\n * @private\n */\n ensureElement() {\n // Store data event listeners.\n this.eventsManager = new EventsManager();\n // Call template function.\n this.template = getResult(this.template, this);\n // If el is provided, delegate events.\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 // Check if the element has a parent node.\n if (!this.el.parentNode) {\n const message = __DEV__ ?\n createDevelopmentErrorMessage(\n `Hydration failed in ${this.constructor.name}#${this.uid}\\n` +\n 'The element must have a parent node for hydration to work.\\n' +\n 'Make sure the element is mounted in the DOM before hydrating.'\n ) :\n createProductionErrorMessage(`Hydration failed in ${this.constructor.name}#${this.uid}`);\n throw new Error(message);\n }\n // Render the component as a string to generate children components.\n this.toString();\n // Hydrate the component.\n this.hydrate(this.el.parentNode);\n }\n }\n\n /**\n * Tell if `Component` is a container.\n * In which case, it will not have an element by itself.\n * It will render a single expression which is expected to return a single component as child.\n * `this.el` will be a reference to that child component's element.\n * @return {boolean}\n * @private\n */\n isContainer() {\n return this.template.elements.length === 0 && this.template.interpolations.length === 1;\n }\n\n /**\n * Subscribes to a `change` event on a model or emitter object and invokes the `onChange` lifecycle method.\n * The subscription is automatically cleaned up when the component is destroyed.\n * By default, the component subscribes to changes on `this.model`, `this.state`, and `this.props`.\n * \n * @param {Object} model - The model or emitter object to listen to.\n * @param {string} [type='change'] - The event type to listen for.\n * @param {Function} [listener=this.onChange] - The callback to invoke when the event is emitted.\n * @returns {Component} The current component instance for chaining.\n */\n subscribe(model, type = 'change', listener = this.onChange) {\n // Check if model has `on` method.\n if (model.on) this.listenTo(model, type, listener);\n return this;\n }\n\n /**\n * Used internally on the render process.\n * Attach the `Component` to the dom element providing `this.el`, delegate events, \n * subscribe to model changes and call `onHydrate` lifecycle method.\n * @param parent {node} The parent node.\n * @return {Component} The component instance.\n * @private\n */\n hydrate(parent) {\n ['model', 'state', 'props'].forEach(key => {\n if (this[key]) this.subscribe(this[key]);\n });\n\n if (this.isContainer()) {\n // Don't hydrate interpolation markers for container components as we know they will have only one child.\n // Call hydrate on children.\n this.children[0].hydrate(parent);\n // Set the first element as the component's element.\n this.el = this.children[0].el;\n } else {\n // Search for every element in template using getSelector\n this.template.elements.forEach((element, index) => {\n if (index === 0) {\n element.hydrate(parent);\n this.el = element.ref;\n }\n else {\n element.hydrate(this.el);\n }\n });\n // Get references for interpolation marker comments\n this.template.interpolations.forEach(interpolation => interpolation.hydrate(this.el));\n this.children.forEach(child => child.hydrate(this.el));\n }\n // Delegate events.\n this.delegateEvents();\n // Call `onHydrate` lifecycle method.\n this.onHydrate.call(this);\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Used internally on the render process.\n * Reuse a `Component` by replacing the placeholder comment with the real nodes.\n * Calls `onBeforeRecycle` lifecycle method at the beginning, before any recycling operations occur.\n * @param parent {node} The parent node. If not provided, the node is already in the correct position and won't be moved.\n * @return {Component} The component instance.\n * @private\n */\n recycle(parent) {\n // Call `onBeforeRecycle` lifecycle method.\n this.onBeforeRecycle.call(this);\n // No parent means the node is already in the correct position. So we don't need to replace it.\n if (parent) {\n // Locate the placeholder comment and replace it with the real nodes\n const placeholder = findComment(parent, Component.MARKER_RECYCLED(this.uid), isComponent);\n replaceNode(placeholder, this.el);\n }\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Update the component's props.\n * Sets the props and calls the `onRecycle` lifecycle method.\n * @param props {object} The props to set on the component.\n * @return {Component} The component instance.\n * @private\n */\n updateProps(props) {\n // Set the props.\n this.props.set(props);\n // Call `onRecycle` lifecycle method.\n this.onRecycle.call(this);\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Get a `comment` marker with same data attribute as this component.\n * Used to replace the component when it is recycled.\n * @return {string} The recycle placeholder.\n * @private\n */\n getRecycledMarker() {\n return `<!--${Component.MARKER_RECYCLED(this.uid)}-->`;\n }\n\n /**\n * Tagged template helper method.\n * Used to create a partial template. \n * It will return a Partial object that preserves structure for position-based recycling.\n * Components will be added as children by the parent component. Template strings literals \n * will be marked as safe HTML to be rendered.\n * This method is bound to the component instance by default, and is also available as `this.html`.\n * @param {TemplateStringsArray} strings - Template strings.\n * @param {...any} expressions - Template expressions.\n * @return {Partial} Partial object containing strings and expressions.\n * @example\n * import { Component } from 'rasti';\n * // Create a Title component.\n * const Title = Component.create`\n * <h1>${({ props }) => props.renderChildren()}</h1>\n * `;\n * // Create Main component.\n * const Main = Component.create`\n * <main>\n * ${self => self.renderHeader()}\n * </main>\n * `.extend({\n * // Render header method.\n * // Use `partial` to render an HTML template adding children components.\n * renderHeader() {\n * return this.partial`\n * <header>\n * <${Title}>${({ model }) => model.title}</${Title}>\n * </header>\n * `;\n * }\n * });\n */\n partial(strings, ...expressions) {\n const items = splitPlaceholders(\n parsePartialElements(\n expandComponents(\n addPlaceholders(\n strings,\n expressions\n ).trim(),\n expressions\n ),\n expressions\n ),\n expressions\n ).map(item => getExpressionResult(item, this, 'partial'));\n\n return new Partial(items);\n }\n\n /**\n * Render a template part.\n * @param {any} part - The template part.\n * @param {function} addChild - The addChild function. It should handle children and return a HTML string.\n * @param {Array} items - The items array. It is used to store the items that are parsed.\n * @return {string} The rendered template part.\n * @private\n */\n renderTemplatePart(part, addChild, tracker) {\n const item = getExpressionResult(part, this, 'template part');\n\n if (typeof item === 'undefined' || item === null || item === false || item === true) {\n return '';\n }\n\n if (item instanceof SafeHTML) {\n return `${item}`;\n }\n\n if (item instanceof Component) {\n return `${addChild(item, tracker)}`;\n }\n\n if (item instanceof Partial) {\n // Single item.\n if (item.items.length === 1) return this.renderTemplatePart(item.items[0], addChild, tracker);\n // Several items.\n tracker.push();\n const out = item.items.map(subItem => {\n tracker.increment();\n return this.renderTemplatePart(subItem, addChild, tracker);\n }).join('');\n tracker.pop();\n return out;\n }\n // Handle arrays (user loops) - disable tracking.\n if (Array.isArray(item)) {\n tracker.pause();\n const out = deepFlat(item).map(subItem => this.renderTemplatePart(subItem, addChild, tracker)).join('');\n tracker.resume();\n return out;\n }\n // Interpolation: add markers and process maintaining tracking.\n if (item instanceof Interpolation) {\n // Reset the tracker.\n const tracker = item.tracker;\n tracker.reset();\n // Add Interpolation markers.\n const startMarker = this.isContainer() ? '' : `<!--${item.getStart()}-->`;\n const endMarker = this.isContainer() ? '' : `<!--${item.getEnd()}-->`;\n return `${startMarker}${this.renderTemplatePart(item.expression, addChild, tracker)}${endMarker}`;\n }\n\n return `${Component.sanitize(item)}`;\n }\n\n /**\n * Render the component as a string.\n * Used internally on the render process. \n * Use it for server-side rendering or static site generation.\n * @return {string} The rendered component.\n * @example\n * import { Component } from 'rasti';\n * const Button = Component.create`\n * <button class=\"button\">Click me</button>\n * `;\n * const App = Component.create`\n * <div>\n * <${Button}>Click me</${Button}>\n * </div>\n * `;\n * \n * const app = new App();\n * \n * console.log(app.toString());\n * // <div data-rst-el=\"r1-1\"><!--rst-s-r1-1--><button class=\"button\" data-rst-el=\"r2-1\">Click me</button><!--rst-e-r1-1--></div>\n * \n * console.log(`${app}`);\n * // <div data-rst-el=\"r1-1\"><!--rst-s-r1-1--><button class=\"button\" data-rst-el=\"r2-1\">Click me</button><!--rst-e-r1-1--></div>\n */\n toString() {\n // Normally there won't be any children, but if there are, destroy them.\n this.destroyChildren();\n // Normally there won't be any data event listeners, but if there are, clear them.\n this.eventsManager.reset();\n // Bind addChild method.\n const addChild = (component, tracker) => {\n tracker.track(component);\n return this.addChild(component);\n };\n // Render the template parts.\n return this.template.parts\n .map(part => this.renderTemplatePart(part, addChild))\n .join('');\n }\n\n /**\n * Render the `Component`.\n * \n * **First render (when `this.el` is not present):**\n * This is the initial render call. The component will be rendered as a string inside a `DocumentFragment` and hydrated, \n * making `this.el` available. `this.el` is the root DOM element of the component that can be applied to the DOM. \n * The `onHydrate` lifecycle method will be called. \n * \n * **Note:** Typically, you don't need to call `render()` directly for the first render. The static method `Component.mount()` \n * handles this process automatically, creating the component instance, rendering it, and appending it to the DOM.\n * \n * **Update render (when `this.el` is present):**\n * This indicates the component is being updated. The method will:\n * - Update only the attributes of the root element and child elements\n * - Update only the content of interpolations (the dynamic parts of the template)\n * - For container components (components that render a single child component), update the single interpolation\n * \n * The `onBeforeUpdate` lifecycle method will be called at the beginning, followed by the `onUpdate` lifecycle method at the end.\n * \n * **Child component handling:**\n * When rendering child components, they can be either recreated or recycled:\n * \n * - **Recreation:** A new component instance is created, running the constructor again. This happens when no matching component \n * is found for recycling.\n * \n * - **Recycling:** The same component instance is reused. Recycling happens in two ways:\n * - Components with a `key` are recycled if a previous child with the same key exists\n * - Unkeyed components are recycled if they have the same type and position in the template or partial\n * \n * When a component is recycled:\n * - The `onBeforeRecycle` lifecycle method is called when recycling starts\n * - The component's `this.props` is updated with the new props from the parent\n * - The `onRecycle` lifecycle method is called after props are updated\n * \n * A recycled component may not use props at all and remain unchanged, or it may be subscribed to a different model \n * (or even the same model as the parent) and update independently in subsequent render cycles.\n * \n * @return {Component} The component instance.\n */\n render() {\n // Prevent a last re render if view is already destroyed.\n if (this.destroyed) return this;\n // If `this.el` is not present, render the view as a string and hydrate it.\n if (!this.el) {\n const fragment = parseHTML(this);\n this.hydrate(fragment);\n return this;\n }\n // Call `onBeforeUpdate` lifecycle method.\n this.onBeforeUpdate.call(this);\n // Clear event listeners.\n this.eventsManager.reset();\n // Store previous children.\n const previousChildren = this.children;\n // Clear current children.\n this.children = [];\n // Store props to update.\n const propsQueue = [];\n // Update interpolations.\n this.template.interpolations.forEach(interpolation => {\n // Reset the tracker.\n const tracker = interpolation.tracker;\n tracker.reset();\n\n const nextChildren = [];\n const recycledChildren = [];\n\n // `addChild` handler is called from `renderTemplatePart` for every component.\n // It should handle children and return a HTML string.\n // In this case, where the component updates, it handles children recycling.\n const addChild = (component) => {\n let out = component;\n let found;\n // Check if child already exists by key.\n if (component.key) {\n found = previousChildren.find(prev => prev.key === component.key);\n } else {\n // Find by position and type using tracker.\n found = tracker.findRecyclable(component);\n }\n\n if (found) {\n // If child already exists, replace it html by its root element.\n out = found.getRecycledMarker();\n // Add child to recycled children.\n recycledChildren.push([found, component]);\n // Track the component.\n tracker.track(found);\n } else {\n // Add new component.\n nextChildren.push(component);\n // Track the component.\n tracker.track(component);\n }\n // Return the component or placeholder.\n return out;\n };\n // Render the interpolation content.\n const rendered = this.renderTemplatePart(interpolation.expression, addChild, tracker);\n\n const recycle = ([recycled, discarded], fragment) => {\n // Store props to update.\n propsQueue.push([recycled, discarded.props.toJSON()]);\n // Add child and recycle (move to new position if needed).\n this.addChild(recycled).recycle(fragment);\n // Destroy discarded component.\n discarded.destroy();\n };\n // Same single component in the same position. Don't move it.\n if (tracker.hasSingleComponent()) {\n recycle(recycledChildren[0], null);\n return;\n }\n // Parse the rendered content and get the fragment.\n const fragment = parseHTML(rendered);\n\n const handleComponents = (parent) => () => {\n // Add recycled components to children and move them to the new template in the fragment.\n recycledChildren.forEach(recycled => recycle(recycled, parent));\n // Add new children. Hydrate them and update the interpolation.\n nextChildren.forEach(child => this.addChild(child).hydrate(parent));\n };\n\n if (this.isContainer()) {\n interpolation.updateElement(this.el, fragment, handleComponents(this.el.parentNode));\n } else {\n interpolation.update(fragment, handleComponents(this.el));\n }\n });\n // Destroy unused children.\n previousChildren.forEach(prev => {\n if (this.children.indexOf(prev) < 0) prev.destroy();\n });\n // Update recycled children props.\n propsQueue.forEach(([recycled, props]) => {\n recycled.updateProps(props);\n });\n // If this component is a container, set el to the child element.\n // Otherwise, update elements attributes and delegate events.\n if (this.isContainer()) {\n this.el = this.children[0].el;\n } else {\n // Update elements attributes.\n this.template.elements.forEach(element => element.update());\n }\n // If there are pending event types, delegate events again.\n if (this.eventsManager.hasPendingTypes()) {\n this.delegateEvents();\n }\n // Call onUpdate lifecycle method.\n this.onUpdate.call(this);\n // Return this for chaining.\n return this;\n }\n\n /**\n * Lifecycle method. Called when the component is created, at the end of the constructor.\n * This method receives the same arguments passed to the constructor (options and any additional parameters).\n * It executes both on client and server.\n * Use this method to define models or state that will be used later in `onHydrate`.\n * @param {...*} args The constructor arguments (options and any additional parameters).\n */\n onCreate() {}\n\n /**\n * Lifecycle method. Called when model emits `change` event.\n * By default calls `render` method.\n * This method can be extended with custom logic.\n * Maybe comparing new attributes with previous ones and calling\n * render when needed.\n * @param model {Model} The model that emitted the event.\n * @param changed {object} Object containing keys and values that has changed.\n * @param [...args] {any} Any extra arguments passed to set method.\n */\n onChange() {\n this.render();\n }\n\n /**\n * Lifecycle method. Called when the component is rendered for the first time and hydrated in a DocumentFragment.\n * This method only executes on the client and only during the first render.\n * Use this method for client-only operations like making API requests or setting up browser-specific functionality.\n */\n onHydrate() {}\n\n /**\n * Lifecycle method. Called before the component is recycled and reused between renders.\n * This method is called at the beginning of the `recycle` method, before any recycling operations occur.\n * \n * A component is recycled when:\n * - It has a `key` and a previous child with the same key exists\n * - It doesn't have a `key` but has the same type and position in the template or partial\n * \n * Use this method to perform operations that need to happen before the component is recycled,\n * such as storing previous state or preparing for the recycling.\n */\n onBeforeRecycle() {}\n\n /**\n * Lifecycle method. Called when the component is recycled and reused between renders.\n * \n * A component is recycled when:\n * - It has a `key` and a previous child with the same key exists\n * - It doesn't have a `key` but has the same type and position in the template or partial\n * \n * During recycling, the component instance is reused and its props are updated with new values.\n * The component's element may be moved in the DOM if the new template structure differs from the previous one.\n */\n onRecycle() {}\n\n /**\n * Lifecycle method. Called before the component is updated or re-rendered.\n * This method is called at the beginning of the `render` method when the component's state, model, or props change and trigger a re-render.\n * Use this method to perform operations that need to happen before the component is updated,\n * such as saving previous state or preparing for the update.\n */\n onBeforeUpdate() {}\n\n /**\n * Lifecycle method. Called when the component is updated or re-rendered.\n * This method is called when the component's state, model, or props change and trigger a re-render.\n * Use this method to perform operations that need to happen on every update.\n */\n onUpdate() {}\n\n /**\n * Lifecycle method. Called when the component is destroyed.\n * Use this method to clean up resources, cancel timers, remove event listeners, etc.\n * @param {...*} args Options object or any arguments passed to `destroy` method.\n */\n onDestroy() {}\n\n /**\n * Mark a string as safe HTML to be rendered. \n * Normally you don't need to use this method, as Rasti will automatically mark string literals \n * as safe HTML when the component is {@link #module_component_create created} and when \n * using the {@link #module_component__partial Component.partial} method. \n * Be sure that the string is safe to be rendered, as it will be inserted into the DOM without any sanitization.\n * @static\n * @param {string} value \n * @return {SafeHTML} A safe HTML object.\n */\n static markAsSafeHTML(value) {\n return new SafeHTML(value);\n }\n\n /**\n * Helper method used to extend a `Component`, creating a subclass.\n * @static\n * @param {object|Function} object Object containing methods to be added to the new `Component` subclass. Also can be a function that receives the parent prototype and returns an object.\n */\n static extend(object) {\n const Current = this;\n\n class Component extends Current {}\n\n Object.assign(\n Component.prototype,\n typeof object === 'function' ? object(Current.prototype) : object\n );\n\n return Component;\n }\n\n /**\n * Mount the component into the DOM.\n * Creates a new component instance with the provided options and optionally mounts it into the DOM.\n * \n * **Mounting modes:**\n * - **Normal mount** (default): Renders the component as HTML and appends it to the provided element. Use this for client-side rendering.\n * - **Hydration mode**: Assumes the DOM already contains the component's HTML (from server-side rendering). \n * \n * If `el` is not provided, the component is instantiated but not mounted (the same as using `new Component(options)`). You can mount it later by calling `render()` and appending the element (`this.el`) to the DOM.\n * \n * @static\n * @param {object} [options={}] The component options. These will be passed to the constructor and can include \n * `model`, `state`, `props`, lifecycle methods, and any other component-specific options.\n * @param {node} [el] The DOM element where the component will be mounted. If provided, the component will be \n * rendered and appended to this element. If not provided, the component is created but not mounted.\n * @param {boolean} [hydrate=false] If `true`, enables hydration mode for server-side rendering. The component will \n * assume the DOM already contains its HTML structure and will only hydrate it.\n * If `false` (default), the component will be rendered from scratch and appended to `el`.\n * @return {Component} The component instance.\n * @example\n * import { Component, Model } from 'rasti';\n * \n * const Button = Component.create`\n * <button class=\"${({ props }) => props.className}\">\n * ${({ props }) => props.label}\n * </button>\n * `;\n * \n * // Normal mount: render and append to DOM.\n * const button = Button.mount({\n * label: 'Click me'\n * }, document.body);\n * \n * // Create without mounting (mount later).\n * const button2 = Button.mount({ className : 'secondary', label : 'Save' });\n * // Later, render and append it to the DOM.\n * document.body.appendChild(button2.render().el);\n * \n * // Hydration mode: hydrate existing server-rendered HTML\n * // Assuming document.body already contains the HTML structure of the button.\n * const hydratedButton = Button.mount({\n * className : 'primary',\n * label : 'Click me'\n * }, document.body, true);\n */\n static mount(options, el, hydrate) {\n // Instantiate component.\n const component = new this(options);\n // If `el` is passed, mount component.\n if (el) {\n if (hydrate) {\n // Hydrate existing DOM, only generate subcomponents calling `toString`.\n component.toString();\n } else {\n // Render the component and append it to the provided element.\n el.append(parseHTML(component));\n }\n // Hydrate in both cases.\n component.hydrate(el);\n }\n // Return component instance.\n return component;\n }\n\n /**\n * Takes a tagged template string or a function that returns another component, and returns a new `Component` class.\n * - The template outer tag and attributes will be used to create the view's root element.\n * - The template inner HTML will be used as the view's template.\n * ```javascript\n * const Button = Component.create`<button class=\"button\">Click me</button>`;\n * ```\n * - Template interpolations that are functions will be evaluated during the render process, receiving the view instance as an argument and being bound to it. If the function returns `null`, `undefined`, `false`, or an empty string, the interpolation won't render any content.\n * ```javascript\n * const Button = Component.create`\n * <button class=\"${({ props }) => props.className}\">\n * ${({ props }) => props.renderChildren()}\n * </button>\n * `;\n * ```\n * - Attach DOM event handlers per element using camel-cased attributes.\n * Event handlers are automatically bound to the component instance (`this`).\n * Internally, Rasti uses event delegation to the component's root element for performance.\n * \n * **Attribute Quoting:**\n * - **Quoted attributes** (`onClick=\"${handler}\"`) evaluate the expression first, useful for dynamic values\n * - **Unquoted attributes** (`onClick=${handler}`) pass the function reference directly\n * \n * **Listener Signature:** `(event, component, matched)`\n * - `event`: The native DOM event object\n * - `component`: The component instance (same as `this`)\n * - `matched`: The element that matched the event (useful for delegation)\n * \n * ```javascript\n * const Button = Component.create`\n * <button \n * onClick=${function(event, component, matched) {\n * // this === component\n * console.log('Button clicked:', matched);\n * }}\n * onMouseOver=\"${({ model }) => () => model.isHovered = true}\"\n * onMouseOut=\"${({ model }) => () => model.isHovered = false}\"\n * >\n * Click me\n * </button>\n * `;\n * ```\n * \n * If you need custom delegation (e.g., `{'click .selector': 'handler'}`), \n * you may override the `events` property as described in {@link #module_view__delegateevents View.delegateEvents}.\n * - Boolean attributes should be passed in the format `attribute=\"${() => true}\"`. `false` attributes won't be rendered. `true` attributes will be rendered without a value.\n * ```javascript\n * const Input = Component.create`\n * <input type=\"text\" disabled=${({ props }) => props.disabled} />\n * `;\n * ```\n * - If the interpolated function returns a component instance, it will be added as a child component.\n * - If the interpolated function returns an array, each item will be evaluated as above.\n * ```javascript\n * // Create a button component.\n * const Button = Component.create`\n * <button class=\"button\">\n * ${({ props }) => props.renderChildren()}\n * </button>\n * `;\n * // Create a navigation component. Add buttons as children. Iterate over items.\n * const Navigation = Component.create`\n * <nav>\n * ${({ props }) => props.items.map(\n * item => Button.mount({ renderChildren : () => item.label })\n * )}\n * </nav>\n * `;\n * // Create a header component. Add navigation as a child.\n * const Header = Component.create`\n * <header>\n * ${({ props }) => Navigation.mount({ items : props.items})}\n * </header>\n * `;\n * ```\n * - Child components can be added using a component tag.\n * ```javascript\n * // Create a button component.\n * const Button = Component.create`\n * <button class=\"button\">\n * ${({ props }) => props.renderChildren()}\n * </button>\n * `;\n * // Create a navigation component. Add buttons as children. Iterate over items.\n * const Navigation = Component.create`\n * <nav>\n * ${({ props, html }) => props.items.map(\n * item => html`<${Button}>${item.label}</${Button}>`\n * )}\n * </nav>\n * `;\n * // Create a header component. Add navigation as a child.\n * const Header = Component.create`\n * <header>\n * <${Navigation} items=\"${({ props }) => props.items}\" />\n * </header>\n * `;\n * ```\n * - If the tagged template contains only one expression that mounts a component, or the tags are references to a component, the component will be considered a <b>container</b>. It will render a single component as a child. `this.el` will be a reference to that child component's element.\n * ```javascript\n * // Create a button component.\n * const Button = Component.create`\n * <button class=\"${({ props }) => props.className}\">\n * ${({ props }) => props.renderChildren()}\n * </button>\n * `;\n * // Create a container that renders a Button component.\n * const ButtonOk = Component.create`\n * <${Button} className=\"ok\">Ok</${Button}>\n * `;\n * // Create a container that renders a Button component, using a function.\n * const ButtonCancel = Component.create(() => Button.mount({\n * className : 'cancel',\n * renderChildren : () => 'Cancel'\n * }));\n * ```\n * - Called on a subclass, the new component extends it, so the template can use its methods.\n * ```javascript\n * class ListBase extends Component {\n * renderItems() {\n * return this.props.items.map(item => this.html`<li>${item}</li>`);\n * }\n * }\n * const List = ListBase.create`\n * <ul>${(self) => self.renderItems()}</ul>\n * `;\n * ```\n * @static\n * @param {string|Function} strings - HTML template for the component or a function that mounts a sub component.\n * @param {...*} expressions - The expressions to be interpolated within the template.\n * @return {Component} The newly created component class.\n */\n static create(strings, ...expressions) {\n // Containers can be created using create as a functions instead of a tagged template.\n if (typeof strings === 'function') {\n expressions = [strings];\n strings = ['', ''];\n }\n // Store original template source for debugging (only in dev mode).\n const source = __DEV__ ? { strings, expressions : [...expressions] } : null;\n // Create elements, interpolations and parts arrays.\n const elements = [], interpolations = [];\n const parts = splitPlaceholders(\n parseInterpolations(\n parseElements(\n expandComponents(\n addPlaceholders(\n strings,\n expressions\n ).trim(),\n expressions\n ),\n expressions,\n elements\n ),\n expressions,\n interpolations\n ),\n expressions\n );\n // Create subclass for this component.\n return this.extend({\n source,\n template() {\n return {\n elements : elements.map(element => new Element({\n getSelector : element.getSelector.bind(this),\n getAttributes : element.getAttributes.bind(this)\n })),\n interpolations : interpolations.map(interpolation => new Interpolation({\n getStart : interpolation.getStart.bind(this),\n getEnd : interpolation.getEnd.bind(this),\n expression : interpolation.expression,\n shouldSkipFind : isComponent,\n shouldSkipSync : containsElement\n })),\n parts,\n };\n }\n });\n }\n}\n\n/*\n * Attributes used to identify elements and events.\n */\nComponent.ATTRIBUTE_ELEMENT = 'data-rst-el';\nComponent.ATTRIBUTE_EVENT = (type, uid) => `data-rst-on-${type}-${uid}`;\n\n/*\n * Dataset attribute used to identify elements.\n */\nComponent.DATASET_ELEMENT = 'rstEl';\n\n/*\n * Placeholders used to temporarily replace expressions in the template.\n */\nComponent.PLACEHOLDER = (idx) => `__RASTI_PLACEHOLDER_${idx}__`;\n\n/*\n * Markers used to identify interpolation and recycled components.\n */\nComponent.MARKER_RECYCLED = (uid) => `rst-r-${uid}`;\nComponent.MARKER_START = (uid) => `rst-s-${uid}`;\nComponent.MARKER_END = (uid) => `rst-e-${uid}`;\n\n/**\n * Components are a special kind of `View` that is designed to be easily composable, \n * making it simple to add child views and build complex user interfaces. \n * Unlike views, which are render-agnostic, components have a specific set of rendering \n * guidelines that allow for a more declarative development style. \n * Components are defined with the {@link #module_component_create Component.create} static method, which takes a tagged template string or a function that returns another component.\n * @module\n * @extends View\n * @param {object} options Object containing options. The following keys will be merged to `this`: model, state, key, onDestroy, onHydrate, onBeforeRecycle, onRecycle, onBeforeUpdate, onUpdate, onCreate, onChange. Any additional options not in the component or view options list will be automatically extracted as props and stored as `this.props`.\n * @property {string} [key] A unique key to identify the component. Components with keys are recycled when the same key is found in the previous render. Unkeyed components are recycled based on type and position.\n * @property {Model} [model] A `Model` or any emitter object containing data and business logic. The component will listen to `change` events and call `onChange` lifecycle method.\n * @property {Model} [state] A `Model` or any emitter object containing data and business logic, to be used as internal state. The component will listen to `change` events and call `onChange` lifecycle method.\n * @property {Model} [props] Automatically created from any options not merged to the component instance. Contains props passed from parent component as a `Model`. The component will listen to `change` events on props and call `onChange` lifecycle method. When a component with a `key` is recycled during parent re-render, new props are automatically updated and any changes trigger a re-render.\n * @property {Function} html Alias of {@link #module_component__partial partial}, bound to the component instance. Not set when a subclass defines its own `html`.\n * @see {@link #module_component_create Component.create}\n * @example\n * import { Component, Model } from 'rasti';\n * // Create Timer component.\n * const Timer = Component.create`\n * <div>\n * Seconds: <span>${({ model }) => model.seconds}</span>\n * </div>\n * `;\n * // Create model to store seconds.\n * const model = new Model({ seconds : 0 });\n * // Mount timer on body.\n * Timer.mount({ model }, document.body);\n * // Increment `model.seconds` every second.\n * setInterval(() => model.seconds++, 1000);\n */\nexport default Component.create`<div></div>`;\n"],"names":["validateListener","listener","TypeError","Emitter","on","type","this","listeners","push","off","once","wrapper","args","filter","fn","length","Object","keys","emit","slice","forEach","listenTo","emitter","listeningTo","stopListening","listenToOnce","item","getResult","expression","context","apply","Model","constructor","super","preinitialize","arguments","attributes","assign","defaults","parse","previous","defineAttribute","bind","key","defineProperty","attributePrefix","get","set","value","rest","attrs","changing","_changing","changed","changedKeys","_pending","pendingChange","data","toJSON","assignOption","target","error","writable","enumerable","configurable","viewOptions","View","options","delegatedEventListeners","children","destroyQueue","ensureUid","ensureElement","$","selector","el","querySelector","$$","querySelectorAll","destroy","destroyChildren","undelegateEvents","onDestroy","destroyed","addChild","child","uid","tag","createElement","delegateEvents","document","setAttribute","removeElement","parentNode","removeChild","events","eventTypes","match","Error","typeListener","event","node","matches","call","cancelBubble","parentElement","addEventListener","removeEventListener","render","sanitize","replace","resetUid","SafeHTML","toString","Partial","items","EventsManager","types","Set","previousSize","addListener","add","reset","size","hasPendingTypes","SYNC_PROPS","Element$1","getSelector","getAttributes","previousAttributes","hydrate","parent","ref","update","remove","getAttributesDiff","attr","removeAttribute","indexOf","PathManager","paused","tracked","Map","positionStack","pop","increment","pause","resume","getPath","join","track","component","hasSingleComponent","currentPath","currentComponent","entries","next","previousPath","previousComponent","findRecyclable","candidate","syncNode","targetNode","sourceNode","nodeType","Node","TEXT_NODE","tagName","targetEl","sourceEl","srcAttrs","tgtAttrs","srcAttrNames","i","l","name","getAttribute","has","prop","syncAttributes","a","b","aChildren","childNodes","bChildren","aLength","isEqualNode","areChildNodesEqual","Array","from","replaceChildren","syncNodeContent","replaceWith","nodeValue","findComment","root","text","shouldSkip","startNode","firstChild","COMMENT_NODE","trim","ELEMENT_NODE","nextSibling","Interpolation","getStart","getEnd","shouldSkipFind","shouldSkipSync","tracker","startMark","endMark","fragment","handleComponents","divider","currentFirstElement","currentEmpty","currentSingleChildElement","insertBefore","createComment","range","createRange","setStartAfter","setEndAfter","deleteContents","updateElement","element","deepFlat","arr","reduce","acc","val","isArray","parseHTML","html","innerHTML","content","VALID_ATTRIBUTE_NAME","getAttributesHTML","test","escapeAttributeValue","isChrome","moveBeforeSupported","preserveFocus","resetFocus","navigator","userAgent","Element","prototype","moveBefore","getExpressionResult","meta","_rasti","message","enhancedError","cause","isComponent","dataset","Component","DATASET_ELEMENT","endsWith","containsElement","ATTRIBUTE_ELEMENT","addPlaceholders","strings","expressions","out","string","PLACEHOLDER","splitPlaceholders","main","PH","matchSinglePlaceholder","RegExp","parseInt","regExp","lastIndex","exec","before","index","markAsSafeHTML","expandAttributes","pair","attribute","expandEvents","eventsManager","getDataAttribute","toLowerCase","expandComponents","skipNormalization","componentRefMap","idx","selfClosingTag","selfClosingIdx","selfClosingAttrs","openTag","openIdx","nonVoidAttrs","inner","attributesStr","innerList","innerTemplate","parsedInner","parsePartialElements","parseAttributes","renderChildren","map","mount","replaceElements","template","replacer","parseElements","elements","rootElementMatch","elementUid","ending","isRoot","currentElementUid","parsedAttributes","generateElementUid","componentUid","elementIndex","ATTRIBUTE_EVENT","parseInterpolations","interpolations","interpolationUid","expressionIndex","offset","beforeMatch","substring","lastIndexOf","currentInterpolationUid","interpolationIndex","MARKER_START","MARKER_END","attributeMatch","attributeIdx","quotes","valueIdx","hasQuotes","componentOptions","props","partial","onChange","onCreate","dataAttribute","matched","currentListener","isContainer","subscribe","model","interpolation","onHydrate","recycle","onBeforeRecycle","oldNode","newNode","activeElement","contains","blur","focus","replaceNode","MARKER_RECYCLED","updateProps","onRecycle","getRecycledMarker","renderTemplatePart","part","subItem","startMarker","endMarker","parts","onBeforeUpdate","previousChildren","propsQueue","nextChildren","recycledChildren","rendered","found","find","prev","recycled","discarded","onUpdate","extend","object","Current","append","create","source","Component_default"],"mappings":"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"}
|