For 1.0.0 version we assume migration from bem-bl to bem-core.
From now everything should be under the ym modular system. All the dependencies have to be mentioned in the code, using global variables have to be minimized to 0 if possible.
Example
modules.define(
'my-module', // Module name
['module-from-library', 'my-another-module'], // Module's dependencies
function(provide, moduleFromLibrary, myAnotherModule) { // Module declaration, runs when all the dependencies are resolved
//Module providing
provide({
myModuleMethod : function() {}
});
});
TODO: add information about changes in build process (usage of special techs for js and instructions for custom builders).
jQuery is represented with a wrapping module jquery
which uses the jQuery
global object if it is available or loads jQuery additionally.
From now jQuery is used only for operations on DOM such as selecting nodes,
binding listeners to events, getting and setting attribute values and so on.
For other operations there are special modules non-dependable on jQuery.
- the
objects
module to operate on objects (withextend
,isEmpty
andeach
methods) *thefunctions
module to operate on functions (withisFunction
andnoop
methods)
All the jQuery plugins which are not fo DOM operation became modules:
- the
events
module used to be the$.observable
jQuery plugin
It works with events, provides "classes"EventsEmitter
andEvent
- the
inherit
module used to be the$.inherit
plugin
It provides an inherit module with classes. - the
cookie
module used to be the$.cookie
plugin - the
identify
module used to be$.identify
plugin - the
functions__throttle
andfunctions__debounce
used to be the$.throttle
and the$.debounce
plugins
Before:
// block code
$.throttle(...
// block code
After:
module.define('my-module', ['functions__throttle'], function(provide, throttle) {
// module code
throttle(...
// module code
Blocks represented in DOM were declared with BEM.DOM.decl. Now they must use
i-bem__dom
module and extend it.
Before:
BEM.DOM.decl('block', ...);
After:
modules.define('i-bem__dom', function(provide, BEMDOM) {
BEMDOM.decl('block', ...);
provide(BEMDOM);
});
You have to use full notation for the callback for the js
modifier in its
inited
value.
Before:
onSetMod : {
js : function() {
// constructor code
After:
onSetMod : {
js : {
inited : function() {
// constructor code
Instead of destruct
method the destructive callback has to be applyed to the
empty value of js
modifier, which corresponds removing a modifier from a
block.
Also you do not need to call __base
to run a descructor from the basic
i-bem__dom
module.
Before:
destruct : function() {
this.__base.apply(this, arguments);
// destructor code
After:
onSetMod : {
js : {
'' : function() {
// destructor code
Instead of changeThis
method you have to use native bind
.
Before:
// block code
obj.on('event', this.changeThis(this._method);
// block code
After:
obj.on('event', this._method.bind(this));
// or better
obj.on('event', this._method, this);
Use the nextTick
method instead of afterCurrentEvent
. The nextTick
assures
that the block exists at the time of running a callback. If the block is already
destructed, the callback will not be run.
Before:
BEM.DOM.decl('block', {
method : function() {
this.afterCurrentEvent(function() { ...
After:
modules.define('i-bem__dom', function(provide, DOM) {
DOM.decl('block', {
method : function() {
this.nextTick(function() { ...
The callback binded to a DOM element as an event handler is now provided with
the link to this DOM element as $(e.currentTarget)
instead of e.data.domElem
.
Before:
onClick : function(e) {
e.data.domElem.attr(...
After:
onClick : function(e) {
$(e.currentTarget).attr(...
Note: Remember that jQuery is unavailable in global scope and you must use jquery
module for access to it.
Channels are not embedded into BEM any more. Now they are the separate
events__channels
module.
Before:
BEM.DOM.decl('block', {
method : function() {
BEM.channel('channel-name').on(....
After:
modules.define('i-bem__dom', ['events__channels'], function(provide, channels, BEMDOM) {
BEMDOM.decl('block', {
method : function() {
channels('channel-name').on(....
The is no i-system
block any more. Instead you can use special modules:
tick
with the tick event and idle
with the events idle and wakeup.
Before:
BEM.DOM.decl('block', {
method : function() {
BEM.channel('sys').on('tick', ...
After:
modules.define('i-bem__dom', ['tick'], function(provide, tick, BEMDOM) {
BEMDOM.decl('block', {
method : function() {
tick.on('tick', ...
Before:
BEM.DOM.decl('block', {
method : function() {
BEM.channel('sys').on('wakeup', ...
After:
modules.define('i-bem__dom', ['idle'], function(provide, idle, BEMDOM) {
BEMDOM.decl('block', {
method : function() {
idle.on('wakeup', ...
If you have BEM blocks just containing some modules without using BEM methodology in them, you can now rewrite them as modules.
Before:
BEM.decl('i-router', {
route : function() { ... }
});
After:
modules.define('router', function(provide) {
provide({
route : function() { ... }
});
});
If you need BEM blocks (not BEM.DOM blocks) anyway, you can extend the i-bem
module.
Before:
BEM.decl('my-block', { ... });
After:
modules.define('i-bem', function(provide, BEM) {
BEM.decl('my-block', { ... });
provide(BEM);
});
Before:
BEM.DOM.decl('b-spin', {
onSetMod : {
'js' : function() {
this._size = this.getMod('size') || /[\d]+/.exec(this.getMod('theme'))[0];
this._bgProp = 'background-position';
this._posPrefix = '0 -';
if (this.elem('icon').css('background-position-y')) { /* A dirty hack for IE which cannot get a background-position property but packground-position-y only */
this._bgProp = 'background-position-y';
this._posPrefix = '-';
}
this._curFrame = 0;
this.hasMod('progress') && this.channel('sys').on('tick', this._onTick, this);
},
'progress' : {
'yes' : function() {
this.channel('sys').on('tick', this._onTick, this);
},
'' : function() {
this.channel('sys').un('tick', this._onTick, this);
}
}
},
_onTick: function(){
var y = ++this._curFrame * this._size;
(y >= this._size * 36) && (this._curFrame = y = 0);
this.elem('icon').css(this._bgProp, this._posPrefix + y +'px');
},
destruct : function() {
this.channel('sys').un('tick', this._onTick, this);
this.__base.apply(this, arguments);
}
});
After:
modules.define(
'i-bem__dom',
['tick'],
function(provide, tick, BEMDOM) {
var FRAME_COUNT = 36;
BEMDOM.decl('b-spin', {
onSetMod : {
'js' : {
'inited' : function() { // constructor
var hasBackgroundPositionY = !!this.elem('icon').css('background-position-y'));
this._bgProp = hasBackgroundPositionY? 'background-position-y' : 'background-position';
this._posPrefix = hasBackgroundPositionY? '-' : '0 -';
this._curFrame = 0;
this._size = Number(this.getMod('size') || /[\d]+/.exec(this.getMod('theme'))[0]);
this.hasMod('progress') && this._bindToTick();
},
'' : function() { // destructor
this._unbindFromTick();
}
},
'progress' : {
'yes' : function() {
this._bindToTick();
},
'' : function() {
this._unbindFromTick();
}
}
},
_bindToTick : function() {
tick.on('tick', this._onTick, this);
},
_unbindFromTick : function() {
tick.un('tick', this._onTick, this);
},
_onTick : function() {
var offset;
this._curFrame++ >= FRAME_COUNT?
offset = this._curFrame * this._size :
this._curFrame = offset = 0;
this.elem('icon').css(this._bgProp, this._posPrefix + offset + 'px');
}
});
provide(BEMDOM);
});