Repository: bem/bh
Pull request #96 (2014-10-01 05:53:40)

`processBemJson()` should return standart BEMJSON


Patch:
diff --git a/lib/bh.js b/lib/bh.js
index 22b7949..50a5490 100644
--- a/lib/bh.js
+++ b/lib/bh.js
@@ -207,16 +207,14 @@ function BH() {
         applyBase: function() {
             var node = this.node;
             var json = node.json;
-
-            if (!json.elem && json.mods) json.blockMods = json.mods;
             var block = json.block;
-            var blockMods = json.blockMods;
+            var blockMods = json.mods;
 
             var subRes = this.bh._fastMatcher(this, json);
             if (subRes !== undefined) {
                 this.ctx = node.arr[node.index] = node.json = subRes;
-                node.blockName = block;
-                node.blockMods = blockMods;
+                node.block = block;
+                node.mods = blockMods;
             }
             return this;
         },
@@ -265,14 +263,13 @@ function BH() {
          * @returns {String|undefined|Ctx}
          */
         mod: function(key, value, force) {
-            var mods;
+            var field = this.ctx.elem ? 'elemMods' : 'mods';
             if (arguments.length > 1) {
-                mods = this.ctx.mods || (this.ctx.mods = {});
+                var mods = this.ctx[field];
                 mods[key] = !mods.hasOwnProperty(key) || force ? value : mods[key];
                 return this;
             } else {
-                mods = this.ctx.mods;
-                return mods ? mods[key] : undefined;
+                return this.ctx[field][key];
             }
         },
         /**
@@ -291,9 +288,10 @@ function BH() {
          * @returns {Object|Ctx}
          */
         mods: function(values, force) {
-            var mods = this.ctx.mods || (this.ctx.mods = {});
+            var field = this.ctx.elem ? 'elemMods' : 'mods';
+            var mods = this.ctx[field];
             if (values !== undefined) {
-                this.ctx.mods = force ? this.extend(mods, values) : this.extend(values, mods);
+                this.ctx[field] = force ? this.extend(mods, values) : this.extend(values, mods);
                 return this;
             } else {
                 return mods;
@@ -734,12 +732,12 @@ BH.prototype = {
                     conds.push('!json.' + fn.__id);
                     if (decl.elemMod) {
                         conds.push(
-                            'json.mods && json.mods["' + decl.elemMod + '"] === ' +
+                            'json.elemMods && json.elemMods["' + decl.elemMod + '"] === ' +
                                 (decl.elemModVal === true || '"' + decl.elemModVal + '"'));
                     }
                     if (decl.blockMod) {
                         conds.push(
-                            'json.blockMods["' + decl.blockMod + '"] === ' +
+                            'json.mods && json.mods["' + decl.blockMod + '"] === ' +
                                 (decl.blockModVal === true || '"' + decl.blockModVal + '"'));
                     }
                     res.push('if (' + conds.join(' && ') + ') {');
@@ -774,7 +772,13 @@ BH.prototype = {
             this._init();
         }
         var resultArr = [bemJson];
-        var nodes = [{ json: bemJson, arr: resultArr, index: 0, blockName: blockName, blockMods: !bemJson.elem && bemJson.mods || {} }];
+        var nodes = [{
+                json: bemJson,
+                arr: resultArr,
+                index: 0,
+                block: blockName,
+                mods: null
+            }];
         var node, json, block, blockMods, i, j, l, p, child, subRes;
         var compiledMatcher = (this._fastMatcher || (this._fastMatcher = Function('ms', this.buildMatcher())(this._matchers)));
         var processContent = !ignoreContent;
@@ -791,13 +795,21 @@ BH.prototype = {
         var ctx = new Ctx();
         while (node = nodes.shift()) {
             json = node.json;
-            block = node.blockName;
-            blockMods = node.blockMods;
+            block = node.block;
+            blockMods = node.mods;
             if (Array.isArray(json)) {
                 for (i = 0, j = 0, l = json.length; i < l; i++) {
                     child = json[i];
                     if (child !== false && child != null && typeof child === 'object') {
-                        nodes.push({ json: child, arr: json, index: i, position: ++j, blockName: block, blockMods: blockMods, parentNode: node });
+                        nodes.push({
+                            json: child,
+                            arr: json,
+                            index: i,
+                            position: ++j,
+                            block: block,
+                            mods: blockMods,
+                            parentNode: node
+                        });
                     }
                 }
                 json._listLength = j;
@@ -805,17 +817,17 @@ BH.prototype = {
                 var content, stopProcess = false;
                 if (json.elem) {
                     block = json.block = json.block || block;
-                    blockMods = json.blockMods = json.blockMods || blockMods;
-                    if (json.elemMods) {
-                        json.mods = json.elemMods;
+                    if (!json.elemMods) {
+                        json.elemMods = json.mods || {};
+                        json.mods = null;
                     }
+                    blockMods = json.mods = json.mods || blockMods;
                 } else if (json.block) {
                     block = json.block;
-                    blockMods = json.blockMods = json.mods || {};
+                    blockMods = json.mods = json.mods || {};
                 }
 
                 if (json.block) {
-
                     if (infiniteLoopDetection) {
                         json.__processCounter = (json.__processCounter || 0) + 1;
                         compiledMatcher.__processCounter = (compiledMatcher.__processCounter || 0) + 1;
@@ -837,13 +849,12 @@ BH.prototype = {
                         if (subRes !== undefined) {
                             json = subRes;
                             node.json = json;
-                            node.blockName = block;
-                            node.blockMods = blockMods;
+                            node.block = block;
+                            node.mods = blockMods;
                             nodes.push(node);
                             stopProcess = true;
                         }
                     }
-
                 }
                 if (!stopProcess) {
                     if (processContent && (content = json.content)) {
@@ -864,12 +875,12 @@ BH.prototype = {
                             for (i = 0, j = 0, l = content.length, p = l - 1; i < l; i++) {
                                 child = content[i];
                                 if (child !== false && child != null && typeof child === 'object') {
-                                    nodes.push({ json: child, arr: content, index: i, position: ++j, blockName: block, blockMods: blockMods, parentNode: node });
+                                    nodes.push({ json: child, arr: content, index: i, position: ++j, block: block, mods: blockMods, parentNode: node });
                                 }
                             }
                             content._listLength = j;
                         } else {
-                            nodes.push({ json: content, arr: json, index: 'content', blockName: block, blockMods: blockMods, parentNode: node });
+                            nodes.push({ json: content, arr: json, index: 'content', block: block, mods: blockMods, parentNode: node });
                         }
                     }
                 }
@@ -1041,7 +1052,7 @@ var toBemCssClasses = function(json, base, parentBase) {
         res += base;
     }
 
-    if (mods = json.mods || json.elem && json.elemMods) {
+    if (mods = json.elem && json.elemMods || json.mods) {
         for (i in mods) {
             mod = mods[i];
             if (mod || mod === 0) {


Test patch:
diff --git a/test/test.applyBase.js b/test/test.applyBase.js
index 23041a8..58a5080 100644
--- a/test/test.applyBase.js
+++ b/test/test.applyBase.js
@@ -20,6 +20,19 @@ describe('ctx.applyBase()', function() {
         );
     });
 
+    it('should apply base matcher for element', function() {
+        bh.match('button__control', function(ctx) {
+            ctx.mod('type', 'span');
+            ctx.applyBase();
+        });
+        bh.match('button__control_type_span', function(ctx) {
+            ctx.tag('span');
+        });
+        bh.apply({ block: 'button', elem: 'control', mods: { disabled: true } }).should.equal(
+            '<span class="button__control button__control_disabled button__control_type_span"></span>'
+        );
+    });
+
     it('should apply base matcher for content', function() {
         bh.match('button', function(ctx) {
             ctx.content([
diff --git a/test/test.mod.js b/test/test.mod.js
index 2ee2b7f..33c9170 100644
--- a/test/test.mod.js
+++ b/test/test.mod.js
@@ -14,6 +14,13 @@ describe('ctx.mod()', function() {
         bh.apply({ block: 'button', mods: { type: 'button' } });
     });
 
+    it('should return elem mod', function() {
+        bh.match('button__control', function(ctx) {
+            ctx.mod('type').should.equal('button');
+        });
+        bh.apply({ block: 'button', elem: 'control', mods: { type: 'button' } });
+    });
+
     it('should return undefined mod', function() {
         bh.match('button', function(ctx) {
             (typeof ctx.mod('type')).should.equal('undefined');
diff --git a/test/test.mods.js b/test/test.mods.js
index a0af62a..0ef5114 100644
--- a/test/test.mods.js
+++ b/test/test.mods.js
@@ -21,6 +21,13 @@ describe('ctx.mods()', function() {
         bh.apply({ block: 'button', mods: { type: 'button' } });
     });
 
+    it('should return elem mods', function() {
+        bh.match('button__control', function(ctx) {
+            ctx.mods().type.should.equal('button');
+        });
+        bh.apply({ block: 'button', elem: 'control', elemMods: { type: 'button' } });
+    });
+
     it('should return boolean mods', function() {
         bh.match('button', function(ctx) {
             ctx.mods().disabled.should.equal(true);
diff --git a/test/test.processBemJson.js b/test/test.processBemJson.js
new file mode 100644
index 0000000..e5027fb
--- /dev/null
+++ b/test/test.processBemJson.js
@@ -0,0 +1,63 @@
+var BH = require('../lib/bh');
+require('chai').should();
+
+// Standart: http://ru.bem.info/technology/bemjson/
+
+describe('bh.processBemJson', function() {
+    var bh;
+    beforeEach(function() {
+        bh = new BH();
+    });
+
+    it('should create empty block mods', function() {
+        bh.processBemJson({
+            block: 'button'
+        }).should.deep.equal({
+            block: 'button',
+            mods: {}
+        });
+    });
+
+    it('should create empty elem mods', function() {
+        bh.processBemJson({
+            block: 'button',
+            elem: 'control'
+        }).should.deep.equal({
+            block: 'button',
+            mods: null,
+            elem: 'control',
+            elemMods: {}
+        });
+    });
+
+    it('should inherit block mods', function() {
+        bh.processBemJson({
+            block: 'button',
+            mods: { disabled: true },
+            content: { elem: 'inner' }
+        }).should.deep.equal({
+            block: 'button',
+            mods: { disabled: true },
+            content: {
+                block: 'button',
+                mods: { disabled: true },
+                elem: 'inner',
+                elemMods: {}
+            }
+        });
+    });
+
+    it('should use elemMods instead of mods if collision', function() {
+        bh.processBemJson({
+            block: 'button',
+            mods: { valid: true },
+            elem: 'inner',
+            elemMods: { disabled: 'yes' },
+        }).should.deep.equal({
+            block: 'button',
+            mods: { valid: true },
+            elem: 'inner',
+            elemMods: { disabled: 'yes' }
+        });
+    });
+});