diff --git a/common.blocks/i-bem-dom/__init/_auto/i-bem-dom__init_auto.deps.js b/common.blocks/i-bem-dom/__init/_auto/i-bem-dom__init_auto.deps.js
new file mode 100644
index 000000000..781b0d791
--- /dev/null
+++ b/common.blocks/i-bem-dom/__init/_auto/i-bem-dom__init_auto.deps.js
@@ -0,0 +1,3 @@
+({
+ shouldDeps : 'jquery'
+})
diff --git a/common.blocks/i-bem/__dom/_init/i-bem__dom_init_auto.js b/common.blocks/i-bem-dom/__init/_auto/i-bem-dom__init_auto.js
similarity index 74%
rename from common.blocks/i-bem/__dom/_init/i-bem__dom_init_auto.js
rename to common.blocks/i-bem-dom/__init/_auto/i-bem-dom__init_auto.js
index 863a9c2ea..c3718d817 100644
--- a/common.blocks/i-bem/__dom/_init/i-bem__dom_init_auto.js
+++ b/common.blocks/i-bem-dom/__init/_auto/i-bem-dom__init_auto.js
@@ -3,7 +3,7 @@
*/
modules.require(
- ['i-bem__dom_init', 'jquery', 'next-tick'],
+ ['i-bem-dom__init', 'jquery', 'next-tick'],
function(init, $, nextTick) {
$(function() {
diff --git a/common.blocks/i-bem/__dom/_init/i-bem__dom_init.js b/common.blocks/i-bem-dom/__init/i-bem-dom__init.js
similarity index 72%
rename from common.blocks/i-bem/__dom/_init/i-bem__dom_init.js
rename to common.blocks/i-bem-dom/__init/i-bem-dom__init.js
index a60cb92ae..1609fd142 100644
--- a/common.blocks/i-bem/__dom/_init/i-bem__dom_init.js
+++ b/common.blocks/i-bem-dom/__init/i-bem-dom__init.js
@@ -1,8 +1,8 @@
/**
- * @module i-bem__dom_init
+ * @module i-bem-dom__init
*/
-modules.define('i-bem__dom_init', ['i-bem__dom'], function(provide, BEMDOM) {
+modules.define('i-bem-dom__init', ['i-bem-dom'], function(provide, BEMDOM) {
provide(
/**
diff --git a/common.blocks/i-bem-dom/__init/i-bem-dom__init.spec.js b/common.blocks/i-bem-dom/__init/i-bem-dom__init.spec.js
new file mode 100644
index 000000000..505892e8c
--- /dev/null
+++ b/common.blocks/i-bem-dom/__init/i-bem-dom__init.spec.js
@@ -0,0 +1,23 @@
+modules.define(
+ 'spec',
+ ['i-bem', 'i-bem-dom'],
+ function(provide, BEM, BEMDOM) {
+
+describe('i-bem-dom__init', function() {
+ it('block should exist on init', function(done) {
+ var name = 'b' + Math.random();
+
+ modules.define(name, ['i-bem-dom'], function(provide, BEMDOM) {
+ provide(BEMDOM.declBlock(this.name, {}));
+ });
+
+ modules.require(['i-bem-dom__init'], function() {
+ BEM.blocks.should.have.property(name);
+ done();
+ });
+ });
+});
+
+provide();
+
+});
diff --git a/common.blocks/i-bem/__dom/i-bem__dom.deps.js b/common.blocks/i-bem-dom/i-bem-dom.deps.js
similarity index 94%
rename from common.blocks/i-bem/__dom/i-bem__dom.deps.js
rename to common.blocks/i-bem-dom/i-bem-dom.deps.js
index 468ddc4d8..d4ab48801 100644
--- a/common.blocks/i-bem/__dom/i-bem__dom.deps.js
+++ b/common.blocks/i-bem-dom/i-bem-dom.deps.js
@@ -1,5 +1,6 @@
[{
shouldDeps : [
+ 'inherit',
'jquery',
'objects',
'functions',
diff --git a/common.blocks/i-bem/__dom/i-bem__dom.js b/common.blocks/i-bem-dom/i-bem-dom.js
similarity index 84%
rename from common.blocks/i-bem/__dom/i-bem__dom.js
rename to common.blocks/i-bem-dom/i-bem-dom.js
index c04019731..8505c0138 100644
--- a/common.blocks/i-bem/__dom/i-bem__dom.js
+++ b/common.blocks/i-bem-dom/i-bem-dom.js
@@ -1,11 +1,29 @@
/**
- * @module i-bem__dom
+ * @module i-bem-dom
*/
modules.define(
- 'i-bem__dom',
- ['i-bem', 'i-bem__internal', 'identify', 'objects', 'functions', 'jquery', 'dom'],
- function(provide, BEM, INTERNAL, identify, objects, functions, $, dom) {
+ 'i-bem-dom',
+ [
+ 'i-bem',
+ 'i-bem__internal',
+ 'inherit',
+ 'identify',
+ 'objects',
+ 'functions',
+ 'jquery',
+ 'dom'
+ ],
+ function(
+ provide,
+ BEM,
+ BEMINTERNAL,
+ inherit,
+ identify,
+ objects,
+ functions,
+ $,
+ dom) {
var undef,
win = $(window),
@@ -24,9 +42,9 @@ var undef,
uniqIdToBlock = {},
/**
- * Storage for DOM element's parent nodes
- * @type Object
- */
+ * Storage for DOM element's parent nodes
+ * @type Object
+ */
domNodesToParents = {},
/**
@@ -53,17 +71,17 @@ var undef,
BEM_SELECTOR = '.' + BEM_CLASS,
BEM_PARAMS_ATTR = 'data-bem',
- NAME_PATTERN = INTERNAL.NAME_PATTERN,
+ NAME_PATTERN = BEMINTERNAL.NAME_PATTERN,
- MOD_DELIM = INTERNAL.MOD_DELIM,
- ELEM_DELIM = INTERNAL.ELEM_DELIM,
+ MOD_DELIM = BEMINTERNAL.MOD_DELIM,
+ ELEM_DELIM = BEMINTERNAL.ELEM_DELIM,
EXTRACT_MODS_RE = RegExp(
'[^' + MOD_DELIM + ']' + MOD_DELIM + '(' + NAME_PATTERN + ')' +
'(?:' + MOD_DELIM + '(' + NAME_PATTERN + '))?$'),
- buildModPostfix = INTERNAL.buildModPostfix,
- buildClass = INTERNAL.buildClass,
+ buildModPostfix = BEMINTERNAL.buildModPostfix,
+ buildClass = BEMINTERNAL.buildClass,
reverse = Array.prototype.reverse;
@@ -118,12 +136,11 @@ function initBlock(blockName, domElem, params, forceLive, callback) {
$.unique(uniqIdToDomElems[uniqId]);
}
- var blockClass = blocks[blockName] || DOM.decl(blockName, {}, { live : true }, true);
- if(!(blockClass._liveInitable = !!blockClass._processLive()) || forceLive || params.live === false) {
+ var blockCls = blocks[blockName] || BEMDOM.declBlock(blockName, {}, { live : true }, true);
+ if(!(blockCls._liveInitable = !!blockCls._processLive()) || forceLive || params.live === false) {
forceLive && domElem.addClass(BEM_CLASS); // add css class for preventing memory leaks in further destructing
- block = new blockClass(uniqIdToDomElems[uniqId], params, !!forceLive);
-
+ block = new blockCls(uniqIdToDomElems[uniqId], params, !!forceLive);
delete uniqIdToDomElems[uniqId];
callback && callback.apply(block, Array.prototype.slice.call(arguments, 4));
return block;
@@ -164,7 +181,7 @@ function findDomElem(ctx, selector, excludeSelf) {
* @param {HTMLElement} domNode DOM node
* @returns {Object}
*/
-function getParams(domNode, blockName) {
+function getParams(domNode) {
var uniqId = identify(domNode);
return domElemToParams[uniqId] ||
(domElemToParams[uniqId] = extractParams(domNode));
@@ -194,17 +211,20 @@ function extractParams(domNode) {
/**
* Uncouple DOM node from the block. If this is the last node, then destroys the block.
- * @param {BEMDOM} block block
+ * @param {i-bem-dom:Block} block block
* @param {HTMLElement} domNode DOM node
*/
function removeDomNodeFromBlock(block, domNode) {
- block.domElem.length === 1?
- block._destruct() :
+ if(block.domElem.length === 1) {
+ block._destruct();
+ delete uniqIdToBlock[block._uniqId];
+ } else {
block.domElem = block.domElem.not(domNode);
+ }
}
/**
- * Fills DOM node's parent nodes to the storage
+ * Stores DOM node's parent nodes to the storage
* @param {jQuery} domElem
*/
function storeDomNodeParents(domElem) {
@@ -214,12 +234,13 @@ function storeDomNodeParents(domElem) {
}
/**
- * @class BEMDOM
+ * @class Block
* @description Base block for creating BEM blocks that have DOM representation
+ * @augments i-bem:Block
* @exports
*/
-var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
+var Block = inherit(BEM.Block,/** @lends Block.prototype */{
/**
* @constructor
* @private
@@ -269,47 +290,47 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
/**
* Finds blocks inside the current block or its elements (including context)
* @param {String|jQuery} [elem] Block element
- * @param {String|Object} block Name or description (block,modName,modVal) of the block to find
+ * @param {Function|Object} Block Block or description (block,modName,modVal) of the block to find
* @returns {BEMDOM[]}
*/
- findBlocksInside : function(elem, block) {
- return this._findBlocks('find', elem, block);
+ findBlocksInside : function(elem, Block) {
+ return this._findBlocks('find', elem, Block);
},
/**
* Finds the first block inside the current block or its elements (including context)
* @param {String|jQuery} [elem] Block element
- * @param {String|Object} block Name or description (block,modName,modVal) of the block to find
+ * @param {Function|Object} Block Block or description (block,modName,modVal) of the block to find
* @returns {BEMDOM}
*/
- findBlockInside : function(elem, block) {
- return this._findBlocks('find', elem, block, true);
+ findBlockInside : function(elem, Block) {
+ return this._findBlocks('find', elem, Block, true);
},
/**
* Finds blocks outside the current block or its elements (including context)
* @param {String|jQuery} [elem] Block element
- * @param {String|Object} block Name or description (block,modName,modVal) of the block to find
+ * @param {Function|Object} Block Block or description (block,modName,modVal) of the block to find
* @returns {BEMDOM[]}
*/
- findBlocksOutside : function(elem, block) {
- return this._findBlocks('parents', elem, block);
+ findBlocksOutside : function(elem, Block) {
+ return this._findBlocks('parents', elem, Block);
},
/**
* Finds the first block outside the current block or its elements (including context)
* @param {String|jQuery} [elem] Block element
- * @param {String|Object} block Name or description (block,modName,modVal) of the block to find
+ * @param {Function|Object} Block Block or description (block,modName,modVal) of the block to find
* @returns {BEMDOM}
*/
- findBlockOutside : function(elem, block) {
- return this._findBlocks('closest', elem, block)[0] || null;
+ findBlockOutside : function(elem, Block) {
+ return this._findBlocks('closest', elem, Block)[0] || null;
},
/**
* Finds blocks on DOM elements of the current block or its elements
* @param {String|jQuery} [elem] Block element
- * @param {String|Object} block Name or description (block,modName,modVal) of the block to find
+ * @param {Function|Object} Block Block or description (block,modName,modVal) of the block to find
* @returns {BEMDOM[]}
*/
findBlocksOn : function(elem, block) {
@@ -319,28 +340,28 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
/**
* Finds the first block on DOM elements of the current block or its elements
* @param {String|jQuery} [elem] Block element
- * @param {String|Object} block Name or description (block,modName,modVal) of the block to find
+ * @param {Function|Object} Block Block or description (block,modName,modVal) of the block to find
* @returns {BEMDOM}
*/
- findBlockOn : function(elem, block) {
- return this._findBlocks('', elem, block, true);
+ findBlockOn : function(elem, Block) {
+ return this._findBlocks('', elem, Block, true);
},
- _findBlocks : function(select, elem, block, onlyFirst) {
- if(!block) {
- block = elem;
+ _findBlocks : function(select, elem, Block, onlyFirst) {
+ if(!Block) {
+ Block = elem;
elem = undef;
}
var ctxElem = elem?
(typeof elem === 'string'? this.findElem(elem) : elem) :
this.domElem,
- isSimpleBlock = typeof block === 'string',
- blockName = isSimpleBlock? block : (block.block || block.blockName),
+ isSimpleBlock = typeof Block !== 'object',
+ blockName = (isSimpleBlock? Block : Block.block).getName(),
selector = '.' +
(isSimpleBlock?
buildClass(blockName) :
- buildClass(blockName, block.modName, block.modVal)) +
+ buildClass(blockName, Block.modName, Block.modVal)) +
(onlyFirst? ':first' : ''),
domElems = ctxElem.filter(selector);
@@ -371,7 +392,7 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* @param {String|Object} event Event name or event object
* @param {Object} [data] Additional event data
* @param {Function} fn Handler function, which will be executed in the block's context
- * @returns {BEMDOM} this
+ * @returns {Block} this
*/
bindToDomElem : function(domElem, event, data, fn) {
if(functions.isFunction(data)) {
@@ -397,7 +418,7 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* @param {String|Object} event Event name or event object
* @param {Object} [data] Additional event data
* @param {Function} fn Handler function, which will be executed in the block's context
- * @returns {BEMDOM} this
+ * @returns {Block} this
*/
bindToDoc : function(event, data, fn) {
this._needSpecialUnbind = true;
@@ -410,7 +431,7 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* @param {String|Object} event Event name or event object
* @param {Object} [data] Additional event data
* @param {Function} fn Handler function, which will be executed in the block's context
- * @returns {BEMDOM} this
+ * @returns {Block} this
*/
bindToWin : function(event, data, fn) {
this._needSpecialUnbind = true;
@@ -424,7 +445,7 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* @param {String|Object} event Event name or event object
* @param {Object} [data] Additional event data
* @param {Function} fn Handler function, which will be executed in the block's context
- * @returns {BEMDOM} this
+ * @returns {Block} this
*/
bindTo : function(elem, event, data, fn) {
var len = arguments.length;
@@ -458,13 +479,13 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
},
/**
- * Removes event handlers from any DOM element
- * @protected
- * @param {jQuery} domElem DOM element where the event was being listened for
- * @param {String|Object} event Event name or event object
- * @param {Function} [fn] Handler function
- * @returns {BEMDOM} this
- */
+ * Removes event handlers from any DOM element
+ * @protected
+ * @param {jQuery} domElem DOM element where the event was being listened for
+ * @param {String|Object} event Event name or event object
+ * @param {Function} [fn] Handler function
+ * @returns {Block} this
+ */
unbindFromDomElem : function(domElem, event, fn) {
if(typeof event === 'string') {
event = this._buildEventName(event);
@@ -481,35 +502,35 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
},
/**
- * Removes event handler from document
- * @protected
- * @param {String|Object} event Event name or event object
- * @param {Function} [fn] Handler function
- * @returns {BEMDOM} this
- */
+ * Removes event handler from document
+ * @protected
+ * @param {String|Object} event Event name or event object
+ * @param {Function} [fn] Handler function
+ * @returns {Block} this
+ */
unbindFromDoc : function(event, fn) {
return this.unbindFromDomElem(doc, event, fn);
},
/**
- * Removes event handler from window
- * @protected
- * @param {String|Object} event Event name or event object
- * @param {Function} [fn] Handler function
- * @returns {BEMDOM} this
- */
+ * Removes event handler from window
+ * @protected
+ * @param {String|Object} event Event name or event object
+ * @param {Function} [fn] Handler function
+ * @returns {Block} this
+ */
unbindFromWin : function(event, fn) {
return this.unbindFromDomElem(win, event, fn);
},
/**
- * Removes event handlers from the block's main DOM elements or its nested elements
- * @protected
- * @param {jQuery|String} [elem] Nested element
- * @param {String|Object} event Event name or event object
- * @param {Function} [fn] Handler function
- * @returns {BEMDOM} this
- */
+ * Removes event handlers from the block's main DOM elements or its nested elements
+ * @protected
+ * @param {jQuery|String} [elem] Nested element
+ * @param {String|Object} event Event name or event object
+ * @param {Function} [fn] Handler function
+ * @returns {Block} this
+ */
unbindFrom : function(elem, event, fn) {
var argLen = arguments.length;
if(argLen === 1) {
@@ -555,10 +576,10 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
if(event.indexOf('.') < 0) return eventNameCache[event] = event + uniq;
- var lego = '.bem_' + this.__self._name;
+ var ns = '.bem_' + this.__self._name;
return eventNameCache[event] = event.split('.').map(function(e, i) {
- return i === 0? e + lego : lego + '_' + e;
+ return i? ns + '_' + e : e + ns;
}).join('') + uniq;
},
@@ -597,7 +618,7 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* @param {jQuery} [elem] Nested element
* @param {String} modName Modifier name
* @param {String} modVal Modifier value
- * @returns {BEMDOM} this
+ * @returns {Block} this
*/
setMod : function(elem, modName, modVal) {
if(elem && typeof modVal !== 'undefined' && elem.length > 1) {
@@ -703,32 +724,21 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
/**
* Finds elements nested in a block
* @param {jQuery} [ctx=this.domElem] Element where search is being performed
- * @param {String} names Nested element name (or names separated by spaces)
- * @param {String} [modName] Modifier name
- * @param {String} [modVal] Modifier value
+ * @param {String|Object} elem Element name or description elem, modName, modVal
* @param {Boolean} [strictMode=false]
* @returns {jQuery} DOM elements
*/
- findElem : function(ctx, names, modName, modVal, strictMode) {
- if(typeof ctx === 'string') {
- strictMode = modVal;
- modVal = modName;
- modName = names;
- names = ctx;
+ findElem : function(ctx, elem, strictMode) {
+ if(!(ctx instanceof $)) {
+ strictMode = elem;
+ elem = ctx;
ctx = this.domElem;
}
- if(typeof modName === 'boolean') {
- strictMode = modName;
- modName = undef;
- }
+ typeof elem === 'string' && (elem = { elem : elem });
- var _self = this.__self,
- selector = '.' +
- names.split(' ').map(function(name) {
- return _self.buildClass(name, modName, modVal);
- }).join(',.'),
- res = findDomElem(ctx, selector);
+ var res = findDomElem(ctx, this.buildSelector(elem.elem, elem.modName, elem.modVal));
+ res.__bemElemName = elem.elem;
return strictMode? this._filterFindElemResults(res) : res;
},
@@ -746,48 +756,18 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
});
},
- /**
- * Finds elements nested in a block
- * @private
- * @param {String} name Nested element name
- * @param {String} [modName] Modifier name
- * @param {String} [modVal] Modifier value
- * @returns {jQuery} DOM elements
- */
- _elem : function(name, modName, modVal) {
- var key = name + buildModPostfix(modName, modVal),
- res;
-
- if(!(res = this._elemCache[key])) {
- res = this._elemCache[key] = this.findElem(name, modName, modVal);
- res.__bemElemName = name;
- }
-
- return res;
- },
-
/**
* Lazy search for elements nested in a block (caches results)
- * @param {String} names Nested element name (or names separated by spaces)
- * @param {String} [modName] Modifier name
- * @param {String} [modVal] Modifier value
+ * @private
+ * @param {String|Object} elem Element name or description elem, modName, modVal
* @returns {jQuery} DOM elements
*/
- elem : function(names, modName, modVal) {
- if(modName && typeof modName !== 'string') {
- modName.__bemElemName = names;
- return modName;
- }
+ elem : function(elem) {
+ typeof elem === 'string' && (elem = { elem : elem });
- if(names.indexOf(' ') < 0) {
- return this._elem(names, modName, modVal);
- }
+ var key = elem.elem + buildModPostfix(elem.modName, elem.modVal);
- var res = $([]);
- names.split(' ').forEach(function(name) {
- res = res.add(this._elem(name, modName, modVal));
- }, this);
- return res;
+ return this._elemCache[key] || (this._elemCache[key] = this.findElem(elem));
},
/**
@@ -806,7 +786,7 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* @param {String} [names] Nested element name (or names separated by spaces)
* @param {String} [modName] Modifier name
* @param {String} [modVal] Modifier value
- * @returns {BEMDOM} this
+ * @returns {Block} this
*/
dropElemCache : function(names, modName, modVal) {
if(names) {
@@ -883,36 +863,18 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* @private
*/
_destruct : function() {
- var _this = this,
- _self = _this.__self;
-
- _this._needSpecialUnbind && _self.doc.add(_self.win).unbind('.' + _this._uniqId);
+ if(this._needSpecialUnbind) {
+ var eventNs = '.' + this._uniqId;
+ doc.off(eventNs);
+ win.off(eventNs);
+ }
- _this.__base();
+ this.__base();
- delete uniqIdToBlock[_this.un()._uniqId];
+ this.un();
}
-}, /** @lends BEMDOM */{
-
- /**
- * Scope, will be set on onDomReady to `
`
- * @type jQuery
- */
- scope : null,
-
- /**
- * Document shortcut
- * @type jQuery
- */
- doc : doc,
-
- /**
- * Window shortcut
- * @type jQuery
- */
- win : win,
-
+}, /** @lends Block */{
/**
* Processes a block's live properties
* @private
@@ -934,126 +896,6 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
return res;
},
- /**
- * Initializes blocks on a fragment of the DOM tree
- * @param {jQuery|String} [ctx=scope] Root DOM node
- * @returns {jQuery} ctx Initialization context
- */
- init : function(ctx) {
- if(typeof ctx === 'string') {
- ctx = $(ctx);
- } else if(!ctx) ctx = DOM.scope;
-
- var uniqInitId = identify();
- findDomElem(ctx, BEM_SELECTOR).each(function() {
- initBlocks($(this), uniqInitId);
- });
-
- this._runInitFns();
-
- return ctx;
- },
-
- /**
- * Destroys blocks on a fragment of the DOM tree
- * @param {jQuery} ctx Root DOM node
- * @param {Boolean} [excludeSelf=false] Exclude the main domElem
- */
- destruct : function(ctx, excludeSelf) {
- var _ctx;
- if(excludeSelf) {
- storeDomNodeParents(_ctx = ctx.children());
- ctx.empty();
- } else {
- storeDomNodeParents(_ctx = ctx);
- ctx.remove();
- }
-
- reverse.call(findDomElem(_ctx, BEM_SELECTOR)).each(function(_, domNode) {
- var params = getParams(domNode);
- objects.each(params, function(blockParams) {
- if(blockParams.uniqId) {
- var block = uniqIdToBlock[blockParams.uniqId];
- block?
- removeDomNodeFromBlock(block, domNode) :
- delete uniqIdToDomElems[blockParams.uniqId];
- }
- });
- delete domElemToParams[identify(domNode)];
- });
-
- // flush parent nodes storage that has been filled above
- domNodesToParents = {};
- },
-
- /**
- * Replaces a fragment of the DOM tree inside the context, destroying old blocks and intializing new ones
- * @param {jQuery} ctx Root DOM node
- * @param {jQuery|String} content New content
- * @returns {jQuery} Updated root DOM node
- */
- update : function(ctx, content) {
- this.destruct(ctx, true);
- return this.init(ctx.html(content));
- },
-
- /**
- * Changes a fragment of the DOM tree including the context and initializes blocks.
- * @param {jQuery} ctx Root DOM node
- * @param {jQuery|String} content Content to be added
- * @returns {jQuery} New content
- */
- replace : function(ctx, content) {
- var prev = ctx.prev(),
- parent = ctx.parent();
-
- this.destruct(ctx);
-
- return this.init(prev.length?
- $(content).insertAfter(prev) :
- $(content).prependTo(parent));
- },
-
- /**
- * Adds a fragment of the DOM tree at the end of the context and initializes blocks
- * @param {jQuery} ctx Root DOM node
- * @param {jQuery|String} content Content to be added
- * @returns {jQuery} New content
- */
- append : function(ctx, content) {
- return this.init($(content).appendTo(ctx));
- },
-
- /**
- * Adds a fragment of the DOM tree at the beginning of the context and initializes blocks
- * @param {jQuery} ctx Root DOM node
- * @param {jQuery|String} content Content to be added
- * @returns {jQuery} New content
- */
- prepend : function(ctx, content) {
- return this.init($(content).prependTo(ctx));
- },
-
- /**
- * Adds a fragment of the DOM tree before the context and initializes blocks
- * @param {jQuery} ctx Contextual DOM node
- * @param {jQuery|String} content Content to be added
- * @returns {jQuery} New content
- */
- before : function(ctx, content) {
- return this.init($(content).insertBefore(ctx));
- },
-
- /**
- * Adds a fragment of the DOM tree after the context and initializes blocks
- * @param {jQuery} ctx Contextual DOM node
- * @param {jQuery|String} content Content to be added
- * @returns {jQuery} New content
- */
- after : function(ctx, content) {
- return this.init($(content).insertAfter(ctx));
- },
-
/**
* Builds a full name for a live event
* @private
@@ -1075,7 +917,7 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
if(!storage) {
storage = liveClassEventStorage[e] = {};
- DOM.scope.bind(e, $.proxy(this._liveClassTrigger, this));
+ BEMDOM.scope.on(e, $.proxy(this._liveClassTrigger, this));
}
storage = storage[className] || (storage[className] = { uniqIds : {}, fns : [] });
@@ -1170,6 +1012,7 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* @param {String|Object} [to] Description (object with modName, modVal, elem) or name of the element or elements (space-separated)
* @param {String} event Event name
* @param {Function} [callback] Handler
+ * @returns {Function} this
*/
liveBindTo : function(to, event, callback, invokeOnInit) {
if(!event || functions.isFunction(event)) {
@@ -1206,16 +1049,10 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* @param {String} [elem] Name of the element or elements (space-separated)
* @param {String} event Event name
* @param {Function} [callback] Handler
+ * @returns {Function} this
*/
liveUnbindFrom : function(elem, event, callback) {
-
- if(!event || functions.isFunction(event)) {
- callback = event;
- event = elem;
- elem = undef;
- }
-
- if(elem && elem.indexOf(' ') > 1) {
+ if(elem.indexOf(' ') > 1) {
elem.split(' ').forEach(function(elem) {
this._liveClassUnbind(
this.buildClass(elem),
@@ -1235,20 +1072,23 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* Helper for live initialization when a different block is initialized
* @private
* @param {String} event Event name
- * @param {String} blockName Name of the block that should trigger a reaction when initialized
+ * @param {Function} Block Block that should emit a reaction when initialized
* @param {Function} callback Handler to be called after successful initialization in the new block's context
* @param {String} findFnName Name of the method for searching
*/
- _liveInitOnBlockEvent : function(event, blockName, callback, findFnName) {
- var name = this._name;
- blocks[blockName].on(event, function(e) {
- var args = arguments,
- blocks = e.target[findFnName](name);
-
- callback && blocks.forEach(function(block) {
- callback.apply(block, args);
- });
- });
+ _liveInitOnBlockEvent : function(event, Block, callback, findFnName) {
+ Block.on(
+ event,
+ function(e) {
+ var args = arguments,
+ blocks = e.target[findFnName](this);
+
+ callback && blocks.forEach(function(block) {
+ callback.apply(block, args);
+ });
+ },
+ this);
+
return this;
},
@@ -1256,22 +1096,24 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* Helper for live initialization for a different block's event on the current block's DOM element
* @protected
* @param {String} event Event name
- * @param {String} blockName Name of the block that should trigger a reaction when initialized
+ * @param {Function} Block Block that should emit a reaction when initialized
* @param {Function} callback Handler to be called after successful initialization in the new block's context
+ * @returns {Function} this
*/
- liveInitOnBlockEvent : function(event, blockName, callback) {
- return this._liveInitOnBlockEvent(event, blockName, callback, 'findBlocksOn');
+ liveInitOnBlockEvent : function(event, Block, callback) {
+ return this._liveInitOnBlockEvent(event, Block, callback, 'findBlocksOn');
},
/**
* Helper for live initialization for a different block's event inside the current block
* @protected
* @param {String} event Event name
- * @param {String} blockName Name of the block that should trigger a reaction when initialized
+ * @param {Function} Block Block that should emit a reaction when initialized
* @param {Function} [callback] Handler to be called after successful initialization in the new block's context
+ * @returns {Function} this
*/
- liveInitOnBlockInsideEvent : function(event, blockName, callback) {
- return this._liveInitOnBlockEvent(event, blockName, callback, 'findBlocksOutside');
+ liveInitOnBlockInsideEvent : function(event, Block, callback) {
+ return this._liveInitOnBlockEvent(event, Block, callback, 'findBlocksOutside');
},
/**
@@ -1281,6 +1123,7 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* @param {Object} [data] Additional information that the handler gets as e.data
* @param {Function} fn Handler
* @param {Object} [fnCtx] Handler's context
+ * @returns {Function} this
*/
on : function(ctx, e, data, fn, fnCtx) {
return typeof ctx === 'object' && ctx.jquery?
@@ -1294,6 +1137,7 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* @param {String} e Event name
* @param {Function} [fn] Handler
* @param {Object} [fnCtx] Handler context
+ * @returns {Function} this
*/
un : function(ctx, e, fn, fnCtx) {
return typeof ctx === 'object' && ctx.jquery?
@@ -1309,7 +1153,7 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* @param {Object} [data] Additional information that the handler gets as e.data
* @param {Function} fn Handler
* @param {Object} [fnCtx] Handler context
- * @returns {BEMDOM} this
+ * @returns {Function} this
*/
_liveCtxBind : function(ctx, e, data, fn, fnCtx) {
if(typeof e === 'object') {
@@ -1363,6 +1207,7 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
* @param {String|Object} e Event name
* @param {Function} [fn] Handler
* @param {Object} [fnCtx] Handler context
+ * @returns {Function} this
*/
_liveCtxUnbind : function(ctx, e, fn, fnCtx) {
if(typeof e === 'object' && functions.isFunction(fn)) { // mod change event
@@ -1467,20 +1312,187 @@ var DOM = BEM.decl('i-bem__dom',/** @lends BEMDOM.prototype */{
/**
* Returns a block on a DOM element and initializes it if necessary
- * @param {String} blockName Block name
+ * @param {Function} Block Block
* @param {Object} params Block parameters
* @returns {BEMDOM}
*/
-$.fn.bem = function(blockName, params) {
- return initBlock(blockName, this, params, true);
+$.fn.bem = function(Block, params) {
+ return initBlock(Block.getName(), this, params, true);
+};
+
+var BEMDOM = /** @exports */{
+
+ /**
+ * Scope
+ * @type jQuery
+ */
+ scope : null,
+
+ /**
+ * Document shortcut
+ * @type jQuery
+ */
+ doc : doc,
+
+ /**
+ * Window shortcut
+ * @type jQuery
+ */
+ win : win,
+
+ /**
+ * Base BEMDOM block
+ * @type Function
+ */
+ Block : Block,
+
+ /**
+ * Declares DOM-based block and creates a block class
+ * @param {String} blockName Block name
+ * @param {Function|Array[Function]} [baseBlocks] base block + mixes
+ * @param {Object} [props] Methods
+ * @param {Object} [staticProps] Static methods
+ * @returns {Function} Block class
+ */
+ declBlock : function(blockName, baseBlocks, props, staticProps) {
+ if(!baseBlocks || (typeof baseBlocks === 'object' && !Array.isArray(baseBlocks))) {
+ staticProps = props;
+ props = baseBlocks;
+ baseBlocks = Block;
+ }
+
+ return BEM.declBlock(blockName, baseBlocks, props, staticProps);
+ },
+
+ declMix : BEM.declMix,
+
+ /**
+ * Initializes blocks on a fragment of the DOM tree
+ * @param {jQuery|String} [ctx=scope] Root DOM node
+ * @returns {jQuery} ctx Initialization context
+ */
+ init : function(ctx) {
+ ctx = typeof ctx === 'string'?
+ $(ctx) :
+ ctx || BEMDOM.scope;
+
+ var uniqInitId = identify();
+ findDomElem(ctx, BEM_SELECTOR).each(function() {
+ initBlocks($(this), uniqInitId);
+ });
+
+ BEM._runInitFns();
+
+ return ctx;
+ },
+
+ /**
+ * Destroys blocks on a fragment of the DOM tree
+ * @param {jQuery} ctx Root DOM node
+ * @param {Boolean} [excludeSelf=false] Exclude the main domElem
+ */
+ destruct : function(ctx, excludeSelf) {
+ var _ctx;
+ if(excludeSelf) {
+ storeDomNodeParents(_ctx = ctx.children());
+ ctx.empty();
+ } else {
+ storeDomNodeParents(_ctx = ctx);
+ ctx.remove();
+ }
+
+ reverse.call(findDomElem(_ctx, BEM_SELECTOR)).each(function(_, domNode) {
+ var params = getParams(domNode);
+ objects.each(params, function(blockParams) {
+ if(blockParams.uniqId) {
+ var block = uniqIdToBlock[blockParams.uniqId];
+ block?
+ removeDomNodeFromBlock(block, domNode) :
+ delete uniqIdToDomElems[blockParams.uniqId];
+ }
+ });
+ delete domElemToParams[identify(domNode)];
+ });
+
+ // flush parent nodes storage that has been filled above
+ domNodesToParents = {};
+ },
+
+ /**
+ * Replaces a fragment of the DOM tree inside the context, destroying old blocks and intializing new ones
+ * @param {jQuery} ctx Root DOM node
+ * @param {jQuery|String} content New content
+ * @returns {jQuery} Updated root DOM node
+ */
+ update : function(ctx, content) {
+ this.destruct(ctx, true);
+ return this.init(ctx.html(content));
+ },
+
+ /**
+ * Changes a fragment of the DOM tree including the context and initializes blocks.
+ * @param {jQuery} ctx Root DOM node
+ * @param {jQuery|String} content Content to be added
+ * @returns {jQuery} New content
+ */
+ replace : function(ctx, content) {
+ var prev = ctx.prev(),
+ parent = ctx.parent();
+
+ this.destruct(ctx);
+
+ return this.init(prev.length?
+ $(content).insertAfter(prev) :
+ $(content).prependTo(parent));
+ },
+
+ /**
+ * Adds a fragment of the DOM tree at the end of the context and initializes blocks
+ * @param {jQuery} ctx Root DOM node
+ * @param {jQuery|String} content Content to be added
+ * @returns {jQuery} New content
+ */
+ append : function(ctx, content) {
+ return this.init($(content).appendTo(ctx));
+ },
+
+ /**
+ * Adds a fragment of the DOM tree at the beginning of the context and initializes blocks
+ * @param {jQuery} ctx Root DOM node
+ * @param {jQuery|String} content Content to be added
+ * @returns {jQuery} New content
+ */
+ prepend : function(ctx, content) {
+ return this.init($(content).prependTo(ctx));
+ },
+
+ /**
+ * Adds a fragment of the DOM tree before the context and initializes blocks
+ * @param {jQuery} ctx Contextual DOM node
+ * @param {jQuery|String} content Content to be added
+ * @returns {jQuery} New content
+ */
+ before : function(ctx, content) {
+ return this.init($(content).insertBefore(ctx));
+ },
+
+ /**
+ * Adds a fragment of the DOM tree after the context and initializes blocks
+ * @param {jQuery} ctx Contextual DOM node
+ * @param {jQuery|String} content Content to be added
+ * @returns {jQuery} New content
+ */
+ after : function(ctx, content) {
+ return this.init($(content).insertAfter(ctx));
+ }
};
// Set default scope after DOM ready
$(function() {
- DOM.scope = $('body');
+ BEMDOM.scope = $('body');
});
-provide(DOM);
+provide(BEMDOM);
});
@@ -1491,8 +1503,8 @@ var origDefine = modules.define;
modules.define = function(name, deps, decl) {
origDefine.apply(modules, arguments);
- name !== 'i-bem__dom_init' && arguments.length > 2 && ~deps.indexOf('i-bem__dom') &&
- modules.define('i-bem__dom_init', [name], function(provide, _, prev) {
+ name !== 'i-bem-dom__init' && arguments.length > 2 && ~deps.indexOf('i-bem-dom') &&
+ modules.define('i-bem-dom__init', [name], function(provide, _, prev) {
provide(prev);
});
};
diff --git a/common.blocks/i-bem-dom/i-bem-dom.spec.js b/common.blocks/i-bem-dom/i-bem-dom.spec.js
new file mode 100644
index 000000000..70bd930f7
--- /dev/null
+++ b/common.blocks/i-bem-dom/i-bem-dom.spec.js
@@ -0,0 +1,1081 @@
+modules.define(
+ 'spec',
+ ['i-bem', 'i-bem-dom', 'objects', 'jquery', 'sinon', 'BEMHTML'],
+ function(provide, BEM, BEMDOM, objects, $, sinon, BEMHTML) {
+
+describe('i-bem-dom', function() {
+ describe('getMod', function() {
+ it('should return properly extracted mod from html', function() {
+ var Block = BEMDOM.declBlock('block'),
+ rootNode;
+
+ [
+ {
+ cls : '',
+ val : ''
+ },
+ {
+ cls : 'block_m1_v1',
+ val : 'v1'
+ },
+ {
+ cls : 'block_m1_v1 bla-block_m1_v2',
+ val : 'v1'
+ },
+ {
+ cls : 'bla-block_m1_v2 block_m1_v1',
+ val : 'v1'
+ },
+ {
+ cls : 'block_m1',
+ val : true
+ }
+ ].forEach(function(data) {
+ (rootNode = $('')).bem(Block).getMod('m1')
+ .should.be.eql(data.val);
+ BEMDOM.destruct(rootNode);
+ });
+
+ delete BEM.blocks['block'];
+ });
+ });
+
+ describe('getMods', function() {
+ it('should return properly extracted block mods from html', function() {
+ var Block = BEMDOM.declBlock('block'),
+ rootNode;
+
+ [
+ {
+ cls : '',
+ mods : { js : 'inited' }
+ },
+ {
+ cls : 'block_m1_v1',
+ mods : { js : 'inited', m1 : 'v1' }
+ },
+ {
+ cls : 'block_m1_v1 block_m2_v2 bla-block_m4_v3 block_m4_v4',
+ mods : { js : 'inited', m1 : 'v1', m2 : 'v2', m4 : 'v4' }
+ },
+ {
+ cls : 'bla-block_m1_v1 block_m2_v2 block_m3_v3 bla-block_m3_v4 block_m4',
+ mods : { js : 'inited', m2 : 'v2', m3 : 'v3', m4 : true }
+ }
+ ].forEach(function(data) {
+ (rootNode = $('')).bem(Block).getMods()
+ .should.be.eql(data.mods);
+ BEMDOM.destruct(rootNode);
+ });
+
+ delete BEM.blocks['block'];
+ });
+
+ it('should return properly extracted elem mods from html', function() {
+ var Block = BEMDOM.declBlock('block'),
+ rootNode;
+
+ [
+ {
+ cls : 'block__e1_m1_v1',
+ mods : { m1 : 'v1' }
+ },
+ {
+ cls : 'block__e1_m1_v1 block__e1_m2_v2 bla-block__e1_m4_v3 block__e1_m4_v4',
+ mods : { m1 : 'v1', m2 : 'v2', m4 : 'v4' }
+ },
+ {
+ cls : 'bla-block__e1_m1_v1 block__e1_m2_v2 block__e1_m3_v3 bla-block__e1_m3_v4 block__e1_m4',
+ mods : { m2 : 'v2', m3 : 'v3', m4 : true }
+ }
+ ].forEach(function(data) {
+ var block = (rootNode = $('')).bem(Block);
+ block.getMods(block.elem('e1')).should.be.eql(data.mods);
+ BEMDOM.destruct(rootNode);
+ });
+
+ delete BEM.blocks['block'];
+ });
+ });
+
+ describe('setMod', function() {
+ it('should properly set CSS classes', function() {
+ var Block = BEMDOM.declBlock('block'),
+ rootNode;
+
+ [
+ {
+ beforeCls : 'block i-bem',
+ afterCls : 'block i-bem block_js_inited block_m1_v1',
+ mods : { m1 : 'v1' }
+ },
+ {
+ beforeCls : 'block i-bem block_m6 block_m7_v7',
+ afterCls : 'block i-bem block_js_inited block_m1_v1 block_m2_v2 block_m3 block_m4_v4 block_m5',
+ mods : { m1 : 'v1', m2 : 'v2', m3 : true, m4 : 'v4', m5 : true, m6 : false, m7 : '' }
+ }
+ ].forEach(function(data) {
+ var block = (rootNode = $('')).bem(Block);
+
+ objects.each(data.mods, function(modVal, modName) {
+ modName === 'm3'?
+ block.setMod(modName) :
+ block.setMod(modName, modVal);
+ });
+
+ block.domElem[0].className.should.be.equal(data.afterCls);
+
+ BEMDOM.destruct(rootNode);
+ });
+
+ delete BEM.blocks['block'];
+ });
+
+ it('should properly set elem CSS classes', function() {
+ var Block = BEMDOM.declBlock('block'),
+ rootNode;
+
+ [
+ {
+ beforeCls : 'block__elem',
+ afterCls : 'block__elem block__elem_m1_v1',
+ mods : { m1 : 'v1' }
+ },
+ {
+ beforeCls : 'block__elem block__elem_m6 block__elem_m7_v7',
+ afterCls : 'block__elem block__elem_m1_v1 block__elem_m2_v2 block__elem_m3 block__elem_m4_v4 block__elem_m5',
+ mods : { m1 : 'v1', m2 : 'v2', m3 : true, m4 : 'v4', m5 : true, m6 : false, m7 : '' }
+ }
+ ].forEach(function(data) {
+ var block = (rootNode = $('')).bem(Block),
+ elem = block.elem('elem');
+
+ objects.each(data.mods, function(modVal, modName) {
+ modName === 'm3'?
+ block.setMod(elem, modName) :
+ block.setMod(elem, modName, modVal);
+ });
+
+ elem[0].className.should.be.equal(data.afterCls);
+
+ BEMDOM.destruct(rootNode);
+ });
+
+ delete BEM.blocks['block'];
+ });
+ });
+
+ describe('elemify', function() {
+ var rootNode, instance;
+ beforeEach(function() {
+ var Block = BEMDOM.declBlock('block');
+ rootNode = BEMDOM.init(BEMHTML.apply({
+ block : 'block',
+ js : true,
+ content : { elem : 'e1', mix : { elem : 'e2' } }
+ }));
+ instance = rootNode.bem(Block);
+ });
+ afterEach(function() {
+ BEMDOM.destruct(rootNode);
+ delete BEM.blocks['block'];
+ });
+
+ it('shouldn\'t change given elem', function() {
+ var elem1 = instance.elem('e1');
+ instance.elemify(elem1, 'e2');
+ instance.__self._extractElemNameFrom(elem1).should.be.equal('e1');
+ });
+
+ it('should return', function() {
+ var elem = instance.elemify(instance.elem('e1'), 'e2');
+ instance.__self._extractElemNameFrom(elem).should.be.equal('e2');
+ });
+ });
+
+ describe('findBlocksInside', function() {
+ function getBlockIds(blocks) {
+ return blocks.map(function(block) {
+ return block.params.id;
+ });
+ }
+
+ var rootNode, rootBlock, B1Block;
+ beforeEach(function() {
+ var RootBlock = BEMDOM.declBlock('root');
+ B1Block = BEMDOM.declBlock('b1');
+ rootNode = $(BEMHTML.apply(
+ {
+ block : 'root',
+ content : {
+ block : 'b1',
+ js : { id : '1' },
+ content : [
+ { block : 'b2' },
+ {
+ block : 'b1',
+ mods : { m1 : 'v1' },
+ js : { id : '2' }
+ },
+ {
+ block : 'b3',
+ content : {
+ block : 'b1',
+ mods : { m1 : 'v2' },
+ js : { id : '3' },
+ content : {
+ block : 'b1',
+ mods : { m1 : true },
+ js : { id : '4' }
+ }
+ }
+ }
+ ]
+ }
+ }));
+ rootBlock = BEMDOM.init(rootNode).bem(RootBlock);
+ });
+
+ afterEach(function() {
+ BEMDOM.destruct(rootNode);
+ delete BEM.blocks['b-root'];
+ delete BEM.blocks['b1'];
+ });
+
+ it('should find all blocks by block', function() {
+ getBlockIds(rootBlock.findBlocksInside(B1Block)).should.be.eql(['1', '2', '3', '4']);
+ });
+
+ it('should find all blocks by block, modName and modVal', function() {
+ getBlockIds(rootBlock.findBlocksInside({ block : B1Block, modName : 'm1', modVal : 'v1' }))
+ .should.be.eql(['2']);
+ });
+
+ it('should find all blocks by block and boolean mod', function() {
+ getBlockIds(rootBlock.findBlocksInside({ block : B1Block, modName : 'm1', modVal : true }))
+ .should.be.eql(['4']);
+ });
+ });
+
+ describe('BEMDOM.init', function() {
+ var spy, rootNode;
+ beforeEach(function() {
+ spy = sinon.spy();
+ });
+
+ afterEach(function() {
+ BEMDOM.destruct(rootNode);
+ delete BEM.blocks['block'];
+ });
+
+ it('should init block', function() {
+ BEMDOM.declBlock('block', {
+ onSetMod : {
+ js : {
+ inited : spy
+ }
+ }
+ });
+
+ rootNode = BEMDOM.init(BEMHTML.apply({
+ tag : 'div',
+ content : { block : 'block', js : true }
+ }));
+
+ spy.should.have.been.called;
+ });
+
+ it('should init block with multiple DOM nodes', function(done) {
+ BEMDOM.declBlock('block', {
+ onSetMod : {
+ js : {
+ inited : function() {
+ this.domElem.length.should.be.equal(2);
+ done();
+ }
+ }
+ }
+ });
+
+ rootNode = BEMDOM.init(BEMHTML.apply({
+ tag : 'div',
+ content : [
+ { block : 'block', js : { id : 'id' } },
+ { block : 'block', js : { id : 'id' } }
+ ]
+ }));
+ });
+
+ it('shouldn\'t init live block', function() {
+ BEMDOM.declBlock('block', {
+ onSetMod : {
+ js : {
+ inited : spy
+ }
+ }
+ }, {
+ live : true
+ });
+
+ rootNode = BEMDOM.init(BEMHTML.apply({
+ tag : 'div',
+ content : { block : 'block', js : true }
+ }));
+
+ BEMDOM.init(rootNode);
+ spy.should.not.have.been.called;
+ });
+
+ it('should allow to pass string', function() {
+ BEMDOM.declBlock('block', {
+ onSetMod : {
+ js : {
+ inited : spy
+ }
+ }
+ });
+
+ rootNode = BEMDOM.init(BEMHTML.apply({
+ tag : 'div',
+ content : { block : 'block', js : true }
+ }));
+
+ spy.should.have.been.called;
+ });
+ });
+
+ describe('BEMDOM.destruct', function() {
+ var spy, rootNode;
+ beforeEach(function() {
+ spy = sinon.spy();
+ });
+
+ afterEach(function() {
+ delete BEM.blocks['block'];
+ });
+
+ it('should destruct block only if it has no dom nodes', function() {
+ BEMDOM.declBlock('block', {
+ onSetMod : {
+ js : {
+ '' : spy
+ }
+ }
+ });
+
+ rootNode = BEMDOM.init(BEMHTML.apply({
+ tag : 'div',
+ content : [
+ { block : 'block', js : { id : 'block' } },
+ { block : 'block', js : { id : 'block' } }
+ ]
+ }));
+
+ BEMDOM.destruct(rootNode.find('.block :eq(0)'));
+ spy.should.not.have.been.called;
+
+ BEMDOM.destruct(rootNode.find('.block'));
+ spy.should.have.been.called;
+
+ BEMDOM.destruct(rootNode);
+ });
+
+ it('should destruct implicitly inited block', function() {
+ var Block = BEMDOM.declBlock('block', {
+ onSetMod : {
+ js : {
+ '' : spy
+ }
+ }
+ });
+
+ rootNode = BEMDOM.init(BEMHTML.apply({
+ tag : 'div',
+ content : { block : 'block' }
+ }));
+
+ var blockNode = rootNode.find('.block');
+ blockNode.bem(Block);
+ BEMDOM.destruct(blockNode);
+ spy.should.have.been.called;
+ });
+ });
+
+ describe('BEMDOM.update', function() {
+ it('should properly update tree', function() {
+ var spyBlock1Destructed = sinon.spy(),
+ spyBlock2Inited = sinon.spy();
+
+ BEMDOM.declBlock('block1', {
+ onSetMod : {
+ js : {
+ '' : spyBlock1Destructed
+ }
+ }
+ });
+ BEMDOM.declBlock('block2', {
+ onSetMod : {
+ js : {
+ inited : spyBlock2Inited
+ }
+ }
+ });
+
+ var rootNode = BEMDOM.init(BEMHTML.apply({
+ tag : 'div',
+ content : { block : 'block1', js : true } }));
+
+ BEMDOM.update(rootNode, BEMHTML.apply({ block : 'block2', js : true }));
+
+ spyBlock1Destructed.called.should.be.true;
+ spyBlock2Inited.called.should.be.true;
+
+ BEMDOM.destruct(rootNode);
+ delete BEM.blocks['block1'];
+ delete BEM.blocks['block2'];
+ });
+
+ it('should allow to pass simple string', function() {
+ var domElem = $('');
+ BEMDOM.update(domElem, 'simple string');
+ domElem.html().should.be.equal('simple string');
+ });
+ });
+
+ describe('BEMDOM.replace', function() {
+ it('should properly replace tree', function() {
+ var spyBlock1Destructed = sinon.spy(),
+ spyBlock2Inited = sinon.spy();
+
+ BEMDOM.declBlock('block1', {
+ onSetMod : {
+ js : {
+ '' : spyBlock1Destructed
+ }
+ }
+ });
+ BEMDOM.declBlock('block2', {
+ onSetMod : {
+ js : {
+ inited : spyBlock2Inited
+ }
+ }
+ });
+
+ var rootNode = BEMDOM.init(BEMHTML.apply({
+ tag : 'div',
+ content : { block : 'block1', js : true }
+ }));
+
+ BEMDOM.replace(rootNode.find('.block1'), BEMHTML.apply({ block : 'block2', js : true }));
+
+ spyBlock1Destructed.should.have.been.calledOnce;
+ spyBlock2Inited.should.have.been.calledOnce;
+
+ rootNode.html().should.be.equal('');
+
+ BEMDOM.destruct(rootNode);
+
+ rootNode = BEMDOM.init(BEMHTML.apply({
+ tag : 'div',
+ content : [{ tag : 'p' }, { block : 'block1', js : true }, { tag : 'p' }]
+ }));
+
+ BEMDOM.replace(rootNode.find('.block1'), BEMHTML.apply({ block : 'block2', js : true }));
+
+ spyBlock1Destructed.should.have.been.calledTwice;
+ spyBlock2Inited.should.have.been.calledTwice;
+
+ rootNode.html().should.be.equal('');
+
+ delete BEM.blocks['block1'];
+ delete BEM.blocks['block2'];
+ });
+ });
+
+ describe('params', function() {
+ it('should properly join params', function() {
+ var Block = BEMDOM.declBlock('block', {
+ getDefaultParams : function() {
+ return { p1 : 1 };
+ }
+ });
+
+ BEMDOM.declBlock('block2', {
+ onSetMod : {
+ 'js' : {
+ 'inited' : function() {
+ var params = this.findBlockOn(Block).params;
+ params.p1.should.be.equal(1);
+ params.p2.should.be.equal(2);
+ params.p3.should.be.equal(3);
+ }
+ }
+ }
+ });
+
+ var rootNode = BEMDOM.init(BEMHTML.apply({
+ tag : 'div',
+ content : [
+ { block : 'block', js : { id : 'bla', p2 : 2 }, mix : { block : 'block2', js : true } },
+ { block : 'block', js : { id : 'bla', p3 : 3 } }
+ ]
+ }));
+
+ BEMDOM.destruct(rootNode);
+ delete BEM.blocks['block'];
+ delete BEM.blocks['block2'];
+ });
+ });
+
+ describe('emit', function() {
+ it('should emit context event with target', function() {
+ var Block = BEMDOM.declBlock('block', {
+ onSetMod : {
+ 'js' : {
+ 'inited' : function() {
+ this.emit('event');
+ }
+ }
+ }
+ }),
+ rootNode = $(''),
+ spy = sinon.spy();
+
+ BEM.blocks['block'].on(rootNode, 'event', spy);
+ BEMDOM.update(rootNode, BEMHTML.apply({ block : 'block', js : true }));
+
+ var block = rootNode.find('.block').bem(Block);
+
+ spy.should.have.been.calledOnce;
+ spy.args[0][0].target.should.be.equal(block);
+
+ delete BEM.blocks['block'];
+ });
+ });
+
+ describe('containsDomElem', function() {
+ var domElem, block, block2;
+ beforeEach(function() {
+ var Block = BEMDOM.declBlock('block'),
+ Block2 = BEMDOM.declBlock('block2');
+
+ domElem = $(BEMHTML.apply([
+ {
+ block : 'block',
+ js : { id : '1' },
+ content : [
+ { elem : 'e1' },
+ { elem : 'e2' }
+ ]
+ },
+ {
+ block : 'block',
+ js : { id : '1' },
+ content : [
+ { elem : 'e1' },
+ { elem : 'e2', content : { elem : 'e2-1' } }
+ ]
+ },
+ {
+ block : 'block2'
+ }
+ ]));
+
+ BEMDOM.init(domElem);
+ block = domElem.filter('.block').bem(Block);
+ block2 = domElem.filter('.block2').bem(Block2);
+ });
+
+ afterEach(function() {
+ BEMDOM.destruct(domElem);
+ delete BEM.blocks['block'];
+ delete BEM.blocks['block2'];
+ });
+
+ it('should properly checks for nested dom elem', function() {
+ block.containsDomElem(block.elem('e2-1')).should.be.true;
+ block.containsDomElem(block2.domElem).should.be.false;
+ });
+
+ it('should properly checks for nested dom elem with given context', function() {
+ block.containsDomElem(block.elem('e1'), block.elem('e2-1')).should.be.false;
+ block.containsDomElem(block.elem('e2'), block.elem('e2-1')).should.be.true;
+ });
+ });
+
+ describe('DOM events', function() {
+ var Block, block, spy1, spy2, spy3, spy4, spy5,
+ data = { data : 'data' };
+
+ beforeEach(function() {
+ spy1 = sinon.spy();
+ spy2 = sinon.spy();
+ spy3 = sinon.spy();
+ spy4 = sinon.spy();
+ spy5 = sinon.spy();
+ });
+
+ afterEach(function() {
+ BEMDOM.destruct(block.domElem);
+ delete BEM.blocks['block'];
+ });
+
+ describe('block domElem events', function() {
+ beforeEach(function() {
+ Block = BEMDOM.declBlock('block', {
+ onSetMod : {
+ 'js' : {
+ 'inited' : function() {
+ this
+ .bindTo('click', spy1)
+ .bindTo('click', spy2)
+ .bindTo('click', data, spy3)
+ .bindTo({ 'click' : spy4 }, data);
+ }
+ }
+ }
+ });
+ block = BEMDOM.init(BEMHTML.apply({ block : 'block' })).bem(Block);
+ });
+
+ it('should properly bind handlers', function() {
+ block.domElem.trigger('click');
+
+ spy1.should.have.been.called;
+ spy2.should.have.been.called;
+
+ spy3.args[0][0].data.should.have.been.equal(data);
+ spy4.args[0][0].data.should.have.been.equal(data);
+ });
+
+ it('should properly unbind all handlers', function() {
+ block.unbindFrom('click');
+ block.domElem.trigger('click');
+
+ spy1.should.not.have.been.called;
+ spy2.should.not.have.been.called;
+ });
+
+ it('should properly unbind specified handler', function() {
+ block.unbindFrom('click', spy1);
+ block.domElem.trigger('click');
+
+ spy1.should.not.have.been.called;
+ spy2.should.have.been.called;
+ });
+ });
+
+ describe('block elems (as string) events', function() {
+ var spy3;
+
+ beforeEach(function() {
+ spy3 = sinon.spy();
+ Block = BEMDOM.declBlock('block', {
+ onSetMod : {
+ 'js' : {
+ 'inited' : function() {
+ this
+ .bindTo('e1', 'click', spy1)
+ .bindTo('e2', 'click', spy2)
+ .bindTo('e2', 'click', spy3)
+ .bindTo('e2', 'click', data, spy4)
+ .bindTo('e2', { 'click' : spy5 }, data);
+ }
+ }
+ }
+ });
+ block = BEMDOM.init(BEMHTML.apply({ block : 'block', content : [{ elem : 'e1' }, { elem : 'e2' }] }))
+ .bem(Block);
+ });
+
+ it('should properly bind handlers', function() {
+ block.elem('e2').trigger('click');
+
+ spy1.should.not.have.been.called;
+ spy2.should.have.been.called;
+ spy3.should.have.been.called;
+
+ spy4.args[0][0].data.should.have.been.equal(data);
+ spy5.args[0][0].data.should.have.been.equal(data);
+ });
+
+ it('should properly unbind all handlers', function() {
+ block.unbindFrom('e2', 'click');
+ block.elem('e2').trigger('click');
+
+ spy1.should.not.have.been.called;
+ spy2.should.not.have.been.called;
+ spy3.should.not.have.been.called;
+ });
+
+ it('should properly unbind specified handler', function() {
+ block.unbindFrom('e2', 'click', spy2);
+ block.elem('e2').trigger('click');
+
+ spy1.should.not.have.been.called;
+ spy2.should.not.have.been.called;
+ spy3.should.have.been.called;
+ });
+ });
+
+ describe('block elems (as $) events', function() {
+ var spy3;
+
+ beforeEach(function() {
+ spy3 = sinon.spy();
+ Block = BEMDOM.declBlock('block', {
+ onSetMod : {
+ 'js' : {
+ 'inited' : function() {
+ this
+ .bindTo(this.elem('e1'), 'click', spy1)
+ .bindTo(this.elem('e2'), 'click', spy2)
+ .bindTo(this.elem('e2'), 'click', spy3)
+ .bindTo(this.elem('e2'), 'click', data, spy4)
+ .bindTo(this.elem('e2'), { 'click' : spy5 }, data);
+ }
+ }
+ }
+ });
+ block = BEMDOM.init(BEMHTML.apply({ block : 'block', content : [{ elem : 'e1' }, { elem : 'e2' }] }))
+ .bem(Block);
+ });
+
+ it('should properly bind handlers', function() {
+ block.elem('e2').trigger('click');
+
+ spy1.should.not.have.been.called;
+ spy2.should.have.been.called;
+ spy3.should.have.been.called;
+
+ spy4.args[0][0].data.should.have.been.equal(data);
+ spy5.args[0][0].data.should.have.been.equal(data);
+ });
+
+ it('should properly unbind all handlers', function() {
+ block.unbindFrom('e2', 'click');
+ block.elem('e2').trigger('click');
+
+ spy1.should.not.have.been.called;
+ spy2.should.not.have.been.called;
+ spy3.should.not.have.been.called;
+ });
+
+ it('should properly unbind specified handler', function() {
+ block.unbindFrom('e2', 'click', spy2);
+ block.elem('e2').trigger('click');
+
+ spy1.should.not.have.been.called;
+ spy2.should.not.have.been.called;
+ spy3.should.have.been.called;
+ });
+ });
+
+ describe('document events', function() {
+ beforeEach(function() {
+ Block = BEMDOM.declBlock('block', {
+ onSetMod : {
+ 'js' : {
+ 'inited' : function() {
+ this
+ .bindToDoc('click', spy1)
+ .bindToDoc('click', spy2)
+ .bindToDoc('click', data, spy3)
+ .bindToDoc({ 'click' : spy4 }, data);
+ }
+ }
+ }
+ });
+ block = BEMDOM.init(BEMHTML.apply({ block : 'block' })).bem(Block);
+ });
+
+ it('should properly bind handlers', function() {
+ BEMDOM.doc.trigger('click');
+
+ spy1.should.have.been.called;
+ spy2.should.have.been.called;
+
+ spy3.args[0][0].data.should.have.been.equal(data);
+ spy4.args[0][0].data.should.have.been.equal(data);
+ });
+
+ it('should properly unbind all handlers', function() {
+ block.unbindFromDoc('click');
+ BEMDOM.doc.trigger('click');
+
+ spy1.should.not.have.been.called;
+ spy2.should.not.have.been.called;
+ });
+
+ it('should properly unbind specified handler', function() {
+ block.unbindFromDoc('click', spy1);
+ BEMDOM.doc.trigger('click');
+
+ spy1.should.not.have.been.called;
+ spy2.should.have.been.called;
+ });
+
+ it('should properly unbind all handlers on block destruct', function() {
+ BEMDOM.destruct(block.domElem);
+ BEMDOM.doc.trigger('click');
+
+ spy1.should.not.have.been.called;
+ spy2.should.not.have.been.called;
+ });
+ });
+
+ describe('window events', function() {
+ beforeEach(function() {
+ Block = BEMDOM.declBlock('block', {
+ onSetMod : {
+ 'js' : {
+ 'inited' : function() {
+ this
+ .bindToWin('resize', spy1)
+ .bindToWin('resize', spy2)
+ .bindToWin('resize', data, spy3)
+ .bindToWin({ 'resize' : spy4 }, data);
+ }
+ }
+ }
+ });
+ block = BEMDOM.init(BEMHTML.apply({ block : 'block' })).bem(Block);
+ });
+
+ it('should properly bind handlers', function() {
+ BEMDOM.win.trigger('resize');
+
+ spy1.should.have.been.called;
+ spy2.should.have.been.called;
+
+ spy3.args[0][0].data.should.have.been.equal(data);
+ spy4.args[0][0].data.should.have.been.equal(data);
+ });
+
+ it('should properly unbind all handlers', function() {
+ block.unbindFromWin('resize');
+ BEMDOM.win.trigger('resize');
+
+ spy1.should.not.have.been.called;
+ spy2.should.not.have.been.called;
+ });
+
+ it('should properly unbind specified handler', function() {
+ block.unbindFromWin('resize', spy1);
+ BEMDOM.win.trigger('resize');
+
+ spy1.should.not.have.been.called;
+ spy2.should.have.been.called;
+ });
+
+ it('should properly unbind all handlers on block destruct', function() {
+ BEMDOM.destruct(block.domElem);
+ BEMDOM.win.trigger('resize');
+
+ spy1.should.not.have.been.called;
+ spy2.should.not.have.been.called;
+ });
+ });
+ });
+
+ describe('closestElem', function() {
+ it('should return the closest element', function() {
+ var Block = BEMDOM.declBlock('block'),
+ rootNode = $(BEMHTML.apply({
+ block : 'block',
+ js : true,
+ content : {
+ elem : 'elem1',
+ content : {
+ elem : 'elem2'
+ }
+ }
+ })),
+ block = rootNode.bem(Block),
+ closest = block.closestElem(block.elem('elem2'), 'elem1');
+
+ closest[0].should.be.equal(block.elem('elem1')[0]);
+
+ BEMDOM.destruct(rootNode);
+ delete BEM.blocks['block'];
+ });
+ });
+
+ describe('liveInitOnBlockInsideEvent', function() {
+ it('should init and call handler on live initialization', function() {
+ var spyInit = sinon.spy(),
+ spyHandler = sinon.spy(),
+ Block2 = BEMDOM.declBlock('block2', {}, {}),
+ Block1 = BEMDOM.declBlock('block1', {
+ onSetMod : {
+ js : {
+ inited : spyInit
+ }
+ }
+ }, {
+ live : function() {
+ this.liveInitOnBlockInsideEvent('event', Block2, spyHandler);
+ }
+ }),
+ rootNode = BEMDOM.init(BEMHTML.apply({
+ block : 'block1',
+ js : true,
+ content : {
+ block : 'block2',
+ js : true
+ }
+ })),
+ block = rootNode.find('.block2').bem(Block2);
+
+ spyInit.called.should.be.false;
+ spyHandler.called.should.be.false;
+
+ block.emit('event');
+
+ spyInit.called.should.be.true;
+ spyHandler.called.should.be.true;
+
+ BEMDOM.destruct(rootNode);
+ delete BEM.blocks['block1'];
+ delete BEM.blocks['block2'];
+ });
+ });
+
+ describe('modules.define patching', function() {
+ it('should provide BEMDOM block', function(done) {
+ var name = 'b' + Math.random(),
+ spy = sinon.spy();
+
+ modules.define(name, ['i-bem-dom'], function(provide, BEMDOM) {
+ spy();
+ provide(BEMDOM.declBlock(this.name, {}));
+ });
+
+ modules.define(name, function(provide, Prev) {
+ spy();
+ Prev.should.be.eql(BEM.blocks[this.name]);
+ provide(BEMDOM.declBlock(this.name, {}));
+ });
+
+ modules.require([name], function(Block) {
+ spy.should.have.been.calledTwice;
+ Block.should.be.eql(BEM.blocks[name]);
+ done();
+ });
+ });
+ });
+
+ describe('mod change events', function() {
+ var block;
+ beforeEach(function() {
+ block = $(BEMHTML.apply(
+ {
+ block : 'block',
+ content : [
+ { elem : 'e1', mods : { 'mod1' : 'val1' } },
+ { elem : 'e1', mods : { 'mod1' : 'val1' } },
+ { elem : 'e2', mods : { 'mod1' : 'val1' } }
+ ]
+ }))
+ .bem(BEMDOM.declBlock('block'));
+ });
+
+ afterEach(function() {
+ delete BEM.blocks['block'];
+ });
+
+ it('should propagate destructing event', function() {
+ var spy = sinon.spy(),
+ Block1 = BEMDOM.declBlock('block1', {
+ onSetMod : {
+ 'js' : {
+ 'inited' : function() {
+ Block2.on(this.domElem, { modName : 'js', modVal : '' }, spy);
+ }
+ }
+ }
+ }),
+ Block2 = BEMDOM.declBlock('block2'),
+ domElem = $(BEMHTML.apply({
+ block : 'block1',
+ content : {
+ block : 'block2'
+ }
+ })).appendTo('body');
+
+ BEMDOM.init(domElem);
+
+ var block1 = domElem.bem(Block1),
+ block2 = block1.findBlockInside(Block2);
+
+ BEMDOM.destruct(block2.domElem);
+
+ spy.should.have.been.called;
+
+ delete BEM.blocks['block2'];
+ delete BEM.blocks['block1'];
+ });
+
+ describe('elems', function() {
+ it('should emit event on elem mod change with correct arguments', function() {
+ var spy1 = sinon.spy(),
+ spy2 = sinon.spy(),
+ spy3 = sinon.spy(),
+ spy4 = sinon.spy(),
+ elem = block.elem('e1');
+
+ block
+ .on({ elem : 'e1', modName : 'mod1', modVal : '*' }, spy1)
+ .on({ elem : 'e1', modName : 'mod1', modVal : 'val2' }, spy2)
+ .on({ elem : 'e1', modName : 'mod1', modVal : 'val3' }, spy3)
+ .on({ elem : 'e2', modName : 'mod1', modVal : 'val2' }, spy4)
+ .setMod(elem, 'mod1', 'val2');
+
+ spy1.should.have.been.called.twice;
+ spy2.should.have.been.called.twice;
+ spy3.should.not.have.been.called;
+ spy4.should.not.have.been.called;
+
+ var eventData = spy1.args[0][1];
+ eventData.modName.should.be.equal('mod1');
+ eventData.modVal.should.be.equal('val2');
+ eventData.oldModVal.should.be.equal('val1');
+ eventData.elem[0].should.be.eql(elem[0]);
+ spy1.args[1][1].elem[0].should.be.eql(elem[1]);
+ });
+
+ it('should emit live event on elem mod change with correct arguments', function() {
+ var spy1 = sinon.spy(),
+ spy2 = sinon.spy(),
+ spy3 = sinon.spy(),
+ spy4 = sinon.spy(),
+ elem = block.elem('e1');
+
+ BEM.blocks['block']
+ .on({ elem : 'e1', modName : 'mod1', modVal : '*' }, spy1)
+ .on({ elem : 'e1', modName : 'mod1', modVal : 'val2' }, spy2)
+ .on({ elem : 'e1', modName : 'mod1', modVal : 'val3' }, spy3)
+ .on({ elem : 'e2', modName : 'mod1', modVal : 'val2' }, spy4);
+
+ block.setMod(elem, 'mod1', 'val2');
+
+ spy1.should.have.been.called.twice;
+ spy2.should.have.been.called.twice;
+ spy3.should.not.have.been.called;
+ spy4.should.not.have.been.called;
+
+ var eventData = spy1.args[0][1];
+ eventData.modName.should.be.equal('mod1');
+ eventData.modVal.should.be.equal('val2');
+ eventData.oldModVal.should.be.equal('val1');
+ eventData.elem[0].should.be.eql(elem[0]);
+ spy1.args[1][1].elem[0].should.be.eql(elem[1]);
+ });
+ });
+ });
+});
+
+provide();
+
+});
diff --git a/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.deps.js b/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.deps.js
deleted file mode 100644
index 3eee2bd20..000000000
--- a/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.deps.js
+++ /dev/null
@@ -1,8 +0,0 @@
-({
- mustDeps : [
- {
- block : 'i-bem',
- mods : { 'elem-instances' : true }
- }
- ]
-})
diff --git a/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.js b/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.js
deleted file mode 100644
index 96f719fcf..000000000
--- a/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.js
+++ /dev/null
@@ -1,320 +0,0 @@
-/**
- * @module i-bem__dom
- */
-
-modules.define(
- 'i-bem__dom',
- ['i-bem', 'i-bem__internal', 'jquery'],
- function(provide, BEM, INTERNAL, $, BEMDOM) {
-
-var buildClass = INTERNAL.buildClass,
- NAME_PATTERN = INTERNAL.NAME_PATTERN,
- MOD_DELIM = INTERNAL.MOD_DELIM,
- ELEM_DELIM = INTERNAL.ELEM_DELIM,
- blocks = BEM.blocks,
- slice = Array.prototype.slice;
-
-/**
- * @class BEMDOM
- * @augments BEMDOM
- * @exports
- */
-provide(BEMDOM.decl(/** @lends BEMDOM.prototype */{
-
- /**
- * Delegates native getMod helper to element's instance
- * @protected
- * @param {jQuery} [elem] Nested element
- * @param {String} modName Modifier name
- * @returns {String} Modifier value
- */
- getMod : function(elem, modName) {
- var elemClass;
-
- if(elem && modName && blocks[elemClass = this.__self._buildElemClass(elem)]) {
- return this.__base.call(this.findBlockOn(elem, elemClass), modName);
- }
- return this.__base(elem, modName);
- },
-
- /**
- * Delegates native getMods helper to element's instance
- * @protected
- * @param {jQuery} [elem] Nested element
- * @param {String} [modName1, ..., modNameN] Modifier names
- * @returns {Object} Hash of modifier values
- */
- getMods : function(elem) {
- var elemClass;
-
- if(elem && typeof elem !== 'string' && blocks[elemClass = this.__self._buildElemClass(elem)]) {
- return this.__base.apply(this.findBlockOn(elem, elemClass), slice.call(arguments, 1));
- }
- return this.__base.apply(this, arguments);
- },
-
- /**
- * Delegates native setMod helper to element's instances
- * @protected
- * @param {jQuery} [elem] Nested element
- * @param {String} modName Modifier name
- * @param {String} modVal Modifier value
- * @returns {BEM}
- */
- setMod : function(elem, modName, modVal) {
- var elemClass;
-
- if(elem && typeof modVal !== 'undefined' && blocks[elemClass = this.__self._buildElemClass(elem)]) {
- this
- .findBlocksOn(elem, elemClass)
- .forEach(function(instance) {
- this.__base.call(instance, modName, modVal);
- }, this);
- return this;
- }
- return this.__base(elem, modName, modVal);
- },
-
- /**
- * Returns and initializes (if necessary) the own block of current element
- * @returns {BEMDOM}
- */
- block : function() {
- return this._block || (this._block = this.findBlockOutside(this.__self._blockName));
- },
-
- /**
- * Executes handlers for setting modifiers
- * If element sets modifier to itself, it executes onElemSetMod handlers of the own block
- * @private
- * @param {String} prefix
- * @param {String} elemName Element name
- * @param {String} modName Modifier name
- * @param {String} modVal Modifier value
- * @param {Array} modFnParams Handler parameters
- */
- _callModFn : function(prefix, elemName, modName, modVal, modFnParams) {
- var result = this.__base.apply(this, arguments),
- selfElemName = this.__self._elemName;
-
- if(selfElemName) {
- this.__base.call(
- this.block(),
- prefix,
- elemName || selfElemName,
- modName,
- modVal,
- elemName? modFnParams : [this.domElem].concat(modFnParams)
- ) === false && (result = false);
- }
- return result;
- },
-
- /**
- * Filters results of findElem helper execution in strict mode
- * @param {jQuery} res DOM elements
- * @returns {jQuery} DOM elements
- */
- _filterFindElemResults : function(res) {
- var _self = this.__self,
- blockSelector = '.' + _self._blockName,
- domElem = _self._elemName? this.domElem.closest(blockSelector) : this.domElem;
- return res.filter(function() {
- return domElem.index($(this).closest(blockSelector)) > -1;
- });
- },
-
- /**
- * Lazy search (caches results) for the first instance of defined element and intializes it (if necessary)
- * @param {String|jQuery} elem Element
- * @param {String} [modName] Modifier name
- * @param {String} [modVal] Modifier value
- * @returns {BEMDOM}
- */
- elemInstance : function() {
- return this._elemInstances(arguments, 'elem', 'findBlockOn');
- },
-
- /**
- * Lazy search (caches results) for instances of defined elements and intializes it (if necessary)
- * @param {String|jQuery} elem Element
- * @param {String} [modName] Modifier name
- * @param {String} [modVal] Modifier value
- * @returns {BEMDOM[]}
- */
- elemInstances : function() {
- return this._elemInstances(arguments, 'elem', 'findBlocksOn');
- },
-
- /**
- * Finds the first instance of defined element and intializes it (if necessary)
- * @param {jQuery} [ctx=this.domElem] Element where search is being performed
- * @param {String|jQuery} elem Element
- * @param {String} [modName] Modifier name
- * @param {String} [modVal] Modifier value
- * @param {Boolean} [strictMode=false]
- * @returns {BEMDOM}
- */
- findElemInstance : function() {
- return this._elemInstances(arguments, 'findElem', 'findBlockOn');
- },
-
- /**
- * Finds instances of defined elements and intializes it (if necessary)
- * @param {jQuery} [ctx=this.domElem] Element where search is being performed
- * @param {String|jQuery} elem Element
- * @param {String} [modName] Modifier name
- * @param {String} [modVal] Modifier value
- * @param {Boolean} [strictMode=false]
- * @returns {BEMDOM[]}
- */
- findElemInstances : function() {
- return this._elemInstances(arguments, 'findElem', 'findBlocksOn');
- },
-
- _elemInstances : function(args, findElemMethod, findBlockMethod) {
- var elem = args[0],
- isString = typeof elem === 'string',
- elemClass;
-
- if(args.length === 1 && !isString) {
- elemClass = this.__self._buildElemClass(elem);
- } else {
- elemClass = buildClass(this.__self._blockName, args[isString? 0 : 1]);
- elem = this[findElemMethod].apply(this, args);
- }
- return this[findBlockMethod](elem, elemClass);
- },
-
- /**
- * Finds elements outside the context or current element
- * @param {jQuery} [ctx=this.domElem] context (current element by default)
- * @param {String} elemName Element name
- * @returns {jQuery} DOM elements
- */
- closestElem : function(ctx, elemName) {
- if(!elemName) {
- elemName = ctx;
- ctx = this.domElem;
- }
- return this.__base(ctx, elemName);
- },
-
- /**
- * Finds instance of defined element outside the context or current element
- * @param {jQuery} [ctx=this.domElem] context (current element by default)
- * @param {String} elemName Element name
- * @returns {BEMDOM}
- */
- closestElemInstance : function(ctx, elemName) {
- return this.findBlockOn(
- this.closestElem.apply(this, arguments),
- buildClass(this.__self._blockName, elemName || ctx));
- },
-
- /**
- * Finds instances of defined elements outside the context or current element
- * @param {jQuery} [ctx=this.domElem] context (current element by default)
- * @param {String} elemName Element name
- * @returns {BEMDOM[]}
- */
- closestElemInstances : function(ctx, elemName) {
- return this.findBlocksOn(
- this.closestElem.apply(this, arguments),
- buildClass(this.__self._blockName, elemName || ctx));
- }
-
-}, /** @lends BEMDOM */{
-
- /**
- * Auto-declarator for elements
- * @protected
- * @param {Object} name Instance name
- * @param {Object} [props] Methods
- * @param {Object} [staticProps] Static methods
- * @param {Object} [_autoDecl] Auto-declaration flag
- * @returns {Function}
- */
- decl : function(name, props, staticProps, _autoDecl) {
- if(_autoDecl) {
- var names = name.split(ELEM_DELIM);
- return this.__base({ block : names[0], elem : names[1] }, props, staticProps);
- } else {
- return this.__base.apply(this, arguments);
- }
- },
-
- /**
- * Helper for live initialization for an own block's event
- * @protected
- * @param {String} event Event name
- * @param {Function} [callback] Handler to be called after successful initialization in the new element's context
- * @returns {Function} this
- */
- liveInitOnBlockEvent : function(event, callback) {
- return (typeof callback === 'string')?
- this.__base.apply(this, arguments) :
- this._liveInitOnOwnBlockEvent(event, callback);
- },
-
- _liveInitOnOwnBlockEvent : function(event, callback) {
- var name = this._elemName;
- blocks[this._blockName].on(event, function(e) {
- var args = arguments,
- elems = e.target.findElemInstances(name, true);
-
- callback && elems.forEach(function(elem) {
- callback.apply(elem, args);
- });
- });
- return this;
- },
-
- /**
- * Builds a CSS class corresponding to the element's instance with extraction it's name from the specified DOM element
- * @private
- * @param {jQuery} elem Element
- * @returns {String}
- */
- _buildElemClass : function(elem) {
- return buildClass(this._blockName, this._extractElemNameFrom(elem));
- },
-
- /**
- * Builds a CSS class corresponding to the block/element and modifier
- * @param {String} [elem] Element name
- * @param {String} [modName] Modifier name
- * @param {String} [modVal] Modifier value
- * @returns {String}
- */
- buildClass : function(elem, modName, modVal) {
- return this._elemName && elem && (modVal || !modName)?
- buildClass(this._blockName, elem, modName, modVal) :
- buildClass(this._name, elem, modName, modVal);
- },
-
- /**
- * Builds a prefix for the CSS class of a DOM element or nested element of the block, based on modifier name
- * @private
- * @param {String} modName Modifier name
- * @param {jQuery|String} [elem] Element
- * @returns {String}
- */
- _buildModClassPrefix : function(modName, elem) {
- return (elem?
- this._blockName + ELEM_DELIM + (typeof elem === 'string'? elem : this._extractElemNameFrom(elem)) :
- this._name) +
- MOD_DELIM + modName;
- },
-
- /**
- * Builds a regular expression for extracting names of elements nested in a block
- * @private
- * @returns {RegExp}
- */
- _buildElemNameRE : function() {
- return new RegExp(this._blockName + ELEM_DELIM + '(' + NAME_PATTERN + ')(?:\\s|$)');
- }
-}));
-
-});
diff --git a/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.ru.desc.wiki b/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.ru.desc.wiki
deleted file mode 100644
index 363fd9046..000000000
--- a/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.ru.desc.wiki
+++ /dev/null
@@ -1,249 +0,0 @@
-Подключение данного модификатора позволяет работать с элементами почти так же, как и с обычными блоками, т.е. любой (но не обязательно каждый) элемент может иметь свой ##BEM##-инстанс. API инстансов элементов аналогично API блоков с некоторыми различиями, описанными ниже.
-
-=== Декларация
-
-Декларация элемента
-
-%%hl js
-BEMDOM.decl({ block: 'menu', elem: 'item' }, { /* properties */ }, { /* static properties */ });
-%%
-
-Декларация модификатора элемента:
-
-%%hl js
-BEMDOM.decl({
- block: 'menu',
- elem: 'item',
- modName: 'state',
- modVal: 'current'
-}, {
- /* properties */
-}, {
- /* static properties */
-});
-%%
-
-Модификаторы элементов работают так же, как модификаторы блоков.
-
-=== BEM-инстансы элементов
-
-Для доступа к ##BEM##-инстансам элементов используется хелпер ##findElemInstances##, API которого аналогично хелперу ##findElem##:
-
-%%hl js
-// поиск всех вложенных элементов 'item'
-this.findElemInstances('item');
-
-// строгий поиск всех вложенных элементов 'item'
-this.findElemInstances('item', true);
-
-// поиск всех вложенных модификаторов элементов 'item'
-this.findElemInstances('item', 'state', 'current');
-
-// строгий поиск всех вложенных модификаторов элементов 'item'
-this.findElemInstances('item', 'state', 'current', true);
-
-// поиск всех элементов 'item' внутри указанного контекста
-this.findElemInstances(ctx, 'item');
-
-// строгий поиск всех элементов 'item' внутри указанного контекста
-this.findElemInstances(ctx, 'item', true);
-
-// поиск всех модификаторов элементов 'item' внутри указанного контекста
-this.findElemInstances(ctx, 'item', 'state', 'current');
-
-// строгий поиск всех модификаторов элементов 'item' внутри указанного контекста
-this.findElemInstances(ctx, 'item', 'state', 'current', true);
-%%
-
-При необходимости, инстансы найденных элементов инициализируются.
-
-Строгий поиск подразумевает фильтрацию элементов вложенных блоков с таким же именем, как у текущего блока:
-
-%%hl js
-{
- block: 'menu',
- content: [
- {
- elem: 'item' // (1)
- },
- {
- block: 'menu',
- content: {
- elem: 'item' // (2)
- }
- }
- ]
-}
-%%
-%%hl js
-// this => block 'menu'
-this.findElemInstances('item'); // найдет инстансы (1) и (2)
-this.findElemInstances('item', true); // найдет только инстанс (1)
-%%
-
-По аналогии с хелпером ##elem##, кэширующим результаты своей работы, для минимизации количества операций с ##DOM## рекомендуется использовать кэширующий поиск ##BEM##-инстансов элементов:
-
-%%hl js
-// кэширующий поиск всех вложенных элементов 'item'
-this.elemInstances('item');
-
-// кэширующий поиск всех вложенных модификаторов элемента 'item'
-this.elemInstances('item', 'state', 'current');
-%%
-
-Также с помощью этого хелпера можно вернуть инстансы элементов, расположенных на ##DOM##-нодах указанной ##jQuery## коллекции:
-
-%%hl js
-this.elemInstances(domElem);
-%%
-
-Для поиска одного (первого) ##BEM##-инстанса элемента есть дополнительные формы этих хелперов с аналогичным API:
-
-%%hl js
-// поиск одного вложенного элемента 'item'
-this.findElemInstance('item');
-
-// кэширующий поиск одного вложенного элемента 'item'
-this.elemInstance('item');
-
-// вернуть инстанс элемента, расположенного на первой DOM-ноде указанной jQuery-коллекции
-this.elemInstance(domElem);
-%%
-
-Если необходимо вернуть ##BEM##-инстанс элемента, на ##DOM##-ноде которого подмешаны другие элементы того же блока:
-
-%%hl js
-this.elemInstance(this.elemify(domElem, 'item'));
-%%
-
-=== Поиск снаружи контекста
-
-Существует хелпер для поиска элемента снаружи указанного контекста:
-
-%%hl js
-{
- block: 'menu',
- content: {
- elem: 'item',
- content: {
- elem: 'link'
- }
- }
-}
-%%
-%%hl js
-// this => block 'menu'
-this.closestElem(this.elem('link'), 'item');
-%%
-
-При поиске от имени ##BEM##-инстанса элемента можно не указывать контекст поиска, тогда поиск будет производиться относительно ##DOM##-ноды этого элемента:
-
-%%hl js
-// this => element 'menu__link'
-this.closestElem('item');
-%%
-
-По аналогии с хелперами ##elem## и ##findElem##, хелпер ##closestElem## возвращает ##jQuery##-коллекцию. Для доступа к ##BEM##-инстансам элементов снаружи контекста используются хелперы ##closestElemInstance## и ##closestElemInstances##:
-
-%%hl js
-// this => block 'menu'
-this.closestElemInstance(this.elem('link'), 'item');
-this.closestElemInstances(this.elem('link'), 'item');
-%%
-%%hl js
-// this => element 'menu__link'
-this.closestElemInstance('item');
-%%
-
-=== Доступ к родительскому блоку
-
-По аналогии с методом ##elem## для получения элемента блока существует метод ##block## для получения блока элемента.
-
-%%hl js
-// this => element 'menu__item'
-this.block() // эквивалентно this.findBlockOutside('menu') с кэшированием результата поиска
-%%
-
-=== Поиск элементов и блоков
-
-Элемент ищет только вложенные в него элементы:
-
-%%hl js
-{
- block: 'menu',
- content: [
- {
- elem: 'item',
- content: {
- elem: 'link' // (1)
- }
- },
- {
- elem: 'link' // (2)
- }
- ]
-}
-%%
-
-%%hl js
-// this => element 'menu__item'
-this.findElem('link'); // будет найден только элемент (1).
-%%
-
-Если нужно найти все элементы ##'link'##, поиск нужно производить от имени блока-родителя:
-
-%%hl js
-// this => element 'menu__item'
-this.block().findElem('link'); // будут найдены элементы (1) и (2).
-%%
-
-Все это справедливо также для хелпера ##elem##, хелперов поиска блоков (##findBlockInside## и др.) и описанных выше хелперов для поиска ##BEM##-инстансов элементов.
-
-=== Реакция на установку модификаторов
-
-Блок делегирует установку модификаторов тем элементам, у которых есть свои ##BEM##-декларации.
-Поэтому обработчики ##onSetMod## элемента выполнятся, если родительский блок установит этому элементу соответствующие модификаторы:
-
-%%hl js
-// this => element 'menu__item'
-onSetMod: {
- 'state': {
- 'current': function() {
- // код обработчика
- }
- }
-}
-%%
-%%hl js
-// this => block 'menu'
-this.setMod(this.elem('item'), 'state', 'current'); // выполнится задекларированный выше обработчик
-%%
-
-Обработчики ##onElemSetMod## блока выполнятся, если инстанс элемента сам установит себе соответствующие модификаторы:
-
-%%hl js
-// this => block 'menu'
-onElemSetMod: {
- 'item': {
- 'state': {
- 'current': function() {
- // код обработчика
- }
- }
- }
-}
-%%
-%%hl js
-// this => element 'menu__item'
-this.setMod('state', 'current'); // выполнится задекларированный выше обработчик
-%%
-
-Обработчики ##onElemSetMod## в контексте инстанса элемента не используются.
-
-=== Отложенная инициализация и live-события
-
-Рекомендуется по возможности всегда использовать отложенную инициализацию ##BEM##-инстансов элементов. Они, как и обычные блоки, поддерживают все хелперы для работы с ##live##-событиями. Кроме того, хелпер ##liveInitOnBlockEvent## имеет дополнительную форму для инициализации по событию родительского блока - для этого просто опускается второй параметр (имя блока):
-
-%%hl js
-this.liveInitOnBlockEvent('switch', function() { /* обработчик */ });
-%%
\ No newline at end of file
diff --git a/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.ru.md b/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.ru.md
deleted file mode 100644
index d28206985..000000000
--- a/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.ru.md
+++ /dev/null
@@ -1,249 +0,0 @@
-Подключение данного модификатора позволяет работать с элементами почти так же, как и с обычными блоками, т.е. любой (но не обязательно каждый) элемент может иметь свой BEM-инстанс. API инстансов элементов аналогично API блоков с некоторыми различиями, описанными ниже.
-
-### Декларация
-
-Декларация элемента
-
-```javascript
-BEMDOM.decl({ block: 'menu', elem: 'item' }, { /* properties */ }, { /* static properties */ });
-```
-
-Декларация модификатора элемента:
-
-```javascript
-BEMDOM.decl({
- block: 'menu',
- elem: 'item',
- modName: 'state',
- modVal: 'current'
-}, {
- /* properties */
-}, {
- /* static properties */
-});
-```
-
-Модификаторы элементов работают так же, как модификаторы блоков.
-
-### BEM-инстансы элементов
-
-Для доступа к BEM-инстансам элементов используется хелпер ```findElemInstances```, API которого аналогично хелперу ```findElem```:
-
-```javascript
-// поиск всех вложенных элементов 'item'
-this.findElemInstances('item');
-
-// строгий поиск всех вложенных элементов 'item'
-this.findElemInstances('item', true);
-
-// поиск всех вложенных модификаторов элементов 'item'
-this.findElemInstances('item', 'state', 'current');
-
-// строгий поиск всех вложенных модификаторов элементов 'item'
-this.findElemInstances('item', 'state', 'current', true);
-
-// поиск всех элементов 'item' внутри указанного контекста
-this.findElemInstances(ctx, 'item');
-
-// строгий поиск всех элементов 'item' внутри указанного контекста
-this.findElemInstances(ctx, 'item', true);
-
-// поиск всех модификаторов элементов 'item' внутри указанного контекста
-this.findElemInstances(ctx, 'item', 'state', 'current');
-
-// строгий поиск всех модификаторов элементов 'item' внутри указанного контекста
-this.findElemInstances(ctx, 'item', 'state', 'current', true);
-```
-
-При необходимости, инстансы найденных элементов инициализируются.
-
-Строгий поиск подразумевает фильтрацию элементов вложенных блоков с таким же именем, как у текущего блока:
-
-```javascript
-{
- block: 'menu',
- content: [
- {
- elem: 'item' // (1)
- },
- {
- block: 'menu',
- content: {
- elem: 'item' // (2)
- }
- }
- ]
-}
-```
-```javascript
-// this => block 'menu'
-this.findElemInstances('item'); // найдет инстансы (1) и (2)
-this.findElemInstances('item', true); // найдет только инстанс (1)
-```
-
-По аналогии с хелпером ```elem```, кэширующим результаты своей работы, для минимизации количества операций с DOM рекомендуется использовать кэширующий поиск BEM-инстансов элементов:
-
-```javascript
-// кэширующий поиск всех вложенных элементов 'item'
-this.elemInstances('item');
-
-// кэширующий поиск всех вложенных модификаторов элемента 'item'
-this.elemInstances('item', 'state', 'current');
-```
-
-Также с помощью этого хелпера можно вернуть инстансы элементов, расположенных на DOM-нодах указанной jQuery-коллекции:
-
-```javascript
-this.elemInstances(domElem);
-```
-
-Для поиска одного (первого) BEM-инстанса элемента есть дополнительные формы этих хелперов с аналогичным API:
-
-```javascript
-// поиск одного вложенного элемента 'item'
-this.findElemInstance('item');
-
-// кэширующий поиск одного вложенного элемента 'item'
-this.elemInstance('item');
-
-// вернуть инстанс элемента, расположенного на первой DOM-ноде указанной jQuery-коллекции
-this.elemInstance(domElem);
-```
-
-Если необходимо вернуть BEM-инстанс элемента, на DOM-ноде которого подмешаны другие элементы того же блока:
-
-```javascript
-this.elemInstance(this.elemify(domElem, 'item'));
-```
-
-### Поиск снаружи контекста
-
-Существует хелпер для поиска элемента снаружи указанного контекста:
-
-```javascript
-{
- block: 'menu',
- content: {
- elem: 'item',
- content: {
- elem: 'link'
- }
- }
-}
-```
-```javascript
-// this => block 'menu'
-this.closestElem(this.elem('link'), 'item');
-```
-
-При поиске от имени BEM-инстанса элемента можно не указывать контекст поиска, тогда поиск будет производиться относительно DOM-ноды этого элемента:
-
-```javascript
-// this => element 'menu__link'
-this.closestElem('item');
-```
-
-По аналогии с хелперами ```elem``` и ```findElem```, хелпер ```closestElem``` возвращает jQuery-коллекцию. Для доступа к BEM-инстансам элементов снаружи контекста используются хелперы ```closestElemInstance``` и ```closestElemInstances```:
-
-```javascript
-// this => block 'menu'
-this.closestElemInstance(this.elem('link'), 'item');
-this.closestElemInstances(this.elem('link'), 'item');
-```
-```javascript
-// this => element 'menu__link'
-this.closestElemInstance('item');
-```
-
-### Доступ к родительскому блоку
-
-По аналогии с методом `elem` для получения элемента блока существует метод `block` для получения блока элемента.
-
-```javascript
-// this => element 'menu__item'
-this.block() // эквивалентно this.findBlockOutside('menu') с кэшированием результата поиска
-```
-
-### Поиск элементов и блоков
-
-Элемент ищет только вложенные в него элементы:
-
-```javascript
-{
- block: 'menu',
- content: [
- {
- elem: 'item',
- content: {
- elem: 'link' // (1)
- }
- },
- {
- elem: 'link' // (2)
- }
- ]
-}
-```
-
-```javascript
-// this => element 'menu__item'
-this.findElem('link'); // будет найден только элемент (1).
-```
-
-Если нужно найти все элементы ```'link'```, поиск нужно производить от имени блока-родителя:
-
-```javascript
-// this => element 'menu__item'
-this.block().findElem('link'); // будут найдены элементы (1) и (2).
-```
-
-Все это справедливо также для хелпера ```elem```, хелперов поиска блоков (```findBlockInside``` и др.) и описанных выше хелперов для поиска BEM-инстансов элементов.
-
-### Реакция на установку модификаторов
-
-Блок делегирует установку модификаторов тем элементам, у которых есть свои BEM-декларации.
-Поэтому обработчики ```onSetMod``` элемента выполнятся, если родительский блок установит этому элементу соответствующие модификаторы:
-
-```javascript
-// this => element 'menu__item'
-onSetMod: {
- 'state': {
- 'current': function() {
- // код обработчика
- }
- }
-}
-```
-```javascript
-// this => block 'menu'
-this.setMod(this.elem('item'), 'state', 'current'); // выполнится задекларированный выше обработчик
-```
-
-Обработчики ```onElemSetMod``` блока выполнятся, если инстанс элемента сам установит себе соответствующие модификаторы:
-
-```javascript
-// this => block 'menu'
-onElemSetMod: {
- 'item': {
- 'state': {
- 'current': function() {
- // код обработчика
- }
- }
- }
-}
-```
-```javascript
-// this => element 'menu__item'
-this.setMod('state', 'current'); // выполнится задекларированный выше обработчик
-```
-
-Обработчики ```onElemSetMod``` в контексте инстанса элемента не используются.
-
-### Отложенная инициализация и live-события
-
-Рекомендуется по возможности всегда использовать отложенную инициализацию BEM-инстансов элементов. Они, как и обычные блоки, поддерживают все хелперы для работы с live-событиями. Кроме того, хелпер ```liveInitOnBlockEvent``` имеет дополнительную форму для инициализации по событию родительского блока - для этого просто опускается второй параметр (имя блока):
-
-```javascript
-this.liveInitOnBlockEvent('switch', function() { /* обработчик */ });
-```
\ No newline at end of file
diff --git a/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.spec.js b/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.spec.js
deleted file mode 100644
index a58035e35..000000000
--- a/common.blocks/i-bem/__dom/_elem-instances/i-bem__dom_elem-instances.spec.js
+++ /dev/null
@@ -1,382 +0,0 @@
-modules.define(
- 'spec',
- ['i-bem__dom', 'jquery', 'sinon', 'BEMHTML'],
- function(provide, DOM, $, sinon, BEMHTML) {
-
-describe('i-bem__dom_elem-instances', function() {
- describe('elemInstance', function() {
- it('should return the instance of element', function() {
- DOM.decl('block', {}, {});
- DOM.decl({ block : 'block', elem : 'elem' }, {}, {});
-
- var rootNode = $(BEMHTML.apply({
- block : 'block',
- js : true,
- content : {
- elem : 'elem',
- js : true
- }
- })),
- block = rootNode.bem('block'),
- elem = block.elemInstance('elem');
-
- elem.should.be.instanceOf(DOM.blocks['block__elem']);
- elem.__self.getName(true).should.be.equal('elem');
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- delete DOM.blocks['block__elem'];
- });
- });
-
- describe('block', function() {
- it('should return instance of the own block', function() {
- DOM.decl('block', {}, {});
- DOM.decl({ block : 'block', elem : 'elem' }, {}, {});
-
- var rootNode = $(BEMHTML.apply({
- block : 'block',
- js : true,
- content : {
- elem : 'elem',
- js : true
- }
- })),
- block = rootNode.bem('block'),
- elem = block.elemInstance('elem');
-
- elem.block().should.be.equal(block);
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- delete DOM.blocks['block__elem'];
- });
- });
-
- describe('closestElem', function() {
- it('should return the closest element', function() {
- DOM.decl('block', {}, {});
- DOM.decl({ block : 'block', elem : 'elem2' }, {}, {});
-
- var rootNode = $(BEMHTML.apply({
- block : 'block',
- js : true,
- content : {
- elem : 'elem1',
- content : {
- elem : 'elem2',
- js : true
- }
- }
- })),
- block = rootNode.bem('block'),
- elem2 = block.elemInstance('elem2'),
- closest = elem2.closestElem('elem1');
-
- closest[0].should.be.equal(block.elem('elem1')[0]);
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- delete DOM.blocks['block__elem2'];
- });
- });
-
- describe('mods', function() {
- it('should update element\'s modifier properly', function() {
- DOM.decl('block', {}, {});
- DOM.decl({ block : 'block', elem : 'elem' }, {}, {});
-
- var rootNode = $(BEMHTML.apply({
- block : 'block',
- js : true,
- content : {
- elem : 'elem',
- elemMods : { mod : 'val1' },
- mix : { block : 'i-bem' },
- js : true
- }
- })),
- block = rootNode.bem('block'),
- elem = block.elemInstance('elem');
-
- elem.hasMod('mod', 'val1').should.be.true;
-
- block.setMod(block.elem('elem'), 'mod', 'val2');
- elem.hasMod('mod', 'val2').should.be.true;
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- delete DOM.blocks['block__elem'];
- });
-
- it('should call block\'s onElemSetMod handler when element updates it\'s own modifier', function() {
- var spy = sinon.spy();
- DOM.decl('block', {
- onElemSetMod : {
- elem : {
- mod : {
- val : spy
- }
- }
- }
- }, {});
- DOM.decl({ block : 'block', elem : 'elem' }, {}, {});
-
- var rootNode = $(BEMHTML.apply({
- block : 'block',
- js : true,
- content : {
- elem : 'elem',
- mix : { block : 'i-bem' },
- js : true
- }
- })),
- block = rootNode.bem('block'),
- elem = block.elemInstance('elem');
-
- elem.setMod('mod', 'val');
- spy.called.should.be.true;
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- delete DOM.blocks['block__elem'];
- });
-
- it('should call block\'s onElemSetMod handler when element updates modifier of another element', function() {
- var spy1 = sinon.spy(),
- spy2 = sinon.spy();
-
- DOM.decl('block', {
- onElemSetMod : {
- elem1 : {
- mod : {
- val : spy1
- }
- },
- elem2 : {
- mod : {
- val : spy2
- }
- }
- }
- }, {});
- DOM.decl({ block : 'block', elem : 'elem1' }, {}, {});
-
- var rootNode = $(BEMHTML.apply({
- block : 'block',
- js : true,
- content : {
- elem : 'elem1',
- mix : { block : 'i-bem' },
- js : true,
- content : { elem : 'elem2' }
- }
- })),
- block = rootNode.bem('block'),
- elem1 = block.elemInstance('elem1'),
- elem2 = block.elem('elem2');
-
- elem1.setMod(elem2, 'mod', 'val');
-
- spy1.called.should.be.false;
- spy2.called.should.be.true;
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- delete DOM.blocks['block__elem'];
- });
- });
-
- describe('findElem', function() {
- it('should filter nested block\'s elements in strict mode', function() {
- DOM.decl('block', {}, {});
- DOM.decl({ block : 'block', elem : 'elem' }, {}, {});
-
- var rootNode = $(BEMHTML.apply({
- block : 'block',
- js : true,
- content : {
- elem : 'elem',
- mix : { block : 'i-bem' },
- js : true,
- content : [
- { elem : 'nested' },
- {
- block : 'block',
- content : { elem : 'nested' }
- }
- ]
- }
- })),
- block = rootNode.bem('block'),
- elem = block.elemInstance('elem'),
- nested = elem.findElem('nested', true);
-
- nested.length.should.be.equal(1);
- nested[0].should.be.equal(elem.domElem[0].children[0]);
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- delete DOM.blocks['block__elem'];
- });
- });
-
- describe('elemParams', function() {
- it('should extract element\'s parameters properly', function() {
- DOM.decl('block', {}, {});
- DOM.decl({ block : 'block', elem : 'elem1' }, {}, {});
-
- var rootNode = $(BEMHTML.apply({
- block : 'block',
- js : true,
- content : {
- elem : 'elem1',
- mix : { block : 'i-bem' },
- js : true,
- content : [
- {
- elem : 'elem2',
- js : { p1 : 'v1' }
- }
- ]
- }
- })),
- block = rootNode.bem('block'),
- elem = block.elemInstance('elem1'),
- elemParams = elem.elemParams('elem2');
-
- elemParams.p1.should.be.equal('v1');
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- delete DOM.blocks['block__elem1'];
- });
- });
-
- describe('decl', function() {
- it('should declare element properly on initialization', function() {
- DOM.decl('block', {}, {});
-
- var rootNode = $(BEMHTML.apply({
- block : 'block',
- js : true,
- content : {
- elem : 'elem'
- }
- })),
- block = rootNode.bem('block'),
- elem = block.elemInstance('elem');
-
- elem.__self.should.be.equal(DOM.blocks['block__elem']);
- elem.__self.getName(true).should.be.equal('elem');
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- delete DOM.blocks['block__elem'];
- });
-
- it('should declare element\'s modifier properly', function() {
- var E1 = DOM.decl({ block : 'block', elem : 'e1' }, {}, {}),
- e2Class = E1.buildClass('e2');
-
- E1.decl({ modName : 'mod', modVal : 'val' }, {}, {});
-
- E1.buildClass('e2').should.be.equal(e2Class);
-
- delete DOM.blocks['block'];
- delete DOM.blocks['block__e1'];
- });
-
- it('should inherit from itself properly', function() {
- var Block = DOM.decl('block'),
- method = function() {};
-
- Block.decl({ method : method });
-
- method.should.be.equal(Block.prototype.method);
-
- delete DOM.blocks['block'];
- });
- });
-
- describe('liveInitOnBlockEvent', function() {
- it('should init and call handler on live initialization', function() {
- var spyInit = sinon.spy(),
- spyHandler = sinon.spy();
-
- DOM.decl('block', {}, {});
- DOM.decl({ block : 'block', elem : 'elem' }, {
- onSetMod : {
- js : {
- inited : spyInit
- }
- }
- }, {
- live : function() {
- this.liveInitOnBlockEvent('event', spyHandler);
- }
- });
-
- var rootNode = DOM.init($(BEMHTML.apply({
- block : 'block',
- js : true,
- content : {
- elem : 'elem',
- js : true,
- mix : { block : 'i-bem' }
- }
- }))),
- block = rootNode.bem('block');
-
- spyInit.called.should.be.false;
- spyHandler.called.should.be.false;
-
- block.emit('event');
-
- spyInit.called.should.be.true;
- spyHandler.called.should.be.true;
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- delete DOM.blocks['block__elem'];
- });
- });
-
- describe('destruct', function() {
- it('should destruct element\'s instance properly', function() {
- var spy = sinon.spy();
-
- DOM.decl('block', {}, {});
- DOM.decl({ block : 'block', elem : 'elem' }, {
- onSetMod : {
- js : {
- '' : spy
- }
- }
- });
-
- var rootNode = DOM.init($(BEMHTML.apply({
- block : 'block',
- js : true,
- content : {
- elem : 'elem',
- js : true,
- mix : { block : 'i-bem' }
- }
- })));
-
- spy.called.should.be.false;
-
- DOM.destruct(rootNode);
-
- spy.called.should.be.true;
-
- delete DOM.blocks['block'];
- delete DOM.blocks['block__elem'];
- });
- });
-});
-
-provide();
-
-});
diff --git a/common.blocks/i-bem/__dom/_init/i-bem__dom_init.spec.js b/common.blocks/i-bem/__dom/_init/i-bem__dom_init.spec.js
deleted file mode 100644
index 3674d1f6f..000000000
--- a/common.blocks/i-bem/__dom/_init/i-bem__dom_init.spec.js
+++ /dev/null
@@ -1,23 +0,0 @@
-modules.define(
- 'spec',
- ['i-bem__dom'],
- function(provide, BEMDOM) {
-
-describe('i-bem__dom_init', function() {
- it('block should exist on init', function(done) {
- var name = 'b' + Math.random();
-
- modules.define(name, ['i-bem__dom'], function(provide, BEMDOM) {
- provide(BEMDOM.decl(this.name, {}));
- });
-
- modules.require(['i-bem__dom_init'], function() {
- BEMDOM.blocks.should.have.property(name);
- done();
- });
- });
-});
-
-provide();
-
-});
diff --git a/common.blocks/i-bem/__dom/_init/i-bem__dom_init_auto.deps.js b/common.blocks/i-bem/__dom/_init/i-bem__dom_init_auto.deps.js
deleted file mode 100644
index 7909b11f3..000000000
--- a/common.blocks/i-bem/__dom/_init/i-bem__dom_init_auto.deps.js
+++ /dev/null
@@ -1,3 +0,0 @@
-({
- shouldDeps : { mod : 'init' }
-})
diff --git a/common.blocks/i-bem/__dom/i-bem__dom.ru.title.txt b/common.blocks/i-bem/__dom/i-bem__dom.ru.title.txt
deleted file mode 100644
index c199ba71f..000000000
--- a/common.blocks/i-bem/__dom/i-bem__dom.ru.title.txt
+++ /dev/null
@@ -1 +0,0 @@
-Хелперы для работы с DOM-представлением
diff --git a/common.blocks/i-bem/__dom/i-bem__dom.spec.js b/common.blocks/i-bem/__dom/i-bem__dom.spec.js
deleted file mode 100644
index e0d12de6e..000000000
--- a/common.blocks/i-bem/__dom/i-bem__dom.spec.js
+++ /dev/null
@@ -1,995 +0,0 @@
-modules.define(
- 'spec',
- ['i-bem__dom', 'objects', 'jquery', 'sinon', 'BEMHTML'],
- function(provide, DOM, objects, $, sinon, BEMHTML) {
-
-describe('i-bem__dom', function() {
- describe('getMod', function() {
- it('should return properly extracted mod from html', function() {
- DOM.decl('block', {});
-
- var rootNode;
- [
- {
- cls : '',
- val : ''
- },
- {
- cls : 'block_m1_v1',
- val : 'v1'
- },
- {
- cls : 'block_m1_v1 bla-block_m1_v2',
- val : 'v1'
- },
- {
- cls : 'bla-block_m1_v2 block_m1_v1',
- val : 'v1'
- },
- {
- cls : 'block_m1',
- val : true
- }
- ].forEach(function(data) {
- (rootNode = $('')).bem('block').getMod('m1')
- .should.be.eql(data.val);
- DOM.destruct(rootNode);
- });
-
- delete DOM.blocks['block'];
- });
- });
-
- describe('getMods', function() {
- it('should return properly extracted block mods from html', function() {
- DOM.decl('block', {});
-
- var rootNode;
- [
- {
- cls : '',
- mods : { js : 'inited' }
- },
- {
- cls : 'block_m1_v1',
- mods : { js : 'inited', m1 : 'v1' }
- },
- {
- cls : 'block_m1_v1 block_m2_v2 bla-block_m4_v3 block_m4_v4',
- mods : { js : 'inited', m1 : 'v1', m2 : 'v2', m4 : 'v4' }
- },
- {
- cls : 'bla-block_m1_v1 block_m2_v2 block_m3_v3 bla-block_m3_v4 block_m4',
- mods : { js : 'inited', m2 : 'v2', m3 : 'v3', m4 : true }
- }
- ].forEach(function(data) {
- (rootNode = $('')).bem('block').getMods()
- .should.be.eql(data.mods);
- DOM.destruct(rootNode);
- });
-
- delete DOM.blocks['block'];
- });
-
- it('should return properly extracted elem mods from html', function() {
- DOM.decl('block', {});
-
- var rootNode;
- [
- {
- cls : 'block__e1_m1_v1',
- mods : { m1 : 'v1' }
- },
- {
- cls : 'block__e1_m1_v1 block__e1_m2_v2 bla-block__e1_m4_v3 block__e1_m4_v4',
- mods : { m1 : 'v1', m2 : 'v2', m4 : 'v4' }
- },
- {
- cls : 'bla-block__e1_m1_v1 block__e1_m2_v2 block__e1_m3_v3 bla-block__e1_m3_v4 block__e1_m4',
- mods : { m2 : 'v2', m3 : 'v3', m4 : true }
- }
- ].forEach(function(data) {
- var block = (rootNode = $('')).bem('block');
- block.getMods(block.elem('e1')).should.be.eql(data.mods);
- DOM.destruct(rootNode);
- });
-
- delete DOM.blocks['block'];
- });
- });
-
- describe('setMod', function() {
- it('should properly set CSS classes', function() {
- DOM.decl('block', {});
-
- var rootNode;
- [
- {
- beforeCls : 'block i-bem',
- afterCls : 'block i-bem block_js_inited block_m1_v1',
- mods : { m1 : 'v1' }
- },
- {
- beforeCls : 'block i-bem block_m6 block_m7_v7',
- afterCls : 'block i-bem block_js_inited block_m1_v1 block_m2_v2 block_m3 block_m4_v4 block_m5',
- mods : { m1 : 'v1', m2 : 'v2', m3 : true, m4 : 'v4', m5 : true, m6 : false, m7 : '' }
- }
- ].forEach(function(data) {
- var block = (rootNode = $('')).bem('block');
-
- objects.each(data.mods, function(modVal, modName) {
- modName === 'm3'?
- block.setMod(modName) :
- block.setMod(modName, modVal);
- });
-
- block.domElem[0].className.should.be.equal(data.afterCls);
-
- DOM.destruct(rootNode);
- });
-
- delete DOM.blocks['block'];
- });
-
- it('should properly set elem CSS classes', function() {
- DOM.decl('block', {});
-
- var rootNode;
- [
- {
- beforeCls : 'block__elem',
- afterCls : 'block__elem block__elem_m1_v1',
- mods : { m1 : 'v1' }
- },
- {
- beforeCls : 'block__elem block__elem_m6 block__elem_m7_v7',
- afterCls : 'block__elem block__elem_m1_v1 block__elem_m2_v2 block__elem_m3 block__elem_m4_v4 block__elem_m5',
- mods : { m1 : 'v1', m2 : 'v2', m3 : true, m4 : 'v4', m5 : true, m6 : false, m7 : '' }
- }
- ].forEach(function(data) {
- var block = (rootNode = $('')).bem('block'),
- elem = block.elem('elem');
-
- objects.each(data.mods, function(modVal, modName) {
- modName === 'm3'?
- block.setMod(elem, modName) :
- block.setMod(elem, modName, modVal);
- });
-
- elem[0].className.should.be.equal(data.afterCls);
-
- DOM.destruct(rootNode);
- });
-
- delete DOM.blocks['block'];
- });
- });
-
- describe('elemify', function() {
- var rootNode, instance;
- beforeEach(function() {
- DOM.decl('block', {});
- rootNode = DOM.init($(BEMHTML.apply({
- block : 'block',
- js : true,
- content : { elem : 'e1', mix : { elem : 'e2' } } })));
- instance = rootNode.bem('block');
- });
- afterEach(function() {
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- });
-
- it('shouldn\'t change given elem', function() {
- var elem1 = instance.elem('e1');
- instance.elemify(elem1, 'e2');
- instance.__self._extractElemNameFrom(elem1).should.be.equal('e1');
- });
-
- it('should return', function() {
- var elem = instance.elemify(instance.elem('e1'), 'e2');
- instance.__self._extractElemNameFrom(elem).should.be.equal('e2');
- });
- });
-
- describe('findBlocksInside', function() {
- function getBlockIds(blocks) {
- return blocks.map(function(block) {
- return block.params.id;
- });
- }
-
- var rootNode, rootBlock;
- beforeEach(function() {
- rootNode = $(BEMHTML.apply(
- {
- block : 'root',
- content : {
- block : 'b1',
- js : { id : '1' },
- content : [
- { block : 'b2' },
- {
- block : 'b1',
- mods : { m1 : 'v1' },
- js : { id : '2' }
- },
- {
- block : 'b3',
- content : {
- block : 'b1',
- mods : { m1 : 'v2' },
- js : { id : '3' },
- content : {
- block : 'b1',
- mods : { m1 : true },
- js : { id : '4' }
- }
- }
- }
- ]
- }
- }));
- rootBlock = DOM.init(rootNode).bem('root');
- });
-
- afterEach(function() {
- DOM.destruct(rootNode);
- delete DOM.blocks['b-root'];
- delete DOM.blocks['b1'];
- });
-
- it('should find all blocks by name', function() {
- getBlockIds(rootBlock.findBlocksInside('b1')).should.be.eql(['1', '2', '3', '4']);
- });
-
- it('should find all blocks by name, modName and modVal', function() {
- getBlockIds(rootBlock.findBlocksInside({ block : 'b1', modName : 'm1', modVal : 'v1' }))
- .should.be.eql(['2']);
- });
-
- it('should find all blocks by name and boolean mod', function() {
- getBlockIds(rootBlock.findBlocksInside({ block : 'b1', modName : 'm1', modVal : true }))
- .should.be.eql(['4']);
- });
- });
-
- describe('DOM.init', function() {
- it('should init block', function() {
- var spy = sinon.spy();
- DOM.decl('block', {
- onSetMod : {
- js : {
- inited : spy
- }
- }
- });
-
- var rootNode = DOM.init($(BEMHTML.apply({
- tag : 'div',
- content : { block : 'block', js : true } })));
-
- spy.called.should.be.true;
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- });
-
- it('shouldn\'t init live block', function() {
- var spy = sinon.spy();
- DOM.decl('block', {
- onSetMod : {
- js : {
- inited : spy
- }
- }
- }, {
- live : true
- });
-
- var rootNode = DOM.init($(BEMHTML.apply({
- tag : 'div',
- content : { block : 'block', js : true } })));
-
- DOM.init(rootNode);
- spy.called.should.be.false;
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- });
-
- it('should allow to pass string', function() {
- var spy = sinon.spy();
- DOM.decl('block', {
- onSetMod : {
- js : {
- inited : spy
- }
- }
- });
-
- var rootNode = DOM.init(BEMHTML.apply({
- tag : 'div',
- content : { block : 'block', js : true } }));
-
- spy.called.should.be.true;
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- });
- });
-
- describe('DOM.destruct', function() {
- it('should destruct block only if it has no dom nodes', function() {
- var spy = sinon.spy();
- DOM.decl('block', {
- onSetMod : {
- js : {
- '' : spy
- }
- }
- });
-
- var rootNode = DOM.init($(BEMHTML.apply({
- tag : 'div',
- content : [
- { block : 'block', js : { id : 'block' } },
- { block : 'block', js : { id : 'block' } }
- ]
- })));
-
- DOM.destruct(rootNode.find('.block :eq(0)'));
- spy.called.should.be.false;
-
- DOM.destruct(rootNode.find('.block'));
- spy.called.should.be.true;
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- });
-
- it('should destruct implicitly inited block', function() {
- var spy = sinon.spy();
- DOM.decl('imp-block', {
- onSetMod : {
- js : {
- '' : spy
- }
- }
- });
-
- var blockNode = DOM.init($(BEMHTML.apply({ block : 'imp-block' })));
- blockNode.bem('imp-block');
- DOM.destruct(blockNode);
- spy.should.have.been.calledOnce;
-
- delete DOM.blocks['imp-block'];
- });
- });
-
- describe('DOM.update', function() {
- it('should update tree', function() {
- var spyBlock1Destructed = sinon.spy(),
- spyBlock2Inited = sinon.spy();
-
- DOM.decl('block1', {
- onSetMod : {
- js : {
- '' : spyBlock1Destructed
- }
- }
- });
- DOM.decl('block2', {
- onSetMod : {
- js : {
- inited : spyBlock2Inited
- }
- }
- });
-
- var rootNode = DOM.init($(BEMHTML.apply({
- tag : 'div',
- content : { block : 'block1', js : true } })));
-
- DOM.update(rootNode, BEMHTML.apply({ block : 'block2', js : true }));
-
- spyBlock1Destructed.called.should.be.true;
- spyBlock2Inited.called.should.be.true;
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block1'];
- delete DOM.blocks['block2'];
- });
-
- it('should allow to pass simple string', function() {
- var domElem = $('');
- DOM.update(domElem, 'simple string');
- domElem.html().should.be.equal('simple string');
- });
- });
-
- describe('DOM.replace', function() {
- it('should properly replace tree', function() {
- var spyBlock1Destructed = sinon.spy(),
- spyBlock2Inited = sinon.spy();
-
- DOM.decl('block1', {
- onSetMod : {
- js : {
- '' : spyBlock1Destructed
- }
- }
- });
- DOM.decl('block2', {
- onSetMod : {
- js : {
- inited : spyBlock2Inited
- }
- }
- });
-
- var rootNode = DOM.init($(BEMHTML.apply({
- tag : 'div',
- content : { block : 'block1', js : true } })));
-
- DOM.replace(rootNode.find('.block1'), BEMHTML.apply({ block : 'block2', js : true }));
-
- spyBlock1Destructed.should.have.been.calledOnce;
- spyBlock2Inited.should.have.been.calledOnce;
-
- rootNode.html().should.be.equal('');
-
- DOM.destruct(rootNode);
-
- rootNode = DOM.init($(BEMHTML.apply({
- tag : 'div',
- content : [{ tag : 'p' }, { block : 'block1', js : true }, { tag : 'p' }] })));
-
- DOM.replace(rootNode.find('.block1'), BEMHTML.apply({ block : 'block2', js : true }));
-
- spyBlock1Destructed.should.have.been.calledTwice;
- spyBlock2Inited.should.have.been.calledTwice;
-
- rootNode.html().should.be.equal('');
-
- delete DOM.blocks['block1'];
- delete DOM.blocks['block2'];
- });
- });
-
- describe('params', function() {
- it('should properly join params', function() {
- DOM.decl('block', {
- getDefaultParams : function() {
- return { p1 : 1 };
- }
- });
-
- DOM.decl('block2', {
- onSetMod : {
- 'js' : {
- 'inited' : function() {
- var params = this.findBlockOn('block').params;
- params.p1.should.be.equal(1);
- params.p2.should.be.equal(2);
- params.p3.should.be.equal(3);
- }
- }
- }
- });
-
- var rootNode = DOM.init($(BEMHTML.apply({
- tag : 'div',
- content : [
- { block : 'block', js : { id : 'bla', p2 : 2 }, mix : { block : 'block2', js : true } },
- { block : 'block', js : { id : 'bla', p3 : 3 } }
- ]
- })));
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- delete DOM.blocks['block2'];
- });
- });
-
- describe('emit', function() {
- it('should emit context event with target', function() {
- DOM.decl('block', {
- onSetMod : {
- 'js' : {
- 'inited' : function() {
- this.emit('event');
- }
- }
- }
- });
-
- var rootNode = $(''),
- spy = sinon.spy();
-
- DOM.blocks['block'].on(rootNode, 'event', spy);
- DOM.update(rootNode, BEMHTML.apply({ block : 'block', js : true }));
-
- var block = rootNode.find('.block').bem('block');
-
- spy.should.have.been.calledOnce;
- spy.args[0][0].target.should.be.equal(block);
-
- delete DOM.blocks['block'];
- });
- });
-
- describe('containsDomElem', function() {
- var domElem, block, block2;
- beforeEach(function() {
- DOM.decl('block');
- DOM.decl('block2');
-
- domElem = $(BEMHTML.apply([
- {
- block : 'block',
- js : { id : '1' },
- content : [
- { elem : 'e1' },
- { elem : 'e2' }
- ]
- },
- {
- block : 'block',
- js : { id : '1' },
- content : [
- { elem : 'e1' },
- { elem : 'e2', content : { elem : 'e2-1' } }
- ]
- },
- {
- block : 'block2'
- }
- ]));
-
- DOM.init(domElem);
- block = domElem.filter('.block').bem('block');
- block2 = domElem.filter('.block2').bem('block2');
- });
-
- afterEach(function() {
- DOM.destruct(domElem);
- delete DOM.blocks['block'];
- delete DOM.blocks['block2'];
- });
-
- it('should properly checks for nested dom elem', function() {
- block.containsDomElem(block.elem('e2-1')).should.be.true;
- block.containsDomElem(block2.domElem).should.be.false;
- });
-
- it('should properly checks for nested dom elem with given context', function() {
- block.containsDomElem(block.elem('e1'), block.elem('e2-1')).should.be.false;
- block.containsDomElem(block.elem('e2'), block.elem('e2-1')).should.be.true;
- });
- });
-
- describe('DOM events', function() {
- var block, spy1, spy2, spy3, spy4, spy5, spy6, spy7, spy8, spy9,
- data = { data : 'data' },
- win = DOM.win,
- doc = DOM.doc;
-
- beforeEach(function() {
- spy1 = sinon.spy();
- spy2 = sinon.spy();
- spy3 = sinon.spy();
- spy4 = sinon.spy();
- spy5 = sinon.spy();
- spy6 = sinon.spy();
- spy7 = sinon.spy();
- spy8 = sinon.spy();
- spy9 = sinon.spy();
-
- DOM.decl('block', {
- bindToClick : function() {
- this
- .bindTo('click', this._handler1)
- .bindTo('click', this._handler2)
- .bindTo('elem', 'click', this._handler3)
- .bindTo(this.elem('elem'), 'click', this._handler4)
- .bindTo(this.elem('elem2'), 'click', this._handler5)
- .bindToWin('resize', this._handler6)
- .bindToWin('resize', this._handler7)
- .bindToDoc('mouseup', this._handler8)
- .bindToDoc('mouseup', this._handler9)
- // bind with data
- .bindTo('dblclick', data, this._handler1)
- .bindTo('elem', 'dblclick', data, this._handler2)
- .bindTo(this.elem('elem'), 'dblclick', data, this._handler3)
- .bindToWin('winevent', data, this._handler4)
- .bindToDoc('docevent', data, this._handler5)
- // bind with data and event object
- .bindTo({ 'mousedown' : this._handler1 }, data)
- .bindTo('elem', { 'mousedown' : this._handler2 }, data)
- .bindTo(this.elem('elem'), { 'mousedown' : this._handler3 }, data)
- .bindToWin({ 'winevent2' : this._handler4 }, data)
- .bindToDoc({ 'docevent2' : this._handler5 }, data);
- },
-
- _handler1 : spy1,
- _handler2 : spy2,
- _handler3 : spy3,
- _handler4 : spy4,
- _handler5 : spy5,
- _handler6 : spy6,
- _handler7 : spy7,
- _handler8 : spy8,
- _handler9 : spy9,
-
- unbindAllFromDomElem : function() {
- this.unbindFrom('click');
- },
-
- unbindHandler1FromDomElem : function() {
- this.unbindFrom('click', this._handler1);
- },
-
- unbindAllFromElemByString : function() {
- this.unbindFrom('elem', 'click');
- },
-
- unbindAllFromElemByElem : function() {
- this.unbindFrom(this.elem('elem'), 'click');
- },
-
- unbindHandler3FromElemByString : function() {
- this.unbindFrom('elem', 'click', this._handler3);
- },
-
- unbindClick4FromElemByElem : function() {
- this.unbindFrom(this.elem('elem'), 'click', this._handler4);
- },
-
- unbindHandler6FromWin : function() {
- this.unbindFromWin('resize', this._handler6);
- },
-
- unbindHandler8FromDoc : function() {
- this.unbindFromDoc('mouseup', this._handler8);
- }
- });
-
- block = DOM.init($(BEMHTML.apply({ block : 'block', content : { elem : 'elem' } }))).bem('block');
- block.bindToClick();
- });
-
- afterEach(function() {
- DOM.destruct(block.domElem);
- delete DOM.blocks['block'];
- });
-
- it('should properly bind to block-self DOM elem', function() {
- block.domElem.click();
- spy1.should.have.been.calledOnce;
- spy2.should.have.been.calledOnce;
- spy3.should.not.have.been.called;
- spy4.should.not.have.been.called;
- spy5.should.not.have.been.called;
- });
-
- it('should properly unbind to block-self DOM elem', function() {
- block.unbindAllFromDomElem();
- block.domElem.click();
- spy1.should.not.have.been.called;
- spy2.should.not.have.been.called;
- });
-
- it('should unbind from block-self DOM elem specified function only', function() {
- block.unbindHandler1FromDomElem();
- block.domElem.click();
- spy1.should.not.have.been.called;
- spy2.should.have.been.calledOnce;
- });
-
- it('should properly bind to block elem', function() {
- block.elem('elem').click();
- spy3.should.have.been.calledOnce;
- spy4.should.have.been.calledOnce;
- spy5.should.not.have.been.called;
- });
-
- it('should properly unbind from block elem by string', function() {
- block.unbindAllFromElemByString();
- block.elem('elem').click();
- spy3.should.not.have.been.called;
- spy4.should.not.have.been.called;
- });
-
- it('should properly unbind from block elem by elem', function() {
- block.unbindAllFromElemByElem();
- block.elem('elem').click();
- spy3.should.not.have.been.called;
- spy4.should.not.have.been.called;
- });
-
- it('should properly unbind specified function from block elem by elem', function() {
- block.unbindHandler3FromElemByString();
- block.elem('elem').click();
- spy3.should.not.have.been.called;
- spy4.should.have.been.calledOnce;
- });
-
- it('should properly unbind specified function from block elem by string', function() {
- block.unbindClick4FromElemByElem();
- block.elem('elem').click();
- spy3.should.have.been.calledOnce;
- spy4.should.not.have.been.called;
- });
-
- it('should properly bind to window event', function() {
- win.trigger('resize');
- spy6.should.have.been.calledOnce;
- spy7.should.have.been.calledOnce;
- });
-
- it('should properly unbind from window event', function() {
- block.unbindFromWin('resize');
- win.trigger('resize');
- spy6.should.not.have.been.called;
- spy7.should.not.have.been.called;
- });
-
- it('should properly unbind specified function from window event', function() {
- block.unbindHandler6FromWin();
- win.trigger('resize');
- spy6.should.not.have.been.called;
- spy7.should.have.been.calledOnce;
- });
-
- it('should properly bind to document event', function() {
- doc.trigger('mouseup');
- spy8.should.have.been.calledOnce;
- spy9.should.have.been.calledOnce;
- });
-
- it('should properly unbind from document event', function() {
- block.unbindFromWin('resize');
- doc.trigger('resize');
- spy8.should.not.have.been.called;
- spy9.should.not.have.been.called;
- });
-
- it('should properly unbind specified function from document event', function() {
- block.unbindHandler8FromDoc();
- doc.trigger('mouseup');
- spy8.should.not.have.been.called;
- spy9.should.have.been.calledOnce;
- });
-
- it('should properly bind with aditional event data', function() {
- block.domElem.dblclick();
- block.elem('elem').dblclick();
- win.trigger('winevent');
- doc.trigger('docevent');
- spy1.args[0][0].data.should.have.been.equal(data);
- spy2.args[0][0].data.should.have.been.equal(data);
- spy3.args[0][0].data.should.have.been.equal(data);
- spy4.args[0][0].data.should.have.been.equal(data);
- spy5.args[0][0].data.should.have.been.equal(data);
- });
-
- it('should properly bind with aditional event data when use event object', function() {
- block.domElem.mousedown();
- block.elem('elem').mousedown();
- win.trigger('winevent2');
- doc.trigger('docevent2');
- spy1.args[0][0].data.should.have.been.equal(data);
- spy2.args[0][0].data.should.have.been.equal(data);
- spy3.args[0][0].data.should.have.been.equal(data);
- spy4.args[0][0].data.should.have.been.equal(data);
- spy5.args[0][0].data.should.have.been.equal(data);
- });
- });
-
- describe('closestElem', function() {
- it('should return the closest element', function() {
- DOM.decl('block', {}, {});
-
- var rootNode = $(BEMHTML.apply({
- block : 'block',
- js : true,
- content : {
- elem : 'elem1',
- content : {
- elem : 'elem2'
- }
- }
- })),
- block = rootNode.bem('block'),
- closest = block.closestElem(block.elem('elem2'), 'elem1');
-
- closest[0].should.be.equal(block.elem('elem1')[0]);
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block'];
- });
- });
-
- describe('liveInitOnBlockInsideEvent', function() {
- it('should init and call handler on live initialization', function() {
- var spyInit = sinon.spy(),
- spyHandler = sinon.spy();
-
- DOM.decl('block1', {
- onSetMod : {
- js : {
- inited : spyInit
- }
- }
- }, {
- live : function() {
- this.liveInitOnBlockInsideEvent('event', 'block2', spyHandler);
- }
- });
- DOM.decl('block2', {}, {});
-
- var rootNode = DOM.init($(BEMHTML.apply({
- block : 'block1',
- js : true,
- content : {
- block : 'block2',
- js : true
- }
- }))),
- block = rootNode.find('.block2').bem('block2');
-
- spyInit.called.should.be.false;
- spyHandler.called.should.be.false;
-
- block.emit('event');
-
- spyInit.called.should.be.true;
- spyHandler.called.should.be.true;
-
- DOM.destruct(rootNode);
- delete DOM.blocks['block1'];
- delete DOM.blocks['block2'];
- });
- });
-
- describe('modules.define patching', function() {
- it('should provide BEMDOM block', function(done) {
- var name = 'b' + Math.random(),
- spy = sinon.spy();
-
- modules.define(name, ['i-bem__dom'], function(provide, BEMDOM) {
- spy();
- provide(BEMDOM.decl(this.name, {}));
- });
-
- modules.define(name, function(provide, Prev) {
- spy();
- Prev.should.be.eql(DOM.blocks[this.name]);
- provide(Prev.decl(this.name, {}));
- });
-
- modules.require([name], function(Block) {
- spy.should.have.been.calledTwice;
- Block.should.be.eql(DOM.blocks[name]);
- done();
- });
- });
- });
-
- describe('mod change events', function() {
- var block;
- beforeEach(function() {
- block = $(BEMHTML.apply(
- {
- block : 'block',
- content : [
- { elem : 'e1', mods : { 'mod1' : 'val1' } },
- { elem : 'e1', mods : { 'mod1' : 'val1' } },
- { elem : 'e2', mods : { 'mod1' : 'val1' } }
- ]
- }))
- .bem('block');
- });
-
- afterEach(function() {
- delete DOM.blocks['block'];
- });
-
- it('should propagate destructing event', function() {
- var spy = sinon.spy();
-
- DOM.decl('block1', {
- onSetMod : {
- 'js' : {
- 'inited' : function() {
- DOM.blocks['block2'].on(this.domElem, { modName : 'js', modVal : '' }, spy);
- }
- }
- }
- });
-
- DOM.decl('block2');
-
- var domElem = $(BEMHTML.apply({
- block : 'block1',
- content : {
- block : 'block2'
- }
- })).appendTo('body');
-
- DOM.init(domElem);
-
- var block1 = domElem.bem('block1'),
- block2 = block1.findBlockInside('block2');
-
- DOM.destruct(block2.domElem);
-
- spy.should.have.been.called;
-
- delete DOM.blocks['block2'];
- delete DOM.blocks['block1'];
- });
-
- describe('elems', function() {
- it('should emit event on elem mod change with correct arguments', function() {
- var spy1 = sinon.spy(),
- spy2 = sinon.spy(),
- spy3 = sinon.spy(),
- spy4 = sinon.spy(),
- elem = block.elem('e1');
-
- block
- .on({ elem : 'e1', modName : 'mod1', modVal : '*' }, spy1)
- .on({ elem : 'e1', modName : 'mod1', modVal : 'val2' }, spy2)
- .on({ elem : 'e1', modName : 'mod1', modVal : 'val3' }, spy3)
- .on({ elem : 'e2', modName : 'mod1', modVal : 'val2' }, spy4)
- .setMod(elem, 'mod1', 'val2');
-
- spy1.should.have.been.called.twice;
- spy2.should.have.been.called.twice;
- spy3.should.not.have.been.called;
- spy4.should.not.have.been.called;
-
- var eventData = spy1.args[0][1];
- eventData.modName.should.be.equal('mod1');
- eventData.modVal.should.be.equal('val2');
- eventData.oldModVal.should.be.equal('val1');
- eventData.elem[0].should.be.eql(elem[0]);
- spy1.args[1][1].elem[0].should.be.eql(elem[1]);
- });
-
- it('should emit live event on elem mod change with correct arguments', function() {
- var spy1 = sinon.spy(),
- spy2 = sinon.spy(),
- spy3 = sinon.spy(),
- spy4 = sinon.spy(),
- elem = block.elem('e1');
-
- DOM.blocks['block']
- .on({ elem : 'e1', modName : 'mod1', modVal : '*' }, spy1)
- .on({ elem : 'e1', modName : 'mod1', modVal : 'val2' }, spy2)
- .on({ elem : 'e1', modName : 'mod1', modVal : 'val3' }, spy3)
- .on({ elem : 'e2', modName : 'mod1', modVal : 'val2' }, spy4);
-
- block.setMod(elem, 'mod1', 'val2');
-
- spy1.should.have.been.called.twice;
- spy2.should.have.been.called.twice;
- spy3.should.not.have.been.called;
- spy4.should.not.have.been.called;
-
- var eventData = spy1.args[0][1];
- eventData.modName.should.be.equal('mod1');
- eventData.modVal.should.be.equal('val2');
- eventData.oldModVal.should.be.equal('val1');
- eventData.elem[0].should.be.eql(elem[0]);
- spy1.args[1][1].elem[0].should.be.eql(elem[1]);
- });
- });
- });
-});
-
-provide();
-
-});
diff --git a/common.blocks/i-bem/_elem-instances/i-bem_elem-instances.deps.js b/common.blocks/i-bem/_elem-instances/i-bem_elem-instances.deps.js
deleted file mode 100644
index df07fafea..000000000
--- a/common.blocks/i-bem/_elem-instances/i-bem_elem-instances.deps.js
+++ /dev/null
@@ -1,8 +0,0 @@
-({
- mustDeps : [
- {
- block : 'i-bem',
- elem : 'internal'
- }
- ]
-})
diff --git a/common.blocks/i-bem/_elem-instances/i-bem_elem-instances.js b/common.blocks/i-bem/_elem-instances/i-bem_elem-instances.js
deleted file mode 100644
index 42b0078e5..000000000
--- a/common.blocks/i-bem/_elem-instances/i-bem_elem-instances.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * @module i-bem
- */
-
-modules.define(
- 'i-bem',
- ['i-bem__internal', 'inherit'],
- function(provide, INTERNAL, inherit, BEM) {
-
-var buildClass = INTERNAL.buildClass;
-
-/**
- * @class BEM
- * @augments BEM
- * @exports
- */
-provide(BEM.decl(null, /** @lends BEM */{
-
- /**
- * Declares elements and creates an elements class
- * @protected
- * @param {Object} decl Element description
- * @param {String} decl.block Block name
- * @param {String} decl.elem Element name
- * @param {String} [decl.baseBlock] Name of the parent block
- * @param {Array} [decl.baseMix] Mixed block names
- * @param {String} [decl.modName] Modifier name
- * @param {String|Array} [decl.modVal] Modifier value
- * @param {Object} [props] Methods
- * @param {Object} [staticProps] Static methods
- * @returns {Function}
- */
- decl : function(decl, props, staticProps) {
- var block;
- if(decl.elem) {
- typeof decl.block === 'undefined' && (decl.block = this._blockName);
- block = this.__base(
- {
- block : buildClass(decl.block, decl.elem),
- baseBlock : decl.baseBlock,
- baseMix : decl.baseMix,
- modName : decl.modName,
- modVal : decl.modVal
- },
- props,
- staticProps);
- block._blockName = decl.block;
- block._elemName = decl.elem;
- } else {
- block = this.__base.apply(this, arguments);
- block._elemName || (block._blockName = block._name);
- }
- return block;
- },
-
- /**
- * Factory method for creating an instance of the element named
- * @param {Object} desc Description
- * @param {Object} [params] Instance parameters
- * @returns {BEM}
- */
- create : function(desc, params) {
- return desc.elem?
- new BEM.blocks[buildClass(desc.block, desc.elem)](desc.mods, params) :
- this.__base(desc, params);
- },
-
- /**
- * Returns the name of the current instance
- * @protected
- * @param {Boolean} [shortName] return the short name of the current instance
- * @returns {String}
- */
- getName : function(shortName) {
- return shortName? (this._elemName || this._blockName) : this._name;
- }
-}));
-
-});
diff --git a/common.blocks/i-bem/_elem-instances/i-bem_elem-instances.spec.js b/common.blocks/i-bem/_elem-instances/i-bem_elem-instances.spec.js
deleted file mode 100644
index 2b2d548b5..000000000
--- a/common.blocks/i-bem/_elem-instances/i-bem_elem-instances.spec.js
+++ /dev/null
@@ -1,73 +0,0 @@
-modules.define('spec', ['i-bem', 'sinon'], function(provide, BEM, sinon) {
-
-describe('i-bem_elem-instances', function() {
- describe('decl', function() {
- it('should return element', function() {
- var block = BEM.decl({ block : 'block', elem : 'elem' }, {});
- block.should.be.equal(BEM.blocks['block__elem']);
- delete BEM.blocks['block__elem'];
- });
-
- it('with mod should apply method only if element has mod', function() {
- var baseMethodSpy = sinon.spy(),
- modsMethodSpy = sinon.spy();
-
- BEM.decl({ block : 'block', elem : 'elem' }, {
- method : baseMethodSpy
- });
- BEM.decl({ block : 'block', elem : 'elem', modName : 'mod1', modVal : 'val1' }, {
- method : modsMethodSpy
- });
-
- var instance = BEM.create({ block : 'block', elem : 'elem', mods : { 'mod1' : 'val1' } });
-
- instance.method();
- baseMethodSpy.called.should.be.false;
- modsMethodSpy.called.should.be.true;
-
- instance.setMod('mod1', 'val2');
- instance.method();
- baseMethodSpy.called.should.be.true;
- modsMethodSpy.callCount.should.be.equal(1);
-
- delete BEM.blocks['block__elem'];
- });
-
- it('should declare element by block properly', function() {
- var Block = BEM.decl('block'),
- Elem = Block.decl({ elem : 'elem' });
-
- Elem.getName().should.be.equal('block__elem');
-
- delete BEM.blocks['block'];
- BEM.blocks['block__elem']?
- delete BEM.blocks['block__elem'] :
- delete BEM.blocks['undefined__elem'];
- });
- });
-
- describe('create', function() {
- it('should return instance of element', function() {
- var elem = BEM.decl({ block : 'block', elem : 'elem' }, {}),
- instance = BEM.create({ block : 'block', elem : 'elem' });
-
- instance.should.be.instanceOf(elem);
- delete BEM.blocks['block__elem'];
- });
- });
-
- describe('getName', function() {
- it('should return correct full and short names of element', function() {
- var elem = BEM.decl({ block : 'block', elem : 'elem' }, {});
-
- elem.getName().should.be.equal('block__elem');
- elem.getName(true).should.be.equal('elem');
-
- delete BEM.blocks['block__elem'];
- });
- });
-});
-
-provide();
-
-});
diff --git a/common.blocks/i-bem/i-bem.spec.js b/common.blocks/i-bem/i-bem.spec.js
index e941a1fd9..71fcaa160 100644
--- a/common.blocks/i-bem/i-bem.spec.js
+++ b/common.blocks/i-bem/i-bem.spec.js
@@ -6,89 +6,73 @@ describe('i-bem', function() {
delete BEM.blocks['block'];
});
- it('should return block', function() {
- var block = BEM.decl('block', {});
- block.should.be.equal(BEM.blocks['block']);
+ it('should enable to declare block', function() {
+ var Block = BEM.declBlock('block', {});
+
+ Block.should.be.equal(BEM.blocks['block']);
+ (new Block()).should.be.instanceOf(BEM.Block);
});
- it('should allow inheritance', function() {
- var block = BEM.decl('block', {}),
- block2 = block.decl('block2', {});
+ it('should enable to inherit block', function() {
+ var Block = BEM.declBlock('block', {}),
+ Block2 = BEM.declBlock('block2', Block, {});
- (new block2()).should.be.instanceOf(block);
- (new block2()).should.be.instanceOf(block2);
+ (new Block2()).should.be.instanceOf(Block);
+ (new Block2()).should.be.instanceOf(Block2);
delete BEM.blocks['block2'];
});
- it('should allow inheritance from itself', function() {
- var block = BEM.decl('block', {}),
- block2 = block.decl({});
-
- block2.should.be.equal(block);
- });
-
- it('should allow to define only modName and modVal', function() {
- var block = BEM.decl('block', {}),
- block2 = block.decl({ modName : 'm1', modVal : 'v1' }, {});
+ it('should enable to inherit to itself', function() {
+ var Block = BEM.declBlock('block', {}),
+ Block2 = BEM.declBlock('block', {});
- block2.should.be.equal(block);
- block2.getName().should.be.equal('block');
+ Block2.should.be.equal(Block);
});
- it('should allow use block class as baseBlock', function() {
- var block = BEM.decl('block', {}),
- block2 = block.decl({ block : 'block2', baseBlock : block }, {});
-
- (new block2()).should.be.instanceOf(block);
- (new block2()).should.be.instanceOf(block2);
+ it('should enable to declare modifier', function() {
+ var Block = BEM.declBlock('block', {}),
+ Block2 = Block.declMod({ modName : 'm1', modVal : 'v1' }, {});
- delete BEM.blocks['block2'];
+ Block2.should.be.equal(Block);
});
it('should apply method only if block has mod', function() {
var baseMethodSpy = sinon.spy(),
- modsMethodSpy = sinon.spy();
-
- BEM.decl('block', {
- method : baseMethodSpy
- });
- BEM.decl({ block : 'block', modName : 'mod1', modVal : 'val1' }, {
- method : modsMethodSpy
- });
-
- var instance = BEM.create({ block : 'block', mods : { 'mod1' : 'val1' } });
+ modsMethodSpy = sinon.spy(),
+ Block = BEM
+ .declBlock('block', { method : baseMethodSpy })
+ .declMod({ modName : 'mod1', modVal : 'val1' }, { method : modsMethodSpy }),
+ instance = new Block({ mod1 : 'val1' });
instance.method();
- baseMethodSpy.called.should.be.false;
- modsMethodSpy.called.should.be.true;
+
+ baseMethodSpy.should.not.have.been.called;
+ modsMethodSpy.should.have.been.calledOnce;
instance.setMod('mod1', 'val2');
instance.method();
- baseMethodSpy.called.should.be.true;
- modsMethodSpy.callCount.should.be.equal(1);
+
+ baseMethodSpy.should.have.been.calledOnce;
+ modsMethodSpy.should.have.been.calledOnce;
});
it('should apply method only if block has boolean mod', function() {
var baseMethodSpy = sinon.spy(),
- modsMethodSpy = sinon.spy();
-
- BEM.decl('block', {
- method : baseMethodSpy
- });
-
- BEM.decl({ block : 'block', modName : 'mod1', modVal : true }, {
- method : modsMethodSpy
- });
-
- var instance = BEM.create({ block : 'block', mods : { 'mod1' : true } });
+ modsMethodSpy = sinon.spy(),
+ Block = BEM
+ .declBlock('block', { method : baseMethodSpy })
+ .declMod({ modName : 'mod1', modVal : true }, { method : modsMethodSpy }),
+ instance = new Block({ mod1 : true });
instance.method();
+
baseMethodSpy.should.not.have.been.called;
modsMethodSpy.should.have.been.calledOnce;
instance.delMod('mod1');
instance.method();
+
baseMethodSpy.should.have.been.calledOnce;
modsMethodSpy.should.have.been.calledOnce;
});
@@ -96,10 +80,10 @@ describe('i-bem', function() {
describe('create', function() {
it('should return instance of block', function() {
- var block = BEM.decl('block', {}),
- instance = BEM.create('block');
+ var Block = BEM.declBlock('block', {}),
+ instance = Block.create();
- instance.should.be.instanceOf(block);
+ instance.should.be.instanceOf(Block);
delete BEM.blocks['block'];
});
});
@@ -107,8 +91,9 @@ describe('i-bem', function() {
describe('mods', function() {
var block;
beforeEach(function() {
- BEM.decl('block', {});
- block = BEM.create({ block : 'block', mods : { mod1 : 'val1', mod2 : true, mod3 : false } });
+ block = BEM
+ .declBlock('block', {})
+ .create({ mods : { mod1 : 'val1', mod2 : true, mod3 : false } });
});
afterEach(function() {
delete BEM.blocks['block'];
@@ -140,15 +125,18 @@ describe('i-bem', function() {
it('should update boolean mod value', function() {
block
.setMod('mod1', true)
- .getMod('mod1').should.be.true;
+ .getMod('mod1')
+ .should.be.true;
block
.setMod('mod1', false)
- .getMod('mod1').should.be.equal('');
+ .getMod('mod1')
+ .should.be.equal('');
block
.setMod('mod1')
- .getMod('mod1').should.be.true;
+ .getMod('mod1')
+ .should.be.true;
});
});
@@ -272,7 +260,7 @@ describe('i-bem', function() {
spyMod2Val1 = sinon.spy(),
spyMod2Val2 = sinon.spy();
- BEM.decl('block', {
+ BEM.declBlock('block', {
beforeSetMod : {
'mod1' : {
'val1' : function() {
@@ -282,7 +270,7 @@ describe('i-bem', function() {
}
});
- BEM.decl('block', {
+ BEM.declBlock('block', {
beforeSetMod : {
'mod1' : function() {
order.push(3);
@@ -294,14 +282,14 @@ describe('i-bem', function() {
}
});
- BEM.decl('block', {
+ BEM.declBlock('block', {
beforeSetMod : function(modName) {
this.__base.apply(this, arguments);
modName === 'mod1' && order.push(2);
}
});
- BEM.decl('block', {
+ BEM.declBlock('block', {
beforeSetMod : {
'mod1' : {
'*' : function() {
@@ -321,7 +309,7 @@ describe('i-bem', function() {
}
});
- var block = BEM.create({ block : 'block', mods : { mod1 : 'val0', mod2 : 'val0' } });
+ var block = BEM.blocks['block'].create({ mods : { mod1 : 'val0', mod2 : 'val0' } });
block.setMod('mod1', 'val1');
order.should.be.eql([1, 2, 3, 4, 5, 6]);
@@ -331,46 +319,51 @@ describe('i-bem', function() {
});
it('should call callbacks before set mod', function(done) {
- BEM.decl('block', {
- beforeSetMod : {
- 'mod1' : {
- 'val1' : function() {
- this.hasMod('mod1', 'val1').should.be.false;
- done();
+ BEM
+ .declBlock('block', {
+ beforeSetMod : {
+ 'mod1' : {
+ 'val1' : function() {
+ this.hasMod('mod1', 'val1').should.be.false;
+ done();
+ }
}
- }
- }
- });
- var block = BEM.create({ block : 'block', mods : { mod1 : 'val0' } });
- block.setMod('mod1', 'val1');
+ }
+ })
+ .create({ mods : { mod1 : 'val0' } })
+ .setMod('mod1', 'val1');
});
it('should set mod after callbacks', function() {
- BEM.decl('block', {
- beforeSetMod : {
- 'mod1' : {
- 'val1' : function() {}
- }
- }
- });
- var block = BEM.create({ block : 'block', mods : { mod1 : 'val0' } });
- block.setMod('mod1', 'val1');
- block.hasMod('mod1', 'val1').should.be.true;
+ BEM
+ .declBlock('block', {
+ beforeSetMod : {
+ 'mod1' : {
+ 'val1' : function() {}
+ }
+ }
+ })
+ .create({ mods : { mod1 : 'val0' } })
+ .setMod('mod1', 'val1')
+ .hasMod('mod1', 'val1')
+ .should.be.true;
});
it('shouldn\'t set mod when callback returns false', function() {
- BEM.decl('block', {
- beforeSetMod : {
- 'mod1' : {
- 'val1' : function() {
- return false;
+ BEM
+ .declBlock('block', {
+ beforeSetMod : {
+ 'mod1' : {
+ 'val1' : function() {
+ return false;
+ }
}
- }
- }
- });
- var block = BEM.create({ block : 'block', mods : { mod1 : 'val0' } });
- block.setMod('mod1', 'val1');
- block.hasMod('mod1', 'val1').should.be.false;
+ }
+ })
+ .create({ mods : { mod1 : 'val0' } })
+ .setMod('mod1', 'val1')
+ .hasMod('mod1', 'val1')
+ .should.be.false;
});
});
@@ -385,7 +378,7 @@ describe('i-bem', function() {
spyMod2Val1 = sinon.spy(),
spyMod2Val2 = sinon.spy();
- BEM.decl('block', {
+ BEM.declBlock('block', {
onSetMod : {
'mod1' : {
'val1' : function() {
@@ -395,7 +388,7 @@ describe('i-bem', function() {
}
});
- BEM.decl('block', {
+ BEM.declBlock('block', {
onSetMod : {
'mod1' : function() {
order.push(3);
@@ -407,14 +400,14 @@ describe('i-bem', function() {
}
});
- BEM.decl('block', {
+ BEM.declBlock('block', {
onSetMod : function(modName) {
this.__base.apply(this, arguments);
modName === 'mod1' && order.push(2);
}
});
- BEM.decl('block', {
+ BEM.declBlock('block', {
onSetMod : {
'mod1' : {
'*' : function() {
@@ -434,8 +427,9 @@ describe('i-bem', function() {
}
});
- var block = BEM.create({ block : 'block', mods : { mod1 : 'val0', mod2 : 'val0' } });
- block.setMod('mod1', 'val1');
+ BEM.blocks['block']
+ .create({ mods : { mod1 : 'val0', mod2 : 'val0' } })
+ .setMod('mod1', 'val1');
order.should.be.eql([1, 2, 3, 4, 5, 6]);
spyMod1Val2.should.not.have.been.called;
@@ -444,39 +438,42 @@ describe('i-bem', function() {
});
it('should call callbacks after set mod', function(done) {
- BEM.decl('block', {
- onSetMod : {
- 'mod1' : {
- 'val1' : function() {
- this.hasMod('mod1', 'val1').should.be.true;
- done();
+ BEM
+ .declBlock('block', {
+ onSetMod : {
+ 'mod1' : {
+ 'val1' : function() {
+ this.hasMod('mod1', 'val1').should.be.true;
+ done();
+ }
}
- }
- }
- });
- var block = BEM.create({ block : 'block', mods : { mod1 : 'val0' } });
- block.setMod('mod1', 'val1');
+ }
+ })
+ .create({ mods : { mod1 : 'val0' } })
+ .setMod('mod1', 'val1');
});
it('shouldn\'t call callbacks if beforeSetMod cancel set mod', function() {
var spy = sinon.spy();
- BEM.decl('block', {
- beforeSetMod : {
- 'mod1' : {
- 'val1' : function() {
- return false;
+ BEM
+ .declBlock('block', {
+ beforeSetMod : {
+ 'mod1' : {
+ 'val1' : function() {
+ return false;
+ }
}
- }
- },
+ },
+
+ onSetMod : {
+ 'mod1' : {
+ 'val1' : spy
+ }
+ }
+ })
+ .create({ mods : { mod1 : 'val0' } })
+ .setMod('mod1', 'val1');
- onSetMod : {
- 'mod1' : {
- 'val1' : spy
- }
- }
- });
- var block = BEM.create({ block : 'block', mods : { mod1 : 'val0' } });
- block.setMod('mod1', 'val1');
spy.should.not.have.been.called;
});
});
@@ -488,7 +485,7 @@ describe('i-bem', function() {
spyMod2ValFalse = sinon.spy(),
spyMod2Val2 = sinon.spy();
- BEM.decl('block', {
+ BEM.declBlock('block', {
beforeSetMod : {
'mod1' : {
'true' : function(modName, modVal, oldModVal) {
@@ -508,7 +505,7 @@ describe('i-bem', function() {
}
});
- BEM.decl('block', {
+ BEM.declBlock('block', {
beforeSetMod : {
'mod1' : function() {
order.push(3);
@@ -530,7 +527,7 @@ describe('i-bem', function() {
}
});
- BEM.decl('block', {
+ BEM.declBlock('block', {
beforeSetMod : function(modName) {
this.__base.apply(this, arguments);
modName === 'mod1' && order.push(2);
@@ -542,7 +539,7 @@ describe('i-bem', function() {
}
});
- BEM.decl('block', {
+ BEM.declBlock('block', {
beforeSetMod : {
'mod1' : {
'*' : function(modName, modVal, oldModVal) {
@@ -595,7 +592,7 @@ describe('i-bem', function() {
}
});
- var block = BEM.create({ block : 'block', mods : { mod1 : false, mod2 : true } });
+ var block = BEM.blocks['block'].create({ mods : { mod1 : false, mod2 : true } });
block.setMod('mod1', true);
spyMod1Val2.should.not.have.been.called;
@@ -612,8 +609,9 @@ describe('i-bem', function() {
describe('nextTick', function() {
var block;
beforeEach(function() {
- BEM.decl('block', {});
- block = BEM.create({ block : 'block', mods : { mod1 : 'val1' } });
+ block = BEM
+ .declBlock('block', {})
+ .create({ mods : { mod1 : 'val1' } });
});
afterEach(function() {
delete BEM.blocks['block'];
@@ -649,28 +647,29 @@ describe('i-bem', function() {
describe('mod change events', function() {
var block;
beforeEach(function() {
- BEM.decl('block', {
- beforeSetMod : {
- 'mod3' : function() {
- return false;
- }
- },
-
- onSetMod : {
- 'js' : {
- 'inited' : function() {
- this.prop = 'val1';
+ block = BEM
+ .declBlock('block', {
+ beforeSetMod : {
+ 'mod3' : function() {
+ return false;
}
},
- 'mod1' : {
- 'val2' : function() {
- this.prop = 'val2';
+ onSetMod : {
+ 'js' : {
+ 'inited' : function() {
+ this.prop = 'val1';
+ }
+ },
+
+ 'mod1' : {
+ 'val2' : function() {
+ this.prop = 'val2';
+ }
}
}
- }
- });
- block = BEM.create({ block : 'block', mods : { mod1 : 'val1', mod2 : true } });
+ })
+ .create({ mods : { mod1 : 'val1', mod2 : true } });
});
afterEach(function() {
delete BEM.blocks['block'];
diff --git a/common.blocks/i-bem/i-bem.vanilla.js b/common.blocks/i-bem/i-bem.vanilla.js
index d24090c33..3ea99399c 100644
--- a/common.blocks/i-bem/i-bem.vanilla.js
+++ b/common.blocks/i-bem/i-bem.vanilla.js
@@ -71,16 +71,12 @@ function modFnsToProps(prefix, modFns, props, elemName) {
} else {
var modName, modVal, modFn;
for(modName in modFns) {
- if(modFns.hasOwnProperty(modName)) {
- modFn = modFns[modName];
- if(functions.isFunction(modFn)) {
- props[buildModFnName(prefix, modName, '*', elemName)] = modFn;
- } else {
- for(modVal in modFn) {
- if(modFn.hasOwnProperty(modVal)) {
- props[buildModFnName(prefix, modName, modVal, elemName)] = modFn[modVal];
- }
- }
+ modFn = modFns[modName];
+ if(functions.isFunction(modFn)) {
+ props[buildModFnName(prefix, modName, '*', elemName)] = modFn;
+ } else {
+ for(modVal in modFn) {
+ props[buildModFnName(prefix, modName, modVal, elemName)] = modFn[modVal];
}
}
}
@@ -119,30 +115,25 @@ function convertModHandlersToMethods(props) {
var elemName;
if(props.beforeElemSetMod) {
for(elemName in props.beforeElemSetMod) {
- if(props.beforeElemSetMod.hasOwnProperty(elemName)) {
- modFnsToProps('before', props.beforeElemSetMod[elemName], props, elemName);
- }
+ modFnsToProps('before', props.beforeElemSetMod[elemName], props, elemName);
}
delete props.beforeElemSetMod;
}
if(props.onElemSetMod) {
for(elemName in props.onElemSetMod) {
- if(props.onElemSetMod.hasOwnProperty(elemName)) {
- modFnsToProps('after', props.onElemSetMod[elemName], props, elemName);
- }
+ modFnsToProps('after', props.onElemSetMod[elemName], props, elemName);
}
delete props.onElemSetMod;
}
}
/**
- * @class BEM
+ * @class Block
* @description Base block for creating BEM blocks
* @augments events:Emitter
- * @exports
*/
-var BEM = inherit(events.Emitter, /** @lends BEM.prototype */ {
+var Block = inherit(events.Emitter, /** @lends Block.prototype */ {
/**
* @constructor
* @private
@@ -191,7 +182,7 @@ var BEM = inherit(events.Emitter, /** @lends BEM.prototype */ {
* @param {Object} [data] Additional data that the handler gets as e.data
* @param {Function} fn Handler
* @param {Object} [ctx] Handler context
- * @returns {BEM} this
+ * @returns {Block} this
*/
on : function(e, data, fn, ctx) {
if(typeof e === 'object' && (functions.isFunction(data) || functions.isFunction(fn))) { // mod change event
@@ -206,7 +197,7 @@ var BEM = inherit(events.Emitter, /** @lends BEM.prototype */ {
* @param {String|Object} [e] Event type
* @param {Function} [fn] Handler
* @param {Object} [ctx] Handler context
- * @returns {BEM} this
+ * @returns {Block} this
*/
un : function(e, fn, ctx) {
if(typeof e === 'object' && functions.isFunction(fn)) { // mod change event
@@ -221,7 +212,7 @@ var BEM = inherit(events.Emitter, /** @lends BEM.prototype */ {
* @protected
* @param {String} e Event name
* @param {Object} [data] Additional information
- * @returns {BEM} this
+ * @returns {Block} this
*/
emit : function(e, data) {
var isModJsEvent = false;
@@ -345,7 +336,7 @@ var BEM = inherit(events.Emitter, /** @lends BEM.prototype */ {
* @param {Object} [elem] Nested element
* @param {String} modName Modifier name
* @param {String} modVal Modifier value
- * @returns {BEM} this
+ * @returns {Block} this
*/
setMod : function(elem, modName, modVal) {
if(typeof modVal === 'undefined') {
@@ -439,7 +430,7 @@ var BEM = inherit(events.Emitter, /** @lends BEM.prototype */ {
* @param {String} modVal1 First modifier value
* @param {String} [modVal2] Second modifier value
* @param {Boolean} [condition] Condition
- * @returns {BEM} this
+ * @returns {Block} this
*/
toggleMod : function(elem, modName, modVal1, modVal2, condition) {
if(typeof elem === 'string') { // if this is a block
@@ -478,7 +469,7 @@ var BEM = inherit(events.Emitter, /** @lends BEM.prototype */ {
* @protected
* @param {Object} [elem] Nested element
* @param {String} modName Modifier name
- * @returns {BEM} this
+ * @returns {Block} this
*/
delMod : function(elem, modName) {
if(!modName) {
@@ -548,7 +539,7 @@ var BEM = inherit(events.Emitter, /** @lends BEM.prototype */ {
* Executes given callback on next turn eventloop in block's context
* @protected
* @param {Function} fn callback
- * @returns {BEM} this
+ * @returns {Block} this
*/
nextTick : function(fn) {
var _this = this;
@@ -557,104 +548,55 @@ var BEM = inherit(events.Emitter, /** @lends BEM.prototype */ {
});
return this;
}
-}, /** @lends BEM */{
-
- _name : 'i-bem',
-
+}, /** @lends Block */{
/**
- * Storage for block declarations (hash by block name)
- * @type Object
+ * Factory method for creating an instance
+ * @param {Object} [desc]
+ * @param {Object} [desc.mods] modifiers
+ * @param {Object} [desc.params] params
+ * @returns {Block}
*/
- blocks : blocks,
+ create : function(desc) {
+ desc || (desc = {});
+ return new this(desc.mods, desc.params);
+ },
/**
- * Declares blocks and creates a block class
- * @param {String|Object} decl Block name (simple syntax) or description
- * @param {String} decl.block|decl.name Block name
- * @param {String} [decl.baseBlock] Name of the parent block
- * @param {Array} [decl.baseMix] Mixed block names
- * @param {String} [decl.modName] Modifier name
- * @param {String|Array} [decl.modVal] Modifier value
- * @param {Object} [props] Methods
- * @param {Object} [staticProps] Static methods
+ * Declares modifier
+ * @param {Object} mod
+ * @param {String} mod.modName
+ * @param {String|Boolean|Array} [mod.modVal]
+ * @param {Object} props
+ * @param {Object} [staticProps]
* @returns {Function}
*/
- decl : function(decl, props, staticProps) {
- // string as block
- typeof decl === 'string' && (decl = { block : decl });
- // inherit from itself
- if(arguments.length <= 2 &&
- typeof decl === 'object' &&
- (!decl || (typeof decl.block !== 'string' && typeof decl.modName !== 'string'))) {
- staticProps = props;
- props = decl;
- decl = {};
- }
- typeof decl.block === 'undefined' && (decl.block = this.getName());
-
- var baseBlock;
- if(typeof decl.baseBlock === 'undefined') {
- baseBlock = blocks[decl.block] || this;
- } else if(typeof decl.baseBlock === 'string') {
- baseBlock = blocks[decl.baseBlock];
- if(!baseBlock)
- throw('baseBlock "' + decl.baseBlock + '" for "' + decl.block + '" is undefined');
- } else {
- baseBlock = decl.baseBlock;
- }
-
- convertModHandlersToMethods(props || (props = {}));
-
- if(decl.modName) {
- var checkMod = buildCheckMod(decl.modName, decl.modVal);
- objects.each(props, function(prop, name) {
- functions.isFunction(prop) &&
- (props[name] = function() {
- var method;
- if(checkMod(this)) {
- method = prop;
- } else {
- var baseMethod = baseBlock.prototype[name];
- baseMethod && baseMethod !== prop &&
- (method = this.__base);
- }
- return method?
- method.apply(this, arguments) :
- undef;
- });
- });
- }
-
- if(staticProps && typeof staticProps.live === 'boolean') {
- var live = staticProps.live;
- staticProps.live = function() {
- return live;
- };
- }
-
- var block, baseBlocks = baseBlock;
- if(decl.baseMix) {
- baseBlocks = [baseBlocks];
- decl.baseMix.forEach(function(mixedBlock) {
- if(!blocks[mixedBlock]) {
- throw('mix block "' + mixedBlock + '" for "' + decl.block + '" is undefined');
- }
- baseBlocks.push(blocks[mixedBlock]);
- });
- }
-
- decl.block === baseBlock.getName()?
- // makes a new "live" if the old one was already executed
- (block = inherit.self(baseBlocks, props, staticProps))._processLive(true) :
- (block = blocks[decl.block] = inherit(baseBlocks, props, staticProps))._name = decl.block;
+ declMod : function(mod, props, staticProps) {
+ props && convertModHandlersToMethods(props);
+
+ var checkMod = buildCheckMod(mod.modName, mod.modVal),
+ basePtp = this.prototype;
+
+ objects.each(props, function(prop, name) {
+ functions.isFunction(prop) &&
+ (props[name] = function() {
+ var method;
+ if(checkMod(this)) {
+ method = prop;
+ } else {
+ var baseMethod = basePtp[name];
+ baseMethod && baseMethod !== prop &&
+ (method = this.__base);
+ }
+ return method?
+ method.apply(this, arguments) :
+ undef;
+ });
+ });
- return block;
+ return inherit.self(this, props, staticProps);
},
- declMix : function(block, props, staticProps) {
- convertModHandlersToMethods(props || (props = {}));
- return blocks[block] = inherit(props, staticProps);
- },
+ _name : 'i-bem',
/**
* Processes a block's live properties
@@ -666,18 +608,6 @@ var BEM = inherit(events.Emitter, /** @lends BEM.prototype */ {
return false;
},
- /**
- * Factory method for creating an instance of the block named
- * @param {String|Object} block Block name or description
- * @param {Object} [params] Block parameters
- * @returns {BEM}
- */
- create : function(block, params) {
- typeof block === 'string' && (block = { block : block });
-
- return new blocks[block.block](block.mods, params);
- },
-
/**
* Returns the name of the current block
* @returns {String}
@@ -729,7 +659,63 @@ var BEM = inherit(events.Emitter, /** @lends BEM.prototype */ {
* @param {Object} elem Nested element
* @returns {String|undefined}
*/
- _extractElemNameFrom : function(elem) {},
+ _extractElemNameFrom : function(elem) {}
+});
+
+provide(/** @exports */{
+ /**
+ * Base BEM block
+ * @type Function
+ */
+ Block : Block,
+
+ /**
+ * Storage for block declarations (hash by block name)
+ * @type Object
+ */
+ blocks : blocks,
+
+ /**
+ * Declares block and creates a block class
+ * @param {String} blockName Block name
+ * @param {Function|Array[Function]} [baseBlocks] base block + mixes
+ * @param {Object} [props] Methods
+ * @param {Object} [staticProps] Static methods
+ * @returns {Function} Block class
+ */
+ declBlock : function(blockName, baseBlocks, props, staticProps) {
+ if(!baseBlocks || (typeof baseBlocks === 'object' && !Array.isArray(baseBlocks))) {
+ staticProps === props;
+ props = baseBlocks;
+ baseBlocks = blocks[blockName] || Block;
+ }
+
+ props && convertModHandlersToMethods(props);
+
+ if(staticProps && typeof staticProps.live === 'boolean') {
+ var live = staticProps.live;
+ staticProps.live = function() {
+ return live;
+ };
+ }
+
+ var BaseBlock = Array.isArray(baseBlocks)?
+ baseBlocks[0] :
+ baseBlocks,
+ blockCls;
+
+ blockName === BaseBlock.getName()?
+ // makes a new "live" if the old one was already executed
+ (blockCls = inherit.self(baseBlocks, props, staticProps))._processLive(true) :
+ (blockCls = blocks[blockName] = inherit(baseBlocks, props, staticProps))._name = blockName;
+
+ return blockCls;
+ },
+
+ declMix : function(blockName, props, staticProps) {
+ convertModHandlersToMethods(props || (props = {}));
+ return blocks[blockName] = inherit(props, staticProps);
+ },
/**
* Executes the block init functions
@@ -749,6 +735,4 @@ var BEM = inherit(events.Emitter, /** @lends BEM.prototype */ {
}
});
-provide(BEM);
-
});