rasti 2.0.0 → 2.0.2
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 +27 -0
- package/dist/rasti.js +1 -1
- package/dist/rasti.min.js +1 -1
- package/es/Component.js +32 -24
- package/lib/Component.js +32 -24
- package/package.json +1 -1
- package/src/Component.js +36 -27
package/README.md
CHANGED
|
@@ -63,6 +63,33 @@ setInterval(() => model.seconds++, 1000);
|
|
|
63
63
|
|
|
64
64
|
[Try it on CodePen](https://codepen.io/8tentaculos/pen/gOQxaOE?editors=0010)
|
|
65
65
|
|
|
66
|
+
### Adding sub components
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
// Create Button component.
|
|
70
|
+
const Button = Component.create`
|
|
71
|
+
<button
|
|
72
|
+
onClick="${{ '&' : function() { this.options.onClick() } }}"
|
|
73
|
+
>
|
|
74
|
+
${({ options }) => options.label}
|
|
75
|
+
</button>
|
|
76
|
+
`;
|
|
77
|
+
// Create Counter component.
|
|
78
|
+
const Counter = Component.create`
|
|
79
|
+
<div>
|
|
80
|
+
${({ model }) => Button.mount({ label : '-', onClick : () => model.count-- })}
|
|
81
|
+
<span>${({ model }) => model.count}</span>
|
|
82
|
+
${({ model }) => Button.mount({ label : '+', onClick : () => model.count++ })}
|
|
83
|
+
</div>
|
|
84
|
+
`;
|
|
85
|
+
// Create model to store count.
|
|
86
|
+
const model = new Model({ count: 0 });
|
|
87
|
+
// Mount counter on body.
|
|
88
|
+
Counter.mount({ model }, document.body);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
[Try it on CodePen](https://codepen.io/8tentaculos/pen/ZEZarEQ?editors=0010)
|
|
92
|
+
|
|
66
93
|
## Example
|
|
67
94
|
|
|
68
95
|
The rasti [GitHub repository](https://github.com/8tentaculos/rasti) includes, in the [example folder](https://github.com/8tentaculos/rasti/tree/master/example/todo), an example [TODO application](http://rasti.js.org/example/todo/index.html) that can be used as starter project.
|
package/dist/rasti.js
CHANGED
|
@@ -26,7 +26,7 @@ return /******/ (() => { // webpackBootstrap
|
|
|
26
26
|
\**************************/
|
|
27
27
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
28
28
|
|
|
29
|
-
eval("\n\nexports.__esModule = true;\nexports[\"default\"] = void 0;\nvar _View2 = _interopRequireDefault(__webpack_require__(/*! ./View.js */ \"./src/View.js\"));\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\nfunction _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); }\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n// This options keys will be extended on view instance.\nvar componentOptions = {\n key: true,\n state: true,\n onCreate: true,\n onChange: true,\n onRender: true\n};\n\n/*\n * Helper function. Extract attributes from html tag\n * @param text {string} html text\n * @return {object} Object with keys / values representing attributes.\n */\nvar extractAttributes = function extractAttributes(text) {\n var attributes = {};\n var re = /([\\w|data-]+)(?:=[\"']?((?:.(?![\"']?\\s+(?:\\S+)=|\\s*\\/?[>\"']))+.)[\"']?)?/g;\n var result;\n while ((result = re.exec(text)) !== null) {\n attributes[result[1]] = typeof result[2] === 'undefined' ? true : result[2];\n }\n return attributes;\n};\n\n/*\n * Helper function. If first arg is a placeholder for an expression, return the expression.\n */\nvar getExpression = function getExpression(placeholder, expressions) {\n var match = placeholder && placeholder.match && placeholder.match(new RegExp(Component.EXPRESSION_PLACEHOLDER_TEMPLATE('(\\\\d+)')));\n return match && match[1] ? expressions[match[1]] : placeholder;\n};\n\n/*\n * Helper function. If expression is a function, call it with context and args.\n */\nvar evalExpression = function evalExpression(expression, context) {\n for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {\n args[_key - 2] = arguments[_key];\n }\n return typeof expression === 'function' ? expression.apply(context, args) : expression;\n};\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 `create` static method, which takes a tagged template.\n * @module\n * @extends Rasti.View\n * @param {object} options Object containing options. The following keys will be merged to `this`: model, state, key, onDestroy, onRender, onCreate, onChange.\n * @property {string} key A unique key to identify the component. Used to recycle child components.\n * @property {object} model A `Rasti.Model` or any emitter object containing data and business logic.\n * @property {object} state A `Rasti.Model` or any emitter object containing data and business logic, to be used as internal state.\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 */\nvar Component = exports[\"default\"] = /*#__PURE__*/function (_View) {\n function Component() {\n var _this;\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _this = _View.apply(this, arguments) || this;\n // Extend \"this\" with options, mapping componentOptions keys.\n Object.keys(options).forEach(function (key) {\n if (componentOptions[key]) _this[key] = options[key];\n });\n // Store options by default.\n _this.options = options;\n // Bind onChange to this to be used as listener.\n // Store bound version, so it can be removed on onDestroy method.\n _this.onChange = _this.onChange.bind(_this);\n // Listen to model changes and call onChange.\n if (_this.model && _this.model.on) _this.model.on('change', _this.onChange);\n if (_this.state && _this.state.on) _this.state.on('change', _this.onChange);\n // Call life cycle method.\n _this.onCreate.apply(_this, arguments);\n return _this;\n }\n\n /*\n * Override. We don't want to ensure an element on instantiation.\n * We will provide it later.\n */\n _inheritsLoose(Component, _View);\n var _proto = Component.prototype;\n _proto.ensureElement = function ensureElement() {\n // If el is provided, delegate events.\n if (this.el) {\n this.delegateEvents();\n this.id = this.el.id;\n }\n // Ensure id.\n if (!this.id) {\n this.id = this.attributes && this.attributes.id ?\n // If id is provided, evaluate it.\n evalExpression(this.attributes.id, this, this) :\n // Generate a unique id and set it as id attribute.\n Component.ID_TEMPLATE(this.uid);\n }\n }\n\n /*\n * Find view's element on parent node, using id.\n */;\n _proto.findElement = function findElement(parent) {\n return (parent || document).querySelector(\"#\" + this.id);\n }\n\n /*\n * Eval attributes expressions.\n */;\n _proto.getAttributes = function getAttributes() {\n var _this2 = this;\n var add = {\n id: this.id\n };\n var remove = {};\n var html = [\"id=\\\"\" + this.id + \"\\\"\"];\n if (this.attributes) {\n Object.keys(this.attributes).forEach(function (key) {\n if (key === 'id') return;\n // Evaluate attribute value.\n var value = evalExpression(_this2.attributes[key], _this2, _this2);\n\n // Transform bool attribute values\n if (value === false) {\n remove[key] = true;\n } else if (value === true) {\n add[key] = '';\n html.push(key);\n } else {\n if (value === null || typeof value === 'undefined') value = '';\n add[key] = value;\n html.push(key + \"=\\\"\" + value + \"\\\"\");\n }\n });\n }\n return {\n add: add,\n remove: remove,\n html: html.join(' ')\n };\n }\n\n /*\n * Used internally on the render process.\n * Attach the view to the dom element.\n */;\n _proto.hydrate = function hydrate(parent) {\n var _this3 = this;\n this.el = this.findElement(parent);\n this.delegateEvents();\n this.children.forEach(function (child) {\n return child.hydrate(_this3.el);\n });\n this.onRender.call(this, 'hydrate');\n }\n\n /*\n * Used internally on the render process.\n * Reuse a view that has `key` when its parent is rendered.\n */;\n _proto.recycle = function recycle(parent) {\n // Find element to be replaced. It has same id.\n var toBeReplaced = this.findElement(parent);\n // Replace it with this.el.\n toBeReplaced.replaceWith(this.el);\n // Call `onRender` lifecycle method.\n this.onRender.call(this, 'recycle');\n }\n\n /*\n * Override. Add some custom logic to super `destroy` method.\n */;\n _proto.destroy = function destroy() {\n _View.prototype.destroy.apply(this, arguments);\n // Stop listening to `change`.\n // Set destroyed flag to prevent a last render after destroyed. TODO: Review\n if (this.model && this.model.off) this.model.off('change', this.onChange);\n if (this.state && this.state.off) this.state.off('change', this.onChange);\n this.destroyed = true;\n }\n\n /**\n * Lifecycle method. Called when the view is created at the end of the constructor.\n * @param options {object} The view options.\n */;\n _proto.onCreate = function onCreate() {}\n\n /**\n * Lifecycle method. Called when model emits `change` event.\n * By default calls render method.\n * This method should be extended with custom logic.\n * Maybe comparing new attributes with previous ones and calling\n * render when needed. Or doing some dom transformation.\n * @param model {Rasti.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 _proto.onChange = function onChange() {\n this.render();\n }\n\n /**\n * Lifecycle method. Called when the view is rendered.\n * @param type {string} The render type. Can be `render`, `hydrate` or `recycle`.\n */;\n _proto.onRender = function onRender() {}\n\n /**\n * Lifecycle method. Called when the view is destroyed.\n * @param {object} options Options object or any arguments passed to `destroy` method.\n */;\n _proto.onDestroy = function onDestroy() {}\n\n /*\n * Replace expressions.\n */;\n _proto.replaceExpressions = function replaceExpressions(string, addChild) {\n var _this4 = this;\n return string.replace(new RegExp(Component.EXPRESSION_PLACEHOLDER_TEMPLATE('(\\\\d+)'), 'g'), function (match) {\n var expression = getExpression(match, _this4.template.expressions);\n // Eval expression. Pass view as argument.\n var result = evalExpression(expression, _this4, _this4);\n // Treat all expressions as arrays.\n var results = result instanceof Array ? result : [result];\n // Replace expression with the result of the evaluation.\n return results.reduce(function (out, result) {\n var parsed;\n // If result is true, replace it with a placeholder.\n if (result === true) parsed = Component.TRUE_PLACEHOLDER;\n // If result is false, replace it with a placeholder.\n else if (result === false) parsed = Component.FALSE_PLACEHOLDER;\n // Replace null or undefined with empty string.\n else if (result === null || typeof result === 'undefined') parsed = '';\n // If result is a view, call addChild callback.\n else if (result && typeof result.render === 'function') parsed = addChild(result);\n // Return expression itself.\n else parsed = result;\n // Concatenate expressions.\n return out + parsed;\n }, '');\n })\n // Replace `attribute=\"true\"` with `attribute`\n .replace(new RegExp(\"([a-z]+)=[\\\"|']\" + Component.TRUE_PLACEHOLDER + \"[\\\"|']\", 'g'), '$1')\n // Replace `attribute=\"false\"` with empty string.\n .replace(new RegExp(\"([a-z]+)=[\\\"|']\" + Component.FALSE_PLACEHOLDER + \"[\\\"|']\", 'g'), '')\n // Replace rest of false expressions with empty string.\n .replace(new RegExp(Component.FALSE_PLACEHOLDER, 'g'), '');\n }\n\n /*\n * Treat the whole view as a HTML string.\n */;\n _proto.toString = function toString() {\n var _this5 = this;\n // Normally there won't be any children, but if there are, destroy them.\n this.destroyChildren();\n // Get tag name.\n var tag = this.tag || 'div';\n // Get attributes.\n var attributes = this.getAttributes().html;\n // Replace expressions of inner template.\n var inner = this.template && this.template.inner && this.replaceExpressions(this.template.inner, function (component) {\n // Add child component.\n return _this5.addChild(component);\n });\n // Generate outer template.\n return inner ? \"<\" + tag + \" \" + attributes + \">\" + inner + \"</\" + tag + \">\" : \"<\" + tag + \" \" + attributes + \" />\";\n }\n\n /*\n * View render method.\n */;\n _proto.render = function render() {\n var _this6 = this;\n // Prevent a last re render if view is already destroyed.\n if (this.destroyed) return this;\n // If `this.el` is not present, create a new `this.tag` element.\n if (!this.el) {\n this.el = this.createElement(this.tag);\n this.delegateEvents();\n }\n // Set `this.el` attributes.\n var attributes = this.getAttributes();\n // Remove attributes.\n Object.keys(attributes.remove).forEach(function (key) {\n _this6.el.removeAttribute(key);\n });\n // Add attributes.\n Object.keys(attributes.add).forEach(function (key) {\n _this6.el.setAttribute(key, attributes.add[key]);\n });\n // Check for `template.inner` to see if view has innerHTML.\n if (this.template && this.template.inner) {\n var previousChildren = this.children;\n this.children = [];\n var nextChildren = [];\n var recycledChildren = [];\n // Store active element.\n var activeElement = document.activeElement;\n // Replace expressions. Set html inside of `this.el`.\n this.el.innerHTML = this.replaceExpressions(this.template.inner, function (component) {\n var out = component;\n // Check if child already exists.\n var found = component.key && previousChildren.find(function (previousChild) {\n return previousChild.key === component.key;\n });\n if (found) {\n var tag = found.el.tagName.toLowerCase();\n var id = found.el.id;\n // If child already exists, replace it html by its root element.\n out = \"<\" + tag + \" id=\\\"\" + id + \"\\\"></\" + tag + \">\";\n // Add child to recycled children.\n recycledChildren.push(found);\n // Destroy new child component. Use recycled one instead.\n component.destroy();\n } else {\n // Not found. Add new child component.\n nextChildren.push(component);\n }\n // Component html.\n return out;\n });\n // Add new children. Hydrate them.\n nextChildren.forEach(function (nextChild) {\n _this6.addChild(nextChild).hydrate(_this6.el);\n });\n // Replace children root elements with recycled components.\n recycledChildren.forEach(function (recycledChild) {\n _this6.addChild(recycledChild).recycle(_this6.el);\n });\n // Destroy unused children.\n previousChildren.forEach(function (previousChild) {\n var found = recycledChildren.indexOf(previousChild) > -1;\n if (!found) previousChild.destroy();\n });\n // Restore focus.\n if (this.el.contains(activeElement)) {\n activeElement.focus();\n }\n }\n // Call onRender lifecycle method.\n this.onRender.call(this, 'render');\n // Return this for chaining.\n return this;\n }\n\n /**\n * Helper method used to extend a `Component`, creating a subclass.\n * @static\n * @param {object} 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 Component.extend = function extend(object) {\n var Current = this;\n var Extended = /*#__PURE__*/function (_Current) {\n function Extended() {\n return _Current.apply(this, arguments) || this;\n }\n _inheritsLoose(Extended, _Current);\n return Extended;\n }(Current);\n Object.assign(Extended.prototype, typeof object === 'function' ? object(Current.prototype) : object);\n return Extended;\n }\n\n /**\n * Mount the component into the dom.\n * It instantiate the Component view using options, \n * appends its element into the DOM (if `el` is provided).\n * And returns the view instance.\n * @static\n * @param {object} options The view options.\n * @param {node} el Dom element to append the view element.\n * @param {boolean} hydrate If true, the view will use existing html.\n * @return {Rasti.Component}\n */;\n Component.mount = function mount(options, el, hydrate) {\n // Instantiate view.\n var view = new this(options);\n // If `el` is passed, mount component.\n if (el) {\n if (hydrate) {\n view.toString();\n } else {\n var fragment = document.createElement('template');\n // Add html text into element inner html.\n fragment.innerHTML = view;\n // Add to dom.\n el.appendChild(fragment.content);\n }\n view.hydrate(el);\n }\n // Return view instance.\n return view;\n }\n\n /**\n * Takes a tagged template containing an HTML string, \n * and returns a new `Component` class.\n * - The template outer tag and attributes will be used to create the view's root element.\n * - Boolean attributes should be passed in the form of `attribute=\"${() => true}\"`.\n * - Event handlers should be passed, at the root element, in the form of `onEventName=${{'selector' : listener }}`. Where `selector` is a css selector. The event will be delegated to the view's root element.\n * - The template inner HTML will be used as the view's template.\n * - Template interpolations that are functions will be evaluated on the render process. Receiving the view instance as argument. And being bound to it.\n * - If the function returns `null`, `undefined`, `false` or empty string, the interpolation won't render any content.\n * - If the function returns a component instance, it will be added as a child component.\n * - If the function returns an array, each item will be evaluated as above.\n * @static\n * @param {string} HTML template for the component.\n * @return {Rasti.Component}\n */;\n Component.create = function create(strings) {\n for (var _len2 = arguments.length, expressions = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n expressions[_key2 - 1] = arguments[_key2];\n }\n var parts = [];\n // Replace functions and objects interpolations with `{number}`.\n // Where `number` is the index on expressions array.\n strings.forEach(function (string, i) {\n // Add string part.\n parts.push(string);\n // Add expression placeholder for later or expression eval.\n if (expressions[i]) {\n parts.push(typeof expressions[i] === 'function' || typeof expressions[i] === 'object' ? Component.EXPRESSION_PLACEHOLDER_TEMPLATE(i) : expressions[i]);\n }\n });\n // Create output text for main template.\n var main = parts.join('').trim().replace(/\\n/g, '');\n // Extract outer tag, attributes and inner html.\n var result = main.match(/^<([a-z]+)(.*?)>(.*)<\\/\\1>$/) || main.match(/^<([a-z]+)(.*?)\\/>$/);\n // Parse attributes from html text into an object.\n var attributes = extractAttributes(result[2]);\n // Events to be delegated.\n var events = {};\n // Filter events. To generate events object.\n // Generate attributes object, replace placeholders with expressions.\n attributes = Object.keys(attributes).reduce(function (out, key) {\n // Is Event?\n var matchKey = key.match(/on(([A-Z]{1}[a-z]+)+)/);\n // Is placeholder for function or object?\n // Get expression or value.\n var value = getExpression(attributes[key], expressions);\n // Is event handler. Add to events object.\n if (matchKey && matchKey[1]) {\n var eventType = matchKey[1].toLowerCase();\n Object.keys(value).forEach(function (selector) {\n return events[\"\" + eventType + (selector === '&' ? '' : \" \" + selector)] = value[selector];\n });\n return out;\n }\n // Is attribute. Add to attributes object.\n out[key] = value;\n return out;\n }, {});\n var Current = this;\n // Create subclass for this component.\n return Current.extend({\n // Set events.\n events: events,\n // Set attributes.\n attributes: attributes,\n // Set template.\n template: {\n // Template for innerHTML of root element.\n inner: result[3],\n // Template expressions.\n expressions: expressions\n },\n // Set root element tag.\n tag: result[1]\n });\n };\n return Component;\n}(_View2[\"default\"]);\nComponent.ID_TEMPLATE = function (uid) {\n return \"rasti-component-\" + uid;\n};\nComponent.EXPRESSION_PLACEHOLDER_TEMPLATE = function (idx) {\n return \"__RASTI_EXPRESSION{\" + idx + \"}\";\n};\nComponent.TRUE_PLACEHOLDER = '__RASTI_TRUE';\nComponent.FALSE_PLACEHOLDER = '__RASTI_FALSE';\n\n//# sourceURL=webpack://Rasti/./src/Component.js?");
|
|
29
|
+
eval("\n\nexports.__esModule = true;\nexports[\"default\"] = void 0;\nvar _View2 = _interopRequireDefault(__webpack_require__(/*! ./View.js */ \"./src/View.js\"));\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { \"default\": obj }; }\nfunction _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); }\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n// This options keys will be extended on view instance.\nvar componentOptions = {\n key: true,\n state: true,\n onCreate: true,\n onChange: true,\n onRender: true\n};\n\n/*\n * Helper function. Extract attributes from html tag\n * @param text {string} html text\n * @return {object} Object with keys / values representing attributes.\n */\nvar extractAttributes = function extractAttributes(text) {\n var attributes = {};\n var re = /([\\w|data-]+)(?:=[\"']?((?:.(?![\"']?\\s+(?:\\S+)=|\\s*\\/?[>\"']))+.)[\"']?)?/g;\n var result;\n while ((result = re.exec(text)) !== null) {\n attributes[result[1]] = typeof result[2] === 'undefined' ? true : result[2];\n }\n return attributes;\n};\n\n/*\n * Helper function. If first arg is a placeholder for an expression, return the expression.\n */\nvar getExpression = function getExpression(placeholder, expressions) {\n var match = placeholder && placeholder.match && placeholder.match(new RegExp(Component.EXPRESSION_PLACEHOLDER_TEMPLATE('(\\\\d+)')));\n return match && match[1] ? expressions[match[1]] : placeholder;\n};\n\n/*\n * Helper function. If expression is a function, call it with context and args.\n */\nvar evalExpression = function evalExpression(expression, context) {\n for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {\n args[_key - 2] = arguments[_key];\n }\n return typeof expression === 'function' ? expression.apply(context, args) : expression;\n};\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 `create` static method, which takes a tagged template.\n * @module\n * @extends Rasti.View\n * @param {object} options Object containing options. The following keys will be merged to `this`: model, state, key, onDestroy, onRender, onCreate, onChange.\n * @property {string} key A unique key to identify the component. Used to recycle child components.\n * @property {object} model A `Rasti.Model` or any emitter object containing data and business logic.\n * @property {object} state A `Rasti.Model` or any emitter object containing data and business logic, to be used as internal state.\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 */\nvar Component = exports[\"default\"] = /*#__PURE__*/function (_View) {\n function Component() {\n var _this;\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n _this = _View.apply(this, arguments) || this;\n // Extend \"this\" with options, mapping componentOptions keys.\n Object.keys(options).forEach(function (key) {\n if (componentOptions[key]) _this[key] = options[key];\n });\n // Store options by default.\n _this.options = options;\n // Bind onChange to this to be used as listener.\n // Store bound version, so it can be removed on onDestroy method.\n _this.onChange = _this.onChange.bind(_this);\n // Listen to model changes and call onChange.\n if (_this.model && _this.model.on) _this.model.on('change', _this.onChange);\n if (_this.state && _this.state.on) _this.state.on('change', _this.onChange);\n // Call life cycle method.\n _this.onCreate.apply(_this, arguments);\n return _this;\n }\n\n /*\n * Override. We don't want to ensure an element on instantiation.\n * We will provide it later.\n */\n _inheritsLoose(Component, _View);\n var _proto = Component.prototype;\n _proto.ensureElement = function ensureElement() {\n // If el is provided, delegate events.\n if (this.el) {\n this.delegateEvents();\n this.id = this.el.id;\n }\n // Ensure id.\n if (!this.id) {\n this.id = this.attributes && this.attributes.id ?\n // If id is provided, evaluate it.\n evalExpression(this.attributes.id, this, this) :\n // Generate a unique id and set it as id attribute.\n Component.ID_TEMPLATE(this.uid);\n }\n }\n\n /*\n * Find view's element on parent node, using id.\n */;\n _proto.findElement = function findElement(parent) {\n return (parent || document).querySelector(\"#\" + this.id);\n }\n\n /*\n * Eval attributes expressions.\n */;\n _proto.getAttributes = function getAttributes() {\n var _this2 = this;\n var add = {\n id: this.id\n };\n var remove = {};\n var html = [\"id=\\\"\" + this.id + \"\\\"\"];\n if (this.attributes) {\n Object.keys(this.attributes).forEach(function (key) {\n if (key === 'id') return;\n // Evaluate attribute value.\n var value = evalExpression(_this2.attributes[key], _this2, _this2);\n\n // Transform bool attribute values\n if (value === false) {\n remove[key] = true;\n } else if (value === true) {\n add[key] = '';\n html.push(key);\n } else {\n if (value === null || typeof value === 'undefined') value = '';\n add[key] = value;\n html.push(key + \"=\\\"\" + value + \"\\\"\");\n }\n });\n }\n return {\n add: add,\n remove: remove,\n html: html.join(' ')\n };\n }\n\n /*\n * Used internally on the render process.\n * Attach the view to the dom element.\n */;\n _proto.hydrate = function hydrate(parent) {\n var _this3 = this;\n this.el = this.findElement(parent);\n this.delegateEvents();\n this.children.forEach(function (child) {\n return child.hydrate(_this3.el);\n });\n this.onRender.call(this, 'hydrate');\n }\n\n /*\n * Used internally on the render process.\n * Reuse a view that has `key` when its parent is rendered.\n */;\n _proto.recycle = function recycle(parent) {\n // Find element to be replaced. It has same id.\n var toBeReplaced = this.findElement(parent);\n // Replace it with this.el.\n toBeReplaced.replaceWith(this.el);\n // Call `onRender` lifecycle method.\n this.onRender.call(this, 'recycle');\n }\n\n /*\n * Override. Add some custom logic to super `destroy` method.\n */;\n _proto.destroy = function destroy() {\n _View.prototype.destroy.apply(this, arguments);\n // Stop listening to `change`.\n if (this.model && this.model.off) this.model.off('change', this.onChange);\n if (this.state && this.state.off) this.state.off('change', this.onChange);\n // Set destroyed flag to prevent a last render after destroyed.\n this.destroyed = true;\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Lifecycle method. Called when the view is created at the end of the constructor.\n * @param options {object} The view options.\n */;\n _proto.onCreate = function onCreate() {}\n\n /**\n * Lifecycle method. Called when model emits `change` event.\n * By default calls render method.\n * This method should be extended with custom logic.\n * Maybe comparing new attributes with previous ones and calling\n * render when needed. Or doing some dom transformation.\n * @param model {Rasti.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 _proto.onChange = function onChange() {\n this.render();\n }\n\n /**\n * Lifecycle method. Called when the view is rendered.\n * @param type {string} The render type. Can be `render`, `hydrate` or `recycle`.\n */;\n _proto.onRender = function onRender() {}\n\n /**\n * Lifecycle method. Called when the view is destroyed.\n * @param {object} options Options object or any arguments passed to `destroy` method.\n */;\n _proto.onDestroy = function onDestroy() {}\n\n /*\n * Replace expressions.\n */;\n _proto.replaceExpressions = function replaceExpressions(string, addChild) {\n var _this4 = this;\n return string.replace(new RegExp(Component.EXPRESSION_PLACEHOLDER_TEMPLATE('(\\\\d+)'), 'g'), function (match) {\n var expression = getExpression(match, _this4.template.expressions);\n // Eval expression. Pass view as argument.\n var result = evalExpression(expression, _this4, _this4);\n // Treat all expressions as arrays.\n var results = result instanceof Array ? result : [result];\n // Replace expression with the result of the evaluation.\n return results.reduce(function (out, result) {\n var parsed;\n // If result is true, replace it with a placeholder.\n if (result === true) parsed = Component.TRUE_PLACEHOLDER;\n // If result is false, replace it with a placeholder.\n else if (result === false) parsed = Component.FALSE_PLACEHOLDER;\n // Replace null or undefined with empty string.\n else if (result === null || typeof result === 'undefined') parsed = '';\n // If result is a view, call addChild callback.\n else if (result && typeof result.render === 'function') parsed = addChild(result);\n // Return expression itself.\n else parsed = result;\n // Concatenate expressions.\n return out + parsed;\n }, '');\n })\n // Replace `attribute=\"true\"` with `attribute` and `attribute=\"false\"` with empty string.\n .replace(new RegExp(\"([\\\\w|data-]+)=([\\\"'])?(\" + Component.TRUE_PLACEHOLDER + \"|\" + Component.FALSE_PLACEHOLDER + \")\\\\2\", 'g'), function (match, attribute, quote, placeholder) {\n return placeholder === Component.TRUE_PLACEHOLDER ? attribute : '';\n })\n // Replace rest of true or false expressions with empty string.\n .replace(new RegExp(Component.TRUE_PLACEHOLDER + \"|\" + Component.FALSE_PLACEHOLDER, 'g'), '');\n }\n\n /*\n * Treat the whole view as a HTML string.\n */;\n _proto.toString = function toString() {\n var _this5 = this;\n // Normally there won't be any children, but if there are, destroy them.\n this.destroyChildren();\n // Get tag name.\n var tag = this.tag || 'div';\n // Get attributes.\n var attributes = this.getAttributes().html;\n // Replace expressions of inner template.\n var close = this.template && this.template.inner;\n var inner = close && this.replaceExpressions(this.template.inner, function (component) {\n // Add child component.\n return _this5.addChild(component);\n });\n // Generate outer template.\n return close ? \"<\" + tag + \" \" + attributes + \">\" + inner + \"</\" + tag + \">\" : \"<\" + tag + \" \" + attributes + \" />\";\n }\n\n /*\n * View render method.\n */;\n _proto.render = function render() {\n var _this6 = this;\n // Prevent a last re render if view is already destroyed.\n if (this.destroyed) return this;\n // If `this.el` is not present, create a new `this.tag` element.\n if (!this.el) {\n this.el = this.createElement(this.tag);\n this.delegateEvents();\n }\n // Set `this.el` attributes.\n var attributes = this.getAttributes();\n // Remove attributes.\n Object.keys(attributes.remove).forEach(function (key) {\n _this6.el.removeAttribute(key);\n });\n // Add attributes.\n Object.keys(attributes.add).forEach(function (key) {\n _this6.el.setAttribute(key, attributes.add[key]);\n });\n // Check for `template.inner` to see if view has innerHTML.\n if (this.template && this.template.inner) {\n var previousChildren = this.children;\n this.children = [];\n var nextChildren = [];\n var recycledChildren = [];\n // Store active element.\n var activeElement = document.activeElement;\n // Replace expressions. Set html inside of `this.el`.\n this.el.innerHTML = this.replaceExpressions(this.template.inner, function (component) {\n var out = component;\n // Check if child already exists.\n var found = component.key && previousChildren.find(function (previousChild) {\n return previousChild.key === component.key;\n });\n if (found) {\n var tag = found.el.tagName.toLowerCase();\n var id = found.el.id;\n // If child already exists, replace it html by its root element.\n out = \"<\" + tag + \" id=\\\"\" + id + \"\\\"></\" + tag + \">\";\n // Add child to recycled children.\n recycledChildren.push(found);\n // Destroy new child component. Use recycled one instead.\n component.destroy();\n } else {\n // Not found. Add new child component.\n nextChildren.push(component);\n }\n // Component html.\n return out;\n });\n // Add new children. Hydrate them.\n nextChildren.forEach(function (nextChild) {\n _this6.addChild(nextChild).hydrate(_this6.el);\n });\n // Replace children root elements with recycled components.\n recycledChildren.forEach(function (recycledChild) {\n _this6.addChild(recycledChild).recycle(_this6.el);\n });\n // Destroy unused children.\n previousChildren.forEach(function (previousChild) {\n var found = recycledChildren.indexOf(previousChild) > -1;\n if (!found) previousChild.destroy();\n });\n // Restore focus.\n if (this.el.contains(activeElement)) {\n activeElement.focus();\n }\n }\n // Call onRender lifecycle method.\n this.onRender.call(this, 'render');\n // Return this for chaining.\n return this;\n }\n\n /**\n * Helper method used to extend a `Component`, creating a subclass.\n * @static\n * @param {object} 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 Component.extend = function extend(object) {\n var Current = this;\n var Extended = /*#__PURE__*/function (_Current) {\n function Extended() {\n return _Current.apply(this, arguments) || this;\n }\n _inheritsLoose(Extended, _Current);\n return Extended;\n }(Current);\n Object.assign(Extended.prototype, typeof object === 'function' ? object(Current.prototype) : object);\n return Extended;\n }\n\n /**\n * Mount the component into the dom.\n * It instantiate the Component view using options, \n * appends its element into the DOM (if `el` is provided).\n * And returns the view instance.\n * @static\n * @param {object} options The view options.\n * @param {node} el Dom element to append the view element.\n * @param {boolean} hydrate If true, the view will use existing html.\n * @return {Rasti.Component}\n */;\n Component.mount = function mount(options, el, hydrate) {\n // Instantiate view.\n var view = new this(options);\n // If `el` is passed, mount component.\n if (el) {\n if (hydrate) {\n view.toString();\n } else {\n var fragment = document.createElement('template');\n // Add html text into element inner html.\n fragment.innerHTML = view;\n // Add to dom.\n el.appendChild(fragment.content);\n }\n view.hydrate(el);\n }\n // Return view instance.\n return view;\n }\n\n /**\n * Takes a tagged template containing an HTML string, \n * and returns a new `Component` class.\n * - The template outer tag and attributes will be used to create the view's root element.\n * - Boolean attributes should be passed in the form of `attribute=\"${() => true}\"`.\n * - Event handlers should be passed, at the root element, in the form of `onEventName=${{'selector' : listener }}`. Where `selector` is a css selector. The event will be delegated to the view's root element.\n * - The template inner HTML will be used as the view's template.\n * - Template interpolations that are functions will be evaluated on the render process. Receiving the view instance as argument. And being bound to it.\n * - If the function returns `null`, `undefined`, `false` or empty string, the interpolation won't render any content.\n * - If the function returns a component instance, it will be added as a child component.\n * - If the function returns an array, each item will be evaluated as above.\n * @static\n * @param {string} HTML template for the component.\n * @return {Rasti.Component}\n */;\n Component.create = function create(strings) {\n for (var _len2 = arguments.length, expressions = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {\n expressions[_key2 - 1] = arguments[_key2];\n }\n var parts = [];\n // Replace functions and objects interpolations with `{number}`.\n // Where `number` is the index on expressions array.\n strings.forEach(function (string, i) {\n // Add string part.\n parts.push(string);\n // Add expression placeholder for later or expression eval.\n if (expressions[i]) {\n parts.push(typeof expressions[i] === 'function' || typeof expressions[i] === 'object' ? Component.EXPRESSION_PLACEHOLDER_TEMPLATE(i) : expressions[i]);\n }\n });\n // Create output text for main template.\n var main = parts.join('').trim().replace(/\\n/g, Component.NEW_LINE_PLACEHOLDER);\n // Extract outer tag, attributes and inner html.\n var result = main.match(/^<([a-z]+[1-6]?)(.*?)>(.*)<\\/\\1>$/) || main.match(/^<([a-z]+)(.*?)\\/>$/);\n // Tag name.\n var tag = result[1];\n // Parse attributes from html string into an object.\n var attributesAndEvents = extractAttributes(result[2].replace(new RegExp(Component.NEW_LINE_PLACEHOLDER, 'g'), ''));\n // Events to be delegated.\n var events = {};\n // Filter events. To generate events object.\n // Generate attributes object, replace placeholders with expressions.\n var attributes = Object.keys(attributesAndEvents).reduce(function (out, key) {\n // Is Event?\n var matchKey = key.match(/on(([A-Z]{1}[a-z]+)+)/);\n // Is placeholder for function or object?\n // Get expression or value.\n var value = getExpression(attributesAndEvents[key], expressions);\n // Is event handler. Add to events object.\n if (matchKey && matchKey[1]) {\n var eventType = matchKey[1].toLowerCase();\n Object.keys(value).forEach(function (selector) {\n return events[\"\" + eventType + (selector === '&' ? '' : \" \" + selector)] = value[selector];\n });\n return out;\n }\n // Is attribute. Add to attributes object.\n out[key] = value;\n return out;\n }, {});\n // Inner html template. Restore new lines.\n var inner = result[3] && result[3].replace(new RegExp(Component.NEW_LINE_PLACEHOLDER, 'g'), '\\n');\n var Current = this;\n // Create subclass for this component.\n return Current.extend({\n // Set root element tag.\n tag: tag,\n // Set attributes.\n attributes: attributes,\n // Set events.\n events: events,\n // Set template.\n template: {\n // Template for innerHTML of root element.\n inner: inner,\n // Template expressions.\n expressions: expressions\n }\n });\n };\n return Component;\n}(_View2[\"default\"]);\nComponent.ID_TEMPLATE = function (uid) {\n return \"rasti-component-\" + uid;\n};\nComponent.EXPRESSION_PLACEHOLDER_TEMPLATE = function (idx) {\n return \"__RASTI_EXPRESSION_{\" + idx + \"}__\";\n};\nComponent.TRUE_PLACEHOLDER = '__RASTI_TRUE__';\nComponent.FALSE_PLACEHOLDER = '__RASTI_FALSE__';\nComponent.NEW_LINE_PLACEHOLDER = '__RASTI_NEW_LINE__';\n\n//# sourceURL=webpack://Rasti/./src/Component.js?");
|
|
30
30
|
|
|
31
31
|
/***/ }),
|
|
32
32
|
|
package/dist/rasti.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Rasti=e():t.Rasti=e()}(self,(()=>(()=>{"use strict";var t={818:(t,e,n)=>{e.__esModule=!0,e.default=void 0;var i,r=(i=n(944))&&i.__esModule?i:{default:i};function s(t,e){t.prototype=Object.create(e.prototype),t.prototype.constructor=t,o(t,e)}function o(t,e){return o=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},o(t,e)}var a={key:!0,state:!0,onCreate:!0,onChange:!0,onRender:!0},u=function(t,e){var n=t&&t.match&&t.match(new RegExp(c.EXPRESSION_PLACEHOLDER_TEMPLATE("(\\d+)")));return n&&n[1]?e[n[1]]:t},h=function(t,e){for(var n=arguments.length,i=new Array(n>2?n-2:0),r=2;r<n;r++)i[r-2]=arguments[r];return"function"==typeof t?t.apply(e,i):t},c=e.default=function(t){function e(){var e,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return e=t.apply(this,arguments)||this,Object.keys(n).forEach((function(t){a[t]&&(e[t]=n[t])})),e.options=n,e.onChange=e.onChange.bind(e),e.model&&e.model.on&&e.model.on("change",e.onChange),e.state&&e.state.on&&e.state.on("change",e.onChange),e.onCreate.apply(e,arguments),e}s(e,t);var n=e.prototype;return n.ensureElement=function(){this.el&&(this.delegateEvents(),this.id=this.el.id),this.id||(this.id=this.attributes&&this.attributes.id?h(this.attributes.id,this,this):e.ID_TEMPLATE(this.uid))},n.findElement=function(t){return(t||document).querySelector("#"+this.id)},n.getAttributes=function(){var t=this,e={id:this.id},n={},i=['id="'+this.id+'"'];return this.attributes&&Object.keys(this.attributes).forEach((function(r){if("id"!==r){var s=h(t.attributes[r],t,t);!1===s?n[r]=!0:!0===s?(e[r]="",i.push(r)):(null==s&&(s=""),e[r]=s,i.push(r+'="'+s+'"'))}})),{add:e,remove:n,html:i.join(" ")}},n.hydrate=function(t){var e=this;this.el=this.findElement(t),this.delegateEvents(),this.children.forEach((function(t){return t.hydrate(e.el)})),this.onRender.call(this,"hydrate")},n.recycle=function(t){this.findElement(t).replaceWith(this.el),this.onRender.call(this,"recycle")},n.destroy=function(){t.prototype.destroy.apply(this,arguments),this.model&&this.model.off&&this.model.off("change",this.onChange),this.state&&this.state.off&&this.state.off("change",this.onChange),this.destroyed=!0},n.onCreate=function(){},n.onChange=function(){this.render()},n.onRender=function(){},n.onDestroy=function(){},n.replaceExpressions=function(t,n){var i=this;return t.replace(new RegExp(e.EXPRESSION_PLACEHOLDER_TEMPLATE("(\\d+)"),"g"),(function(t){var r=u(t,i.template.expressions),s=h(r,i,i);return(s instanceof Array?s:[s]).reduce((function(t,i){return t+(!0===i?e.TRUE_PLACEHOLDER:!1===i?e.FALSE_PLACEHOLDER:null==i?"":i&&"function"==typeof i.render?n(i):i)}),"")})).replace(new RegExp("([a-z]+)=[\"|']"+e.TRUE_PLACEHOLDER+"[\"|']","g"),"$1").replace(new RegExp("([a-z]+)=[\"|']"+e.FALSE_PLACEHOLDER+"[\"|']","g"),"").replace(new RegExp(e.FALSE_PLACEHOLDER,"g"),"")},n.toString=function(){var t=this;this.destroyChildren();var e=this.tag||"div",n=this.getAttributes().html,i=this.template&&this.template.inner&&this.replaceExpressions(this.template.inner,(function(e){return t.addChild(e)}));return i?"<"+e+" "+n+">"+i+"</"+e+">":"<"+e+" "+n+" />"},n.render=function(){var t=this;if(this.destroyed)return this;this.el||(this.el=this.createElement(this.tag),this.delegateEvents());var e=this.getAttributes();if(Object.keys(e.remove).forEach((function(e){t.el.removeAttribute(e)})),Object.keys(e.add).forEach((function(n){t.el.setAttribute(n,e.add[n])})),this.template&&this.template.inner){var n=this.children;this.children=[];var i=[],r=[],s=document.activeElement;this.el.innerHTML=this.replaceExpressions(this.template.inner,(function(t){var e=t,s=t.key&&n.find((function(e){return e.key===t.key}));if(s){var o=s.el.tagName.toLowerCase();e="<"+o+' id="'+s.el.id+'"></'+o+">",r.push(s),t.destroy()}else i.push(t);return e})),i.forEach((function(e){t.addChild(e).hydrate(t.el)})),r.forEach((function(e){t.addChild(e).recycle(t.el)})),n.forEach((function(t){r.indexOf(t)>-1||t.destroy()})),this.el.contains(s)&&s.focus()}return this.onRender.call(this,"render"),this},e.extend=function(t){var e=function(t){function e(){return t.apply(this,arguments)||this}return s(e,t),e}(this);return Object.assign(e.prototype,"function"==typeof t?t(this.prototype):t),e},e.mount=function(t,e,n){var i=new this(t);if(e){if(n)i.toString();else{var r=document.createElement("template");r.innerHTML=i,e.appendChild(r.content)}i.hydrate(e)}return i},e.create=function(t){for(var n=arguments.length,i=new Array(n>1?n-1:0),r=1;r<n;r++)i[r-1]=arguments[r];var s=[];t.forEach((function(t,n){s.push(t),i[n]&&s.push("function"==typeof i[n]||"object"==typeof i[n]?e.EXPRESSION_PLACEHOLDER_TEMPLATE(n):i[n])}));var o=s.join("").trim().replace(/\n/g,""),a=o.match(/^<([a-z]+)(.*?)>(.*)<\/\1>$/)||o.match(/^<([a-z]+)(.*?)\/>$/),h=function(t){for(var e,n={},i=/([\w|data-]+)(?:=["']?((?:.(?!["']?\s+(?:\S+)=|\s*\/?[>"']))+.)["']?)?/g;null!==(e=i.exec(t));)n[e[1]]=void 0===e[2]||e[2];return n}(a[2]),c={};return h=Object.keys(h).reduce((function(t,e){var n=e.match(/on(([A-Z]{1}[a-z]+)+)/),r=u(h[e],i);if(n&&n[1]){var s=n[1].toLowerCase();return Object.keys(r).forEach((function(t){return c[s+("&"===t?"":" "+t)]=r[t]})),t}return t[e]=r,t}),{}),this.extend({events:c,attributes:h,template:{inner:a[3],expressions:i},tag:a[1]})},e}(r.default);c.ID_TEMPLATE=function(t){return"rasti-component-"+t},c.EXPRESSION_PLACEHOLDER_TEMPLATE=function(t){return"__RASTI_EXPRESSION{"+t+"}"},c.TRUE_PLACEHOLDER="__RASTI_TRUE",c.FALSE_PLACEHOLDER="__RASTI_FALSE"},527:(t,e)=>{e.__esModule=!0,e.default=void 0,e.default=function(){function t(){}var e=t.prototype;return e.on=function(t,e){if("function"!=typeof e)throw TypeError("Listener must be a function");this.listeners||(this.listeners={}),this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e)},e.once=function(t,e){if("function"==typeof e){var n=this,i=e;e=function(){i.apply(void 0,arguments),n.off(t,e)}}this.on(t,e)},e.off=function(t,e){if(t)if(e){var n=this.listeners[t];n&&(n.slice().forEach((function(t,i){t===e&&n.splice(i,1)})),n.length||delete this.listeners[t])}else delete this.listeners[t];else this.listeners={}},e.emit=function(t){for(var e=arguments.length,n=new Array(e>1?e-1:0),i=1;i<e;i++)n[i-1]=arguments[i];var r=this.listeners&&this.listeners[t];r&&r.length&&r.slice().forEach((function(t){t.apply(void 0,n)}))},t}()},4:(t,e,n)=>{e.__esModule=!0,e.default=void 0;var i,r=(i=n(527))&&i.__esModule?i:{default:i};function s(t,e){return s=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},s(t,e)}e.default=function(t){function e(){var e,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return(e=t.call(this)||this).preinitialize.apply(e,arguments),e.attributes=Object.assign({},e.defaults||{},n),e.previous={},Object.keys(e.attributes).forEach(e.defineAttribute.bind(e)),e}var n,i;i=t,(n=e).prototype=Object.create(i.prototype),n.prototype.constructor=n,s(n,i);var r=e.prototype;return r.preinitialize=function(){},r.defineAttribute=function(t){var e=this;Object.defineProperty(this,t,{get:function(){return e.get(t)},set:function(n){e.set(t,n)}})},r.get=function(t){return this.attributes[t]},r.set=function(t,e){for(var n,i,r,s=this,o=arguments.length,a=new Array(o>2?o-2:0),u=2;u<o;u++)a[u-2]=arguments[u];"object"==typeof t?(n=t,i=[e].concat(a)):((r={})[t]=e,n=r,i=a);var h=this._changing;this._changing=!0;var c={};h||(this.previous=Object.assign({},this.attributes)),Object.keys(n).forEach((function(t){n[t]!==s.attributes[t]&&(c[t]=n[t],s.attributes[t]=n[t])}));var l=Object.keys(c);if(l.length&&(this._pending=["change",this,c].concat(i)),l.forEach((function(t){s.emit.apply(s,["change:"+t,s,n[t]].concat(i))})),h)return this;for(;this._pending;){var f=this._pending;this._pending=null,this.emit.apply(this,f)}return this._pending=null,this._changing=!1,this},r.toJSON=function(){return this.attributes},e}(r.default)},944:(t,e,n)=>{e.__esModule=!0,e.default=void 0;var i,r=(i=n(527))&&i.__esModule?i:{default:i};function s(t,e){return s=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},s(t,e)}var o={el:!0,tag:!0,attributes:!0,events:!0,model:!0,template:!0,onDestroy:!0};(e.default=function(t){function e(){var n,i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return(n=t.call(this)||this).preinitialize.apply(n,arguments),n.uid="uid"+ ++e.uid,n.delegatedEventListeners=[],n.children=[],Object.keys(i).forEach((function(t){o[t]&&(n[t]=i[t])})),n.ensureElement(),n}var n,i;i=t,(n=e).prototype=Object.create(i.prototype),n.prototype.constructor=n,s(n,i);var r=e.prototype;return r.preinitialize=function(){},r.$=function(t){return this.el.querySelector(t)},r.$$=function(t){return this.el.querySelectorAll(t)},r.destroy=function(){return this.destroyChildren(),this.undelegateEvents(),this.off(),this.onDestroy.apply(this,arguments),this},r.onDestroy=function(){},r.addChild=function(t){return this.children.push(t),t},r.destroyChildren=function(){for(;this.children.length;)this.children.shift().destroy()},r.ensureElement=function(){this.el||(this.el=this.createElement(this.tag,this.attributes)),this.delegateEvents()},r.createElement=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"div",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=document.createElement(t);return Object.keys(e).forEach((function(t){return n.setAttribute(t,e[t])})),n},r.removeElement=function(){return this.el.parentNode.removeChild(this.el),this},r.delegateEvents=function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.events;if(!e)return this;this.delegatedEventListeners.length&&this.undelegateEvents();var n={};return Object.keys(e).forEach((function(i){var r=i.split(" "),s=r.shift(),o=r.join(" "),a=e[i];a=("string"==typeof a?t[a]:a).bind(t),n[s]||(n[s]=[]),n[s].push({selector:o,listener:a})})),Object.keys(n).forEach((function(e){var i=function(i){n[e].forEach((function(e){var n=e.selector,r=e.listener;n&&!i.target.closest(n)||r(i,t)}))};t.delegatedEventListeners.push({type:e,listener:i}),t.el.addEventListener(e,i)})),this},r.undelegateEvents=function(){var t=this;return this.delegatedEventListeners.forEach((function(e){var n=e.type,i=e.listener;t.el.removeEventListener(n,i)})),this.delegatedEventListeners=[],this},r.render=function(){return this.template&&(this.el.innerHTML=this.template(this.model)),this},e}(r.default)).uid=0}},e={};function n(i){var r=e[i];if(void 0!==r)return r.exports;var s=e[i]={exports:{}};return t[i](s,s.exports,n),s.exports}var i={};return(()=>{var t=i;t.__esModule=!0;var e=a(n(527));t.Emitter=e.default;var r=a(n(4));t.Model=r.default;var s=a(n(944));t.View=s.default;var o=a(n(818));function a(t){return t&&t.__esModule?t:{default:t}}t.Component=o.default})(),i})()));
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Rasti=e():t.Rasti=e()}(self,(()=>(()=>{"use strict";var t={818:(t,e,n)=>{e.__esModule=!0,e.default=void 0;var i,r=(i=n(944))&&i.__esModule?i:{default:i};function s(t,e){t.prototype=Object.create(e.prototype),t.prototype.constructor=t,o(t,e)}function o(t,e){return o=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},o(t,e)}var a={key:!0,state:!0,onCreate:!0,onChange:!0,onRender:!0},u=function(t,e){var n=t&&t.match&&t.match(new RegExp(c.EXPRESSION_PLACEHOLDER_TEMPLATE("(\\d+)")));return n&&n[1]?e[n[1]]:t},h=function(t,e){for(var n=arguments.length,i=new Array(n>2?n-2:0),r=2;r<n;r++)i[r-2]=arguments[r];return"function"==typeof t?t.apply(e,i):t},c=e.default=function(t){function e(){var e,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return e=t.apply(this,arguments)||this,Object.keys(n).forEach((function(t){a[t]&&(e[t]=n[t])})),e.options=n,e.onChange=e.onChange.bind(e),e.model&&e.model.on&&e.model.on("change",e.onChange),e.state&&e.state.on&&e.state.on("change",e.onChange),e.onCreate.apply(e,arguments),e}s(e,t);var n=e.prototype;return n.ensureElement=function(){this.el&&(this.delegateEvents(),this.id=this.el.id),this.id||(this.id=this.attributes&&this.attributes.id?h(this.attributes.id,this,this):e.ID_TEMPLATE(this.uid))},n.findElement=function(t){return(t||document).querySelector("#"+this.id)},n.getAttributes=function(){var t=this,e={id:this.id},n={},i=['id="'+this.id+'"'];return this.attributes&&Object.keys(this.attributes).forEach((function(r){if("id"!==r){var s=h(t.attributes[r],t,t);!1===s?n[r]=!0:!0===s?(e[r]="",i.push(r)):(null==s&&(s=""),e[r]=s,i.push(r+'="'+s+'"'))}})),{add:e,remove:n,html:i.join(" ")}},n.hydrate=function(t){var e=this;this.el=this.findElement(t),this.delegateEvents(),this.children.forEach((function(t){return t.hydrate(e.el)})),this.onRender.call(this,"hydrate")},n.recycle=function(t){this.findElement(t).replaceWith(this.el),this.onRender.call(this,"recycle")},n.destroy=function(){return t.prototype.destroy.apply(this,arguments),this.model&&this.model.off&&this.model.off("change",this.onChange),this.state&&this.state.off&&this.state.off("change",this.onChange),this.destroyed=!0,this},n.onCreate=function(){},n.onChange=function(){this.render()},n.onRender=function(){},n.onDestroy=function(){},n.replaceExpressions=function(t,n){var i=this;return t.replace(new RegExp(e.EXPRESSION_PLACEHOLDER_TEMPLATE("(\\d+)"),"g"),(function(t){var r=u(t,i.template.expressions),s=h(r,i,i);return(s instanceof Array?s:[s]).reduce((function(t,i){return t+(!0===i?e.TRUE_PLACEHOLDER:!1===i?e.FALSE_PLACEHOLDER:null==i?"":i&&"function"==typeof i.render?n(i):i)}),"")})).replace(new RegExp("([\\w|data-]+)=([\"'])?("+e.TRUE_PLACEHOLDER+"|"+e.FALSE_PLACEHOLDER+")\\2","g"),(function(t,n,i,r){return r===e.TRUE_PLACEHOLDER?n:""})).replace(new RegExp(e.TRUE_PLACEHOLDER+"|"+e.FALSE_PLACEHOLDER,"g"),"")},n.toString=function(){var t=this;this.destroyChildren();var e=this.tag||"div",n=this.getAttributes().html,i=this.template&&this.template.inner,r=i&&this.replaceExpressions(this.template.inner,(function(e){return t.addChild(e)}));return i?"<"+e+" "+n+">"+r+"</"+e+">":"<"+e+" "+n+" />"},n.render=function(){var t=this;if(this.destroyed)return this;this.el||(this.el=this.createElement(this.tag),this.delegateEvents());var e=this.getAttributes();if(Object.keys(e.remove).forEach((function(e){t.el.removeAttribute(e)})),Object.keys(e.add).forEach((function(n){t.el.setAttribute(n,e.add[n])})),this.template&&this.template.inner){var n=this.children;this.children=[];var i=[],r=[],s=document.activeElement;this.el.innerHTML=this.replaceExpressions(this.template.inner,(function(t){var e=t,s=t.key&&n.find((function(e){return e.key===t.key}));if(s){var o=s.el.tagName.toLowerCase();e="<"+o+' id="'+s.el.id+'"></'+o+">",r.push(s),t.destroy()}else i.push(t);return e})),i.forEach((function(e){t.addChild(e).hydrate(t.el)})),r.forEach((function(e){t.addChild(e).recycle(t.el)})),n.forEach((function(t){r.indexOf(t)>-1||t.destroy()})),this.el.contains(s)&&s.focus()}return this.onRender.call(this,"render"),this},e.extend=function(t){var e=function(t){function e(){return t.apply(this,arguments)||this}return s(e,t),e}(this);return Object.assign(e.prototype,"function"==typeof t?t(this.prototype):t),e},e.mount=function(t,e,n){var i=new this(t);if(e){if(n)i.toString();else{var r=document.createElement("template");r.innerHTML=i,e.appendChild(r.content)}i.hydrate(e)}return i},e.create=function(t){for(var n=arguments.length,i=new Array(n>1?n-1:0),r=1;r<n;r++)i[r-1]=arguments[r];var s=[];t.forEach((function(t,n){s.push(t),i[n]&&s.push("function"==typeof i[n]||"object"==typeof i[n]?e.EXPRESSION_PLACEHOLDER_TEMPLATE(n):i[n])}));var o=s.join("").trim().replace(/\n/g,e.NEW_LINE_PLACEHOLDER),a=o.match(/^<([a-z]+[1-6]?)(.*?)>(.*)<\/\1>$/)||o.match(/^<([a-z]+)(.*?)\/>$/),h=a[1],c=function(t){for(var e,n={},i=/([\w|data-]+)(?:=["']?((?:.(?!["']?\s+(?:\S+)=|\s*\/?[>"']))+.)["']?)?/g;null!==(e=i.exec(t));)n[e[1]]=void 0===e[2]||e[2];return n}(a[2].replace(new RegExp(e.NEW_LINE_PLACEHOLDER,"g"),"")),l={},f=Object.keys(c).reduce((function(t,e){var n=e.match(/on(([A-Z]{1}[a-z]+)+)/),r=u(c[e],i);if(n&&n[1]){var s=n[1].toLowerCase();return Object.keys(r).forEach((function(t){return l[s+("&"===t?"":" "+t)]=r[t]})),t}return t[e]=r,t}),{}),d=a[3]&&a[3].replace(new RegExp(e.NEW_LINE_PLACEHOLDER,"g"),"\n");return this.extend({tag:h,attributes:f,events:l,template:{inner:d,expressions:i}})},e}(r.default);c.ID_TEMPLATE=function(t){return"rasti-component-"+t},c.EXPRESSION_PLACEHOLDER_TEMPLATE=function(t){return"__RASTI_EXPRESSION_{"+t+"}__"},c.TRUE_PLACEHOLDER="__RASTI_TRUE__",c.FALSE_PLACEHOLDER="__RASTI_FALSE__",c.NEW_LINE_PLACEHOLDER="__RASTI_NEW_LINE__"},527:(t,e)=>{e.__esModule=!0,e.default=void 0,e.default=function(){function t(){}var e=t.prototype;return e.on=function(t,e){if("function"!=typeof e)throw TypeError("Listener must be a function");this.listeners||(this.listeners={}),this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(e)},e.once=function(t,e){if("function"==typeof e){var n=this,i=e;e=function(){i.apply(void 0,arguments),n.off(t,e)}}this.on(t,e)},e.off=function(t,e){if(t)if(e){var n=this.listeners[t];n&&(n.slice().forEach((function(t,i){t===e&&n.splice(i,1)})),n.length||delete this.listeners[t])}else delete this.listeners[t];else this.listeners={}},e.emit=function(t){for(var e=arguments.length,n=new Array(e>1?e-1:0),i=1;i<e;i++)n[i-1]=arguments[i];var r=this.listeners&&this.listeners[t];r&&r.length&&r.slice().forEach((function(t){t.apply(void 0,n)}))},t}()},4:(t,e,n)=>{e.__esModule=!0,e.default=void 0;var i,r=(i=n(527))&&i.__esModule?i:{default:i};function s(t,e){return s=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},s(t,e)}e.default=function(t){function e(){var e,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return(e=t.call(this)||this).preinitialize.apply(e,arguments),e.attributes=Object.assign({},e.defaults||{},n),e.previous={},Object.keys(e.attributes).forEach(e.defineAttribute.bind(e)),e}var n,i;i=t,(n=e).prototype=Object.create(i.prototype),n.prototype.constructor=n,s(n,i);var r=e.prototype;return r.preinitialize=function(){},r.defineAttribute=function(t){var e=this;Object.defineProperty(this,t,{get:function(){return e.get(t)},set:function(n){e.set(t,n)}})},r.get=function(t){return this.attributes[t]},r.set=function(t,e){for(var n,i,r,s=this,o=arguments.length,a=new Array(o>2?o-2:0),u=2;u<o;u++)a[u-2]=arguments[u];"object"==typeof t?(n=t,i=[e].concat(a)):((r={})[t]=e,n=r,i=a);var h=this._changing;this._changing=!0;var c={};h||(this.previous=Object.assign({},this.attributes)),Object.keys(n).forEach((function(t){n[t]!==s.attributes[t]&&(c[t]=n[t],s.attributes[t]=n[t])}));var l=Object.keys(c);if(l.length&&(this._pending=["change",this,c].concat(i)),l.forEach((function(t){s.emit.apply(s,["change:"+t,s,n[t]].concat(i))})),h)return this;for(;this._pending;){var f=this._pending;this._pending=null,this.emit.apply(this,f)}return this._pending=null,this._changing=!1,this},r.toJSON=function(){return this.attributes},e}(r.default)},944:(t,e,n)=>{e.__esModule=!0,e.default=void 0;var i,r=(i=n(527))&&i.__esModule?i:{default:i};function s(t,e){return s=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(t,e){return t.__proto__=e,t},s(t,e)}var o={el:!0,tag:!0,attributes:!0,events:!0,model:!0,template:!0,onDestroy:!0};(e.default=function(t){function e(){var n,i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return(n=t.call(this)||this).preinitialize.apply(n,arguments),n.uid="uid"+ ++e.uid,n.delegatedEventListeners=[],n.children=[],Object.keys(i).forEach((function(t){o[t]&&(n[t]=i[t])})),n.ensureElement(),n}var n,i;i=t,(n=e).prototype=Object.create(i.prototype),n.prototype.constructor=n,s(n,i);var r=e.prototype;return r.preinitialize=function(){},r.$=function(t){return this.el.querySelector(t)},r.$$=function(t){return this.el.querySelectorAll(t)},r.destroy=function(){return this.destroyChildren(),this.undelegateEvents(),this.off(),this.onDestroy.apply(this,arguments),this},r.onDestroy=function(){},r.addChild=function(t){return this.children.push(t),t},r.destroyChildren=function(){for(;this.children.length;)this.children.shift().destroy()},r.ensureElement=function(){this.el||(this.el=this.createElement(this.tag,this.attributes)),this.delegateEvents()},r.createElement=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"div",e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=document.createElement(t);return Object.keys(e).forEach((function(t){return n.setAttribute(t,e[t])})),n},r.removeElement=function(){return this.el.parentNode.removeChild(this.el),this},r.delegateEvents=function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.events;if(!e)return this;this.delegatedEventListeners.length&&this.undelegateEvents();var n={};return Object.keys(e).forEach((function(i){var r=i.split(" "),s=r.shift(),o=r.join(" "),a=e[i];a=("string"==typeof a?t[a]:a).bind(t),n[s]||(n[s]=[]),n[s].push({selector:o,listener:a})})),Object.keys(n).forEach((function(e){var i=function(i){n[e].forEach((function(e){var n=e.selector,r=e.listener;n&&!i.target.closest(n)||r(i,t)}))};t.delegatedEventListeners.push({type:e,listener:i}),t.el.addEventListener(e,i)})),this},r.undelegateEvents=function(){var t=this;return this.delegatedEventListeners.forEach((function(e){var n=e.type,i=e.listener;t.el.removeEventListener(n,i)})),this.delegatedEventListeners=[],this},r.render=function(){return this.template&&(this.el.innerHTML=this.template(this.model)),this},e}(r.default)).uid=0}},e={};function n(i){var r=e[i];if(void 0!==r)return r.exports;var s=e[i]={exports:{}};return t[i](s,s.exports,n),s.exports}var i={};return(()=>{var t=i;t.__esModule=!0;var e=a(n(527));t.Emitter=e.default;var r=a(n(4));t.Model=r.default;var s=a(n(944));t.View=s.default;var o=a(n(818));function a(t){return t&&t.__esModule?t:{default:t}}t.Component=o.default})(),i})()));
|
package/es/Component.js
CHANGED
|
@@ -191,10 +191,12 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
191
191
|
_proto.destroy = function destroy() {
|
|
192
192
|
_View.prototype.destroy.apply(this, arguments);
|
|
193
193
|
// Stop listening to `change`.
|
|
194
|
-
// Set destroyed flag to prevent a last render after destroyed. TODO: Review
|
|
195
194
|
if (this.model && this.model.off) this.model.off('change', this.onChange);
|
|
196
195
|
if (this.state && this.state.off) this.state.off('change', this.onChange);
|
|
196
|
+
// Set destroyed flag to prevent a last render after destroyed.
|
|
197
197
|
this.destroyed = true;
|
|
198
|
+
// Return `this` for chaining.
|
|
199
|
+
return this;
|
|
198
200
|
}
|
|
199
201
|
|
|
200
202
|
/**
|
|
@@ -257,12 +259,12 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
257
259
|
return out + parsed;
|
|
258
260
|
}, '');
|
|
259
261
|
})
|
|
260
|
-
// Replace `attribute="true"` with `attribute`
|
|
261
|
-
.replace(new RegExp("([
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
// Replace rest of false expressions with empty string.
|
|
265
|
-
.replace(new RegExp(Component.FALSE_PLACEHOLDER, 'g'), '');
|
|
262
|
+
// Replace `attribute="true"` with `attribute` and `attribute="false"` with empty string.
|
|
263
|
+
.replace(new RegExp("([\\w|data-]+)=([\"'])?(" + Component.TRUE_PLACEHOLDER + "|" + Component.FALSE_PLACEHOLDER + ")\\2", 'g'), function (match, attribute, quote, placeholder) {
|
|
264
|
+
return placeholder === Component.TRUE_PLACEHOLDER ? attribute : '';
|
|
265
|
+
})
|
|
266
|
+
// Replace rest of true or false expressions with empty string.
|
|
267
|
+
.replace(new RegExp(Component.TRUE_PLACEHOLDER + "|" + Component.FALSE_PLACEHOLDER, 'g'), '');
|
|
266
268
|
}
|
|
267
269
|
|
|
268
270
|
/*
|
|
@@ -277,12 +279,13 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
277
279
|
// Get attributes.
|
|
278
280
|
var attributes = this.getAttributes().html;
|
|
279
281
|
// Replace expressions of inner template.
|
|
280
|
-
var
|
|
282
|
+
var close = this.template && this.template.inner;
|
|
283
|
+
var inner = close && this.replaceExpressions(this.template.inner, function (component) {
|
|
281
284
|
// Add child component.
|
|
282
285
|
return _this5.addChild(component);
|
|
283
286
|
});
|
|
284
287
|
// Generate outer template.
|
|
285
|
-
return
|
|
288
|
+
return close ? "<" + tag + " " + attributes + ">" + inner + "</" + tag + ">" : "<" + tag + " " + attributes + " />";
|
|
286
289
|
}
|
|
287
290
|
|
|
288
291
|
/*
|
|
@@ -442,21 +445,23 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
442
445
|
}
|
|
443
446
|
});
|
|
444
447
|
// Create output text for main template.
|
|
445
|
-
var main = parts.join('').trim().replace(/\n/g,
|
|
448
|
+
var main = parts.join('').trim().replace(/\n/g, Component.NEW_LINE_PLACEHOLDER);
|
|
446
449
|
// Extract outer tag, attributes and inner html.
|
|
447
|
-
var result = main.match(/^<([a-z]+)(.*?)>(.*)<\/\1>$/) || main.match(/^<([a-z]+)(.*?)\/>$/);
|
|
448
|
-
//
|
|
449
|
-
var
|
|
450
|
+
var result = main.match(/^<([a-z]+[1-6]?)(.*?)>(.*)<\/\1>$/) || main.match(/^<([a-z]+)(.*?)\/>$/);
|
|
451
|
+
// Tag name.
|
|
452
|
+
var tag = result[1];
|
|
453
|
+
// Parse attributes from html string into an object.
|
|
454
|
+
var attributesAndEvents = extractAttributes(result[2].replace(new RegExp(Component.NEW_LINE_PLACEHOLDER, 'g'), ''));
|
|
450
455
|
// Events to be delegated.
|
|
451
456
|
var events = {};
|
|
452
457
|
// Filter events. To generate events object.
|
|
453
458
|
// Generate attributes object, replace placeholders with expressions.
|
|
454
|
-
attributes = Object.keys(
|
|
459
|
+
var attributes = Object.keys(attributesAndEvents).reduce(function (out, key) {
|
|
455
460
|
// Is Event?
|
|
456
461
|
var matchKey = key.match(/on(([A-Z]{1}[a-z]+)+)/);
|
|
457
462
|
// Is placeholder for function or object?
|
|
458
463
|
// Get expression or value.
|
|
459
|
-
var value = getExpression(
|
|
464
|
+
var value = getExpression(attributesAndEvents[key], expressions);
|
|
460
465
|
// Is event handler. Add to events object.
|
|
461
466
|
if (matchKey && matchKey[1]) {
|
|
462
467
|
var eventType = matchKey[1].toLowerCase();
|
|
@@ -469,22 +474,24 @@ var Component = /*#__PURE__*/function (_View) {
|
|
|
469
474
|
out[key] = value;
|
|
470
475
|
return out;
|
|
471
476
|
}, {});
|
|
477
|
+
// Inner html template. Restore new lines.
|
|
478
|
+
var inner = result[3] && result[3].replace(new RegExp(Component.NEW_LINE_PLACEHOLDER, 'g'), '\n');
|
|
472
479
|
var Current = this;
|
|
473
480
|
// Create subclass for this component.
|
|
474
481
|
return Current.extend({
|
|
475
|
-
// Set
|
|
476
|
-
|
|
482
|
+
// Set root element tag.
|
|
483
|
+
tag: tag,
|
|
477
484
|
// Set attributes.
|
|
478
485
|
attributes: attributes,
|
|
486
|
+
// Set events.
|
|
487
|
+
events: events,
|
|
479
488
|
// Set template.
|
|
480
489
|
template: {
|
|
481
490
|
// Template for innerHTML of root element.
|
|
482
|
-
inner:
|
|
491
|
+
inner: inner,
|
|
483
492
|
// Template expressions.
|
|
484
493
|
expressions: expressions
|
|
485
|
-
}
|
|
486
|
-
// Set root element tag.
|
|
487
|
-
tag: result[1]
|
|
494
|
+
}
|
|
488
495
|
});
|
|
489
496
|
};
|
|
490
497
|
return Component;
|
|
@@ -494,7 +501,8 @@ Component.ID_TEMPLATE = function (uid) {
|
|
|
494
501
|
return "rasti-component-" + uid;
|
|
495
502
|
};
|
|
496
503
|
Component.EXPRESSION_PLACEHOLDER_TEMPLATE = function (idx) {
|
|
497
|
-
return "
|
|
504
|
+
return "__RASTI_EXPRESSION_{" + idx + "}__";
|
|
498
505
|
};
|
|
499
|
-
Component.TRUE_PLACEHOLDER = '
|
|
500
|
-
Component.FALSE_PLACEHOLDER = '
|
|
506
|
+
Component.TRUE_PLACEHOLDER = '__RASTI_TRUE__';
|
|
507
|
+
Component.FALSE_PLACEHOLDER = '__RASTI_FALSE__';
|
|
508
|
+
Component.NEW_LINE_PLACEHOLDER = '__RASTI_NEW_LINE__';
|
package/lib/Component.js
CHANGED
|
@@ -195,10 +195,12 @@ var Component = exports["default"] = /*#__PURE__*/function (_View) {
|
|
|
195
195
|
_proto.destroy = function destroy() {
|
|
196
196
|
_View.prototype.destroy.apply(this, arguments);
|
|
197
197
|
// Stop listening to `change`.
|
|
198
|
-
// Set destroyed flag to prevent a last render after destroyed. TODO: Review
|
|
199
198
|
if (this.model && this.model.off) this.model.off('change', this.onChange);
|
|
200
199
|
if (this.state && this.state.off) this.state.off('change', this.onChange);
|
|
200
|
+
// Set destroyed flag to prevent a last render after destroyed.
|
|
201
201
|
this.destroyed = true;
|
|
202
|
+
// Return `this` for chaining.
|
|
203
|
+
return this;
|
|
202
204
|
}
|
|
203
205
|
|
|
204
206
|
/**
|
|
@@ -261,12 +263,12 @@ var Component = exports["default"] = /*#__PURE__*/function (_View) {
|
|
|
261
263
|
return out + parsed;
|
|
262
264
|
}, '');
|
|
263
265
|
})
|
|
264
|
-
// Replace `attribute="true"` with `attribute`
|
|
265
|
-
.replace(new RegExp("([
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
// Replace rest of false expressions with empty string.
|
|
269
|
-
.replace(new RegExp(Component.FALSE_PLACEHOLDER, 'g'), '');
|
|
266
|
+
// Replace `attribute="true"` with `attribute` and `attribute="false"` with empty string.
|
|
267
|
+
.replace(new RegExp("([\\w|data-]+)=([\"'])?(" + Component.TRUE_PLACEHOLDER + "|" + Component.FALSE_PLACEHOLDER + ")\\2", 'g'), function (match, attribute, quote, placeholder) {
|
|
268
|
+
return placeholder === Component.TRUE_PLACEHOLDER ? attribute : '';
|
|
269
|
+
})
|
|
270
|
+
// Replace rest of true or false expressions with empty string.
|
|
271
|
+
.replace(new RegExp(Component.TRUE_PLACEHOLDER + "|" + Component.FALSE_PLACEHOLDER, 'g'), '');
|
|
270
272
|
}
|
|
271
273
|
|
|
272
274
|
/*
|
|
@@ -281,12 +283,13 @@ var Component = exports["default"] = /*#__PURE__*/function (_View) {
|
|
|
281
283
|
// Get attributes.
|
|
282
284
|
var attributes = this.getAttributes().html;
|
|
283
285
|
// Replace expressions of inner template.
|
|
284
|
-
var
|
|
286
|
+
var close = this.template && this.template.inner;
|
|
287
|
+
var inner = close && this.replaceExpressions(this.template.inner, function (component) {
|
|
285
288
|
// Add child component.
|
|
286
289
|
return _this5.addChild(component);
|
|
287
290
|
});
|
|
288
291
|
// Generate outer template.
|
|
289
|
-
return
|
|
292
|
+
return close ? "<" + tag + " " + attributes + ">" + inner + "</" + tag + ">" : "<" + tag + " " + attributes + " />";
|
|
290
293
|
}
|
|
291
294
|
|
|
292
295
|
/*
|
|
@@ -446,21 +449,23 @@ var Component = exports["default"] = /*#__PURE__*/function (_View) {
|
|
|
446
449
|
}
|
|
447
450
|
});
|
|
448
451
|
// Create output text for main template.
|
|
449
|
-
var main = parts.join('').trim().replace(/\n/g,
|
|
452
|
+
var main = parts.join('').trim().replace(/\n/g, Component.NEW_LINE_PLACEHOLDER);
|
|
450
453
|
// Extract outer tag, attributes and inner html.
|
|
451
|
-
var result = main.match(/^<([a-z]+)(.*?)>(.*)<\/\1>$/) || main.match(/^<([a-z]+)(.*?)\/>$/);
|
|
452
|
-
//
|
|
453
|
-
var
|
|
454
|
+
var result = main.match(/^<([a-z]+[1-6]?)(.*?)>(.*)<\/\1>$/) || main.match(/^<([a-z]+)(.*?)\/>$/);
|
|
455
|
+
// Tag name.
|
|
456
|
+
var tag = result[1];
|
|
457
|
+
// Parse attributes from html string into an object.
|
|
458
|
+
var attributesAndEvents = extractAttributes(result[2].replace(new RegExp(Component.NEW_LINE_PLACEHOLDER, 'g'), ''));
|
|
454
459
|
// Events to be delegated.
|
|
455
460
|
var events = {};
|
|
456
461
|
// Filter events. To generate events object.
|
|
457
462
|
// Generate attributes object, replace placeholders with expressions.
|
|
458
|
-
attributes = Object.keys(
|
|
463
|
+
var attributes = Object.keys(attributesAndEvents).reduce(function (out, key) {
|
|
459
464
|
// Is Event?
|
|
460
465
|
var matchKey = key.match(/on(([A-Z]{1}[a-z]+)+)/);
|
|
461
466
|
// Is placeholder for function or object?
|
|
462
467
|
// Get expression or value.
|
|
463
|
-
var value = getExpression(
|
|
468
|
+
var value = getExpression(attributesAndEvents[key], expressions);
|
|
464
469
|
// Is event handler. Add to events object.
|
|
465
470
|
if (matchKey && matchKey[1]) {
|
|
466
471
|
var eventType = matchKey[1].toLowerCase();
|
|
@@ -473,22 +478,24 @@ var Component = exports["default"] = /*#__PURE__*/function (_View) {
|
|
|
473
478
|
out[key] = value;
|
|
474
479
|
return out;
|
|
475
480
|
}, {});
|
|
481
|
+
// Inner html template. Restore new lines.
|
|
482
|
+
var inner = result[3] && result[3].replace(new RegExp(Component.NEW_LINE_PLACEHOLDER, 'g'), '\n');
|
|
476
483
|
var Current = this;
|
|
477
484
|
// Create subclass for this component.
|
|
478
485
|
return Current.extend({
|
|
479
|
-
// Set
|
|
480
|
-
|
|
486
|
+
// Set root element tag.
|
|
487
|
+
tag: tag,
|
|
481
488
|
// Set attributes.
|
|
482
489
|
attributes: attributes,
|
|
490
|
+
// Set events.
|
|
491
|
+
events: events,
|
|
483
492
|
// Set template.
|
|
484
493
|
template: {
|
|
485
494
|
// Template for innerHTML of root element.
|
|
486
|
-
inner:
|
|
495
|
+
inner: inner,
|
|
487
496
|
// Template expressions.
|
|
488
497
|
expressions: expressions
|
|
489
|
-
}
|
|
490
|
-
// Set root element tag.
|
|
491
|
-
tag: result[1]
|
|
498
|
+
}
|
|
492
499
|
});
|
|
493
500
|
};
|
|
494
501
|
return Component;
|
|
@@ -497,7 +504,8 @@ Component.ID_TEMPLATE = function (uid) {
|
|
|
497
504
|
return "rasti-component-" + uid;
|
|
498
505
|
};
|
|
499
506
|
Component.EXPRESSION_PLACEHOLDER_TEMPLATE = function (idx) {
|
|
500
|
-
return "
|
|
507
|
+
return "__RASTI_EXPRESSION_{" + idx + "}__";
|
|
501
508
|
};
|
|
502
|
-
Component.TRUE_PLACEHOLDER = '
|
|
503
|
-
Component.FALSE_PLACEHOLDER = '
|
|
509
|
+
Component.TRUE_PLACEHOLDER = '__RASTI_TRUE__';
|
|
510
|
+
Component.FALSE_PLACEHOLDER = '__RASTI_FALSE__';
|
|
511
|
+
Component.NEW_LINE_PLACEHOLDER = '__RASTI_NEW_LINE__';
|
package/package.json
CHANGED
package/src/Component.js
CHANGED
|
@@ -180,10 +180,12 @@ export default class Component extends View {
|
|
|
180
180
|
destroy() {
|
|
181
181
|
super.destroy.apply(this, arguments);
|
|
182
182
|
// Stop listening to `change`.
|
|
183
|
-
// Set destroyed flag to prevent a last render after destroyed. TODO: Review
|
|
184
183
|
if (this.model && this.model.off) this.model.off('change', this.onChange);
|
|
185
184
|
if (this.state && this.state.off) this.state.off('change', this.onChange);
|
|
185
|
+
// Set destroyed flag to prevent a last render after destroyed.
|
|
186
186
|
this.destroyed = true;
|
|
187
|
+
// Return `this` for chaining.
|
|
188
|
+
return this;
|
|
187
189
|
}
|
|
188
190
|
|
|
189
191
|
/**
|
|
@@ -247,12 +249,13 @@ export default class Component extends View {
|
|
|
247
249
|
}, '');
|
|
248
250
|
|
|
249
251
|
})
|
|
250
|
-
// Replace `attribute="true"` with `attribute`
|
|
251
|
-
.replace(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
252
|
+
// Replace `attribute="true"` with `attribute` and `attribute="false"` with empty string.
|
|
253
|
+
.replace(
|
|
254
|
+
new RegExp(`([\\w|data-]+)=(["'])?(${Component.TRUE_PLACEHOLDER}|${Component.FALSE_PLACEHOLDER})\\2`, 'g'),
|
|
255
|
+
(match, attribute, quote, placeholder) => placeholder === Component.TRUE_PLACEHOLDER ? attribute : ''
|
|
256
|
+
)
|
|
257
|
+
// Replace rest of true or false expressions with empty string.
|
|
258
|
+
.replace(new RegExp(`${Component.TRUE_PLACEHOLDER}|${Component.FALSE_PLACEHOLDER}`, 'g'), '');
|
|
256
259
|
}
|
|
257
260
|
|
|
258
261
|
/*
|
|
@@ -266,14 +269,14 @@ export default class Component extends View {
|
|
|
266
269
|
// Get attributes.
|
|
267
270
|
const attributes = this.getAttributes().html;
|
|
268
271
|
// Replace expressions of inner template.
|
|
269
|
-
const
|
|
270
|
-
|
|
272
|
+
const close = this.template && this.template.inner;
|
|
273
|
+
const inner = close &&
|
|
271
274
|
this.replaceExpressions(this.template.inner, (component) => {
|
|
272
275
|
// Add child component.
|
|
273
276
|
return this.addChild(component);
|
|
274
277
|
});
|
|
275
278
|
// Generate outer template.
|
|
276
|
-
return
|
|
279
|
+
return close ?
|
|
277
280
|
`<${tag} ${attributes}>${inner}</${tag}>` :
|
|
278
281
|
`<${tag} ${attributes} />`;
|
|
279
282
|
}
|
|
@@ -438,21 +441,23 @@ export default class Component extends View {
|
|
|
438
441
|
}
|
|
439
442
|
});
|
|
440
443
|
// Create output text for main template.
|
|
441
|
-
const main = parts.join('').trim().replace(/\n/g,
|
|
444
|
+
const main = parts.join('').trim().replace(/\n/g, Component.NEW_LINE_PLACEHOLDER);
|
|
442
445
|
// Extract outer tag, attributes and inner html.
|
|
443
|
-
const result = main.match(/^<([a-z]+)(.*?)>(.*)<\/\1>$/) || main.match(/^<([a-z]+)(.*?)\/>$/);
|
|
444
|
-
//
|
|
445
|
-
|
|
446
|
+
const result = main.match(/^<([a-z]+[1-6]?)(.*?)>(.*)<\/\1>$/) || main.match(/^<([a-z]+)(.*?)\/>$/);
|
|
447
|
+
// Tag name.
|
|
448
|
+
const tag = result[1];
|
|
449
|
+
// Parse attributes from html string into an object.
|
|
450
|
+
const attributesAndEvents = extractAttributes(result[2].replace(new RegExp(Component.NEW_LINE_PLACEHOLDER, 'g'), ''));
|
|
446
451
|
// Events to be delegated.
|
|
447
|
-
|
|
452
|
+
const events = {};
|
|
448
453
|
// Filter events. To generate events object.
|
|
449
454
|
// Generate attributes object, replace placeholders with expressions.
|
|
450
|
-
attributes = Object.keys(
|
|
455
|
+
const attributes = Object.keys(attributesAndEvents).reduce((out, key) => {
|
|
451
456
|
// Is Event?
|
|
452
457
|
const matchKey = key.match(/on(([A-Z]{1}[a-z]+)+)/);
|
|
453
458
|
// Is placeholder for function or object?
|
|
454
459
|
// Get expression or value.
|
|
455
|
-
const value = getExpression(
|
|
460
|
+
const value = getExpression(attributesAndEvents[key], expressions);
|
|
456
461
|
// Is event handler. Add to events object.
|
|
457
462
|
if (matchKey && matchKey[1]) {
|
|
458
463
|
const eventType = matchKey[1].toLowerCase();
|
|
@@ -463,30 +468,34 @@ export default class Component extends View {
|
|
|
463
468
|
}
|
|
464
469
|
// Is attribute. Add to attributes object.
|
|
465
470
|
out[key] = value;
|
|
471
|
+
|
|
466
472
|
return out;
|
|
467
473
|
}, {});
|
|
474
|
+
// Inner html template. Restore new lines.
|
|
475
|
+
const inner = result[3] && result[3].replace(new RegExp(Component.NEW_LINE_PLACEHOLDER, 'g'), '\n');
|
|
468
476
|
|
|
469
477
|
const Current = this;
|
|
470
478
|
// Create subclass for this component.
|
|
471
479
|
return Current.extend({
|
|
472
|
-
// Set
|
|
473
|
-
|
|
480
|
+
// Set root element tag.
|
|
481
|
+
tag,
|
|
474
482
|
// Set attributes.
|
|
475
483
|
attributes,
|
|
484
|
+
// Set events.
|
|
485
|
+
events,
|
|
476
486
|
// Set template.
|
|
477
487
|
template : {
|
|
478
488
|
// Template for innerHTML of root element.
|
|
479
|
-
inner
|
|
489
|
+
inner,
|
|
480
490
|
// Template expressions.
|
|
481
|
-
expressions
|
|
482
|
-
}
|
|
483
|
-
// Set root element tag.
|
|
484
|
-
tag : result[1]
|
|
491
|
+
expressions
|
|
492
|
+
}
|
|
485
493
|
});
|
|
486
494
|
}
|
|
487
495
|
}
|
|
488
496
|
|
|
489
497
|
Component.ID_TEMPLATE = (uid) => `rasti-component-${uid}`;
|
|
490
|
-
Component.EXPRESSION_PLACEHOLDER_TEMPLATE = (idx) => `
|
|
491
|
-
Component.TRUE_PLACEHOLDER = '
|
|
492
|
-
Component.FALSE_PLACEHOLDER = '
|
|
498
|
+
Component.EXPRESSION_PLACEHOLDER_TEMPLATE = (idx) => `__RASTI_EXPRESSION_{${idx}}__`;
|
|
499
|
+
Component.TRUE_PLACEHOLDER = '__RASTI_TRUE__';
|
|
500
|
+
Component.FALSE_PLACEHOLDER = '__RASTI_FALSE__';
|
|
501
|
+
Component.NEW_LINE_PLACEHOLDER = '__RASTI_NEW_LINE__';
|