From 70cfeb51c6fa8493a60706eda9545120991f7fd5 Mon Sep 17 00:00:00 2001 From: miripiruni Date: Wed, 7 Dec 2016 19:00:40 +0300 Subject: [PATCH] BEMTREE: js(), mix(), mods(), elemMods() added --- docs/en/5-templates-syntax.md | 3 +- docs/ru/5-templates-syntax.md | 6 +- lib/bemhtml/entity.js | 41 ++------ lib/bemtree/entity.js | 29 +++--- lib/bemtree/index.js | 23 ++++- lib/bemxjst/entity.js | 23 +++++ test/bemtree-test.js | 170 +++++++++++++++++++++++++++++++++- 7 files changed, 240 insertions(+), 55 deletions(-) diff --git a/docs/en/5-templates-syntax.md b/docs/en/5-templates-syntax.md index 79a552b1..d708f21a 100644 --- a/docs/en/5-templates-syntax.md +++ b/docs/en/5-templates-syntax.md @@ -726,7 +726,8 @@ More information about [apply()](7-runtime.md#apply). ## BEMTREE -Only the [def](#def), [content](#content), [replace](#replace), +In BEMTREE engine only data related modes are avaliable: [def](#def), [js](#js), [mix](#mix), +[mods](#mods), [elemMods](#elemMods), [content](#content), [replace](#replace), [extend](#extend) and [wrap](#wrap) modes are used by the BEMTREE engine. User-defined modes can also be used. The other modes described in the documentation above can only be used in BEMHTML. *** diff --git a/docs/ru/5-templates-syntax.md b/docs/ru/5-templates-syntax.md index a4547c3f..f3e080d6 100644 --- a/docs/ru/5-templates-syntax.md +++ b/docs/ru/5-templates-syntax.md @@ -724,8 +724,10 @@ block('control')( ## BEMTREE -Движком BEMTREE используются только режимы [def](#def), [content](#content) и -режимы-хелперы [replace](#replace), [extend](#extend) и [wrap](#wrap). Пользовательские режимы тоже могут быть использованы. Остальные режимы, описанные в документации выше, применимы только к BEMHTML. +В движке BEMTREE используются только режимы ориентированные на данные: [def](#def), [js](#js), [mix](#mix), [mods](#mods), [elemMods](#elemMods), [content](#content) и +режимы-хелперы [replace](#replace), [extend](#extend) и [wrap](#wrap). +Пользовательские режимы тоже могут быть использованы. Остальные режимы, +ориентированные на HTML, описанные в документации выше, применимы только к BEMHTML. *** diff --git a/lib/bemhtml/entity.js b/lib/bemhtml/entity.js index b6cfe0c7..4e49317d 100644 --- a/lib/bemhtml/entity.js +++ b/lib/bemhtml/entity.js @@ -14,13 +14,9 @@ function Entity(bemxjst) { this.jsClass = null; - // "Fast modes" + // "Fast modes" about HTML this.tag = new Match(this, 'tag'); this.attrs = new Match(this, 'attrs'); - this.mix = new Match(this, 'mix'); - this.js = new Match(this, 'js'); - this.mods = new Match(this, 'mods'); - this.elemMods = new Match(this, 'elemMods'); this.bem = new Match(this, 'bem'); this.cls = new Match(this, 'cls'); @@ -38,7 +34,7 @@ Entity.prototype.init = function init(block, elem) { this.jsClass = this.bemxjst.classBuilder.build(this.block, this.elem); }; -var keys = { +Entity.prototype._keys = { tag: true, content: true, attrs: true, @@ -50,17 +46,6 @@ var keys = { bem: true }; -Entity.prototype._initRest = function _initRest(key) { - if (key === 'default') { - this.rest[key] = this.def; - } else if (keys[key]) { - this.rest[key] = this[key]; - } else { - if (!this.rest.hasOwnProperty(key)) - this.rest[key] = new Match(this, key); - } -}; - Entity.prototype.defaultBody = function defaultBody(context) { var mods = this.mods.exec(context); context.mods = mods; @@ -73,23 +58,15 @@ Entity.prototype.defaultBody = function defaultBody(context) { // Notice: other modes must be after context.mods/context.elemMods changes - var tag = this.tag.exec(context); - var content = this.content.exec(context); - var attrs = this.attrs.exec(context); - var mix = this.mix.exec(context); - var js = this.js.exec(context); - var bem = this.bem.exec(context); - var cls = this.cls.exec(context); - return this.bemxjst.render(context, this, - tag, - js, - bem, - cls, - mix, - attrs, - content, + this.tag.exec(context), + this.js.exec(context), + this.bem.exec(context), + this.cls.exec(context), + this.mix.exec(context), + this.attrs.exec(context), + this.content.exec(context), mods, elemMods); }; diff --git a/lib/bemtree/entity.js b/lib/bemtree/entity.js index 365f2067..3dabf2ab 100644 --- a/lib/bemtree/entity.js +++ b/lib/bemtree/entity.js @@ -1,5 +1,4 @@ var inherits = require('inherits'); -var Match = require('../bemxjst/match').Match; var BemxjstEntity = require('../bemxjst/entity').Entity; function Entity() { @@ -9,17 +8,23 @@ function Entity() { inherits(Entity, BemxjstEntity); exports.Entity = Entity; -Entity.prototype._initRest = function _initRest(key) { - if (key === 'default') { - this.rest[key] = this.def; - } else if (key === 'content') { - this.rest[key] = this[key]; - } else { - if (!this.rest.hasOwnProperty(key)) - this.rest[key] = new Match(this, key); +Entity.prototype.defaultBody = function defaultBody(context) { + var mods = this.mods.exec(context); + context.mods = mods; + + var elemMods; + if (context.ctx.elem) { + elemMods = this.elemMods.exec(context); + context.elemMods = elemMods; } -}; -Entity.prototype.defaultBody = function defaultBody(context) { - return this.bemxjst.render(context, this, this.content.exec(context)); + // Notice: other modes must be after context.mods/context.elemMods changes + + return this.bemxjst.render(context, + this, + this.content.exec(context), + this.js.exec(context), + this.mix.exec(context), + mods, + elemMods); }; diff --git a/lib/bemtree/index.js b/lib/bemtree/index.js index 65f964b5..cc499874 100644 --- a/lib/bemtree/index.js +++ b/lib/bemtree/index.js @@ -1,6 +1,7 @@ var inherits = require('inherits'); var BEMXJST = require('../bemxjst'); var Entity = require('./entity').Entity; +var utils = require('../bemxjst/utils'); function BEMTREE() { BEMXJST.apply(this, arguments); @@ -39,11 +40,25 @@ BEMTREE.prototype.runMany = function runMany(arr) { return out; }; -BEMTREE.prototype.render = function render(context, entity, content) { - var ctx = context.ctx; - var isBEM = !!(ctx.block || ctx.elem || ctx.bem); // TODO: check +BEMTREE.prototype.render = function render(context, entity, content, js, mix, + mods, elemMods) { + var ctx = utils.extend({}, context.ctx); + var isBEM = !!(ctx.block || ctx.elem || ctx.bem); - if (typeof content === 'undefined') return ctx; + if (typeof js !== 'undefined') + ctx.js = js; + + if (typeof mix !== 'undefined') + ctx.mix = mix; + + if (!entity.elem && mods && Object.keys(mods).length > 0) + ctx.mods = utils.extend(ctx.mods || {}, mods); + + if (entity.elem && elemMods && Object.keys(elemMods).length > 0) + ctx.elemMods = utils.extend(ctx.elemMods || {}, elemMods); + + if (typeof content === 'undefined') + return ctx; ctx.content = this.renderContent(content, isBEM); diff --git a/lib/bemxjst/entity.js b/lib/bemxjst/entity.js index f0346af0..f3aabbe0 100644 --- a/lib/bemxjst/entity.js +++ b/lib/bemxjst/entity.js @@ -19,6 +19,10 @@ function Entity(bemxjst, block, elem, templates) { // "Fast modes" this.def = new Match(this); + this.mix = new Match(this, 'mix'); + this.js = new Match(this, 'js'); + this.mods = new Match(this, 'mods'); + this.elemMods = new Match(this, 'elemMods'); this.content = new Match(this, 'content'); // "Slow modes" @@ -35,6 +39,25 @@ Entity.prototype.init = function init(block, elem) { this.elem = elem; }; +Entity.prototype._keys = { + content: true, + mix: true, + js: true, + mods: true, + elemMods: true +}; + +Entity.prototype._initRest = function _initRest(key) { + if (key === 'default') { + this.rest[key] = this.def; + } else if (this._keys[key]) { + this.rest[key] = this[key]; + } else { + if (!this.rest.hasOwnProperty(key)) + this.rest[key] = new Match(this, key); + } +}; + function contentMode() { return this.ctx.content; } diff --git a/test/bemtree-test.js b/test/bemtree-test.js index 33beafc3..aa6e9d15 100644 --- a/test/bemtree-test.js +++ b/test/bemtree-test.js @@ -314,7 +314,7 @@ describe('BEMTREE engine tests', function() { }); block('b1').mod('a', 'b').content()('ok'); - }, { block: 'b1' }, { block: 'b1', content: 'ok' }); + }, { block: 'b1' }, { block: 'b1', mods: { a: 'b' }, content: 'ok' }); }); it('should inherit mods properly', function() { @@ -397,9 +397,13 @@ describe('BEMTREE engine tests', function() { content: { block: 'b2' } - }, { block: 'b1', content: [ - 'yes', { block: 'b2' } - ] }); + }, { + block: 'b1', + mods: { a: 'yes' }, + content: [ + 'yes', { block: 'b2' } + ] + }); }); }); @@ -530,4 +534,162 @@ describe('BEMTREE engine tests', function() { null, '', undefined, { block: 'b1' }, undefined, 0 ]); }); + + describe('modes', function() { + it('should support wrap() mode', function() { + test(function() { + block('b1').wrap()(function() { + return { + block: 'wrap', + content: this.ctx + }; + }); + }, + { block: 'b1' }, + { block: 'wrap', content: { block: 'b1' } }); + }); + + it('should support replace() mode', function() { + test(function() { + block('b1').replace()(function() { + return { block: 'a2' }; + }); + }, + { block: 'b1' }, + { block: 'a2' }); + }); + + it('should support extend() mode', function() { + test(function() { + block('b1')( + extend()(function() { + return { test: 42 }; + }), + content()(function() { + return this.test; + }) + ); + }, + { block: 'b1' }, + { block: 'b1', content: 42 }); + }); + + it('should support def() mode', function() { + test(function() { + block('b1').def()(function() { + return 42; + }); + }, + { block: 'b1' }, + 42); + }); + + it('should support content() mode', function() { + test(function() { + block('b1').content()(function() { + return 42; + }); + }, + { block: 'b1' }, + { block: 'b1', content: 42 }); + }); + + it('should support appendContent() mode', function() { + test(function() { + block('b1').appendContent()(function() { + return 42; + }); + }, + { block: 'b1', content: 1 }, + { block: 'b1', content: [ 1, 42 ] }); + }); + + it('should support prependContent() mode', function() { + test(function() { + block('b1').prependContent()(function() { + return 42; + }); + }, + { block: 'b1', content: 1 }, + { block: 'b1', content: [ 42, 1 ] }); + }); + + it('should support js() mode', function() { + test(function() { + block('b1').js()(true); + }, + { block: 'b1', js: false }, + { block: 'b1', js: true }); + }); + + it('should support addJs() mode', function() { + test(function() { + block('b1').addJs()(function() { + return { more: 'sea' }; + }); + }, + { block: 'b1', js: { river: 'neva' } }, + { block: 'b1', js: { river: 'neva', more: 'sea' } }); + }); + + it('should support mix() mode', function() { + test(function() { + block('b1').mix()(function() { + return { block: 'mixed' }; + }); + }, + { block: 'b1' }, + { block: 'b1', mix: { block: 'mixed' } }); + }); + + it('should support addMix() mode', function() { + test(function() { + block('b1').addMix()(function() { + return { block: 'mixed' }; + }); + }, + { block: 'b1', mix: { block: 'i-bem' } }, + { block: 'b1', mix: [ { block: 'i-bem' }, { block: 'mixed' } ] }); + }); + + it('should support mods() mode', function() { + test(function() { + block('b1').mods()(function() { + return { disabled: 'true' }; + }); + }, + { block: 'b1' }, + { block: 'b1', mods: { disabled: 'true' } }); + }); + + it('should support addMods() mode', function() { + test(function() { + block('b1').addMods()(function() { + return { disabled: 'true' }; + }); + }, + { block: 'b1', mods: { size: 'm' } }, + { block: 'b1', mods: { size: 'm', disabled: 'true' } }); + }); + + it('should support elemMods() mode', function() { + test(function() { + block('b1').elem('e').elemMods()(function() { + return { size: 'm' }; + }); + }, + { block: 'b1', elem: 'e' }, + { block: 'b1', elem: 'e', elemMods: { size: 'm' } }); + }); + + it('should support addElemMods() mode', function() { + test(function() { + block('b1').elem('e').addElemMods()(function() { + return { disabled: 'true' }; + }); + }, + { block: 'b1', elem: 'e', elemMods: { size: 'm' } }, + { block: 'b1', elem: 'e', elemMods: { size: 'm', disabled: 'true' } }); + }); + }); });