Skip to content

Commit

Permalink
Merge f711cfd into 9ab08cc
Browse files Browse the repository at this point in the history
  • Loading branch information
belozer committed Jun 23, 2018
2 parents 9ab08cc + f711cfd commit 64daddc
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 19 deletions.
94 changes: 85 additions & 9 deletions common.blocks/i-bem-dom/i-bem-dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ function initEntity(entityName, domElem, params, ignoreLazyInit, callback) {

entityCls._processInit();

if(ignoreLazyInit || params.lazyInit === false || !entityCls.lazyInit && !params.lazyInit) {
if(ignoreLazyInit || params.lazyInit === false || !entityCls._lazyInitCheck(domElem[0]) && !params.lazyInit) {
ignoreLazyInit && domElem.addClass(BEM_CLASS_NAME); // add css class for preventing memory leaks in further destructing

entity = new entityCls(uniqIdToDomElems[uniqId], params, !!ignoreLazyInit);
Expand Down Expand Up @@ -341,6 +341,42 @@ function getEntityBase(baseCls, entityName, base) {
return base;
}

/**
* Extract lazyInit property from staticProps
* @param {Object} [staticProps]
* @returns {?Boolean}
*/
function extractLazyInitProp(staticProps) {
if(staticProps && staticProps.lazyInit !== undef) {
var lazyInit = staticProps.lazyInit;
delete staticProps.lazyInit;
return lazyInit;
}

return null;
}

/**
* Processing lazyInit rules for entity
* @param {Function} entity BemDomEntity
* @param {Object} [mod] mod declaration
* @param {Boolean} lazyInit lazyInit behavior
* @returns {?Boolean}
*/
function processLazyInitRule(entity, mod, lazyInit) {
if(arguments.length < 3) {
lazyInit = mod;
mod = undef;
}

var rules = entity._lazyInitRules || (entity._lazyInitRules = []);

rules.push({
check : mod? entity._buildModValRE(mod.modName, mod.modVal) : entity._buildRE(),
lazyInit : lazyInit
});
}

/**
* @class BemDomEntity
* @description Base mix for BEM entities that have DOM representation
Expand Down Expand Up @@ -770,11 +806,12 @@ var BemDomEntity = inherit(/** @lends BemDomEntity.prototype */{

/** @override */
declMod : function(mod, props, staticProps) {
if(staticProps && staticProps.lazyInit !== undef) {
throw Error('declMod with lazyInit prop not allowed. Your need use \'lazyInit\' in data-bem params');
}
var lazyInit = extractLazyInitProp(staticProps),
entity = this.__base.apply(this, arguments);

return this.__base.apply(this, arguments);
lazyInit !== null && processLazyInitRule(entity, mod, lazyInit);

return entity;
},

/** @override */
Expand Down Expand Up @@ -827,17 +864,29 @@ var BemDomEntity = inherit(/** @lends BemDomEntity.prototype */{
return this.getEntityName() + MOD_DELIM + modName;
},

/**
* Builds a regular expression for check entity on DOM element
* @private
* @returns {RegExp}
*/
_buildRE : function() {
return new RegExp('(\\s|^)' + this.getEntityName() + '?(?=\\s|$)');
},

/**
* Builds a regular expression for extracting modifier values from a DOM element of an entity
* @private
* @param {String} modName Modifier name
* @param {String} [modVal] Modifier value
* @returns {RegExp}
*/
_buildModValRE : function(modName) {
_buildModValRE : function(modName, modVal) {
modVal = (modVal === '*' || modVal === undef)? NAME_PATTERN : modVal;

return new RegExp(
'(\\s|^)' +
this._buildModClassNamePrefix(modName) +
'(?:' + MOD_DELIM + '(' + NAME_PATTERN + '))?(?=\\s|$)');
'(?:' + MOD_DELIM + '(' + modVal + '))?(?=\\s|$)');
},

/**
Expand All @@ -860,6 +909,23 @@ var BemDomEntity = inherit(/** @lends BemDomEntity.prototype */{
*/
_buildSelector : function(modName, modVal) {
return '.' + this._buildClassName(modName, modVal);
},

/**
* Check domNode for lazy initialization entity
* @protected
* @returns {?Boolean}
*/
_lazyInitCheck : function(domNode) {
var rules = this._lazyInitRules, rule;
if(!rules) return null;

var len = rules.length;
while(rule = rules[--len]) {
if(rule.check.test(domNode.className)) return rule.lazyInit;
}

return null;
}
});

Expand Down Expand Up @@ -960,7 +1026,12 @@ bemDom = /** @exports */{

base = getEntityBase(Block, blockName, base);

return bem.declBlock(blockName, base, props, staticProps);
var lazyInit = extractLazyInitProp(staticProps),
entity = bem.declBlock(blockName, base, props, staticProps);

lazyInit !== null && processLazyInitRule(entity, lazyInit);

return entity;
},

/**
Expand All @@ -983,7 +1054,12 @@ bemDom = /** @exports */{

base = getEntityBase(Elem, entityName, base);

return bem.declElem(blockName, elemName, base, props, staticProps);
var lazyInit = extractLazyInitProp(staticProps),
entity = bem.declElem(blockName, elemName, base, props, staticProps);

lazyInit !== null && processLazyInitRule(entity, lazyInit);

return entity;
},

declMixin : bem.declMixin,
Expand Down
74 changes: 64 additions & 10 deletions common.blocks/i-bem-dom/i-bem-dom.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,16 +164,6 @@ describe('i-bem-dom', function() {
block2.should.be.instanceOf(Block1);
elem2.should.be.instanceOf(Elem1);
});

it('should throw error if declMod contains lazyInit static property', function() {
var Block = bemDom.declBlock('block');

function mod() {
Block.declMod({ modName : 'mod' }, null, { lazyInit : true });
}

mod.should.throw(Error, 'declMod with lazyInit prop not allowed. Your need use \'lazyInit\' in data-bem params');
});
});

describe('getMod', function() {
Expand Down Expand Up @@ -1675,6 +1665,70 @@ describe('i-bem-dom', function() {
describe('lazy init', function() {
var spy;

['block', 'elem'].forEach(function(entityType) {
it('should have different lazyInit for base ' + entityType + ' and modifiers', function() {
var spy1 = sinon.spy(),
spy2 = sinon.spy(),
spy3 = sinon.spy(),
spy4 = sinon.spy(),
spy5 = sinon.spy(),

Entity = entityType === 'block'? bemDom.declBlock('block', {
onSetMod : { js : { inited : spy1 } }
}, {
lazyInit : true
}) : bemDom.declElem('block', 'elem', {
onSetMod : { js : { inited : spy1 } }
}, {
lazyInit : true
});

Entity.declMod({ modName : 'm1' }, {
onSetMod : { js : { inited : spy2 } }
}, {
lazyInit : false
});

Entity.declMod({ modName : 'm2', modVal : true }, {
onSetMod : { js : { inited : spy3 } }
});

Entity.declMod({ modName : 'm3', modVal : '*' }, {
onSetMod : { js : { inited : spy4 } }
}, {
lazyInit : false
});

Entity.declMod({ modName : 'm3', modVal : 'v2' }, {
onSetMod : { js : { inited : spy5 } }
}, {
lazyInit : true
});

rootNode = initDom([
{ },
{ m1 : true },
{ m2 : true },
{ m3 : 'v1' },
{ m3 : 'v2' }
].map(function(mods) {
var bemjson = entityType === 'block'? { mods : mods }
: { elem : 'elem', elemMods : mods };

bemjson.block = 'block';
bemjson.js = true;

return bemjson;
}));

spy1.should.have.not.been.called;
spy2.should.have.been.called;
spy3.should.have.not.been.called;
spy4.should.have.been.called;
spy5.should.have.not.been.called;
});
});

it('should be possible to force initialization', function() {
spy = sinon.spy();

Expand Down

0 comments on commit 64daddc

Please sign in to comment.