diff --git a/lib/bemhtml/entity.js b/lib/bemhtml/entity.js index 1992100c..fb316ee4 100644 --- a/lib/bemhtml/entity.js +++ b/lib/bemhtml/entity.js @@ -15,13 +15,13 @@ function Entity(bemxjst) { this.jsClass = null; // "Fast modes" - this.tag = new Match(this); - this.attrs = new Match(this); + this.tag = new Match(this, 'tag'); + this.attrs = new Match(this, 'attrs'); this.mod = new Match(this); - this.js = new Match(this); - this.mix = new Match(this); - this.bem = new Match(this); - this.cls = new Match(this); + this.js = new Match(this, 'js'); + this.mix = new Match(this, 'mix'); + this.bem = new Match(this, 'bem'); + this.cls = new Match(this, 'cls'); BemxjstEntity.apply(this, arguments); } @@ -50,14 +50,12 @@ Entity.prototype._initRest = function _initRest(key) { this.rest[key] = this[key]; } else { if (!this.rest.hasOwnProperty(key)) - this.rest[key] = new Match(this); + this.rest[key] = new Match(this, key); } }; Entity.prototype.defaultBody = function defaultBody(context) { var tag = this.tag.exec(context); - if (tag === undefined) - tag = context.ctx.tag; var js; if (context.ctx.js !== false) @@ -69,10 +67,6 @@ Entity.prototype.defaultBody = function defaultBody(context) { var attrs = this.attrs.exec(context); var content = this.content.exec(context); - // Default content - if (this.content.count === 0 && content === undefined) - content = context.ctx.content; - return this.bemxjst.render(context, this, tag, diff --git a/lib/bemhtml/index.js b/lib/bemhtml/index.js index 4a2711f1..8f9faa6f 100644 --- a/lib/bemhtml/index.js +++ b/lib/bemhtml/index.js @@ -69,13 +69,11 @@ BEMHTML.prototype.render = function render(context, if (js === true) js = {}; - if (js) { + if (js && js !== ctx.js) { if (ctxJS !== true) js = utils.extend(ctxJS, js); } else if (ctxJS === true) { js = {}; - } else { - js = ctxJS; } } @@ -114,16 +112,11 @@ BEMHTML.prototype.render = function render(context, out += entity.jsClass; out += this.buildModsClasses(entity.block, entity.elem, mods); - var totalMix = mix; - if (ctx.mix) { - if (totalMix) - totalMix = [].concat(totalMix, ctx.mix); - else - totalMix = ctx.mix; - } + if (ctx.mix && mix && mix !== ctx.mix) + mix = [].concat(mix, ctx.mix); - if (totalMix) { - var m = this.renderMix(entity, totalMix, jsParams, addJSInitClass); + if (mix) { + var m = this.renderMix(entity, mix, jsParams, addJSInitClass); out += m.out; jsParams = m.jsParams; addJSInitClass = m.addJSInitClass; @@ -156,11 +149,15 @@ BEMHTML.prototype.renderClose = function renderClose(prefix, ctx, content) { var out = prefix; + var isObj = function isObj(val) { + return val && typeof val === 'object' && !Array.isArray(val) && + val !== null; + }; // NOTE: maybe we need to make an array for quicker serialization - attrs = utils.extend(attrs, ctx.attrs); - if (attrs && typeof attrs === 'object' && !Array.isArray(attrs) && - attrs !== null) { + if (isObj(attrs) || isObj(ctx.attrs)) { + attrs = utils.extend(attrs, ctx.attrs); + /* jshint forin : false */ for (var name in attrs) { var attr = attrs[name]; diff --git a/lib/bemtree/entity.js b/lib/bemtree/entity.js index 14eaff5c..365f2067 100644 --- a/lib/bemtree/entity.js +++ b/lib/bemtree/entity.js @@ -16,16 +16,10 @@ Entity.prototype._initRest = function _initRest(key) { this.rest[key] = this[key]; } else { if (!this.rest.hasOwnProperty(key)) - this.rest[key] = new Match(this); + this.rest[key] = new Match(this, key); } }; Entity.prototype.defaultBody = function defaultBody(context) { - var content = this.content.exec(context); - - // Default content - if (this.content.count === 0 && content === undefined) - content = context.ctx.content; - - return this.bemxjst.render(context, this, content); + return this.bemxjst.render(context, this, this.content.exec(context)); }; diff --git a/lib/bemxjst/entity.js b/lib/bemxjst/entity.js index 8a437e98..f0346af0 100644 --- a/lib/bemxjst/entity.js +++ b/lib/bemxjst/entity.js @@ -19,7 +19,7 @@ function Entity(bemxjst, block, elem, templates) { // "Fast modes" this.def = new Match(this); - this.content = new Match(this); + this.content = new Match(this, 'content'); // "Slow modes" this.rest = {}; diff --git a/lib/bemxjst/index.js b/lib/bemxjst/index.js index 0ccb76fe..99d25606 100644 --- a/lib/bemxjst/index.js +++ b/lib/bemxjst/index.js @@ -472,7 +472,7 @@ BEMXJST.prototype.applyNext = function applyNext() { BEMXJST.prototype.applyMode = function applyMode(mode, changes) { var match = this.match.entity.rest[mode]; if (!match) - return; + return this.context.ctx[mode]; if (!changes) return match.exec(this.context); diff --git a/lib/bemxjst/match.js b/lib/bemxjst/match.js index 57fce9d8..66837eed 100644 --- a/lib/bemxjst/match.js +++ b/lib/bemxjst/match.js @@ -115,8 +115,9 @@ function MatchTemplate(mode, template) { } exports.MatchTemplate = MatchTemplate; -function Match(entity) { +function Match(entity, modeName) { this.entity = entity; + this.modeName = modeName; this.bemxjst = this.entity.bemxjst; this.templates = []; @@ -135,7 +136,7 @@ function Match(entity) { exports.Match = Match; Match.prototype.clone = function clone(entity) { - var res = new Match(entity); + var res = new Match(entity, this.modeName); res.templates = this.templates.slice(); res.mask = this.mask.slice(); @@ -206,7 +207,7 @@ Match.prototype.exec = function exec(context) { } if (i === this.count) - return undefined; + return context.ctx[this.modeName]; var oldMask = mask; var oldMatch = this.bemxjst.match; diff --git a/test/modes-attrs-test.js b/test/modes-attrs-test.js index d42ac7ce..80febd7d 100644 --- a/test/modes-attrs-test.js +++ b/test/modes-attrs-test.js @@ -43,4 +43,12 @@ describe('Modes attrs', function() { }, '
'); }); + + it('should merge attrs from templates and from bemjson', function() { + test(function() { + block('b').attrs()({ templ: '1' }); + }, + { block: 'b', attrs: { bemjson: '2' } }, + '
'); + }); }); diff --git a/test/modes-js-test.js b/test/modes-js-test.js index 64cdd209..b31464b8 100644 --- a/test/modes-js-test.js +++ b/test/modes-js-test.js @@ -78,4 +78,13 @@ describe('Modes js', function() { '
', { elemJsInstances: true }); }); + + it('should merge js from templates and js from bemjson', function() { + test(function() { + block('b').js()({ templ: '1' }); + }, + { block: 'b', js: { bemjson: '2' } }, + '
'); + }); }); diff --git a/test/modes-mix-test.js b/test/modes-mix-test.js index f9dc99ea..fa73749f 100644 --- a/test/modes-mix-test.js +++ b/test/modes-mix-test.js @@ -150,4 +150,15 @@ describe('Modes mix', function() { elem: 'elem' }, '
'); }); + + it('should concat mix from templates with mix from bemjson', function() { + test(function() { + block('b1')( + mix()({ block: 'template' }) + ); + }, { + block: 'b1', + mix: { block: 'bemjson' } + }, '
'); + }); }); diff --git a/test/runtime-apply-test.js b/test/runtime-apply-test.js new file mode 100644 index 00000000..7fdc571a --- /dev/null +++ b/test/runtime-apply-test.js @@ -0,0 +1,97 @@ +var fixtures = require('./fixtures')('bemhtml'); +var test = fixtures.test; + +describe('Runtime apply()', function() { + it('apply(\'content\') from def()', function() { + test(function() { + block('b').def()(function() { + return apply('content'); + }); + }, { block: 'b', content: 'test' }, + 'test'); + }); + + it('apply(\'mix\') from def()', function() { + test(function() { + block('b').def()(function() { + return apply('mix'); + }); + }, { block: 'b', mix: 'test' }, + 'test'); + }); + + it('apply(\'tag\') from tag()', function() { + test(function() { + block('b').tag()(function() { + return 'span'; + }); + block('b').tag()(function() { + return apply('tag'); + }); + }, { block: 'b', tag: 'a' }, + ''); + }); + + it('apply(\'tag\') from def()', function() { + test(function() { + block('b').def()(function() { + return apply('tag'); + }); + }, { block: 'b', tag: 'a' }, + 'a'); + }); + + it('apply(\'bem\') from def()', function() { + test(function() { + block('b').def()(function() { + return apply('bem'); + }); + }, { block: 'b', bem: false }, + false); + }); + + it('apply(\'cls\') from def()', function() { + test(function() { + block('b').def()(function() { + return apply('cls'); + }); + }, { block: 'b', cls: 'test' }, + 'test'); + }); + + it('apply(\'attrs\') from def()', function() { + test(function() { + block('b').def()(function() { + return apply('attrs'); + }); + }, { block: 'b', attrs: { target: '_blank' } }, + { target: '_blank' }); + }); + + it('apply(\'js\') from def()', function() { + test(function() { + block('b').def()(function() { + return apply('js'); + }); + }, { block: 'b', js: { data: 'test' } }, + { data: 'test' }); + }); + + it('apply(\'usermode\') from def()', function() { + test(function() { + block('b').def()(function() { + return apply('usermode'); + }); + }, { block: 'b', usermode: 'test' }, + 'test'); + }); + + it('apply(\'undefusermode\') from def()', function() { + test(function() { + block('b').def()(function() { + return apply('undefusermode'); + }); + }, { block: 'b' }, + undefined); + }); +});