bem
Version:
1,856 lines (1,464 loc) • 94.6 kB
JavaScript
/* ../../bem-bl/blocks-common/i-jquery/__inherit/i-jquery__inherit.js begin */
/**
* Inheritance plugin
*
* Copyright (c) 2010 Filatov Dmitry (dfilatov@yandex-team.ru)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* @version 1.3.5
*/
(function($) {
var hasIntrospection = (function(){_}).toString().indexOf('_') > -1,
emptyBase = function() {},
objCreate = Object.create || function(ptp) {
var inheritance = function() {};
inheritance.prototype = ptp;
return new inheritance();
},
needCheckProps = true,
testPropObj = { toString : '' };
for(var i in testPropObj) { // fucking ie hasn't toString, valueOf in for
testPropObj.hasOwnProperty(i) && (needCheckProps = false);
}
var specProps = needCheckProps? ['toString', 'valueOf'] : null;
function override(base, result, add) {
var hasSpecProps = false;
if(needCheckProps) {
var addList = [];
$.each(specProps, function() {
add.hasOwnProperty(this) && (hasSpecProps = true) && addList.push({
name : this,
val : add[this]
});
});
if(hasSpecProps) {
$.each(add, function(name) {
addList.push({
name : name,
val : this
});
});
add = addList;
}
}
$.each(add, function(name, prop) {
if(hasSpecProps) {
name = prop.name;
prop = prop.val;
}
if($.isFunction(prop) &&
(!hasIntrospection || prop.toString().indexOf('.__base') > -1)) {
var baseMethod = base[name] || function() {};
result[name] = function() {
var baseSaved = this.__base;
this.__base = baseMethod;
var result = prop.apply(this, arguments);
this.__base = baseSaved;
return result;
};
}
else {
result[name] = prop;
}
});
}
$.inherit = function() {
var args = arguments,
hasBase = $.isFunction(args[0]),
base = hasBase? args[0] : emptyBase,
props = args[hasBase? 1 : 0] || {},
staticProps = args[hasBase? 2 : 1],
result = props.__constructor || (hasBase && base.prototype.__constructor)?
function() {
return this.__constructor.apply(this, arguments);
} : function() {};
if(!hasBase) {
result.prototype = props;
result.prototype.__self = result.prototype.constructor = result;
return $.extend(result, staticProps);
}
$.extend(result, base);
var basePtp = base.prototype,
resultPtp = result.prototype = objCreate(basePtp);
resultPtp.__self = resultPtp.constructor = result;
override(basePtp, resultPtp, props);
staticProps && override(base, result, staticProps);
return result;
};
$.inheritSelf = function(base, props, staticProps) {
var basePtp = base.prototype;
override(basePtp, basePtp, props);
staticProps && override(base, base, staticProps);
return base;
};
})(jQuery);
/* ../../bem-bl/blocks-common/i-jquery/__inherit/i-jquery__inherit.js end */
;
/* ../../bem-bl/blocks-common/i-jquery/__identify/i-jquery__identify.js begin */
/**
* Identify plugin
*
* @version 1.0.0
*/
(function($) {
var counter = 0,
expando = '__' + (+new Date),
get = function() {
return 'uniq' + ++counter;
};
/**
* Makes unique ID
* @param {Object} [obj] Object that needs to be identified
* @param {Boolean} [onlyGet=false] Return a unique value only if it had already been assigned before
* @returns {String} ID
*/
$.identify = function(obj, onlyGet) {
if(!obj) return get();
var key = 'uniqueID' in obj? 'uniqueID' : expando; // Use when possible. native uniqueID for elements in IE
return onlyGet || key in obj?
obj[key] :
obj[key] = get();
};
})(jQuery);
/* ../../bem-bl/blocks-common/i-jquery/__identify/i-jquery__identify.js end */
;
/* ../../bem-bl/blocks-common/i-jquery/__is-empty-object/i-jquery__is-empty-object.js begin */
(function($) {
$.isEmptyObject || ($.isEmptyObject = function(obj) {
for(var i in obj) return false;
return true;
});
})(jQuery);
/* ../../bem-bl/blocks-common/i-jquery/__is-empty-object/i-jquery__is-empty-object.js end */
;
/* ../../bem-bl/blocks-common/i-jquery/__debounce/i-jquery__debounce.js begin */
/**
* Debounce and throttle function's decorator plugin 1.0.6
*
* Copyright (c) 2009 Filatov Dmitry (alpha@zforms.ru)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
(function($) {
$.extend({
debounce : function(fn, timeout, invokeAsap, ctx) {
if(arguments.length == 3 && typeof invokeAsap != 'boolean') {
ctx = invokeAsap;
invokeAsap = false;
}
var timer;
return function() {
var args = arguments;
ctx = ctx || this;
invokeAsap && !timer && fn.apply(ctx, args);
clearTimeout(timer);
timer = setTimeout(function() {
invokeAsap || fn.apply(ctx, args);
timer = null;
}, timeout);
};
},
throttle : function(fn, timeout, ctx) {
var timer, args, needInvoke;
return function() {
args = arguments;
needInvoke = true;
ctx = ctx || this;
timer || (function() {
if(needInvoke) {
fn.apply(ctx, args);
needInvoke = false;
timer = setTimeout(arguments.callee, timeout);
}
else {
timer = null;
}
})();
};
}
});
})(jQuery);
/* ../../bem-bl/blocks-common/i-jquery/__debounce/i-jquery__debounce.js end */
;
/* ../../bem-bl/blocks-common/i-jquery/__observable/i-jquery__observable.js begin */
/**
* Observable plugin
*
* Copyright (c) 2010 Filatov Dmitry (alpha@zforms.ru)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* @version 1.0.0
* @requires $.identify
* @requires $.inherit
*/
(function($) {
var storageExpando = '__' + (+new Date) + 'storage',
getFnId = function(fn, ctx) {
return $.identify(fn) + (ctx? $.identify(ctx) : '');
},
Observable = /** @lends $.observable.prototype */{
/**
* Builds full event name
* @protected
* @param {String} e Event type
* @returns {String}
*/
buildEventName : function(e) {
return e;
},
/**
* Adding event handler
* @param {String} e Event type
* @param {Object} [data] Additional data that the handler gets as e.data
* @param {Function} fn Handler
* @param {Object} [ctx] Handler context
* @returns {$.observable}
*/
on : function(e, data, fn, ctx, _special) {
if(typeof e == 'string') {
if($.isFunction(data)) {
ctx = fn;
fn = data;
data = undefined;
}
var id = getFnId(fn, ctx),
storage = this[storageExpando] || (this[storageExpando] = {}),
eList = e.split(' '),
i = 0,
eStorage;
while(e = eList[i++]) {
e = this.buildEventName(e);
eStorage = storage[e] || (storage[e] = { ids : {}, list : {} });
if(!(id in eStorage.ids)) {
var list = eStorage.list,
item = { fn : fn, data : data, ctx : ctx, special : _special };
if(list.last) {
list.last.next = item;
item.prev = list.last;
} else {
list.first = item;
}
eStorage.ids[id] = list.last = item;
}
}
} else {
var _this = this;
$.each(e, function(e, fn) {
_this.on(e, fn, data, _special);
});
}
return this;
},
onFirst : function(e, data, fn, ctx) {
return this.on(e, data, fn, ctx, { one : true });
},
/**
* Removing event handler(s)
* @param {String} [e] Event type
* @param {Function} [fn] Handler
* @param {Object} [ctx] Handler context
* @returns {$.observable}
*/
un : function(e, fn, ctx) {
if(typeof e == 'string' || typeof e == 'undefined') {
var storage = this[storageExpando];
if(storage) {
if(e) { // if event type was passed
var eList = e.split(' '),
i = 0,
eStorage;
while(e = eList[i++]) {
e = this.buildEventName(e);
if(eStorage = storage[e]) {
if(fn) { // if specific handler was passed
var id = getFnId(fn, ctx),
ids = eStorage.ids;
if(id in ids) {
var list = eStorage.list,
item = ids[id],
prev = item.prev,
next = item.next;
if(prev) {
prev.next = next;
}
else if(item === list.first) {
list.first = next;
}
if(next) {
next.prev = prev;
}
else if(item === list.last) {
list.last = prev;
}
delete ids[id];
}
} else {
delete this[storageExpando][e];
}
}
}
} else {
delete this[storageExpando];
}
}
} else {
var _this = this;
$.each(e, function(e, fn) {
_this.un(e, fn, ctx);
});
}
return this;
},
/**
* Fires event handlers
* @param {String|$.Event} e Event
* @param {Object} [data] Additional data
* @returns {$.observable}
*/
trigger : function(e, data) {
var _this = this,
storage = _this[storageExpando],
rawType;
typeof e === 'string'?
e = $.Event(_this.buildEventName(rawType = e)) :
e.type = _this.buildEventName(rawType = e.type);
e.target || (e.target = _this);
if(storage && (storage = storage[e.type])) {
var item = storage.list.first,
ret;
while(item) {
e.data = item.data;
ret = item.fn.call(item.ctx || _this, e, data);
if(typeof ret !== 'undefined') {
e.result = ret;
if(ret === false) {
e.preventDefault();
e.stopPropagation();
}
}
item.special && item.special.one &&
_this.un(rawType, item.fn, item.ctx);
item = item.next;
}
}
return this;
}
};
$.observable = $.inherit(Observable, Observable);
})(jQuery);
/* ../../bem-bl/blocks-common/i-jquery/__observable/i-jquery__observable.js end */
;
/* ../../bem-bl/blocks-common/i-bem/i-bem.js begin */
/** @requires jquery.inherit */
/** @requires jquery.isEmptyObject */
/** @requires jquery.identify */
/** @requires jquery.observable */
(function($, undefined) {
/**
* Storage for deferred functions
* @private
* @type Array
*/
var afterCurrentEventFns = [],
/**
* Storage for block declarations (hash by block name)
* @private
* @type Object
*/
blocks = {},
/**
* Communication channels
* @static
* @private
* @type Object
*/
channels = {};
/**
* Builds the name of the handler method for setting a modifier
* @static
* @private
* @param {String} elemName Element name
* @param {String} modName Modifier name
* @param {String} modVal Modifier value
* @returns {String}
*/
function buildModFnName(elemName, modName, modVal) {
return (elemName? '__elem_' + elemName : '') +
'__mod' +
(modName? '_' + modName : '') +
(modVal? '_' + modVal : '');
}
/**
* Transforms a hash of modifier handlers to methods
* @static
* @private
* @param {Object} modFns
* @param {Object} props
* @param {String} [elemName]
*/
function modFnsToProps(modFns, props, elemName) {
$.isFunction(modFns)?
(props[buildModFnName(elemName, '*', '*')] = modFns) :
$.each(modFns, function(modName, modFn) {
$.isFunction(modFn)?
(props[buildModFnName(elemName, modName, '*')] = modFn) :
$.each(modFn, function(modVal, modFn) {
props[buildModFnName(elemName, modName, modVal)] = modFn;
});
});
}
function buildCheckMod(modName, modVal) {
return modVal?
Array.isArray(modVal)?
function(block) {
var i = 0, len = modVal.length;
while(i < len)
if(block.hasMod(modName, modVal[i++]))
return true;
return false;
} :
function(block) {
return block.hasMod(modName, modVal);
} :
function(block) {
return block.hasMod(modName);
};
}
/** @namespace */
this.BEM = $.inherit($.observable, /** @lends BEM.prototype */ {
/**
* @class Base block for creating BEM blocks
* @constructs
* @private
* @param {Object} mods Block modifiers
* @param {Object} params Block parameters
* @param {Boolean} [initImmediately=true]
*/
__constructor : function(mods, params, initImmediately) {
var _this = this;
/**
* Cache of block modifiers
* @private
* @type Object
*/
_this._modCache = mods || {};
/**
* Current modifiers in the stack
* @private
* @type Object
*/
_this._processingMods = {};
/**
* The block's parameters, taking into account the defaults
* @protected
* @type Object
*/
_this._params = params; // это нужно для правильной сборки параметров у блока из нескольких нод
_this.params = null;
initImmediately !== false?
_this._init() :
_this.afterCurrentEvent(function() {
_this._init();
});
},
/**
* Initializes the block
* @private
*/
_init : function() {
if(!this._initing && !this.hasMod('js', 'inited')) {
this._initing = true;
if(!this.params) {
this.params = $.extend(this.getDefaultParams(), this._params);
delete this._params;
}
this.setMod('js', 'inited');
delete this._initing;
this.hasMod('js', 'inited') && this.trigger('init');
}
return this;
},
/**
* Changes the context of the function being passed
* @protected
* @param {Function} fn
* @param {Object} [ctx=this] Context
* @returns {Function} Function with a modified context
*/
changeThis : function(fn, ctx) {
return fn.bind(ctx || this);
},
/**
* Executes the function in the context of the block, after the "current event"
* @protected
* @param {Function} fn
* @param {Object} [ctx] Context
*/
afterCurrentEvent : function(fn, ctx) {
this.__self.afterCurrentEvent(this.changeThis(fn, ctx));
},
/**
* Executes the block's event handlers and live event handlers
* @protected
* @param {String} e Event name
* @param {Object} [data] Additional information
* @returns {BEM}
*/
trigger : function(e, data) {
this
.__base(e = this.buildEvent(e), data)
.__self.trigger(e, data);
return this;
},
buildEvent : function(e) {
typeof e == 'string' && (e = $.Event(e));
e.block = this;
return e;
},
/**
* Checks whether a block or nested element has a modifier
* @protected
* @param {Object} [elem] Nested element
* @param {String} modName Modifier name
* @param {String} [modVal] Modifier value
* @returns {Boolean}
*/
hasMod : function(elem, modName, modVal) {
var len = arguments.length,
invert = false;
if(len == 1) {
modVal = '';
modName = elem;
elem = undefined;
invert = true;
}
else if(len == 2) {
if(typeof elem == 'string') {
modVal = modName;
modName = elem;
elem = undefined;
}
else {
modVal = '';
invert = true;
}
}
var res = this.getMod(elem, modName) === modVal;
return invert? !res : res;
},
/**
* Returns the value of the modifier of the block/nested element
* @protected
* @param {Object} [elem] Nested element
* @param {String} modName Modifier name
* @returns {String} Modifier value
*/
getMod : function(elem, modName) {
var type = typeof elem;
if(type === 'string' || type === 'undefined') { // elem either omitted or undefined
modName = elem || modName;
var modCache = this._modCache;
return modName in modCache?
modCache[modName] :
modCache[modName] = this._extractModVal(modName);
}
return this._getElemMod(modName, elem);
},
/**
* Returns the value of the modifier of the nested element
* @private
* @param {String} modName Modifier name
* @param {Object} elem Nested element
* @param {Object} [elem] Nested element name
* @returns {String} Modifier value
*/
_getElemMod : function(modName, elem, elemName) {
return this._extractModVal(modName, elem, elemName);
},
/**
* Returns values of modifiers of the block/nested element
* @protected
* @param {Object} [elem] Nested element
* @param {String} [modName1, ..., modNameN] Modifier names
* @returns {Object} Hash of modifier values
*/
getMods : function(elem) {
var hasElem = elem && typeof elem != 'string',
_this = this,
modNames = [].slice.call(arguments, hasElem? 1 : 0),
res = _this._extractMods(modNames, hasElem? elem : undefined);
if(!hasElem) { // caching
modNames.length?
modNames.forEach(function(name) {
_this._modCache[name] = res[name];
}):
_this._modCache = res;
}
return res;
},
/**
* Sets the modifier for a block/nested element
* @protected
* @param {Object} [elem] Nested element
* @param {String} modName Modifier name
* @param {String} modVal Modifier value
* @returns {BEM}
*/
setMod : function(elem, modName, modVal) {
if(typeof modVal == 'undefined') {
modVal = modName;
modName = elem;
elem = undefined;
}
var _this = this;
if(!elem || elem[0]) {
var modId = (elem && elem[0]? $.identify(elem[0]) : '') + '_' + modName;
if(this._processingMods[modId]) return _this;
var elemName,
curModVal = elem?
_this._getElemMod(modName, elem, elemName = _this.__self._extractElemNameFrom(elem)) :
_this.getMod(modName);
if(curModVal === modVal) return _this;
this._processingMods[modId] = true;
var needSetMod = true,
modFnParams = [modName, modVal, curModVal];
elem && modFnParams.unshift(elem);
[['*', '*'], [modName, '*'], [modName, modVal]].forEach(function(mod) {
needSetMod = _this._callModFn(elemName, mod[0], mod[1], modFnParams) !== false && needSetMod;
});
!elem && needSetMod && (_this._modCache[modName] = modVal);
needSetMod && _this._afterSetMod(modName, modVal, curModVal, elem, elemName);
delete this._processingMods[modId];
}
return _this;
},
/**
* Function after successfully changing the modifier of the block/nested element
* @protected
* @param {String} modName Modifier name
* @param {String} modVal Modifier value
* @param {String} oldModVal Old modifier value
* @param {Object} [elem] Nested element
* @param {String} [elemName] Element name
*/
_afterSetMod : function(modName, modVal, oldModVal, elem, elemName) {},
/**
* Sets a modifier for a block/nested element, depending on conditions.
* If the condition parameter is passed: when true, modVal1 is set; when false, modVal2 is set.
* If the condition parameter is not passed: modVal1 is set if modVal2 was set, or vice versa.
* @protected
* @param {Object} [elem] Nested element
* @param {String} modName Modifier name
* @param {String} modVal1 First modifier value
* @param {String} [modVal2] Second modifier value
* @param {Boolean} [condition] Condition
* @returns {BEM}
*/
toggleMod : function(elem, modName, modVal1, modVal2, condition) {
if(typeof elem == 'string') { // if this is a block
condition = modVal2;
modVal2 = modVal1;
modVal1 = modName;
modName = elem;
elem = undefined;
}
if(typeof modVal2 == 'undefined') {
modVal2 = '';
} else if(typeof modVal2 == 'boolean') {
condition = modVal2;
modVal2 = '';
}
var modVal = this.getMod(elem, modName);
(modVal == modVal1 || modVal == modVal2) &&
this.setMod(
elem,
modName,
typeof condition === 'boolean'?
(condition? modVal1 : modVal2) :
this.hasMod(elem, modName, modVal1)? modVal2 : modVal1);
return this;
},
/**
* Removes a modifier from a block/nested element
* @protected
* @param {Object} [elem] Nested element
* @param {String} modName Modifier name
* @returns {BEM}
*/
delMod : function(elem, modName) {
if(!modName) {
modName = elem;
elem = undefined;
}
return this.setMod(elem, modName, '');
},
/**
* Executes handlers for setting modifiers
* @private
* @param {String} elemName Element name
* @param {String} modName Modifier name
* @param {String} modVal Modifier value
* @param {Array} modFnParams Handler parameters
*/
_callModFn : function(elemName, modName, modVal, modFnParams) {
var modFnName = buildModFnName(elemName, modName, modVal);
return this[modFnName]?
this[modFnName].apply(this, modFnParams) :
undefined;
},
/**
* Retrieves the value of the modifier
* @private
* @param {String} modName Modifier name
* @param {Object} [elem] Element
* @returns {String} Modifier value
*/
_extractModVal : function(modName, elem) {
return '';
},
/**
* Retrieves name/value for a list of modifiers
* @private
* @param {Array} modNames Names of modifiers
* @param {Object} [elem] Element
* @returns {Object} Hash of modifier values by name
*/
_extractMods : function(modNames, elem) {
return {};
},
/**
* Returns a named communication channel
* @param {String} [id='default'] Channel ID
* @param {Boolean} [drop=false] Destroy the channel
* @returns {$.observable|undefined} Communication channel
*/
channel : function(id, drop) {
return this.__self.channel(id, drop);
},
/**
* Returns a block's default parameters
* @returns {Object}
*/
getDefaultParams : function() {
return {};
},
/**
* Helper for cleaning up block properties
* @param {Object} [obj=this]
*/
del : function(obj) {
var args = [].slice.call(arguments);
typeof obj == 'string' && args.unshift(this);
this.__self.del.apply(this.__self, args);
return this;
},
/**
* Deletes a block
*/
destruct : function() {}
}, /** @lends BEM */{
_name : 'i-bem',
/**
* Storage for block declarations (hash by block name)
* @static
* @protected
* @type Object
*/
blocks : blocks,
/**
* Declares blocks and creates a block class
* @static
* @protected
* @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 {String} [decl.modName] Modifier name
* @param {String} [decl.modVal] Modifier value
* @param {Object} [props] Methods
* @param {Object} [staticProps] Static methods
*/
decl : function(decl, props, staticProps) {
if(typeof decl == 'string')
decl = { block : decl };
else if(decl.name) {
decl.block = decl.name;
}
if(decl.baseBlock && !blocks[decl.baseBlock])
throw('baseBlock "' + decl.baseBlock + '" for "' + decl.block + '" is undefined');
props || (props = {});
if(props.onSetMod) {
modFnsToProps(props.onSetMod, props);
delete props.onSetMod;
}
if(props.onElemSetMod) {
$.each(props.onElemSetMod, function(elemName, modFns) {
modFnsToProps(modFns, props, elemName);
});
delete props.onElemSetMod;
}
var baseBlock = blocks[decl.baseBlock || decl.block] || this;
if(decl.modName) {
var checkMod = buildCheckMod(decl.modName, decl.modVal);
$.each(props, function(name, prop) {
$.isFunction(prop) &&
(props[name] = function() {
var method;
if(checkMod(this)) {
method = prop;
} else {
var baseMethod = baseBlock.prototype[name];
baseMethod && baseMethod !== props[name] &&
(method = this.__base);
}
return method?
method.apply(this, arguments) :
undefined;
});
});
}
if(staticProps && typeof staticProps.live === 'boolean') {
var live = staticProps.live;
staticProps.live = function() {
return live;
};
}
var block;
decl.block == baseBlock._name?
// makes a new "live" if the old one was already executed
(block = $.inheritSelf(baseBlock, props, staticProps))._processLive(true) :
(block = blocks[decl.block] = $.inherit(baseBlock, props, staticProps))._name = decl.block;
return block;
},
/**
* Processes a block's live properties
* @private
* @param {Boolean} [heedLive=false] Whether to take into account that the block already processed its live properties
* @returns {Boolean} Whether the block is a live block
*/
_processLive : function(heedLive) {
return false;
},
/**
* Factory method for creating an instance of the block named
* @static
* @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
* @static
* @protected
* @returns {String}
*/
getName : function() {
return this._name;
},
/**
* Retrieves the name of an element nested in a block
* @static
* @private
* @param {Object} elem Nested element
* @returns {String|undefined}
*/
_extractElemNameFrom : function(elem) {},
/**
* Adds a function to the queue for executing after the "current event"
* @static
* @protected
* @param {Function} fn
* @param {Object} ctx
*/
afterCurrentEvent : function(fn, ctx) {
afterCurrentEventFns.push({ fn : fn, ctx : ctx }) == 1 &&
setTimeout(this._runAfterCurrentEventFns, 0);
},
/**
* Executes the queue
* @private
*/
_runAfterCurrentEventFns : function() {
var fnsLen = afterCurrentEventFns.length;
if(fnsLen) {
var fnObj,
fnsCopy = afterCurrentEventFns.splice(0, fnsLen);
while(fnObj = fnsCopy.shift()) fnObj.fn.call(fnObj.ctx || this);
}
},
/**
* Changes the context of the function being passed
* @protected
* @param {Function} fn
* @param {Object} ctx Context
* @returns {Function} Function with a modified context
*/
changeThis : function(fn, ctx) {
return fn.bind(ctx || this);
},
/**
* Helper for cleaning out properties
* @param {Object} [obj=this]
*/
del : function(obj) {
var delInThis = typeof obj == 'string',
i = delInThis? 0 : 1,
len = arguments.length;
delInThis && (obj = this);
while(i < len) delete obj[arguments[i++]];
return this;
},
/**
* Returns/destroys a named communication channel
* @param {String} [id='default'] Channel ID
* @param {Boolean} [drop=false] Destroy the channel
* @returns {$.observable|undefined} Communication channel
*/
channel : function(id, drop) {
if(typeof id == 'boolean') {
drop = id;
id = undefined;
}
id || (id = 'default');
if(drop) {
if(channels[id]) {
channels[id].un();
delete channels[id];
}
return;
}
return channels[id] || (channels[id] = new $.observable());
}
});
})(jQuery);
/* ../../bem-bl/blocks-common/i-bem/i-bem.js end */
;
/* ../../bem-bl/blocks-common/i-ecma/__object/i-ecma__object.js begin */
(function() {
/**
* Возвращает массив свойств объекта
* @param {Object} obj объект
* @returns {Array}
*/
Object.keys || (Object.keys = function(obj) {
var res = [];
for(var i in obj) obj.hasOwnProperty(i) &&
res.push(i);
return res;
});
})();
/* ../../bem-bl/blocks-common/i-ecma/__object/i-ecma__object.js end */
;
/* ../../bem-bl/blocks-common/i-ecma/__array/i-ecma__array.js begin */
(function() {
var ptp = Array.prototype,
toStr = Object.prototype.toString,
methods = {
/**
* Finds the index of an element in an array
* @param {Object} item
* @param {Number} [fromIdx] Starting from index (length - 1 - fromIdx, if fromIdx < 0)
* @returns {Number} Element index or -1, if not found
*/
indexOf : function(item, fromIdx) {
fromIdx = +(fromIdx || 0);
var t = this, len = t.length;
if(len > 0 && fromIdx < len) {
fromIdx = fromIdx < 0? Math.ceil(fromIdx) : Math.floor(fromIdx);
fromIdx < -len && (fromIdx = 0);
fromIdx < 0 && (fromIdx = fromIdx + len);
while(fromIdx < len) {
if(fromIdx in t && t[fromIdx] === item)
return fromIdx;
++fromIdx;
}
}
return -1;
},
/**
* Calls the callback for each element
* @param {Function} callback Called for each element
* @param {Object} [ctx=null] Callback context
*/
forEach : function(callback, ctx) {
var i = -1, t = this, len = t.length;
while(++i < len) i in t &&
(ctx? callback.call(ctx, t[i], i, t) : callback(t[i], i, t));
},
/**
* Creates array B from array A so that B[i] = callback(A[i])
* @param {Function} callback Called for each element
* @param {Object} [ctx=null] Callback context
* @returns {Array}
*/
map : function(callback, ctx) {
var i = -1, t = this, len = t.length,
res = new Array(len);
while(++i < len) i in t &&
(res[i] = ctx? callback.call(ctx, t[i], i, t) : callback(t[i], i, t));
return res;
},
/**
* Creates an array containing only the elements from the source array that the callback returns true for.
* @param {Function} callback Called for each element
* @param {Object} [ctx] Callback context
* @returns {Array}
*/
filter : function(callback, ctx) {
var i = -1, t = this, len = t.length,
res = [];
while(++i < len) i in t &&
(ctx? callback.call(ctx, t[i], i, t) : callback(t[i], i, t)) && res.push(t[i]);
return res;
},
/**
* Wraps the array using an accumulator
* @param {Function} callback Called for each element
* @param {Object} [initialVal] Initial value of the accumulator
* @returns {Object} Accumulator
*/
reduce : function(callback, initialVal) {
var i = -1, t = this, len = t.length,
res;
if(arguments.length < 2) {
while(++i < len) {
if(i in t) {
res = t[i];
break;
}
}
}
else {
res = initialVal;
}
while(++i < len) i in t &&
(res = callback(res, t[i], i, t));
return res;
},
/**
* Checks whether at least one element in the array meets the condition in the callback
* @param {Function} callback
* @param {Object} [ctx=this] Callback context
* @returns {Boolean}
*/
some : function(callback, ctx) {
var i = -1, t = this, len = t.length;
while(++i < len)
if(i in t && (ctx ? callback.call(ctx, t[i], i, t) : callback(t[i], i, t)))
return true;
return false;
},
/**
* Checks whether every element in the array meets the condition in the callback
* @param {Function} callback
* @param {Object} [ctx=this] Context of the callback call
* @returns {Boolean}
*/
every : function(callback, ctx) {
var i = -1, t = this, len = t.length;
while(++i < len)
if(i in t && !(ctx ? callback.call(ctx, t[i], i, t) : callback(t[i], i, t)))
return false;
return true;
}
};
for(var name in methods)
ptp[name] || (ptp[name] = methods[name]);
Array.isArray || (Array.isArray = function(obj) {
return toStr.call(obj) === '[object Array]';
});
})();
/* ../../bem-bl/blocks-common/i-ecma/__array/i-ecma__array.js end */
;
/* ../../bem-bl/blocks-common/i-ecma/__function/i-ecma__function.js begin */
(function() {
var slice = Array.prototype.slice;
Function.prototype.bind || (Function.prototype.bind = function(ctx) {
var fn = this,
args = slice.call(arguments, 1);
return function () {
return fn.apply(ctx, args.concat(slice.call(arguments)));
}
});
})();
/* ../../bem-bl/blocks-common/i-ecma/__function/i-ecma__function.js end */
;
/* ../../bem-bl/blocks-common/i-bem/__internal/i-bem__internal.js begin */
/** @fileOverview Module for internal BEM helpers */
/** @requires BEM */
(function(BEM, $, undefined) {
/**
* Separator for modifiers and their values
* @const
* @type String
*/
var MOD_DELIM = '_',
/**
* Separator between names of a block and a nested element
* @const
* @type String
*/
ELEM_DELIM = '__',
/**
* Pattern for acceptable element and modifier names
* @const
* @type String
*/
NAME_PATTERN = '[a-zA-Z0-9-]+';
function buildModPostfix(modName, modVal, buffer) {
buffer.push(MOD_DELIM, modName, MOD_DELIM, modVal);
}
function buildBlockClass(name, modName, modVal, buffer) {
buffer.push(name);
modVal && buildModPostfix(modName, modVal, buffer);
}
function buildElemClass(block, name, modName, modVal, buffer) {
buildBlockClass(block, undefined, undefined, buffer);
buffer.push(ELEM_DELIM, name);
modVal && buildModPostfix(modName, modVal, buffer);
}
BEM.INTERNAL = {
NAME_PATTERN : NAME_PATTERN,
MOD_DELIM : MOD_DELIM,
ELEM_DELIM : ELEM_DELIM,
buildModPostfix : function(modName, modVal, buffer) {
var res = buffer || [];
buildModPostfix(modName, modVal, res);
return buffer? res : res.join('');
},
/**
* Builds the class of a block or element with a modifier
* @private
* @param {String} block Block name
* @param {String} [elem] Element name
* @param {String} [modName] Modifier name
* @param {String} [modVal] Modifier value
* @param {Array} [buffer] Buffer
* @returns {String|Array} Class or buffer string (depending on whether the buffer parameter is present)
*/
buildClass : function(block, elem, modName, modVal, buffer) {
var typeOf = typeof modName;
if(typeOf == 'string') {
if(typeof modVal != 'string') {
buffer = modVal;
modVal = modName;
modName = elem;
elem = undefined;
}
} else if(typeOf != 'undefined') {
buffer = modName;
modName = undefined;
} else if(elem && typeof elem != 'string') {
buffer = elem;
elem = undefined;
}
if(!(elem || modName || buffer)) { // оптимизация для самого простого случая
return block;
}
var res = buffer || [];
elem?
buildElemClass(block, elem, modName, modVal, res) :
buildBlockClass(block, modName, modVal, res);
return buffer? res : res.join('');
},
/**
* Builds full classes for a buffer or element with modifiers
* @private
* @param {String} block Block name
* @param {String} [elem] Element name
* @param {Object} [mods] Modifiers
* @param {Array} [buffer] Buffer
* @returns {String|Array} Class or buffer string (depending on whether the buffer parameter is present)
*/
buildClasses : function(block, elem, mods, buffer) {
if(elem && typeof elem != 'string') {
buffer = mods;
mods = elem;
elem = undefined;
}
var res = buffer || [];
elem?
buildElemClass(block, elem, undefined, undefined, res) :
buildBlockClass(block, undefined, undefined, res);
mods && $.each(mods, function(modName, modVal) {
if(modVal) {
res.push(' ');
elem?
buildElemClass(block, elem, modName, modVal, res) :
buildBlockClass(block, modName, modVal, res);
}
});
return buffer? res : res.join('');
/*var typeOf = typeof elem;
if(typeOf != 'string' && typeOf != 'undefined') {
buffer = mods;
mods = elem;
elem = undefined;
}
if($.isArray(mods)) {
buffer = mods;
mods = undefined;
}
var res = buffer || [];
buildClasses(block, elem, mods, res);
return buffer? res : res.join('');*/
}
}
})(BEM, jQuery);
/* ../../bem-bl/blocks-common/i-bem/__internal/i-bem__internal.js end */
;
/* ../../bem-bl/blocks-common/i-bem/__dom/i-bem__dom.js begin */
/** @requires BEM */
/** @requires BEM.INTERNAL */
(function(BEM, $, undefined) {
var win = $(window),
doc = $(document),
/**
* Storage for DOM elements by unique key
* @private
* @type Object
*/
uniqIdToDomElems = {},
/**
* Storage for blocks by unique key
* @static
* @private
* @type Object
*/
uniqIdToBlock = {},
/**
* Storage for block parameters
* @private
* @type Object
*/
domElemToParams = {},
/**
* Storage for liveCtx event handlers
* @private
* @type Object
*/
liveEventCtxStorage = {},
/**
* Storage for liveClass event handlers
* @private
* @type Object
*/
liveClassEventStorage = {},
blocks = BEM.blocks,
INTERNAL = BEM.INTERNAL,
NAME_PATTERN = INTERNAL.NAME_PATTERN,
MOD_DELIM = INTERNAL.MOD_DELIM,
ELEM_DELIM = INTERNAL.ELEM_DELIM,
buildModPostfix = INTERNAL.buildModPostfix,
buildClass = INTERNAL.buildClass;
/**
* Initializes blocks on a DOM element
* @private
* @param {jQuery} domElem DOM element
* @param {String} uniqInitId ID of the "initialization wave"
*/
function init(domElem, uniqInitId) {
var domNode = domElem[0];
$.each(getParams(domNode), function(blockName, params) {
processParams(params, domNode, blockName, uniqInitId);
var block = uniqIdToBlock[params.uniqId];
if(block) {
if(block.domElem.index(domNode) < 0) {
block.domElem = block.domElem.add(domElem);
$.extend(block._params, params);
}
} else {
initBlock(blockName, domElem, params);
}
});
}
/**
* Initializes a specific block on a DOM element, or returns the existing block if it was already created
* @private
* @param {String} blockName Block name
* @param {jQuery} domElem DOM element
* @param {Object} [params] Initialization parameters
* @param {Boolean} [forceLive] Force live initialization
* @param {Function} [callback] Handler to call after complete initialization
*/
function initBlock(blockName, domElem, params, forceLive, callback) {
if(typeof params == 'boolean') {
callback = forceLive;
forceLive = params;
params = undefined;
}
var domNode = domElem[0];
params = processParams(params || getParams(domNode)[blockName], domNode, blockName);
var uniqId = params.uniqId;
if(uniqIdToBlock[uniqId]) {
return uniqIdToBlock[uniqId]._init();
}
uniqIdToDomElems[uniqId] = uniqIdToDomElems[uniqId]?
uniqIdToDomElems[uniqId].add(domElem) :
domElem;
var parentDomNode = domNode.parentNode;
if(!parentDomNode || parentDomNode.nodeType === 11) { // jquery doesn't unique disconnected node
$.unique(uniqIdToDomElems[uniqId]);
}
var blockClass = blocks[blockName] || DOM.decl(blockName, {}, { live : true });
if(!(blockClass._liveInitable = !!blockClass._processLive()) || forceLive || params.live === false) {
var block = new blockClass(uniqIdToDomElems[uniqId], params, !!forceLive);
delete uniqIdToDomElems[uniqId];
callback && callback.apply(block, Array.prototype.slice.call(arguments, 4));
return block;
}
}
/**
* Processes and adds necessary block parameters
* @private
* @param {Object} params Initialization parameters
* @param {HTMLElement} domNode DOM node
* @param {String} blockName Block name
* @param {String} [uniqInitId] ID of the "initialization wave"
*/
function processParams(params, domNode, blockName, uniqInitId) {
(params || (params = {})).uniqId ||
(params.uniqId = (params.id? blockName + '-id-' + params.id : $.identify()) + (uniqInitId || $.identify()));
var domUniqId = $.identify(domNode),
domParams = domElemToParams[domUniqId] || (domElemToParams[domUniqId] = {});
domParams[blockName] || (domParams[blockName] = params);
return params;
}
/**
* Helper for searching for a DOM element using a selector inside the context, including the context itself
* @private
* @param {jQuery} ctx Context
* @param {String} selector CSS selector
* @param {Boolean} [excludeSelf=false] Exclude context from search
* @returns {jQuery}
*/
function findDomElem(ctx, selector, excludeSelf) {
var res = ctx.find(selector);
return excludeSelf?
res :
res.add(ctx.filter(selector));
}
/**
* Returns parameters of a block's DOM element
* @private
* @param {HTMLElement} domNode DOM node
* @returns {Object}
*/
function getParams(domNode) {
var uniqId = $.identify(domNode);
return domElemToParams[uniqId] ||
(domElemToParams[uniqId] = extractParams(domNode));
}
/**
* Retrieves block parameters from a DOM element
* @private
* @param {HTMLElement} domNode DOM node
* @returns {Object}
*/
function extractParams(domNode) {
var fn = domNode.onclick || domNode.ondblclick;
if(!fn && domNode.tagName.toLowerCase() == 'body') { // LEGO-2027 in FF onclick doesn't work on body
var elem = $(domNode),
attr = elem.attr('onclick') || elem.attr('ondblclick');
attr && (fn = Function(attr));
}
return fn? fn() : {};
}
/**
* Cleans up all the BEM storages associated with a DOM node
* @private
* @param {HTMLElement} domNode DOM node
*/
function cleanupDomNode(domNode) {
delete domElemToParams[$.identify(domNode)];
}
/**
* Uncople DOM node from the block. If this is the last node, then destroys the block.
* @private
* @param {BEM.DOM} block block
* @param {HTMLElement} domNode DOM node
*/
function removeDomNodeFromBlock(block, domNode) {
block.domElem.length === 1?
block.destruct(true) :
block.domElem = block.domElem.not(domNode);
}
/**
* Returns a DOM node for calculating the window size in IE
* @returns {HTMLElement}
*/
function getClientNode() {
return doc[0][$.support.boxModel? 'documentElement' : 'body'];
}
/**
* Returns a block on a DOM element and initializes it if necessary
* @param {String} blockName Block name
* @param {Object} params Block parameters
* @returns {BEM}
*/
$.fn.bem = function(blockName, params) {
return initBlock(blockName, this, params, true);
};
/**
* @namespace
* @name BEM.DOM
*/
var DOM = BEM.DOM = BEM.decl('i-bem__dom',/** @lends BEM.DOM.prototype */{
/**
* @class Base block for creating BEM blocks that have DOM representation
* @constructs
* @private
* @param {jQuery} domElem DOM element that the block is created on
* @param {Object} params Block parameters
* @param {Boolean} [initImmediately=true]
*/
__constructor : function(domElem, params, initImmediately) {
var _this = this;
/**
* Block's DOM elements
* @protected
* @type jQuery
*/
_this.domElem = domElem;
/**
* Cache for names of events on DOM elements
* @private
* @type Object
*/
_this._eventNameCache = {};
/**
* Cache for elements
* @private
* @type Object
*/
_this._elemCache = {};
/**
* Unique block ID
* @private
* @type String
*/
uniqIdToBlock[_this._uniqId = params.uniqId || $.identify(_this)] = _this;
/**
* Flag for whether it's necessary to unbind from the document and window when destroying the block
* @private
* @type Boolean
*/
_this._needSpecialUnbind = false;
_this.__base(null, params, initImmediately);
},
/**
* Finds blocks inside the current block or its elements (including context)
* @protected
* @param {String|jQuery} [elem] Block element
* @param {String|Object} block Name or description (block,modName,modVal) of the block to find
* @returns {BEM[]}
*/