Repository: bem/bh
Pull request #149 (2015-06-03 14:09:50)

add custom short tags
Уж очень хочется кастомные теги

coveralls: 
[![Coverage Status](https://coveralls.io/builds/2718914/badge)](https://coveralls.io/builds/2718914)

Coverage decreased (-0.25%) to 99.75% when pulling **0356b904bb4d6a2a489978c4013facc5b0a9c7d9 on theprotein:feature/tags-as-options** into **b70bfea37e1da7e4b25f53fd039ffb3a0b4ab367 on bem:master**.

mishanga: Можешь привести пример?
Ну и большая просьба добавить тесты и документацию про это.
verybigman: Ex: `<g><rect /></g>`

Тестов добавлю!
Ср, 3 июня 2015 г. в 18:28, Mikhail Troshev <notifications@github.com>:

> Можешь привести пример?
> Ну и большая просьба добавить тесты и документацию про это.
>
> —
> Reply to this email directly or view it on GitHub
> <https://github.com/bem/bh/pull/149#issuecomment-108483657>.
>

sladex: @verybigman только в pr ты перезаписываешь объект по которому проверяется принадлежность к самозакрывающимся тегам. По идее надо добавлять опциональные теги к имеющемуся набору?
verybigman: Была мысль про extend да, но быстро сделать не получится. Надо дотаскивать this.extend
sladex: В любом случае так задавать свои теги издевательство:
```js
shortTags: { rect: 1, path: 1 }
```
В таком виде они хранятся из соображений [производительности](http://jsperf.com/indexof-vs-object).
Разумнее списком: `['rect', 'path']`. И копировать его обычным `for`-ом в `setOptions`.
mishanga: +1 к @sladex 
verybigman: Тогда можно и дефолтные так хранить, а новые пушить в setOptions
Чт, 4 июня 2015 г. в 15:59, Mikhail Troshev <notifications@github.com>:

> +1 к @sladex <https://github.com/sladex>
>
> —
> Reply to this email directly or view it on GitHub
> <https://github.com/bem/bh/pull/149#issuecomment-108886828>.
>

zxqfox: > Тогда можно и дефолтные так хранить, а новые пушить в setOptions

@verybigman Ну тут речь про хранить против передавать. Передавать в виде массива, а внутри хранить хешом.
verybigman: Какой сакральный смысл хранить хешом и не создавать его в опциях? Это
упростит экстенд в разы
Чт, 4 июня 2015 г. в 20:39, Alexej Yaroshevich <notifications@github.com>:

> Тогда можно и дефолтные так хранить, а новые пушить в setOptions
>
> @verybigman <https://github.com/verybigman> Ну тут речь про хранить
> против передавать. Передавать в виде массива, а внутри хранить хешом.
>
> —
> Reply to this email directly or view it on GitHub
> <https://github.com/bem/bh/pull/149#issuecomment-108986902>.
>

verybigman: Айда я сделаю нормальный экстенд?)
sladex: @verybigman Смысл не в хранении, а в том [как и где](https://github.com/bem/bh/blob/1e2678cc4b13e6dec3b6794a68034b944d191c9b/lib/bh.js#L1079) это потом используется внутри bh.
Проверка по хэшу на порядки быстрее, чем поиск по массиву через `Array.prototype.indexOf`. Ссылку на сравнение производительности я уже приводил выше. В принципе, если делать копирование кастомных тегов, то можно и дефолтные в коде bh привести в виде массива:
```
['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr']
```
Потом кастомные теги через `Array.prototype.concat` удобно приклеивать будет.
А перебор по массиву и копирование в объект делать придется и так, и так...
verybigman: @sladex все так. Храним дефолтные в массиве. А в месте склеивания превращаем в объект. Производительность никак не страдает. Поиск так и будет по объекту.
sladex: @verybigman sounds good.
coveralls: 
[![Coverage Status](https://coveralls.io/builds/2739627/badge)](https://coveralls.io/builds/2739627)

Coverage remained the same at 100.0% when pulling **4633e27b273ed5c19d5926dc01dc01a7573ab613 on theprotein:feature/tags-as-options** into **b70bfea37e1da7e4b25f53fd039ffb3a0b4ab367 on bem:master**.

mishanga: @verybigman по реализации ок, по тестам отписал. И добавь еще в документацию, пожалуйста.
verybigman: Ок
Пт, 5 июня 2015 г. в 22:43, Mikhail Troshev <notifications@github.com>:

> @verybigman <https://github.com/verybigman> по реализации ок, по тестам
> отписал. И добавь еще в документацию, пожалуйста.
>
> —
> Reply to this email directly or view it on GitHub
> <https://github.com/bem/bh/pull/149#issuecomment-109417912>.
>

verybigman: Не в одну строку правда, но кажется так понятней, да и все выше тесты так написаны.
coveralls: 
[![Coverage Status](https://coveralls.io/builds/2742811/badge)](https://coveralls.io/builds/2742811)

Coverage remained the same at 100.0% when pulling **955508538e9b74e4e488bf618d06358a3bb17c1b on theprotein:feature/tags-as-options** into **b70bfea37e1da7e4b25f53fd039ffb3a0b4ab367 on bem:master**.

mishanga: @verybigman поправишь остальное?
verybigman: Да, конечно. Я в отпуске просто:) поправлю сегодня завтра
Пн, 8 июня 2015 г. в 13:52, Mikhail Troshev <notifications@github.com>:

> @verybigman <https://github.com/verybigman> поправишь остальное?
>
> —
> Reply to this email directly or view it on GitHub
> <https://github.com/bem/bh/pull/149#issuecomment-109948963>.
>

mishanga: @verybigman конфликт при ребейзе. Поправишь?
verybigman: Поправлю. Я в отпуске до 23го)

Patch:
diff --git a/README.md b/README.md
index 1551dbc..0c9c6bf 100644
--- a/README.md
+++ b/README.md
@@ -224,6 +224,14 @@ bh.apply({ block: 'button', mods: { disabled: true } });
 <div class="button button--disabled"></div>
 ```
 
+### shortTags
+
+`shortTags` extend default short tag list.
+
+```javascript
+bh.setOptions({ shortTags: ['rect'] });
+```
+
 ## Additional examples
 
 For example, if you want to set `state` modifier with `closed` value for all blocks do the following:
diff --git a/README.ru.md b/README.ru.md
index e3e5e55..043a1ed 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -223,6 +223,14 @@ bh.apply({ block: 'button', mods: { disabled: true } });
 <div class="button button--disabled"></div>
 ```
 
+### shortTags
+
+`shortTags` расширяет стандартный набор коротких тегов.
+
+```javascript
+bh.setOptions({ shortTags: ['rect'] });
+```
+
 ## Дополнительные примеры
 
 Например, мы хотим установить модификатор `state` со значением `closed` для всех блоков `popup`:
diff --git a/lib/bh.js b/lib/bh.js
index 4f40338..fecc663 100644
--- a/lib/bh.js
+++ b/lib/bh.js
@@ -40,6 +40,13 @@ function BH() {
      * @type {Object}
      */
     this.lib = {};
+    /**
+     * Оптимизация коротких тегов. Они могут быть расширены через setOptions({ shortTags: [...] })
+     */
+    this._shortTags = {};
+    for (var i = 0; i < SHORT_TAGS.length; i++) {
+        this._shortTags[SHORT_TAGS[i]] = 1;
+    }
     /**
      * Опции BH. Задаются через setOptions.
      * @type {Object}
@@ -582,6 +589,12 @@ BH.prototype = {
         if (options.delimMod) {
             this._optDelimMod = options.delimMod;
         }
+        if (options.shortTags) {
+            for (var j = 0; j < options.shortTags.length; j++) {
+                this._shortTags[options.shortTags[j]] = 1;
+            }
+        }
+
         return this;
     },
 
@@ -1084,7 +1097,7 @@ BH.prototype = {
             var tag = (json.tag || 'div');
             this._buf += '<' + tag + (cls ? ' class="' + cls + '"' : '') + (attrs ? attrs : '');
 
-            if (selfCloseHtmlTags[tag]) {
+            if (this._shortTags[tag]) {
                 this._buf += '/>';
             } else {
                 this._buf += '>';
@@ -1099,25 +1112,7 @@ BH.prototype = {
     }
 };
 
-var selfCloseHtmlTags = {
-    area: 1,
-    base: 1,
-    br: 1,
-    col: 1,
-    command: 1,
-    embed: 1,
-    hr: 1,
-    img: 1,
-    input: 1,
-    keygen: 1,
-    link: 1,
-    menuitem: 1,
-    meta: 1,
-    param: 1,
-    source: 1,
-    track: 1,
-    wbr: 1
-};
+var SHORT_TAGS = 'area base br col command embed hr img input keygen link menuitem meta param source track wbr'.split(' ');
 
 var xmlEscape = BH.prototype.xmlEscape = function(str) {
     return (str + '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');


Test patch:
diff --git a/test/test.options.js b/test/test.options.js
index 0457642..f2ce7c7 100644
--- a/test/test.options.js
+++ b/test/test.options.js
@@ -120,5 +120,10 @@ describe('options', function() {
                 '</div>'
             );
         });
+
+        it('should use extended short tags', function() {
+            bh.setOptions({ shortTags: ['rect'] });
+            bh.apply({ tag: 'rect' }).should.equal('<rect/>');
+        });
     });
 });