rasti 0.0.12 → 1.0.0

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 CHANGED
@@ -1,9 +1,9 @@
1
1
  # <a href='http://rasti.js.org'><img src='logo.svg' height='80' alt='Rasti' aria-label='rasti.js.org' /></a>
2
2
 
3
- Rasti is a minimalistic JavaScript MV framework for building user interfaces.<br />
4
- It gives structure to applications by providing [models](docs/api.md#module_Model) that emit events on properties changes, and [views](docs/api.md#module_View) with declarative event handling to define UI components.<br />
5
- Rasti is inspired by Backbone. You can consider it as an ES6 subset of Backbone with no dependencies.<br />
6
- It's ideal for building simple lightweight applications, without the need of config or boilerplate. Projects where a resulting small codebase is prioritized over using a complex rendering system.<br />
3
+ Rasti is a minimalistic JavaScript MV library for building user interfaces.<br />
4
+ It gives structure to applications by providing [models](docs/api.md#module_Model) that emit events on properties changes, and [views](docs/api.md#module_View) with declarative dom events handling to define UI components.<br />
5
+ Rasti is inspired by the Backbone.js architecture. It can be considered as an ES6 subset of Backbone.js for modern browsers.<br />
6
+ It's ideal for building simple lightweight applications, without the need of configuration or boilerplate. Projects where a resulting small codebase is prioritized over using a complex rendering system.<br />
7
7
  The project is [hosted on GitHub](https://github.com/8tentaculos/rasti), and it's available for use under the [MIT](LICENSE.md) software license.<br />
8
8
  You can report bugs and discuss features on the [GitHub issues page](https://github.com/8tentaculos/rasti/issues).
9
9
 
@@ -20,7 +20,13 @@ $ npm install --save rasti
20
20
  ```
21
21
 
22
22
  ```javascript
23
- import { View } from 'rasti';
23
+ import { Model, View } from 'rasti';
24
+ ```
25
+
26
+ #### Using native modules
27
+
28
+ ```javascript
29
+ import { Model, View } from 'https://unpkg.com/rasti/es';
24
30
  ```
25
31
 
26
32
  #### Using `<script>` tag
@@ -30,27 +36,39 @@ import { View } from 'rasti';
30
36
  ```
31
37
 
32
38
  ```javascript
33
- const { View } = Rasti;
39
+ const { Model, View } = Rasti;
34
40
  ```
35
41
 
36
42
  #### A simple `View`
37
43
 
38
44
  ```javascript
39
- const hello = new View({
40
- tag : 'h1',
41
- template : (model) => `Hello ${model.who}!`,
42
- model : { who : 'World' }
43
- });
44
-
45
- document.body.appendChild(hello.render().el); // <h1>Hello World!</h1>
45
+ class Timer extends View {
46
+ constructor(options) {
47
+ super(options);
48
+ // Create model to store internal state. Set `seconds` attribute into 0.
49
+ this.model = new Model({ seconds : 0 });
50
+ // Listen to changes in model `seconds` attribute and re render.
51
+ this.model.on('change:seconds', this.render.bind(this));
52
+ // Increment model `seconds` attribute every 1000 milliseconds.
53
+ this.interval = setInterval(() => this.model.seconds++, 1000);
54
+ }
55
+
56
+ template(model) {
57
+ return `Seconds: <span>${model.seconds}</span>`;
58
+ }
59
+ }
60
+ // Render view and append view's element into body.
61
+ document.body.appendChild(new Timer().render().el);
46
62
  ```
47
63
 
64
+ [Try it on CodePen](https://codepen.io/8tentaculos/pen/dyXgGMp?editors=0010)
65
+
48
66
  The [rasti npm package](https://www.npmjs.com/package/rasti) includes precompiled production and development UMD builds in the dist folder. They can be used directly without a bundler and are thus compatible with many popular JavaScript module loaders and environments.<br />
49
- For example, you can drop a UMD build as a `<script>` tag on the page, or tell Bower to install it. The UMD builds make Rasti available as a window.Rasti global variable.
67
+ The UMD builds make Rasti available as a `window.Rasti` global variable.
50
68
 
51
69
  ## Example
52
70
 
53
- The rasti [GitHub repository](https://github.com/8tentaculos/rasti) includes, in the example folder, a [vanila js](https://github.com/8tentaculos/rasti/tree/master/example/todo) and a [webpack](https://github.com/8tentaculos/rasti/tree/master/example/todo-webpack) versions of a [Todo app](http://rasti.js.org/example/todo/index.html) that can be used as starter project.
71
+ 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.
54
72
 
55
73
  ## API
56
74
 
package/dist/rasti.js CHANGED
@@ -104,7 +104,7 @@ return /******/ (function(modules) { // webpackBootstrap
104
104
  /***/ (function(module, exports, __webpack_require__) {
105
105
 
106
106
  "use strict";
107
- eval("\n\nexports.__esModule = true;\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\n/**\n * Emitter is a class that can be extended giving the subclass the ability to emit \n * and bind custom named events. Model and View inherits from it.\n * @module\n * @example\n * import { Emitter } from 'rasti';\n * // Define App class, inherits from Emitter.\n * class App extends Emitter {\n * constructor() {\n * super()\n * // Fetch data and emit `ready` event.\n * fetch('/api/init').then(() => this.emit('ready')); \n * }\n * }\n * // Instantiate App.\n * const app = new App();\n * // Add event listener to ready event.\n * app.on('ready', () => console.log('app is ready!'));\n */\nvar Emitter = function () {\n function Emitter() {\n _classCallCheck(this, Emitter);\n }\n\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 emited.\n * @example\n * this.model.on('change', this.render.bind(this)); // Re render when model changes.\n */\n Emitter.prototype.on = function on(type, listener) {\n if (typeof listener !== 'function') {\n throw TypeError('Listener must be a function');\n }\n\n if (!this.listeners) this.listeners = {};\n if (!this.listeners[type]) this.listeners[type] = [];\n\n this.listeners[type].push(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 emited.\n * @example\n * this.model.once('change', () => console.log('This will happen once'));\n */\n\n\n Emitter.prototype.once = function once(type, _listener2) {\n if (typeof _listener2 === 'function') {\n var self = this;\n var _listener = _listener2;\n\n _listener2 = function listener() {\n _listener.apply(undefined, arguments);\n self.off(type, _listener2);\n };\n }\n this.on(type, _listener2);\n };\n\n /**\n * Removes event listeners.\n * @param {string} [type] Type of the event (e.g. `change`). If is not provided, it removes all listeners.\n * @param {function} [listener] Callback function to be called when the event is emited. If listener is not provided, it removes all listeners for specified type.\n * @example\n * this.model.off('change'); // Stop listening to changes.\n */\n\n\n Emitter.prototype.off = function off(type, listener) {\n if (!type) {\n this.listeners = {};\n } else {\n if (!listener) {\n delete this.listeners[type];\n } else {\n var listeners = this.listeners[type];\n if (listeners) {\n var copy = listeners.slice();\n\n copy.forEach(function (fn, idx) {\n if (fn === listener) listeners.splice(idx, 1);\n });\n\n if (!listeners.length) {\n delete this.listeners[type];\n }\n }\n }\n }\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] Arguments to be passed to listener.\n * @example\n * this.emit('invalid'); // Emit validation error event.\n */\n\n\n Emitter.prototype.emit = function emit(type) {\n for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n args[_key - 1] = arguments[_key];\n }\n\n var listeners = this.listeners && this.listeners[type];\n\n if (!listeners || !listeners.length) return;\n\n var copy = listeners.slice();\n\n copy.forEach(function (fn) {\n fn.apply(undefined, args);\n });\n };\n\n return Emitter;\n}();\n\nexports['default'] = Emitter;\n\n//# sourceURL=webpack://Rasti/./src/Emitter.js?");
107
+ eval("\n\nexports.__esModule = true;\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\n/**\n * Emitter is a class that can be extended giving the subclass the ability to emit \n * and bind custom named events. Model and View inherits from it.\n * @module\n * @example\n * import { Emitter } from 'rasti';\n * // Define App class, inherits from Emitter.\n * class App extends Emitter {\n * constructor() {\n * super()\n * // Fetch data and emit `ready` event.\n * fetch('/api/init').then(() => this.emit('ready')); \n * }\n * }\n * // Instantiate App.\n * const app = new App();\n * // Add event listener to ready event.\n * app.on('ready', () => console.log('app is ready!'));\n */\nvar Emitter = function () {\n function Emitter() {\n _classCallCheck(this, Emitter);\n }\n\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 * @example\n * this.model.on('change', this.render.bind(this)); // Re render when model changes.\n */\n Emitter.prototype.on = function on(type, listener) {\n if (typeof listener !== 'function') {\n throw TypeError('Listener must be a function');\n }\n\n if (!this.listeners) this.listeners = {};\n if (!this.listeners[type]) this.listeners[type] = [];\n\n this.listeners[type].push(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 * @example\n * this.model.once('change', () => console.log('This will happen once'));\n */\n\n\n Emitter.prototype.once = function once(type, _listener2) {\n if (typeof _listener2 === 'function') {\n var self = this;\n var _listener = _listener2;\n\n _listener2 = function listener() {\n _listener.apply(undefined, arguments);\n self.off(type, _listener2);\n };\n }\n this.on(type, _listener2);\n };\n\n /**\n * Removes event listeners.\n * @param {string} [type] Type of the event (e.g. `change`). If is not provided, it removes all listeners.\n * @param {function} [listener] Callback function to be called when the event is emitted. If listener is not provided, it removes all listeners for specified type.\n * @example\n * this.model.off('change'); // Stop listening to changes.\n */\n\n\n Emitter.prototype.off = function off(type, listener) {\n if (!type) {\n this.listeners = {};\n } else {\n if (!listener) {\n delete this.listeners[type];\n } else {\n var listeners = this.listeners[type];\n if (listeners) {\n var copy = listeners.slice();\n\n copy.forEach(function (fn, idx) {\n if (fn === listener) listeners.splice(idx, 1);\n });\n\n if (!listeners.length) {\n delete this.listeners[type];\n }\n }\n }\n }\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] Arguments to be passed to listener.\n * @example\n * this.emit('invalid'); // Emit validation error event.\n */\n\n\n Emitter.prototype.emit = function emit(type) {\n for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n args[_key - 1] = arguments[_key];\n }\n\n var listeners = this.listeners && this.listeners[type];\n\n if (!listeners || !listeners.length) return;\n\n var copy = listeners.slice();\n\n copy.forEach(function (fn) {\n fn.apply(undefined, args);\n });\n };\n\n return Emitter;\n}();\n\nexports['default'] = Emitter;\n\n//# sourceURL=webpack://Rasti/./src/Emitter.js?");
108
108
 
109
109
  /***/ }),
110
110
 
@@ -116,7 +116,7 @@ eval("\n\nexports.__esModule = true;\n\nfunction _classCallCheck(instance, Const
116
116
  /***/ (function(module, exports, __webpack_require__) {
117
117
 
118
118
  "use strict";
119
- eval("\n\nexports.__esModule = true;\n\nvar _Emitter2 = __webpack_require__(/*! ./Emitter */ \"./src/Emitter.js\");\n\nvar _Emitter3 = _interopRequireDefault(_Emitter2);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\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 \n * when any of its data is modified.<br />\n * Models may handle syncing data with a persistence layer.<br />\n * Design your models as the atomic reusable objects containing all of the helpful functions for \n * manipulating their particular bit of data.<br /> \n * Models should be able to be passed around throughout your app, and used anywhere that bit of data is needed.<br />\n * Rasti Models stores its attributes in `this.attributes`, which is extended from `this.defaults` and constructor `attrs` parameter.\n * For every attribute, a getter is generated, which retrieve the model property from `this.attributes`.\n * And a setter, which sets the model property in `this.attributes` and emits `change` and `change:attribute` events.\n * @module\n * @param {object} attrs Object containing model attributes to extend `this.attributes`. Getters and setters are generated for `this.attributtes`, in order to emit `change` events.\n * @example\n * // Todo model\n * class TodoModel extends Rasti.Model {\n * toggle() {\n * // Set completed property. This will call a setter that will set `completed` \n * // in this.attributes, and emit `change` and `change:completed` events.\n * this.completed = !this.completed; \n * }\n * }\n * // Todo model has `title` and `completed` default attributes. `defaults` will extend `this.attributes`. Getters and setters are generated for `this.attributtes`, in order to emit `change` events.\n * TodoModel.prototype.defaults = {\n * title : '',\n * completed : false\n * };\n * // Create todo. Pass `title` attribute as argument.\n * const todo = new TodoModel({ title : 'Learn Rasti' });\n * // Listen to `change:completed` event.\n * todo.on('change:completed', () => console.log('Completed:', todo.completed));\n * // Complete todo.\n * todo.toggle(); // Completed: true\n */\nvar Model = function (_Emitter) {\n _inherits(Model, _Emitter);\n\n function Model() {\n var attrs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n _classCallCheck(this, Model);\n\n // attributes object\n var _this = _possibleConstructorReturn(this, _Emitter.call(this));\n\n _this.attributes = Object.assign({}, _this.defaults || {}, attrs);\n // previous attributes\n _this.previous = {};\n // Generate getters/setters for every attr\n Object.keys(_this.attributes).forEach(_this.defineAttribute.bind(_this));\n return _this;\n }\n\n /**\n * Generate getter/setter for the given key. In order to emit `change` events.\n * This method is called internally by the constructor\n * for `this.attributes`.\n * @param {string} key Attribute key.\n */\n\n\n Model.prototype.defineAttribute = function defineAttribute(key) {\n var _this2 = this;\n\n Object.defineProperty(this, key, {\n get: function get() {\n return _this2.get(key);\n },\n set: function set(value) {\n return _this2.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\n\n Model.prototype.get = function get(key) {\n return this.attributes[key];\n };\n\n /**\n * Set an attribute into `this.attributes`.\n * Emit `change` and `change:attribute` if value change.\n * Could be called in two forms, `this.set('key', value)` and\n * `this.set({ key : value })`.\n * This method is called internally by generated setters.\n * @param {string} key Attribute key or object containing keys/values.\n * @param [value] Attribute value.\n * @return {this} This model.\n * @emits change\n * @emits change:attribute\n */\n\n\n Model.prototype.set = function set(key, value) {\n var _ref,\n _this3 = this;\n\n var attrs = typeof key === 'object' ? key : (_ref = {}, _ref[key] = value, _ref);\n Object.keys(attrs).forEach(function (key) {\n var changed = key in _this3.attributes && attrs[key] !== _this3.attributes[key];\n\n _this3.previous[key] = _this3.attributes[key];\n _this3.attributes[key] = attrs[key];\n\n if (changed) {\n _this3.emit('change', _this3, key, attrs[key]);\n _this3.emit('change:' + key, _this3, attrs[key]);\n }\n });\n return this;\n };\n\n /**\n * Return object representation of the model to be used for JSON serialization.\n * By default returns `this.attributes`.\n * @return {object} Object representation of the model to be used for JSON serialization.\n */\n\n\n Model.prototype.toJSON = function toJSON() {\n return this.attributes;\n };\n\n return Model;\n}(_Emitter3['default']);\n\nexports['default'] = Model;\n\n//# sourceURL=webpack://Rasti/./src/Model.js?");
119
+ eval("\n\nexports.__esModule = true;\n\nvar _Emitter2 = __webpack_require__(/*! ./Emitter */ \"./src/Emitter.js\");\n\nvar _Emitter3 = _interopRequireDefault(_Emitter2);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\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 \n * when any of its data is modified.<br />\n * Models may handle syncing data with a persistence layer.<br />\n * Design your models as the atomic reusable objects containing all of the helpful functions for \n * manipulating their particular bit of data.<br /> \n * Models should be able to be passed around throughout your app, and used anywhere that bit of data is needed.<br />\n * Rasti Models stores its attributes in `this.attributes`, which is extended from `this.defaults` and constructor `attrs` parameter.\n * For every attribute, a getter is generated, which retrieve the model property from `this.attributes`.\n * And a setter, which sets the model property in `this.attributes` and emits `change` and `change:attribute` events.\n * @module\n * @param {object} attrs Object containing model attributes to extend `this.attributes`. Getters and setters are generated for `this.attributtes`, in order to emit `change` events.\n * @example\n * // Todo model\n * class TodoModel extends Rasti.Model {\n * toggle() {\n * // Set completed property. This will call a setter that will set `completed` \n * // in this.attributes, and emit `change` and `change:completed` events.\n * this.completed = !this.completed; \n * }\n * }\n * // Todo model has `title` and `completed` default attributes. `defaults` will extend `this.attributes`. Getters and setters are generated for `this.attributtes`, in order to emit `change` events.\n * TodoModel.prototype.defaults = {\n * title : '',\n * completed : false\n * };\n * // Create todo. Pass `title` attribute as argument.\n * const todo = new TodoModel({ title : 'Learn Rasti' });\n * // Listen to `change:completed` event.\n * todo.on('change:completed', () => console.log('Completed:', todo.completed));\n * // Complete todo.\n * todo.toggle(); // Completed: true\n */\nvar Model = function (_Emitter) {\n _inherits(Model, _Emitter);\n\n function Model() {\n var attrs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n _classCallCheck(this, Model);\n\n // Call preinitialize.\n var _this = _possibleConstructorReturn(this, _Emitter.call(this));\n\n _this.preinitialize.apply(_this, arguments);\n // attributes object.\n _this.attributes = Object.assign({}, _this.defaults || {}, attrs);\n // Previous attributes.\n _this.previous = {};\n // Generate getters/setters for every attr.\n Object.keys(_this.attributes).forEach(_this.defineAttribute.bind(_this));\n return _this;\n }\n\n /**\n * If you define a preinitialize method, it will be invoked when the Model is first created, before any instantiation logic is run for the Model.\n * @param {object} attrs Object containing model attributes to extend `this.attributes`.\n */\n\n\n Model.prototype.preinitialize = function preinitialize() {};\n\n /**\n * Generate getter/setter for the given key. In order to emit `change` events.\n * This method is called internally by the constructor\n * for `this.attributes`.\n * @param {string} key Attribute key.\n */\n\n\n Model.prototype.defineAttribute = function defineAttribute(key) {\n var _this2 = this;\n\n Object.defineProperty(this, key, {\n get: function get() {\n return _this2.get(key);\n },\n set: function set(value) {\n return _this2.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\n\n Model.prototype.get = function get(key) {\n return this.attributes[key];\n };\n\n /**\n * Set an attribute into `this.attributes`.\n * Emit `change` and `change:attribute` if value change.\n * Could be called in two forms, `this.set('key', value)` and\n * `this.set({ key : value })`.\n * This method is called internally by generated setters.\n * @param {string} key Attribute key or object containing keys/values.\n * @param [value] Attribute value.\n * @return {this} This model.\n * @emits change\n * @emits change:attribute\n */\n\n\n Model.prototype.set = function set(key, value) {\n var _ref,\n _this3 = this;\n\n var attrs = typeof key === 'object' ? key : (_ref = {}, _ref[key] = value, _ref);\n Object.keys(attrs).forEach(function (key) {\n var changed = key in _this3.attributes && attrs[key] !== _this3.attributes[key];\n\n _this3.previous[key] = _this3.attributes[key];\n _this3.attributes[key] = attrs[key];\n // Emit change events.\n if (changed) {\n _this3.emit('change', _this3, key, attrs[key]);\n _this3.emit('change:' + key, _this3, attrs[key]);\n }\n });\n return this;\n };\n\n /**\n * Return object representation of the model to be used for JSON serialization.\n * By default returns `this.attributes`.\n * @return {object} Object representation of the model to be used for JSON serialization.\n */\n\n\n Model.prototype.toJSON = function toJSON() {\n return this.attributes;\n };\n\n return Model;\n}(_Emitter3['default']);\n\nexports['default'] = Model;\n\n//# sourceURL=webpack://Rasti/./src/Model.js?");
120
120
 
121
121
  /***/ }),
122
122
 
@@ -128,7 +128,7 @@ eval("\n\nexports.__esModule = true;\n\nvar _Emitter2 = __webpack_require__(/*!
128
128
  /***/ (function(module, exports, __webpack_require__) {
129
129
 
130
130
  "use strict";
131
- eval("\n\nexports.__esModule = true;\n\nvar _Emitter2 = __webpack_require__(/*! ./Emitter */ \"./src/Emitter.js\");\n\nvar _Emitter3 = _interopRequireDefault(_Emitter2);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\n// This options keys will be extended on view instance.\nvar viewOptions = {\n el: true,\n tag: true,\n attributes: true,\n events: true,\n model: true,\n template: true,\n onDestroy: true\n};\n\n/**\n * - Listens for changes and renders UI.\n * - Handles user input and interactivity.\n * - Sends captured input to the model.\n *\n * A View is an atomic chunk of user interface. It often renders the data from a specific model, \n * or number of models, but views can also be data-less chunks of UI that stand alone.<br /> \n * Models must be unaware of views. Instead, views listen to the model \"change\" events, \n * and react or re-render themselves appropriately.<br />\n * Views has a root element, `this.el`. That element is used for event delegation. Elements lookups are scoped to that element. And render and dom manipulations should be done inside that element. \n * If `this.el` is not present, an element will be created using `this.tag` (or `div` as default), and `this.attributes`.<br />\n * The following options passed to the constructor are extended to `this`.\n * @module\n * @param {object} options Object containing options. The following will be merged to `this`: `el`, `tag`, `attributes`, `events`, `model`, `template`, `onDestroy`.\n * @property {node} el Every view has a root element, `this.el`. If not present it will be created.\n * @property {string} tag If `this.el` is not present, an element will be created using `this.tag`. Default is `div`.\n * @property {object} attributes If `this.el` is not present, an element will be created using `this.attributes`.\n * @property {object} events Object in the format `{'event selector' : 'listener\"'}`. Used to bind delegated event listeners to root element.\n * @property {object} model A `Rasti.Model` or any object containing data and business logic.\n * @property {function} template A function that receives data and returns a markup string (html for example).\n * @example\n * // Counter view.\n * class CounterView extends View {\n * constructor(options) {\n * super(options);\n * // Bind method to `this`, to be called as listener.\n * this.render = this.render.bind(this);\n * // Listen to model change and re render.\n * this.model.on('change:count', this.render);\n * }\n * onDestroy() {\n * // Unbind events when destroyed.\n * this.model.off('change:count', this.render);\n * }\n * // Listener method. Called when button is clicked.\n * onClickIncrement() {\n * // Increment count on model.\n * this.model.count++;\n * }\n * }\n * Object.assign(CounterView.prototype, {\n * // Set delegated events.\n * // Call `onClickIncrement` when button is clicked.\n * events : {\n * 'click button' : 'onClickIncrement'\n * },\n * // View's template.\n * template : (model) => `\n * <div>The count is: ${model.count}</div>\n * <button>Increment</button>\n * `\n * });\n * // Model.\n * const model = new Model({ count : 0 });\n * // Instantiate CounterView.\n * const counterView = new CounterView({ model });\n * // Add to DOM.\n * document.body.appendChild(counterView.render().el);\n */\n\nvar View = function (_Emitter) {\n _inherits(View, _Emitter);\n\n function View() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n _classCallCheck(this, View);\n\n // Generate unique id.\n // Useful to generate elements ids.\n var _this = _possibleConstructorReturn(this, _Emitter.call(this));\n\n _this.uid = 'uid' + ++_this.constructor.uid;\n // Store delegated events listeners,\n // so they can be unbinded later.\n _this.delegatedEventListeners = [];\n // Store child views,\n // so they can be destroyed.\n _this.children = [];\n // Extend \"this\" with options, mapping viewOptions keys.\n Object.keys(options).forEach(function (key) {\n if (viewOptions[key]) _this[key] = options[key];\n });\n // Ensure that the view has a root element at `this.el`.\n _this.ensureElement();\n return _this;\n }\n\n /**\n * Returns the first element 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} Element matching selector within the view's root element (`this.el`).\n */\n\n\n View.prototype.$ = function $(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\n\n View.prototype.$$ = function $$(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 * Pass options.remove as true to remove the view's root element (`this.el`) from the DOM.\n * @param {object} options.remove Remove the view's root element (`this.el`) from the DOM.\n */\n\n\n View.prototype.destroy = function destroy() {\n var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},\n remove = _ref.remove;\n\n // Call destroy on children.\n this.destroyChildren();\n // Undelegate \"this.el\" event listeners\n this.undelegateEvents();\n // Unbind \"this\" events.\n this.off();\n // Remove \"this.el\" if \"options.remove\" is true.\n if (remove) this.removeElement();\n // Call onDestroy lifecycle method\n this.onDestroy();\n };\n\n /**\n * `onDestroy` lifecycle method is called after view is destroyed.\n * Override with your code. Useful to stop listening to model's events.\n */\n\n\n View.prototype.onDestroy = function onDestroy() {};\n\n /**\n * Add a view as a child.\n * Children views are stored at `this.children`, and destroyed when the parent is destroyed.\n * Returns the child for chaining.\n * @param {Rasti.View} child\n * @return {Rasti.View}\n */\n\n\n View.prototype.addChild = function addChild(child) {\n this.children.push(child);\n return child;\n };\n\n /**\n * Call destroy method on children views.\n */\n\n\n View.prototype.destroyChildren = function destroyChildren() {\n while (this.children.length) {\n this.children.shift().destroy();\n }\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 constructor.\n * You may override it if you want to use a different logic or to \n * postpone element creation.\n */\n\n\n View.prototype.ensureElement = function ensureElement() {\n // If \"this.el\" is not present,\n // create a new element according \"this.tag\"\n // and \"this.attributes\".\n if (!this.el) this.el = this.createElement(this.tag, this.attributes);\n // Delegate events on element.\n this.delegateEvents();\n };\n\n /**\n * Create an element.\n * Called from constructor if `this.el` is undefined, to ensure\n * the view to have a root element.\n * @param {string} tag Tag for the element. Default to `div`\n * @param {object} attrs Attributes for the element.\n * @return {node} The created element.\n */\n\n\n View.prototype.createElement = function createElement() {\n var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';\n var attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n // Create dom element.\n var el = document.createElement(tag);\n // Add element attributes.\n Object.keys(attrs).forEach(function (key) {\n return el.setAttribute(key, attrs[key]);\n });\n\n return el;\n };\n\n /**\n * Remove `this.el` from DOM.\n */\n\n\n View.prototype.removeElement = function removeElement() {\n this.el.parentNode.removeChild(this.el);\n };\n\n /**\n * Delegate event listeners. Called at the constructor.\n * Parse `events` parameter or `this.events`, and bind event listeners to `this.el`.<br />\n * Events are written in the format `{'event selector': 'listener'}`.\n * The listener may be either the name of a method on the view,\n * or a direct function body.\n * Omitting the selector causes the event to be bound to `this.el`.\n * @param {object} [events] Object in the format `{'event selector' : 'listener\"'}`. Used to bind delegated event listeners to root element.\n * @example\n * model.prototype.events = {\n * 'click button.ok' : 'onClickOkButton'\n * };\n */\n\n\n View.prototype.delegateEvents = function delegateEvents() {\n var _this2 = this;\n\n var events = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.events;\n\n if (!events) return;\n // Store events by type i.e.: \"click\", \"submit\", etc.\n var eventTypes = {};\n\n Object.keys(events).forEach(function (key) {\n var keyParts = key.split(' '),\n type = keyParts[0],\n selector = keyParts[1],\n listener = events[key];\n\n if (typeof listener === 'string') listener = _this2[listener].bind(_this2);\n\n if (!eventTypes[type]) eventTypes[type] = [];\n\n eventTypes[type].push({ selector: selector, listener: listener });\n });\n\n var indexOf = Array.prototype.indexOf;\n\n Object.keys(eventTypes).forEach(function (type) {\n var self = _this2;\n var typeListener = function typeListener(event) {\n eventTypes[type].forEach(function (_ref2) {\n var selector = _ref2.selector,\n listener = _ref2.listener;\n\n if (!selector && self.el === event.target || indexOf.call(self.el.querySelectorAll(selector), event.target) > -1) {\n listener(event, self);\n }\n });\n };\n\n _this2.delegatedEventListeners.push({ type: type, listener: typeListener });\n _this2.el.addEventListener(type, typeListener);\n });\n };\n\n /**\n * Undelegate event listeners. Called when the view is destroyed.\n */\n\n\n View.prototype.undelegateEvents = function undelegateEvents() {\n var _this3 = this;\n\n this.delegatedEventListeners.forEach(function (_ref3) {\n var type = _ref3.type,\n listener = _ref3.listener;\n\n _this3.el.removeEventListener(type, listener);\n });\n\n this.delegatedEventListeners = [];\n };\n\n /**\n * Render the view.\n * This method should be overriden with custom logic.\n * The default implementation sets innerHTML of `this.el` with `this.template`.\n * Conventions are to only manipulate the dom in the scope of `this.el`, \n * and to return `this` for chaining.\n * If you added any child view, you must call `this.destroyChildren`.\n * @return {Rasti.View} Return `this` for chaining.\n */\n\n\n View.prototype.render = function render() {\n this.destroyChildren();\n if (this.template) this.el.innerHTML = this.template(this.model);\n return this;\n };\n\n return View;\n}(_Emitter3['default']);\n\n/*\n * Unique Id\n */\n\n\nexports['default'] = View;\nView.uid = 0;\n\n//# sourceURL=webpack://Rasti/./src/View.js?");
131
+ eval("\n\nexports.__esModule = true;\n\nvar _Emitter2 = __webpack_require__(/*! ./Emitter */ \"./src/Emitter.js\");\n\nvar _Emitter3 = _interopRequireDefault(_Emitter2);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\n// This options keys will be extended on view instance.\nvar viewOptions = {\n el: true,\n tag: true,\n attributes: true,\n events: true,\n model: true,\n template: true,\n onDestroy: true\n};\n\n/**\n * - Listens for changes and renders UI.\n * - Handles user input and interactivity.\n * - Sends captured input to the model.\n *\n * A View is an atomic chunk of user interface. It often renders the data from a specific model, \n * or number of models, but views can also be data-less chunks of UI that stand alone.<br /> \n * Models must be unaware of views. Instead, views listen to the model \"change\" events, \n * and react or re-render themselves appropriately.<br />\n * Views has a root element, `this.el`. That element is used for event delegation. Elements lookups are scoped to that element. And render and dom manipulations should be done inside that element. \n * If `this.el` is not present, an element will be created using `this.tag` (or `div` as default), and `this.attributes`.<br />\n * @module\n * @param {object} options Object containing options. The following keys will be merged to `this`: el, tag, attributes, events, model, template, onDestroy.\n * @property {node} el Every view has a root element, `this.el`. If not present it will be created.\n * @property {string} tag If `this.el` is not present, an element will be created using `this.tag`. Default is `div`.\n * @property {object} attributes If `this.el` is not present, an element will be created using `this.attributes`.\n * @property {object} events Object in the format `{'event selector' : 'listener\"'}`. Used to bind delegated event listeners to root element.\n * @property {object} model A `Rasti.Model` or any object containing data and business logic.\n * @property {function} template A function that receives data and returns a markup string (html for example).\n * @example\n * // Counter view.\n * class CounterView extends View {\n * constructor(options) {\n * super(options);\n * // Bind method to `this`, to be called as listener.\n * this.render = this.render.bind(this);\n * // Listen to model change and re render.\n * this.model.on('change:count', this.render);\n * }\n * onDestroy() {\n * // Unbind events when destroyed.\n * this.model.off('change:count', this.render);\n * }\n * // Listener method. Called when button is clicked.\n * onClickIncrement() {\n * // Increment count on model.\n * this.model.count++;\n * }\n * }\n * Object.assign(CounterView.prototype, {\n * // Set delegated events.\n * // Call `onClickIncrement` when button is clicked.\n * events : {\n * 'click button' : 'onClickIncrement'\n * },\n * // View's template.\n * template : (model) => `\n * <div>The count is: ${model.count}</div>\n * <button>Increment</button>\n * `\n * });\n * // Model.\n * const model = new Model({ count : 0 });\n * // Instantiate CounterView.\n * const counterView = new CounterView({ model });\n * // Add to DOM.\n * document.body.appendChild(counterView.render().el);\n */\n\nvar View = function (_Emitter) {\n _inherits(View, _Emitter);\n\n function View() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n _classCallCheck(this, View);\n\n // Call preinitialize.\n var _this = _possibleConstructorReturn(this, _Emitter.call(this));\n\n _this.preinitialize.apply(_this, arguments);\n // Generate unique id.\n // Useful to generate elements ids.\n _this.uid = 'uid' + ++View.uid;\n // Store delegated events listeners,\n // so they can be unbinded later.\n _this.delegatedEventListeners = [];\n // Store child views,\n // so they can be destroyed.\n _this.children = [];\n // Extend \"this\" with options, mapping viewOptions keys.\n Object.keys(options).forEach(function (key) {\n if (viewOptions[key]) _this[key] = options[key];\n });\n // Ensure that the view has a root element at `this.el`.\n _this.ensureElement();\n return _this;\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} attrs Object containing model attributes to extend `this.attributes`.\n */\n\n\n View.prototype.preinitialize = function preinitialize() {};\n\n /**\n * Returns the first element 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} Element matching selector within the view's root element (`this.el`).\n */\n\n\n View.prototype.$ = function $(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\n\n View.prototype.$$ = function $$(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 * Pass options.remove as true to remove the view's root element (`this.el`) from the DOM.\n * @param {object} options.remove Remove the view's root element (`this.el`) from the DOM.\n */\n\n\n View.prototype.destroy = function destroy() {\n var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},\n remove = _ref.remove;\n\n // Call destroy on children.\n this.destroyChildren();\n // Undelegate `this.el` event listeners\n this.undelegateEvents();\n // Unbind `this` events.\n this.off();\n // Remove `this.el` if \"options.remove\" is true.\n if (remove) this.removeElement();\n // Call onDestroy lifecycle method\n this.onDestroy();\n };\n\n /**\n * `onDestroy` lifecycle method is called after view is destroyed.\n * Override with your code. Useful to stop listening to model's events.\n */\n\n\n View.prototype.onDestroy = function onDestroy() {};\n\n /**\n * Add a view as a child.\n * Children views are stored at `this.children`, and destroyed when the parent is destroyed.\n * Returns the child for chaining.\n * @param {Rasti.View} child\n * @return {Rasti.View}\n */\n\n\n View.prototype.addChild = function addChild(child) {\n this.children.push(child);\n return child;\n };\n\n /**\n * Call destroy method on children views.\n */\n\n\n View.prototype.destroyChildren = function destroyChildren() {\n while (this.children.length) {\n this.children.shift().destroy();\n }\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 constructor.\n * You may override it if you want to use a different logic or to \n * postpone element creation.\n */\n\n\n View.prototype.ensureElement = function ensureElement() {\n // If \"this.el\" is not present,\n // create a new element according \"this.tag\"\n // and \"this.attributes\".\n if (!this.el) this.el = this.createElement(this.tag, this.attributes);\n // Delegate events on element.\n this.delegateEvents();\n };\n\n /**\n * Create an element.\n * Called from constructor if `this.el` is undefined, to ensure\n * the view to have a root element.\n * @param {string} tag Tag for the element. Default to `div`\n * @param {object} attrs Attributes for the element.\n * @return {node} The created element.\n */\n\n\n View.prototype.createElement = function createElement() {\n var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';\n var attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n // Create dom element.\n var el = document.createElement(tag);\n // Add element attributes.\n Object.keys(attrs).forEach(function (key) {\n return el.setAttribute(key, attrs[key]);\n });\n\n return el;\n };\n\n /**\n * Remove `this.el` from DOM.\n */\n\n\n View.prototype.removeElement = function removeElement() {\n this.el.parentNode.removeChild(this.el);\n };\n\n /**\n * Delegate event listeners. Called at the constructor.\n * Parse `events` parameter or `this.events`, and bind event listeners to `this.el`.<br />\n * Events are written in the format `{'event selector': 'listener'}`.\n * The listener may be either the name of a method on the view,\n * or a direct function body.\n * Omitting the selector causes the event to be bound to `this.el`.\n * @param {object} [events] Object in the format `{'event selector' : 'listener\"'}`. Used to bind delegated event listeners to root element.\n * @example\n * view.prototype.events = {\n * 'click button.ok' : 'onClickOkButton'\n * };\n */\n\n\n View.prototype.delegateEvents = function delegateEvents() {\n var _this2 = this;\n\n var events = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.events;\n\n if (!events) return;\n // Store events by type i.e.: \"click\", \"submit\", etc.\n var eventTypes = {};\n\n Object.keys(events).forEach(function (key) {\n var keyParts = key.split(' '),\n type = keyParts.shift(),\n selector = keyParts.join(' '),\n listener = events[key];\n\n if (typeof listener === 'string') listener = _this2[listener].bind(_this2);\n\n if (!eventTypes[type]) eventTypes[type] = [];\n\n eventTypes[type].push({ selector: selector, listener: listener });\n });\n\n var indexOf = Array.prototype.indexOf;\n\n Object.keys(eventTypes).forEach(function (type) {\n var self = _this2;\n var typeListener = function typeListener(event) {\n eventTypes[type].forEach(function (_ref2) {\n var selector = _ref2.selector,\n listener = _ref2.listener;\n\n if (!selector && self.el === event.target || indexOf.call(self.el.querySelectorAll(selector), event.target) > -1) {\n listener(event, self);\n }\n });\n };\n\n _this2.delegatedEventListeners.push({ type: type, listener: typeListener });\n _this2.el.addEventListener(type, typeListener);\n });\n };\n\n /**\n * Undelegate event listeners. Called when the view is destroyed.\n */\n\n\n View.prototype.undelegateEvents = function undelegateEvents() {\n var _this3 = this;\n\n this.delegatedEventListeners.forEach(function (_ref3) {\n var type = _ref3.type,\n listener = _ref3.listener;\n\n _this3.el.removeEventListener(type, listener);\n });\n\n this.delegatedEventListeners = [];\n };\n\n /**\n * Render the view.\n * This method should be overridden with custom logic.\n * The default implementation sets innerHTML of `this.el` with `this.template`.\n * Conventions are to only manipulate the dom in the scope of `this.el`, \n * and to return `this` for chaining.\n * If you added any child view, you must call `this.destroyChildren`.\n * @return {Rasti.View} Return `this` for chaining.\n */\n\n\n View.prototype.render = function render() {\n if (this.template) this.el.innerHTML = this.template(this.model);\n return this;\n };\n\n return View;\n}(_Emitter3['default']);\n\n/*\n * Unique Id\n */\n\n\nexports['default'] = View;\nView.uid = 0;\n\n//# sourceURL=webpack://Rasti/./src/View.js?");
132
132
 
133
133
  /***/ }),
134
134
 
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()}(window,function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=1)}([function(t,e,n){"use strict";e.__esModule=!0;var r=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t)}return t.prototype.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)},t.prototype.once=function(t,e){if("function"==typeof e){var n=this,r=e;e=function(){r.apply(void 0,arguments),n.off(t,e)}}this.on(t,e)},t.prototype.off=function(t,e){if(t)if(e){var n=this.listeners[t];if(n)n.slice().forEach(function(t,r){t===e&&n.splice(r,1)}),n.length||delete this.listeners[t]}else delete this.listeners[t];else this.listeners={}},t.prototype.emit=function(t){for(var e=arguments.length,n=Array(e>1?e-1:0),r=1;r<e;r++)n[r-1]=arguments[r];var o=this.listeners&&this.listeners[t];o&&o.length&&o.slice().forEach(function(t){t.apply(void 0,n)})},t}();e.default=r},function(t,e,n){t.exports=n(2)},function(t,e,n){"use strict";e.__esModule=!0,e.View=e.Model=e.Emitter=void 0;var r=s(n(0)),o=s(n(3)),i=s(n(4));function s(t){return t&&t.__esModule?t:{default:t}}e.Emitter=r.default,e.Model=o.default,e.View=i.default},function(t,e,n){"use strict";e.__esModule=!0;var r=function(t){function e(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e);var r=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}(this,t.call(this));return r.attributes=Object.assign({},r.defaults||{},n),r.previous={},Object.keys(r.attributes).forEach(r.defineAttribute.bind(r)),r}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e.prototype.defineAttribute=function(t){var e=this;Object.defineProperty(this,t,{get:function(){return e.get(t)},set:function(n){return e.set(t,n)}})},e.prototype.get=function(t){return this.attributes[t]},e.prototype.set=function(t,e){var n,r=this,o="object"==typeof t?t:((n={})[t]=e,n);return Object.keys(o).forEach(function(t){var e=t in r.attributes&&o[t]!==r.attributes[t];r.previous[t]=r.attributes[t],r.attributes[t]=o[t],e&&(r.emit("change",r,t,o[t]),r.emit("change:"+t,r,o[t]))}),this},e.prototype.toJSON=function(){return this.attributes},e}(function(t){return t&&t.__esModule?t:{default:t}}(n(0)).default);e.default=r},function(t,e,n){"use strict";e.__esModule=!0;var r={el:!0,tag:!0,attributes:!0,events:!0,model:!0,template:!0,onDestroy:!0},o=function(t){function e(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e);var o=function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}(this,t.call(this));return o.uid="uid"+ ++o.constructor.uid,o.delegatedEventListeners=[],o.children=[],Object.keys(n).forEach(function(t){r[t]&&(o[t]=n[t])}),o.ensureElement(),o}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e.prototype.$=function(t){return this.el.querySelector(t)},e.prototype.$$=function(t){return this.el.querySelectorAll(t)},e.prototype.destroy=function(){var t=(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}).remove;this.destroyChildren(),this.undelegateEvents(),this.off(),t&&this.removeElement(),this.onDestroy()},e.prototype.onDestroy=function(){},e.prototype.addChild=function(t){return this.children.push(t),t},e.prototype.destroyChildren=function(){for(;this.children.length;)this.children.shift().destroy()},e.prototype.ensureElement=function(){this.el||(this.el=this.createElement(this.tag,this.attributes)),this.delegateEvents()},e.prototype.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},e.prototype.removeElement=function(){this.el.parentNode.removeChild(this.el)},e.prototype.delegateEvents=function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.events;if(e){var n={};Object.keys(e).forEach(function(r){var o=r.split(" "),i=o[0],s=o[1],u=e[r];"string"==typeof u&&(u=t[u].bind(t)),n[i]||(n[i]=[]),n[i].push({selector:s,listener:u})});var r=Array.prototype.indexOf;Object.keys(n).forEach(function(e){var o=t,i=function(t){n[e].forEach(function(e){var n=e.selector,i=e.listener;(!n&&o.el===t.target||r.call(o.el.querySelectorAll(n),t.target)>-1)&&i(t,o)})};t.delegatedEventListeners.push({type:e,listener:i}),t.el.addEventListener(e,i)})}},e.prototype.undelegateEvents=function(){var t=this;this.delegatedEventListeners.forEach(function(e){var n=e.type,r=e.listener;t.el.removeEventListener(n,r)}),this.delegatedEventListeners=[]},e.prototype.render=function(){return this.destroyChildren(),this.template&&(this.el.innerHTML=this.template(this.model)),this},e}(function(t){return t&&t.__esModule?t:{default:t}}(n(0)).default);e.default=o,o.uid=0}])});
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()}(window,(function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=1)}([function(t,e,n){"use strict";e.__esModule=!0;var r=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t)}return t.prototype.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)},t.prototype.once=function(t,e){if("function"==typeof e){var n=this,r=e;e=function(){r.apply(void 0,arguments),n.off(t,e)}}this.on(t,e)},t.prototype.off=function(t,e){if(t)if(e){var n=this.listeners[t];if(n)n.slice().forEach((function(t,r){t===e&&n.splice(r,1)})),n.length||delete this.listeners[t]}else delete this.listeners[t];else this.listeners={}},t.prototype.emit=function(t){for(var e=arguments.length,n=Array(e>1?e-1:0),r=1;r<e;r++)n[r-1]=arguments[r];var o=this.listeners&&this.listeners[t];if(o&&o.length){var i=o.slice();i.forEach((function(t){t.apply(void 0,n)}))}},t}();e.default=r},function(t,e,n){t.exports=n(2)},function(t,e,n){"use strict";e.__esModule=!0,e.View=e.Model=e.Emitter=void 0;var r=s(n(0)),o=s(n(3)),i=s(n(4));function s(t){return t&&t.__esModule?t:{default:t}}e.Emitter=r.default,e.Model=o.default,e.View=i.default},function(t,e,n){"use strict";e.__esModule=!0;var r,o=n(0);function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function s(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}var u=function(t){function e(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};i(this,e);var r=s(this,t.call(this));return r.preinitialize.apply(r,arguments),r.attributes=Object.assign({},r.defaults||{},n),r.previous={},Object.keys(r.attributes).forEach(r.defineAttribute.bind(r)),r}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e.prototype.preinitialize=function(){},e.prototype.defineAttribute=function(t){var e=this;Object.defineProperty(this,t,{get:function(){return e.get(t)},set:function(n){return e.set(t,n)}})},e.prototype.get=function(t){return this.attributes[t]},e.prototype.set=function(t,e){var n,r=this,o="object"==typeof t?t:((n={})[t]=e,n);return Object.keys(o).forEach((function(t){var e=t in r.attributes&&o[t]!==r.attributes[t];r.previous[t]=r.attributes[t],r.attributes[t]=o[t],e&&(r.emit("change",r,t,o[t]),r.emit("change:"+t,r,o[t]))})),this},e.prototype.toJSON=function(){return this.attributes},e}(((r=o)&&r.__esModule?r:{default:r}).default);e.default=u},function(t,e,n){"use strict";e.__esModule=!0;var r,o=n(0),i=(r=o)&&r.__esModule?r:{default:r};function s(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function u(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}var f={el:!0,tag:!0,attributes:!0,events:!0,model:!0,template:!0,onDestroy:!0},l=function(t){function e(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};s(this,e);var r=u(this,t.call(this));return r.preinitialize.apply(r,arguments),r.uid="uid"+ ++e.uid,r.delegatedEventListeners=[],r.children=[],Object.keys(n).forEach((function(t){f[t]&&(r[t]=n[t])})),r.ensureElement(),r}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e.prototype.preinitialize=function(){},e.prototype.$=function(t){return this.el.querySelector(t)},e.prototype.$$=function(t){return this.el.querySelectorAll(t)},e.prototype.destroy=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=t.remove;this.destroyChildren(),this.undelegateEvents(),this.off(),e&&this.removeElement(),this.onDestroy()},e.prototype.onDestroy=function(){},e.prototype.addChild=function(t){return this.children.push(t),t},e.prototype.destroyChildren=function(){for(;this.children.length;)this.children.shift().destroy()},e.prototype.ensureElement=function(){this.el||(this.el=this.createElement(this.tag,this.attributes)),this.delegateEvents()},e.prototype.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},e.prototype.removeElement=function(){this.el.parentNode.removeChild(this.el)},e.prototype.delegateEvents=function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.events;if(e){var n={};Object.keys(e).forEach((function(r){var o=r.split(" "),i=o.shift(),s=o.join(" "),u=e[r];"string"==typeof u&&(u=t[u].bind(t)),n[i]||(n[i]=[]),n[i].push({selector:s,listener:u})}));var r=Array.prototype.indexOf;Object.keys(n).forEach((function(e){var o=t,i=function(t){n[e].forEach((function(e){var n=e.selector,i=e.listener;(!n&&o.el===t.target||r.call(o.el.querySelectorAll(n),t.target)>-1)&&i(t,o)}))};t.delegatedEventListeners.push({type:e,listener:i}),t.el.addEventListener(e,i)}))}},e.prototype.undelegateEvents=function(){var t=this;this.delegatedEventListeners.forEach((function(e){var n=e.type,r=e.listener;t.el.removeEventListener(n,r)})),this.delegatedEventListeners=[]},e.prototype.render=function(){return this.template&&(this.el.innerHTML=this.template(this.model)),this},e}(i.default);e.default=l,l.uid=0}])}));
package/es/Emitter.js CHANGED
@@ -27,7 +27,7 @@ var Emitter = function () {
27
27
  /**
28
28
  * Adds event listener.
29
29
  * @param {string} type Type of the event (e.g. `change`).
30
- * @param {function} listener Callback function to be called when the event is emited.
30
+ * @param {function} listener Callback function to be called when the event is emitted.
31
31
  * @example
32
32
  * this.model.on('change', this.render.bind(this)); // Re render when model changes.
33
33
  */
@@ -45,7 +45,7 @@ var Emitter = function () {
45
45
  /**
46
46
  * Adds event listener that executes once.
47
47
  * @param {string} type Type of the event (e.g. `change`).
48
- * @param {function} listener Callback function to be called when the event is emited.
48
+ * @param {function} listener Callback function to be called when the event is emitted.
49
49
  * @example
50
50
  * this.model.once('change', () => console.log('This will happen once'));
51
51
  */
@@ -67,7 +67,7 @@ var Emitter = function () {
67
67
  /**
68
68
  * Removes event listeners.
69
69
  * @param {string} [type] Type of the event (e.g. `change`). If is not provided, it removes all listeners.
70
- * @param {function} [listener] Callback function to be called when the event is emited. If listener is not provided, it removes all listeners for specified type.
70
+ * @param {function} [listener] Callback function to be called when the event is emitted. If listener is not provided, it removes all listeners for specified type.
71
71
  * @example
72
72
  * this.model.off('change'); // Stop listening to changes.
73
73
  */
package/es/Model.js CHANGED
@@ -50,17 +50,27 @@ var Model = function (_Emitter) {
50
50
 
51
51
  _classCallCheck(this, Model);
52
52
 
53
- // attributes object
53
+ // Call preinitialize.
54
54
  var _this = _possibleConstructorReturn(this, _Emitter.call(this));
55
55
 
56
+ _this.preinitialize.apply(_this, arguments);
57
+ // attributes object.
56
58
  _this.attributes = Object.assign({}, _this.defaults || {}, attrs);
57
- // previous attributes
59
+ // Previous attributes.
58
60
  _this.previous = {};
59
- // Generate getters/setters for every attr
61
+ // Generate getters/setters for every attr.
60
62
  Object.keys(_this.attributes).forEach(_this.defineAttribute.bind(_this));
61
63
  return _this;
62
64
  }
63
65
 
66
+ /**
67
+ * If you define a preinitialize method, it will be invoked when the Model is first created, before any instantiation logic is run for the Model.
68
+ * @param {object} attrs Object containing model attributes to extend `this.attributes`.
69
+ */
70
+
71
+
72
+ Model.prototype.preinitialize = function preinitialize() {};
73
+
64
74
  /**
65
75
  * Generate getter/setter for the given key. In order to emit `change` events.
66
76
  * This method is called internally by the constructor
@@ -118,7 +128,7 @@ var Model = function (_Emitter) {
118
128
 
119
129
  _this3.previous[key] = _this3.attributes[key];
120
130
  _this3.attributes[key] = attrs[key];
121
-
131
+ // Emit change events.
122
132
  if (changed) {
123
133
  _this3.emit('change', _this3, key, attrs[key]);
124
134
  _this3.emit('change:' + key, _this3, attrs[key]);
package/es/View.js CHANGED
@@ -28,9 +28,8 @@ var viewOptions = {
28
28
  * and react or re-render themselves appropriately.<br />
29
29
  * Views has a root element, `this.el`. That element is used for event delegation. Elements lookups are scoped to that element. And render and dom manipulations should be done inside that element.
30
30
  * If `this.el` is not present, an element will be created using `this.tag` (or `div` as default), and `this.attributes`.<br />
31
- * The following options passed to the constructor are extended to `this`.
32
31
  * @module
33
- * @param {object} options Object containing options. The following will be merged to `this`: `el`, `tag`, `attributes`, `events`, `model`, `template`, `onDestroy`.
32
+ * @param {object} options Object containing options. The following keys will be merged to `this`: el, tag, attributes, events, model, template, onDestroy.
34
33
  * @property {node} el Every view has a root element, `this.el`. If not present it will be created.
35
34
  * @property {string} tag If `this.el` is not present, an element will be created using `this.tag`. Default is `div`.
36
35
  * @property {object} attributes If `this.el` is not present, an element will be created using `this.attributes`.
@@ -85,11 +84,13 @@ var View = function (_Emitter) {
85
84
 
86
85
  _classCallCheck(this, View);
87
86
 
88
- // Generate unique id.
89
- // Useful to generate elements ids.
87
+ // Call preinitialize.
90
88
  var _this = _possibleConstructorReturn(this, _Emitter.call(this));
91
89
 
92
- _this.uid = 'uid' + ++_this.constructor.uid;
90
+ _this.preinitialize.apply(_this, arguments);
91
+ // Generate unique id.
92
+ // Useful to generate elements ids.
93
+ _this.uid = 'uid' + ++View.uid;
93
94
  // Store delegated events listeners,
94
95
  // so they can be unbinded later.
95
96
  _this.delegatedEventListeners = [];
@@ -105,6 +106,14 @@ var View = function (_Emitter) {
105
106
  return _this;
106
107
  }
107
108
 
109
+ /**
110
+ * If you define a preinitialize method, it will be invoked when the view is first created, before any instantiation logic is run.
111
+ * @param {object} attrs Object containing model attributes to extend `this.attributes`.
112
+ */
113
+
114
+
115
+ View.prototype.preinitialize = function preinitialize() {};
116
+
108
117
  /**
109
118
  * Returns the first element that match the selector,
110
119
  * scoped to DOM elements within the current view's root element (`this.el`).
@@ -143,11 +152,11 @@ var View = function (_Emitter) {
143
152
 
144
153
  // Call destroy on children.
145
154
  this.destroyChildren();
146
- // Undelegate "this.el" event listeners
155
+ // Undelegate `this.el` event listeners
147
156
  this.undelegateEvents();
148
- // Unbind "this" events.
157
+ // Unbind `this` events.
149
158
  this.off();
150
- // Remove "this.el" if "options.remove" is true.
159
+ // Remove `this.el` if "options.remove" is true.
151
160
  if (remove) this.removeElement();
152
161
  // Call onDestroy lifecycle method
153
162
  this.onDestroy();
@@ -245,7 +254,7 @@ var View = function (_Emitter) {
245
254
  * Omitting the selector causes the event to be bound to `this.el`.
246
255
  * @param {object} [events] Object in the format `{'event selector' : 'listener"'}`. Used to bind delegated event listeners to root element.
247
256
  * @example
248
- * model.prototype.events = {
257
+ * view.prototype.events = {
249
258
  * 'click button.ok' : 'onClickOkButton'
250
259
  * };
251
260
  */
@@ -262,8 +271,8 @@ var View = function (_Emitter) {
262
271
 
263
272
  Object.keys(events).forEach(function (key) {
264
273
  var keyParts = key.split(' '),
265
- type = keyParts[0],
266
- selector = keyParts[1],
274
+ type = keyParts.shift(),
275
+ selector = keyParts.join(' '),
267
276
  listener = events[key];
268
277
 
269
278
  if (typeof listener === 'string') listener = _this2[listener].bind(_this2);
@@ -313,7 +322,7 @@ var View = function (_Emitter) {
313
322
 
314
323
  /**
315
324
  * Render the view.
316
- * This method should be overriden with custom logic.
325
+ * This method should be overridden with custom logic.
317
326
  * The default implementation sets innerHTML of `this.el` with `this.template`.
318
327
  * Conventions are to only manipulate the dom in the scope of `this.el`,
319
328
  * and to return `this` for chaining.
@@ -323,7 +332,6 @@ var View = function (_Emitter) {
323
332
 
324
333
 
325
334
  View.prototype.render = function render() {
326
- this.destroyChildren();
327
335
  if (this.template) this.el.innerHTML = this.template(this.model);
328
336
  return this;
329
337
  };
package/lib/Emitter.js CHANGED
@@ -31,7 +31,7 @@ var Emitter = function () {
31
31
  /**
32
32
  * Adds event listener.
33
33
  * @param {string} type Type of the event (e.g. `change`).
34
- * @param {function} listener Callback function to be called when the event is emited.
34
+ * @param {function} listener Callback function to be called when the event is emitted.
35
35
  * @example
36
36
  * this.model.on('change', this.render.bind(this)); // Re render when model changes.
37
37
  */
@@ -49,7 +49,7 @@ var Emitter = function () {
49
49
  /**
50
50
  * Adds event listener that executes once.
51
51
  * @param {string} type Type of the event (e.g. `change`).
52
- * @param {function} listener Callback function to be called when the event is emited.
52
+ * @param {function} listener Callback function to be called when the event is emitted.
53
53
  * @example
54
54
  * this.model.once('change', () => console.log('This will happen once'));
55
55
  */
@@ -71,7 +71,7 @@ var Emitter = function () {
71
71
  /**
72
72
  * Removes event listeners.
73
73
  * @param {string} [type] Type of the event (e.g. `change`). If is not provided, it removes all listeners.
74
- * @param {function} [listener] Callback function to be called when the event is emited. If listener is not provided, it removes all listeners for specified type.
74
+ * @param {function} [listener] Callback function to be called when the event is emitted. If listener is not provided, it removes all listeners for specified type.
75
75
  * @example
76
76
  * this.model.off('change'); // Stop listening to changes.
77
77
  */
package/lib/Model.js CHANGED
@@ -58,17 +58,27 @@ var Model = function (_Emitter) {
58
58
 
59
59
  _classCallCheck(this, Model);
60
60
 
61
- // attributes object
61
+ // Call preinitialize.
62
62
  var _this = _possibleConstructorReturn(this, _Emitter.call(this));
63
63
 
64
+ _this.preinitialize.apply(_this, arguments);
65
+ // attributes object.
64
66
  _this.attributes = Object.assign({}, _this.defaults || {}, attrs);
65
- // previous attributes
67
+ // Previous attributes.
66
68
  _this.previous = {};
67
- // Generate getters/setters for every attr
69
+ // Generate getters/setters for every attr.
68
70
  Object.keys(_this.attributes).forEach(_this.defineAttribute.bind(_this));
69
71
  return _this;
70
72
  }
71
73
 
74
+ /**
75
+ * If you define a preinitialize method, it will be invoked when the Model is first created, before any instantiation logic is run for the Model.
76
+ * @param {object} attrs Object containing model attributes to extend `this.attributes`.
77
+ */
78
+
79
+
80
+ Model.prototype.preinitialize = function preinitialize() {};
81
+
72
82
  /**
73
83
  * Generate getter/setter for the given key. In order to emit `change` events.
74
84
  * This method is called internally by the constructor
@@ -126,7 +136,7 @@ var Model = function (_Emitter) {
126
136
 
127
137
  _this3.previous[key] = _this3.attributes[key];
128
138
  _this3.attributes[key] = attrs[key];
129
-
139
+ // Emit change events.
130
140
  if (changed) {
131
141
  _this3.emit('change', _this3, key, attrs[key]);
132
142
  _this3.emit('change:' + key, _this3, attrs[key]);
package/lib/View.js CHANGED
@@ -36,9 +36,8 @@ var viewOptions = {
36
36
  * and react or re-render themselves appropriately.<br />
37
37
  * Views has a root element, `this.el`. That element is used for event delegation. Elements lookups are scoped to that element. And render and dom manipulations should be done inside that element.
38
38
  * If `this.el` is not present, an element will be created using `this.tag` (or `div` as default), and `this.attributes`.<br />
39
- * The following options passed to the constructor are extended to `this`.
40
39
  * @module
41
- * @param {object} options Object containing options. The following will be merged to `this`: `el`, `tag`, `attributes`, `events`, `model`, `template`, `onDestroy`.
40
+ * @param {object} options Object containing options. The following keys will be merged to `this`: el, tag, attributes, events, model, template, onDestroy.
42
41
  * @property {node} el Every view has a root element, `this.el`. If not present it will be created.
43
42
  * @property {string} tag If `this.el` is not present, an element will be created using `this.tag`. Default is `div`.
44
43
  * @property {object} attributes If `this.el` is not present, an element will be created using `this.attributes`.
@@ -93,11 +92,13 @@ var View = function (_Emitter) {
93
92
 
94
93
  _classCallCheck(this, View);
95
94
 
96
- // Generate unique id.
97
- // Useful to generate elements ids.
95
+ // Call preinitialize.
98
96
  var _this = _possibleConstructorReturn(this, _Emitter.call(this));
99
97
 
100
- _this.uid = 'uid' + ++_this.constructor.uid;
98
+ _this.preinitialize.apply(_this, arguments);
99
+ // Generate unique id.
100
+ // Useful to generate elements ids.
101
+ _this.uid = 'uid' + ++View.uid;
101
102
  // Store delegated events listeners,
102
103
  // so they can be unbinded later.
103
104
  _this.delegatedEventListeners = [];
@@ -113,6 +114,14 @@ var View = function (_Emitter) {
113
114
  return _this;
114
115
  }
115
116
 
117
+ /**
118
+ * If you define a preinitialize method, it will be invoked when the view is first created, before any instantiation logic is run.
119
+ * @param {object} attrs Object containing model attributes to extend `this.attributes`.
120
+ */
121
+
122
+
123
+ View.prototype.preinitialize = function preinitialize() {};
124
+
116
125
  /**
117
126
  * Returns the first element that match the selector,
118
127
  * scoped to DOM elements within the current view's root element (`this.el`).
@@ -151,11 +160,11 @@ var View = function (_Emitter) {
151
160
 
152
161
  // Call destroy on children.
153
162
  this.destroyChildren();
154
- // Undelegate "this.el" event listeners
163
+ // Undelegate `this.el` event listeners
155
164
  this.undelegateEvents();
156
- // Unbind "this" events.
165
+ // Unbind `this` events.
157
166
  this.off();
158
- // Remove "this.el" if "options.remove" is true.
167
+ // Remove `this.el` if "options.remove" is true.
159
168
  if (remove) this.removeElement();
160
169
  // Call onDestroy lifecycle method
161
170
  this.onDestroy();
@@ -253,7 +262,7 @@ var View = function (_Emitter) {
253
262
  * Omitting the selector causes the event to be bound to `this.el`.
254
263
  * @param {object} [events] Object in the format `{'event selector' : 'listener"'}`. Used to bind delegated event listeners to root element.
255
264
  * @example
256
- * model.prototype.events = {
265
+ * view.prototype.events = {
257
266
  * 'click button.ok' : 'onClickOkButton'
258
267
  * };
259
268
  */
@@ -270,8 +279,8 @@ var View = function (_Emitter) {
270
279
 
271
280
  Object.keys(events).forEach(function (key) {
272
281
  var keyParts = key.split(' '),
273
- type = keyParts[0],
274
- selector = keyParts[1],
282
+ type = keyParts.shift(),
283
+ selector = keyParts.join(' '),
275
284
  listener = events[key];
276
285
 
277
286
  if (typeof listener === 'string') listener = _this2[listener].bind(_this2);
@@ -321,7 +330,7 @@ var View = function (_Emitter) {
321
330
 
322
331
  /**
323
332
  * Render the view.
324
- * This method should be overriden with custom logic.
333
+ * This method should be overridden with custom logic.
325
334
  * The default implementation sets innerHTML of `this.el` with `this.template`.
326
335
  * Conventions are to only manipulate the dom in the scope of `this.el`,
327
336
  * and to return `this` for chaining.
@@ -331,7 +340,6 @@ var View = function (_Emitter) {
331
340
 
332
341
 
333
342
  View.prototype.render = function render() {
334
- this.destroyChildren();
335
343
  if (this.template) this.el.innerHTML = this.template(this.model);
336
344
  return this;
337
345
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rasti",
3
- "version": "0.0.12",
4
- "description": "Minimalistic JavaScript MV framework for building user interfaces.",
3
+ "version": "1.0.0",
4
+ "description": "Minimalistic JavaScript MV library for building user interfaces.",
5
5
  "main": "lib/index.js",
6
6
  "jsnext:main": "es/index.js",
7
7
  "files": [
@@ -66,19 +66,19 @@
66
66
  "babel-plugin-transform-es2015-unicode-regex": "^6.24.1",
67
67
  "babel-plugin-transform-es3-member-expression-literals": "^6.22.0",
68
68
  "babel-plugin-transform-es3-property-literals": "^6.22.0",
69
- "chai": "^4.1.2",
69
+ "chai": "^4.2.0",
70
70
  "cross-env": "^5.2.0",
71
- "eslint": "^5.2.0",
71
+ "eslint": "^5.16.0",
72
72
  "gitbook-cli": "^2.3.2",
73
73
  "gitbook-plugin-favicon": "0.0.2",
74
74
  "gitbook-plugin-toolbar": "^0.6.0",
75
- "jsdoc-to-markdown": "^4.0.1",
76
- "jsdom": "11.11.0",
75
+ "jsdoc-to-markdown": "^5.0.0",
76
+ "jsdom": "15.1.1",
77
77
  "jsdom-global": "3.0.2",
78
- "mocha": "^5.2.0",
79
- "rimraf": "^2.6.2",
80
- "webpack": "^4.16.3",
81
- "webpack-cli": "^3.1.0"
78
+ "mocha": "^6.1.4",
79
+ "rimraf": "^2.6.3",
80
+ "webpack": "^4.34.0",
81
+ "webpack-cli": "^3.3.4"
82
82
  },
83
83
  "directories": {
84
84
  "example": "example",
package/src/Emitter.js CHANGED
@@ -21,7 +21,7 @@ export default class Emitter {
21
21
  /**
22
22
  * Adds event listener.
23
23
  * @param {string} type Type of the event (e.g. `change`).
24
- * @param {function} listener Callback function to be called when the event is emited.
24
+ * @param {function} listener Callback function to be called when the event is emitted.
25
25
  * @example
26
26
  * this.model.on('change', this.render.bind(this)); // Re render when model changes.
27
27
  */
@@ -39,7 +39,7 @@ export default class Emitter {
39
39
  /**
40
40
  * Adds event listener that executes once.
41
41
  * @param {string} type Type of the event (e.g. `change`).
42
- * @param {function} listener Callback function to be called when the event is emited.
42
+ * @param {function} listener Callback function to be called when the event is emitted.
43
43
  * @example
44
44
  * this.model.once('change', () => console.log('This will happen once'));
45
45
  */
@@ -59,7 +59,7 @@ export default class Emitter {
59
59
  /**
60
60
  * Removes event listeners.
61
61
  * @param {string} [type] Type of the event (e.g. `change`). If is not provided, it removes all listeners.
62
- * @param {function} [listener] Callback function to be called when the event is emited. If listener is not provided, it removes all listeners for specified type.
62
+ * @param {function} [listener] Callback function to be called when the event is emitted. If listener is not provided, it removes all listeners for specified type.
63
63
  * @example
64
64
  * this.model.off('change'); // Stop listening to changes.
65
65
  */
package/src/Model.js CHANGED
@@ -38,14 +38,22 @@ import Emitter from './Emitter';
38
38
  export default class Model extends Emitter {
39
39
  constructor(attrs = {}) {
40
40
  super();
41
- // attributes object
41
+ // Call preinitialize.
42
+ this.preinitialize.apply(this, arguments);
43
+ // attributes object.
42
44
  this.attributes = Object.assign({}, (this.defaults || {}), attrs);
43
- // previous attributes
45
+ // Previous attributes.
44
46
  this.previous = {};
45
- // Generate getters/setters for every attr
47
+ // Generate getters/setters for every attr.
46
48
  Object.keys(this.attributes).forEach(this.defineAttribute.bind(this));
47
49
  }
48
-
50
+
51
+ /**
52
+ * If you define a preinitialize method, it will be invoked when the Model is first created, before any instantiation logic is run for the Model.
53
+ * @param {object} attrs Object containing model attributes to extend `this.attributes`.
54
+ */
55
+ preinitialize() {}
56
+
49
57
  /**
50
58
  * Generate getter/setter for the given key. In order to emit `change` events.
51
59
  * This method is called internally by the constructor
@@ -61,7 +69,7 @@ export default class Model extends Emitter {
61
69
  }
62
70
  );
63
71
  }
64
-
72
+
65
73
  /**
66
74
  * Get an attribute from `this.attributes`.
67
75
  * This method is called internally by generated getters.
@@ -71,7 +79,7 @@ export default class Model extends Emitter {
71
79
  get(key) {
72
80
  return this.attributes[key];
73
81
  }
74
-
82
+
75
83
  /**
76
84
  * Set an attribute into `this.attributes`.
77
85
  * Emit `change` and `change:attribute` if value change.
@@ -91,7 +99,7 @@ export default class Model extends Emitter {
91
99
 
92
100
  this.previous[key] = this.attributes[key];
93
101
  this.attributes[key] = attrs[key];
94
-
102
+ // Emit change events.
95
103
  if (changed) {
96
104
  this.emit('change', this, key, attrs[key]);
97
105
  this.emit(`change:${key}`, this, attrs[key]);
@@ -99,7 +107,7 @@ export default class Model extends Emitter {
99
107
  });
100
108
  return this;
101
109
  }
102
-
110
+
103
111
  /**
104
112
  * Return object representation of the model to be used for JSON serialization.
105
113
  * By default returns `this.attributes`.
package/src/View.js CHANGED
@@ -22,9 +22,8 @@ const viewOptions = {
22
22
  * and react or re-render themselves appropriately.<br />
23
23
  * Views has a root element, `this.el`. That element is used for event delegation. Elements lookups are scoped to that element. And render and dom manipulations should be done inside that element.
24
24
  * If `this.el` is not present, an element will be created using `this.tag` (or `div` as default), and `this.attributes`.<br />
25
- * The following options passed to the constructor are extended to `this`.
26
25
  * @module
27
- * @param {object} options Object containing options. The following will be merged to `this`: `el`, `tag`, `attributes`, `events`, `model`, `template`, `onDestroy`.
26
+ * @param {object} options Object containing options. The following keys will be merged to `this`: el, tag, attributes, events, model, template, onDestroy.
28
27
  * @property {node} el Every view has a root element, `this.el`. If not present it will be created.
29
28
  * @property {string} tag If `this.el` is not present, an element will be created using `this.tag`. Default is `div`.
30
29
  * @property {object} attributes If `this.el` is not present, an element will be created using `this.attributes`.
@@ -73,9 +72,11 @@ const viewOptions = {
73
72
  export default class View extends Emitter {
74
73
  constructor(options = {}) {
75
74
  super();
75
+ // Call preinitialize.
76
+ this.preinitialize.apply(this, arguments);
76
77
  // Generate unique id.
77
78
  // Useful to generate elements ids.
78
- this.uid = `uid${++this.constructor.uid}`;
79
+ this.uid = `uid${++View.uid}`;
79
80
  // Store delegated events listeners,
80
81
  // so they can be unbinded later.
81
82
  this.delegatedEventListeners = [];
@@ -83,13 +84,18 @@ export default class View extends Emitter {
83
84
  // so they can be destroyed.
84
85
  this.children = [];
85
86
  // Extend "this" with options, mapping viewOptions keys.
86
- Object.keys(options)
87
- .forEach(key => {
88
- if (viewOptions[key]) this[key] = options[key];
89
- });
87
+ Object.keys(options).forEach(key => {
88
+ if (viewOptions[key]) this[key] = options[key];
89
+ });
90
90
  // Ensure that the view has a root element at `this.el`.
91
91
  this.ensureElement();
92
92
  }
93
+
94
+ /**
95
+ * If you define a preinitialize method, it will be invoked when the view is first created, before any instantiation logic is run.
96
+ * @param {object} attrs Object containing model attributes to extend `this.attributes`.
97
+ */
98
+ preinitialize() {}
93
99
 
94
100
  /**
95
101
  * Returns the first element that match the selector,
@@ -120,11 +126,11 @@ export default class View extends Emitter {
120
126
  destroy({ remove } = {}) {
121
127
  // Call destroy on children.
122
128
  this.destroyChildren();
123
- // Undelegate "this.el" event listeners
129
+ // Undelegate `this.el` event listeners
124
130
  this.undelegateEvents();
125
- // Unbind "this" events.
131
+ // Unbind `this` events.
126
132
  this.off();
127
- // Remove "this.el" if "options.remove" is true.
133
+ // Remove `this.el` if "options.remove" is true.
128
134
  if (remove) this.removeElement();
129
135
  // Call onDestroy lifecycle method
130
136
  this.onDestroy();
@@ -205,7 +211,7 @@ export default class View extends Emitter {
205
211
  * Omitting the selector causes the event to be bound to `this.el`.
206
212
  * @param {object} [events] Object in the format `{'event selector' : 'listener"'}`. Used to bind delegated event listeners to root element.
207
213
  * @example
208
- * model.prototype.events = {
214
+ * view.prototype.events = {
209
215
  * 'click button.ok' : 'onClickOkButton'
210
216
  * };
211
217
  */
@@ -216,8 +222,8 @@ export default class View extends Emitter {
216
222
 
217
223
  Object.keys(events).forEach(key => {
218
224
  let keyParts = key.split(' '),
219
- type = keyParts[0],
220
- selector = keyParts[1],
225
+ type = keyParts.shift(),
226
+ selector = keyParts.join(' '),
221
227
  listener = events[key];
222
228
 
223
229
  if (typeof listener === 'string') listener = this[listener].bind(this);
@@ -258,7 +264,7 @@ export default class View extends Emitter {
258
264
 
259
265
  /**
260
266
  * Render the view.
261
- * This method should be overriden with custom logic.
267
+ * This method should be overridden with custom logic.
262
268
  * The default implementation sets innerHTML of `this.el` with `this.template`.
263
269
  * Conventions are to only manipulate the dom in the scope of `this.el`,
264
270
  * and to return `this` for chaining.
@@ -266,7 +272,6 @@ export default class View extends Emitter {
266
272
  * @return {Rasti.View} Return `this` for chaining.
267
273
  */
268
274
  render() {
269
- this.destroyChildren();
270
275
  if (this.template) this.el.innerHTML = this.template(this.model);
271
276
  return this;
272
277
  }