definer('Selector', /** @exports Selector */ function(is) {
/**
* Модуль работы с БЭМ-селектором.
*
* @constructor
* @param {string} [selector] БЭМ-селектор
*/
function Selector(selector) {
/**
* БЭМ-селектор.
*
* @private
* @type {string}
*/
this._selector = selector || '';
/**
* Имя блока.
*
* @private
* @type {string}
*/
this._block = '';
/**
* Имя модификатора блока.
*
* @private
* @type {string}
*/
this._modName = '';
/**
* Значение модификатора блока.
*
* @private
* @type {string}
*/
this._modVal = '';
/**
* Имя элемента.
*
* @private
* @type {string}
*/
this._elem = '';
/**
* Имя модификатора элемента.
*
* @private
* @type {string}
*/
this._elemModName = '';
/**
* Значение модификатора элемента.
*
* @private
* @type {string}
*/
this._elemModVal = '';
/**
* Вес селектора.
*
* @private
* @type {number}
*/
this._weight = 0;
this.info();
}
/**
* Разделители имён.
*
* @type {{mod: string, elem: string}}
* @property {string} mod Разделитель блока и модификатора, элемента и модификатора, модификатора и значения
* @property {string} elem Разделитель блока и элемента
*/
Selector.delimiters = {
mod: '_',
elem: '__'
};
/**
* Символ любого значения.
*
* @type {string}
*/
Selector.any = '*';
Selector.prototype = {
/**
* Получить информацию по БЭМ-сущности.
*
* @returns {object}
*/
info: function() {
var blockAndElem = this._getBlockAndElem(),
block = this._getObjectAndMods(blockAndElem.block),
elem = this._getObjectAndMods(blockAndElem.elem);
return {
block: this._block || (this._block = block.object),
modName: this._modName || (this._modName = block.modName),
modVal: this._modVal || (this._modVal = block.modVal),
elem: this._elem || (this._elem = elem.object),
elemModName: this._elemModName || (this._elemModName = elem.modName),
elemModVal: this._elemModVal || (this._elemModVal = elem.modVal)
};
},
/**
* Проверить сущность на блок.
*
* @returns {boolean}
*/
isBlock: function() {
return !!this._block && !this.isElem();
},
/**
* Проверить сущность на элемент.
*
* @returns {boolean}
*/
isElem: function() {
return !!this._elem;
},
/**
* Получить/установить имя блока.
*
* @param {string} [name] Имя блока
* @returns {string|Selector}
*/
block: function(name) {
return this._getSet('_block', name);
},
/**
* Получить/установить модификатор блока.
*
* @param {string} [name] Имя модификатора
* @param {string|boolean} [val] Значение модификатора
* @returns {{name: string, val: string}|Selector}
*/
mod: function(name, val) {
if(name === undefined && val === undefined) return {
name: this.modName(),
val: this.modVal()
};
this.modName(name);
this.modVal(val);
return this;
},
/**
* Получить/установить имя модификатора блока.
*
* @param {string} [name] Имя модификатора
* @returns {string|Selector}
*/
modName: function(name) {
return this._getSet('_modName', name);
},
/**
* Получить/установить значение модификатора блока.
*
* @param {string|boolean} [val] Значение модификатора
* @returns {string|Selector}
*/
modVal: function(val) {
return this._getSet('_modVal', val, true);
},
/**
* Получить/установить элемент.
*
* @param {string} [name] Имя элемента
* @returns {string|Selector}
*/
elem: function(name) {
return this._getSet('_elem', name);
},
/**
* Получить/установить модификатор элемента.
*
* @param {string} [name] Имя модификатора
* @param {string|boolean} [val] Значение модификатора
* @returns {{name: string, val: string}|Selector}
*/
elemMod: function(name, val) {
if(name === undefined && val === undefined) return {
name: this.elemModName(),
val: this.elemModVal()
};
this.elemModName(name);
this.elemModVal(val);
return this;
},
/**
* Получить/установить имя модификатора элемента.
*
* @param {string} [name] Имя модификатора
* @returns {string|Selector}
*/
elemModName: function(name) {
return this._getSet('_elemModName', name);
},
/**
* Получить/установить значение модификатора элемента.
*
* @param {string|boolean} [val] Значение модификатора
* @returns {string|Selector}
*/
elemModVal: function(val) {
return this._getSet('_elemModVal', val, true);
},
/**
* Получить строковое представление БЭМ-сущности.
*
* @returns {string}
*/
toString: function() {
var name = [this._block].concat(this._getMod('_modName', '_modVal'));
if(this._elem) {
name = name.concat(
Selector.delimiters.elem, this._elem,
this._getMod('_elemModName', '_elemModVal')
);
}
return name.join('');
},
/**
* Получить/установить вес селектора.
*
* @param {number} [weight] Вес селектора
* @returns {number|Selector}
*/
weight: function(weight) {
if(weight) {
this._weight = weight;
return this;
}
if(this._weight) {
return this._weight;
}
var weights = {
block: 2,
modName: 2,
modVal: 2,
elem: 10,
elemModName: 6,
elemModVal: 6
};
return [
'block', 'modName', 'modVal',
'elem', 'elemModName', 'elemModVal'
].reduce(function(weight, partName) {
var part = this[partName]();
if(part) {
weight += part === Selector.any ? weights[partName] / 2 : weights[partName];
}
return weight;
}.bind(this), 0);
},
/**
* Получить строковую информацию о блоке и его элементе.
*
* @private
* @returns {{block: string, elem: string}}
*/
_getBlockAndElem: function() {
var blockAndElem = this._selector.split(Selector.delimiters.elem);
return {
block: blockAndElem[0] || '',
elem: blockAndElem[1] || ''
};
},
/**
* Получить информацию об объекте (блоке или элементе) и его модификаторе.
*
* @private
* @param {string} object Строковая информация об объекте
* @returns {{object: string, modName: string, modVal: string}}
*/
_getObjectAndMods: function(object) {
var blockAndMod = object.split(Selector.delimiters.mod);
return {
object: blockAndMod[0],
modName: blockAndMod[1] || '',
modVal: blockAndMod[2] || ''
};
},
/**
* Получить модификатор.
*
* @private
* @param {string} name Имя поля имени модификатора
* @param {string} val Имя поля значения модификатора
* @returns {string[]}
*/
_getMod: function(name, val) {
var mod = [];
name = this[name];
val = this[val];
if(name && val !== false) {
mod.push(Selector.delimiters.mod, name);
if(val && val !== true) {
mod.push(Selector.delimiters.mod, val);
}
}
return mod;
},
/**
* Получить/установить значение полю.
*
* @private
* @param {string} name Имя поля
* @param {string|boolean} [val] Значение
* @param {boolean} [isCanBeBoolean] Значение поля может быть логическим
* @returns {*|Selector}
*/
_getSet: function(name, val, isCanBeBoolean) {
if(val === undefined) return this[name];
this[name] = isCanBeBoolean === true && is.boolean(val) ? val : String(val);
return this;
}
};
return Selector;
});