|
|
@@ -94,6 +94,189 @@ export default class Template {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * An entry point to the interface which allows binding DOM nodes to {@link utils.ObservableMixin}.
|
|
|
+ * There are two types of bindings:
|
|
|
+ *
|
|
|
+ * * `HTMLElement` attributes or Text Node `textContent` can be synchronized with {@link utils.ObservableMixin}
|
|
|
+ * instance attributes. See {@link ui.Template.bind#to} and {@link ui.Template.bind#if}.
|
|
|
+ *
|
|
|
+ * * DOM events fired on `HTMLElement` can be propagated through {@link utils.ObservableMixin}.
|
|
|
+ * See {@link ui.Template.bind#to}.
|
|
|
+ *
|
|
|
+ * @param {utils.ObservableMixin} observable An instance of ObservableMixin class.
|
|
|
+ * @param {utils.EmitterMixin} emitter An instance of `EmitterMixin` class. It listens
|
|
|
+ * to `observable` attribute changes and DOM Events, depending on the binding. Usually {@link ui.View} instance.
|
|
|
+ * @returns {ui.TemplateBinding}
|
|
|
+ */
|
|
|
+ static bind( observable, emitter ) {
|
|
|
+ return {
|
|
|
+ /**
|
|
|
+ * Binds {@link utils.ObservableMixin} instance to:
|
|
|
+ * * HTMLElement attribute or Text Node `textContent` so remains in sync with the Observable when it changes:
|
|
|
+ * * HTMLElement DOM event, so the DOM events are propagated through Observable.
|
|
|
+ *
|
|
|
+ * const bind = Template.bind( observableInstance, emitterInstance );
|
|
|
+ *
|
|
|
+ * new Template( {
|
|
|
+ * tag: 'p',
|
|
|
+ * attributes: {
|
|
|
+ * // class="..." attribute gets bound to `observableInstance#a`
|
|
|
+ * 'class': bind.to( 'a' )
|
|
|
+ * },
|
|
|
+ * children: [
|
|
|
+ * // <p>...</p> gets bound to `observableInstance#b`; always `toUpperCase()`.
|
|
|
+ * { text: bind.to( 'b', ( value, node ) => value.toUpperCase() ) }
|
|
|
+ * ],
|
|
|
+ * on: {
|
|
|
+ * click: [
|
|
|
+ * // "clicked" event will be fired on `observableInstance` when "click" fires in DOM.
|
|
|
+ * bind( 'clicked' ),
|
|
|
+ *
|
|
|
+ * // A custom callback function will be executed when "click" fires in DOM.
|
|
|
+ * bind( () => {
|
|
|
+ * ...
|
|
|
+ * } )
|
|
|
+ * ]
|
|
|
+ * }
|
|
|
+ * } ).render();
|
|
|
+ *
|
|
|
+ * const bind = Template.bind( observableInstance, emitterInstance );
|
|
|
+ *
|
|
|
+ * new Template( {
|
|
|
+ * tag: 'p',
|
|
|
+ * } ).render();
|
|
|
+ *
|
|
|
+ * @static
|
|
|
+ * @method ui.Template.bind#to
|
|
|
+ * @param {String} attribute Name of {@link utils.ObservableMixin} used in the binding.
|
|
|
+ * @param {Function} [callback] Allows processing of the value. Accepts `Node` and `value` as arguments.
|
|
|
+ * @return {ui.TemplateBinding}
|
|
|
+ */
|
|
|
+ to( eventNameOrFuncionOrAttribute, callback ) {
|
|
|
+ return {
|
|
|
+ type: bindToSymbol,
|
|
|
+ eventNameOrFunction: eventNameOrFuncionOrAttribute,
|
|
|
+ attribute: eventNameOrFuncionOrAttribute,
|
|
|
+ observable, emitter, callback
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Binds {@link utils.ObservableMixin} to HTMLElement attribute or Text Node `textContent`
|
|
|
+ * so remains in sync with the Model when it changes. Unlike {@link ui.Template.bind#to},
|
|
|
+ * it controls the presence of the attribute/`textContent` depending on the "falseness" of
|
|
|
+ * {@link utils.ObservableMixin} attribute.
|
|
|
+ *
|
|
|
+ * const bind = Template.bind( observableInstance, emitterInstance );
|
|
|
+ *
|
|
|
+ * new Template( {
|
|
|
+ * tag: 'input',
|
|
|
+ * attributes: {
|
|
|
+ * // <input checked> when `observableInstance#a` is not undefined/null/false/''
|
|
|
+ * // <input> when `observableInstance#a` is undefined/null/false
|
|
|
+ * checked: bind.if( 'a' )
|
|
|
+ * },
|
|
|
+ * children: [
|
|
|
+ * {
|
|
|
+ * // <input>"b-is-not-set"</input> when `observableInstance#b` is undefined/null/false/''
|
|
|
+ * // <input></input> when `observableInstance#b` is not "falsy"
|
|
|
+ * text: bind.if( 'b', 'b-is-not-set', ( value, node ) => !value )
|
|
|
+ * }
|
|
|
+ * ]
|
|
|
+ * } ).render();
|
|
|
+ *
|
|
|
+ * @static
|
|
|
+ * @method ui.Template.bind#if
|
|
|
+ * @param {String} attribute An attribute name of {@link utils.ObservableMixin} used in the binding.
|
|
|
+ * @param {String} [valueIfTrue] Value set when {@link utils.ObservableMixin} attribute is not undefined/null/false/''.
|
|
|
+ * @param {Function} [callback] Allows processing of the value. Accepts `Node` and `value` as arguments.
|
|
|
+ * @return {ui.TemplateBinding}
|
|
|
+ */
|
|
|
+ if( attribute, valueIfTrue, callback ) {
|
|
|
+ return {
|
|
|
+ type: bindIfSymbol,
|
|
|
+ observable, emitter, attribute, valueIfTrue, callback
|
|
|
+ };
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Extends {@link ui.Template} or {@link ui.TemplateDefinition} with additional content.
|
|
|
+ *
|
|
|
+ * const bind = Template.bind( observable, emitterInstance );
|
|
|
+ * const instance = new Template( {
|
|
|
+ * tag: 'p',
|
|
|
+ * attributes: {
|
|
|
+ * class: 'a',
|
|
|
+ * data-x: bind.to( 'foo' )
|
|
|
+ * },
|
|
|
+ * children: [
|
|
|
+ * {
|
|
|
+ * tag: 'span',
|
|
|
+ * attributes: {
|
|
|
+ * class: 'b'
|
|
|
+ * },
|
|
|
+ * children: [
|
|
|
+ * 'Span'
|
|
|
+ * ]
|
|
|
+ * }
|
|
|
+ * ]
|
|
|
+ * } );
|
|
|
+ *
|
|
|
+ * // Instance-level extension.
|
|
|
+ * Template.extend( instance, {
|
|
|
+ * attributes: {
|
|
|
+ * class: 'b',
|
|
|
+ * data-x: bind.to( 'bar' )
|
|
|
+ * },
|
|
|
+ * children: [
|
|
|
+ * {
|
|
|
+ * attributes: {
|
|
|
+ * class: 'c'
|
|
|
+ * }
|
|
|
+ * }
|
|
|
+ * ]
|
|
|
+ * } );
|
|
|
+ *
|
|
|
+ * // Fragment extension.
|
|
|
+ * Template.extend( instance.definition.children[ 0 ], {
|
|
|
+ * attributes: {
|
|
|
+ * class: 'd'
|
|
|
+ * }
|
|
|
+ * } );
|
|
|
+ *
|
|
|
+ * the `instance.render().outerHTML` is
|
|
|
+ *
|
|
|
+ * <p class="a b" data-x="{ observable.foo } { observable.bar }">
|
|
|
+ * <span class="b c d">Span</span>
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * @param {ui.Template|ui.TemplateDefinition} instanceOrDef Existing Template instance or definition to be extended.
|
|
|
+ * @param {ui.TemplateDefinition} extDef An extension to existing instance or definition.
|
|
|
+ */
|
|
|
+ static extend( instanceOrDef, extDef ) {
|
|
|
+ const extDefClone = clone( extDef );
|
|
|
+
|
|
|
+ normalize( extDefClone );
|
|
|
+
|
|
|
+ if ( instanceOrDef instanceof Template ) {
|
|
|
+ extendTemplateDefinition( instanceOrDef.definition, extDefClone );
|
|
|
+ }
|
|
|
+ // Extend a particular child in existing template instance.
|
|
|
+ //
|
|
|
+ // Template.extend( instance.definition.children[ 0 ], {
|
|
|
+ // attributes: {
|
|
|
+ // class: 'd'
|
|
|
+ // }
|
|
|
+ // } );
|
|
|
+ //
|
|
|
+ else {
|
|
|
+ extendTemplateDefinition( instanceOrDef, extDefClone );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* Renders a DOM Node from definition.
|
|
|
*
|
|
|
* @protected
|
|
|
@@ -316,192 +499,6 @@ export default class Template {
|
|
|
|
|
|
mix( Template, EmitterMixin );
|
|
|
|
|
|
-/**
|
|
|
- * An entry point to the interface which allows binding DOM nodes to {@link utils.ObservableMixin}.
|
|
|
- * There are two types of bindings:
|
|
|
- *
|
|
|
- * * `HTMLElement` attributes or Text Node `textContent` can be synchronized with {@link utils.ObservableMixin}
|
|
|
- * instance attributes. See {@link ui.Template.bind#to} and {@link ui.Template.bind#if}.
|
|
|
- *
|
|
|
- * * DOM events fired on `HTMLElement` can be propagated through {@link utils.ObservableMixin}.
|
|
|
- * See {@link ui.Template.bind#to}.
|
|
|
- *
|
|
|
- * @property ui.Template#bind
|
|
|
- * @param {utils.ObservableMixin} observable An instance of ObservableMixin class.
|
|
|
- * @param {utils.EmitterMixin} emitter An instance of `EmitterMixin` class. It listens
|
|
|
- * to `observable` attribute changes and DOM Events, depending on the binding. Usually {@link ui.View} instance.
|
|
|
- * @returns {Object}
|
|
|
- */
|
|
|
-Template.bind = ( observable, emitter ) => {
|
|
|
- return {
|
|
|
- /**
|
|
|
- * Binds {@link utils.ObservableMixin} instance to:
|
|
|
- * * HTMLElement attribute or Text Node `textContent` so remains in sync with the Observable when it changes:
|
|
|
- * * HTMLElement DOM event, so the DOM events are propagated through Observable.
|
|
|
- *
|
|
|
- * const bind = Template.bind( observableInstance, emitterInstance );
|
|
|
- *
|
|
|
- * new Template( {
|
|
|
- * tag: 'p',
|
|
|
- * attributes: {
|
|
|
- * // class="..." attribute gets bound to `observableInstance#a`
|
|
|
- * 'class': bind.to( 'a' )
|
|
|
- * },
|
|
|
- * children: [
|
|
|
- * // <p>...</p> gets bound to `observableInstance#b`; always `toUpperCase()`.
|
|
|
- * { text: bind.to( 'b', ( value, node ) => value.toUpperCase() ) }
|
|
|
- * ],
|
|
|
- * on: {
|
|
|
- * click: [
|
|
|
- * // "clicked" event will be fired on `observableInstance` when "click" fires in DOM.
|
|
|
- * bind( 'clicked' ),
|
|
|
- *
|
|
|
- * // A custom callback function will be executed when "click" fires in DOM.
|
|
|
- * bind( () => {
|
|
|
- * ...
|
|
|
- * } )
|
|
|
- * ]
|
|
|
- * }
|
|
|
- * } ).render();
|
|
|
- *
|
|
|
- * const bind = Template.bind( observableInstance, emitterInstance );
|
|
|
- *
|
|
|
- * new Template( {
|
|
|
- * tag: 'p',
|
|
|
- * } ).render();
|
|
|
- *
|
|
|
- * @static
|
|
|
- * @method ui.Template.bind#to
|
|
|
- * @param {String} attribute Name of {@link utils.ObservableMixin} used in the binding.
|
|
|
- * @param {Function} [callback] Allows processing of the value. Accepts `Node` and `value` as arguments.
|
|
|
- * @return {ui.TemplateBinding}
|
|
|
- */
|
|
|
- to( eventNameOrFuncionOrAttribute, callback ) {
|
|
|
- return {
|
|
|
- type: bindToSymbol,
|
|
|
- eventNameOrFunction: eventNameOrFuncionOrAttribute,
|
|
|
- attribute: eventNameOrFuncionOrAttribute,
|
|
|
- observable, emitter, callback
|
|
|
- };
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * Binds {@link utils.ObservableMixin} to HTMLElement attribute or Text Node `textContent`
|
|
|
- * so remains in sync with the Model when it changes. Unlike {@link ui.Template.bind#to},
|
|
|
- * it controls the presence of the attribute/`textContent` depending on the "falseness" of
|
|
|
- * {@link utils.ObservableMixin} attribute.
|
|
|
- *
|
|
|
- * const bind = Template.bind( observableInstance, emitterInstance );
|
|
|
- *
|
|
|
- * new Template( {
|
|
|
- * tag: 'input',
|
|
|
- * attributes: {
|
|
|
- * // <input checked> when `observableInstance#a` is not undefined/null/false/''
|
|
|
- * // <input> when `observableInstance#a` is undefined/null/false
|
|
|
- * checked: bind.if( 'a' )
|
|
|
- * },
|
|
|
- * children: [
|
|
|
- * {
|
|
|
- * // <input>"b-is-not-set"</input> when `observableInstance#b` is undefined/null/false/''
|
|
|
- * // <input></input> when `observableInstance#b` is not "falsy"
|
|
|
- * text: bind.if( 'b', 'b-is-not-set', ( value, node ) => !value )
|
|
|
- * }
|
|
|
- * ]
|
|
|
- * } ).render();
|
|
|
- *
|
|
|
- * @static
|
|
|
- * @method ui.Template.bind#if
|
|
|
- * @param {String} attribute An attribute name of {@link utils.ObservableMixin} used in the binding.
|
|
|
- * @param {String} [valueIfTrue] Value set when {@link utils.ObservableMixin} attribute is not undefined/null/false/''.
|
|
|
- * @param {Function} [callback] Allows processing of the value. Accepts `Node` and `value` as arguments.
|
|
|
- * @return {ui.TemplateBinding}
|
|
|
- */
|
|
|
- if( attribute, valueIfTrue, callback ) {
|
|
|
- return {
|
|
|
- type: bindIfSymbol,
|
|
|
- observable, emitter, attribute, valueIfTrue, callback
|
|
|
- };
|
|
|
- }
|
|
|
- };
|
|
|
-};
|
|
|
-
|
|
|
-/**
|
|
|
- * Extends {@link ui.Template} or {@link ui.TemplateDefinition} with additional content.
|
|
|
- *
|
|
|
- * const bind = Template.bind( observable, emitterInstance );
|
|
|
- * const instance = new Template( {
|
|
|
- * tag: 'p',
|
|
|
- * attributes: {
|
|
|
- * class: 'a',
|
|
|
- * data-x: bind.to( 'foo' )
|
|
|
- * },
|
|
|
- * children: [
|
|
|
- * {
|
|
|
- * tag: 'span',
|
|
|
- * attributes: {
|
|
|
- * class: 'b'
|
|
|
- * },
|
|
|
- * children: [
|
|
|
- * 'Span'
|
|
|
- * ]
|
|
|
- * }
|
|
|
- * ]
|
|
|
- * } );
|
|
|
- *
|
|
|
- * // Instance-level extension.
|
|
|
- * Template.extend( instance, {
|
|
|
- * attributes: {
|
|
|
- * class: 'b',
|
|
|
- * data-x: bind.to( 'bar' )
|
|
|
- * },
|
|
|
- * children: [
|
|
|
- * {
|
|
|
- * attributes: {
|
|
|
- * class: 'c'
|
|
|
- * }
|
|
|
- * }
|
|
|
- * ]
|
|
|
- * } );
|
|
|
- *
|
|
|
- * // Fragment extension.
|
|
|
- * Template.extend( instance.definition.children[ 0 ], {
|
|
|
- * attributes: {
|
|
|
- * class: 'd'
|
|
|
- * }
|
|
|
- * } );
|
|
|
- *
|
|
|
- * the `instance.render().outerHTML` is
|
|
|
- *
|
|
|
- * <p class="a b" data-x="{ observable.foo } { observable.bar }">
|
|
|
- * <span class="b c d">Span</span>
|
|
|
- * </p>
|
|
|
- *
|
|
|
- * @static
|
|
|
- * @method ui.Template#extend
|
|
|
- * @param {ui.Template|ui.TemplateDefinition} instanceOrDef Existing Template instance or definition to be extended.
|
|
|
- * @param {ui.TemplateDefinition} extDef An extension to existing instance or definition.
|
|
|
- */
|
|
|
-Template.extend = ( instanceOrDef, extDef ) => {
|
|
|
- const extDefClone = clone( extDef );
|
|
|
-
|
|
|
- normalize( extDefClone );
|
|
|
-
|
|
|
- if ( instanceOrDef instanceof Template ) {
|
|
|
- extendTemplateDefinition( instanceOrDef.definition, extDefClone );
|
|
|
- }
|
|
|
- // Extend a particular child in existing template instance.
|
|
|
- //
|
|
|
- // Template.extend( instance.definition.children[ 0 ], {
|
|
|
- // attributes: {
|
|
|
- // class: 'd'
|
|
|
- // }
|
|
|
- // } );
|
|
|
- //
|
|
|
- else {
|
|
|
- extendTemplateDefinition( instanceOrDef, extDefClone );
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
// Checks whether given {@link ui.TemplateValueSchema} contains a
|
|
|
// {@link ui.TemplateBinding}.
|
|
|
//
|