@@ -29,21 +29,25 @@ class BH {
29
29
* Неймспейс для библиотек. Сюда можно писать различный функционал для дальнейшего использования в шаблонах.
30
30
*
31
31
* ```javascript
32
- * bh.lib.objects = bh.lib.objects || [];
33
- * bh.lib.objects.inverse = bh.lib.objects.inverse || function(obj) { ... };
32
+ * $bh->lib->i18n = BEM.I18N;
33
+ * $bh->lib->objects = bh.lib.objects || [];
34
+ * $bh->lib->objects.inverse = bh.lib.objects.inverse || function(obj) { ... };
34
35
* ```
35
- * @var array
36
+ * @var ArrayObject
36
37
*/
37
- protected $ lib = [] ;
38
+ public $ lib ;
38
39
39
40
/**
40
41
* Опции BH. Задаются через setOptions.
41
42
* @var array
42
43
*/
43
44
protected $ _options = [];
45
+
44
46
protected $ _optJsAttrName = 'onclick ' ;
45
47
protected $ _optJsAttrIsJs = true ;
48
+ protected $ _optJsCls = 'i-bem ' ;
46
49
protected $ _optEscapeContent = false ;
50
+ protected $ _optNobaseMods = false ;
47
51
48
52
/**
49
53
* Флаг, включающий автоматическую систему поиска зацикливаний. Следует использовать в development-режиме,
@@ -80,6 +84,7 @@ class BH {
80
84
* @constructor
81
85
*/
82
86
function __construct () {
87
+ $ this ->lib = new \ArrayObject ([], \ArrayObject::ARRAY_AS_PROPS );
83
88
}
84
89
85
90
/**
@@ -100,15 +105,23 @@ function setOptions ($options) {
100
105
$ this ->_options [$ k ] = $ options [$ k ];
101
106
}
102
107
103
- if (! empty ($ options ['jsAttrName ' ])) {
108
+ if (isset ($ options ['jsAttrName ' ])) {
104
109
$ this ->_optJsAttrName = $ options ['jsAttrName ' ];
105
110
}
106
111
107
- if (! empty ($ options ['jsAttrScheme ' ])) {
112
+ if (isset ($ options ['jsAttrScheme ' ])) {
108
113
$ this ->_optJsAttrIsJs = $ options ['jsAttrScheme ' ] === 'js ' ;
109
114
}
110
115
111
- if (!empty ($ options ['escapeContent ' ])) {
116
+ if (isset ($ options ['jsCls ' ])) {
117
+ $ this ->_optJsCls = $ options ['jsCls ' ];
118
+ }
119
+
120
+ if (isset ($ options ['clsNobaseMods ' ])) {
121
+ $ this ->_optNobaseMods = true ;
122
+ }
123
+
124
+ if (isset ($ options ['escapeContent ' ])) {
112
125
$ this ->_optEscapeContent = $ options ['escapeContent ' ];
113
126
}
114
127
@@ -213,6 +226,52 @@ function match ($expr, $matcher = null) {
213
226
return $ this ;
214
227
}
215
228
229
+ /**
230
+ * Объявляет глобальный шаблон, применяемый перед остальными.
231
+ *
232
+ * ```php
233
+ * $bh->beforeEach(function ($ctx, $json) {
234
+ * $ctx->attr('onclick', $json->counter);
235
+ * });
236
+ * ```
237
+ *
238
+ * @param Closure $matcher
239
+ * @return BH
240
+ */
241
+ function beforeEach ($ matcher ) {
242
+ return $ this ->match ('$before ' , $ matcher );
243
+ }
244
+
245
+ /**
246
+ * Объявляет глобальный шаблон, применяемый после остальных.
247
+ *
248
+ * ```php
249
+ * $bh->afterEach(function ($ctx) {
250
+ * $ctx->tag('xdiv');
251
+ * });
252
+ * ```
253
+ *
254
+ * @param Closure $matcher
255
+ * @return BH
256
+ */
257
+ function afterEach ($ matcher ) {
258
+ return $ this ->match ('$after ' , $ matcher );
259
+ }
260
+
261
+ /**
262
+ * Вставляет вызов шаблона в очередь вызова.
263
+ *
264
+ * @param Array $res
265
+ * @param String $fnId
266
+ * @param Number $index
267
+ */
268
+ static protected function pushMatcher (&$ res , $ fnId , $ index ) {
269
+ $ res [] = (' $json->_m[ ' . $ fnId . '] = true; ' );
270
+ $ res [] = (' $subRes = $ms[ ' . $ index . ']["fn"]($ctx, $json); ' );
271
+ $ res [] = (' if ($subRes !== null) { return ($subRes ?: ""); } ' );
272
+ $ res [] = (' if ($json->_stop) return; ' );
273
+ }
274
+
216
275
/**
217
276
* Вспомогательный метод для компиляции шаблонов с целью их быстрого дальнейшего исполнения.
218
277
* @return string
@@ -233,8 +292,19 @@ function buildMatcher () {
233
292
234
293
$ res [] = 'return function ($ctx, $json) use ($ms) { ' ;
235
294
236
- $ res [] = 'switch ($json->block ?: __undefined) { ' ;
237
295
$ declByBlock = static ::groupBy ($ declarations , 'block ' );
296
+
297
+ if (isset ($ declByBlock ['$before ' ])) {
298
+ foreach ($ declByBlock ['$before ' ] as $ decl ) {
299
+ static ::pushMatcher ($ res , $ decl ['__id ' ], $ decl ['index ' ]);
300
+ }
301
+ }
302
+
303
+ $ afterEach = isset ($ declByBlock ['$after ' ]) ? $ declByBlock ['$after ' ] : null ;
304
+ unset($ declByBlock ['$before ' ], $ declByBlock ['$after ' ]);
305
+
306
+ if ($ declByBlock ) :
307
+ $ res [] = 'switch ($json->block ?: __undefined) { ' ;
238
308
foreach ($ declByBlock as $ blockName => $ blockData ) {
239
309
$ res [] = 'case " ' . $ blockName . '": ' ;
240
310
@@ -251,30 +321,35 @@ function buildMatcher () {
251
321
if (isset ($ decl ['elemMod ' ])) {
252
322
$ modKey = $ decl ['elemMod ' ];
253
323
$ conds [] = (
254
- 'isset($json->mods ) && $json->mods ->{" ' . $ modKey . '"} === ' .
324
+ 'isset($json->elemMods ) && $json->elemMods ->{" ' . $ modKey . '"} === ' .
255
325
($ decl ['elemModVal ' ] === true ? 'true ' : '" ' . $ decl ['elemModVal ' ] . '" ' ));
256
326
}
257
327
if (isset ($ decl ['blockMod ' ])) {
258
328
$ modKey = $ decl ["blockMod " ];
259
329
$ conds [] = (
260
- 'isset($json->blockMods ) && $json->blockMods ->{" ' . $ modKey . '"} === ' .
330
+ 'isset($json->mods ) && $json->mods ->{" ' . $ modKey . '"} === ' .
261
331
($ decl ['blockModVal ' ] === true ? 'true ' : '" ' . $ decl ['blockModVal ' ] . '" ' ));
262
332
}
263
333
264
334
$ res [] = (' if ( ' . join (' && ' , $ conds ) . ') { ' );
265
- $ res [] = (' $json->_m[ ' . $ __id . '] = true; ' );
266
- $ res [] = (' $subRes = $ms[ ' . $ decl ['index ' ] . ']["fn"]($ctx, $json); ' );
267
- $ res [] = (' if ($subRes !== null) { return ($subRes ?: ""); } ' );
268
- $ res [] = (' if ($json->_stop) return; ' );
335
+ static ::pushMatcher ($ res , $ __id , $ decl ['index ' ]);
269
336
$ res [] = (' } ' );
270
337
}
271
338
272
- $ res [] = (' return ; ' );
339
+ $ res [] = (' break ; ' );
273
340
}
274
341
$ res [] = ('} ' );
275
- $ res [] = (' return ; ' );
342
+ $ res [] = (' break ; ' );
276
343
}
277
344
$ res [] = ('} ' );
345
+ endif ;
346
+
347
+ if ($ afterEach ) {
348
+ foreach ($ afterEach as $ decl ) {
349
+ static ::pushMatcher ($ res , $ decl ['__id ' ], $ decl ['index ' ]);
350
+ }
351
+ }
352
+
278
353
$ res [] = ('}; ' );
279
354
280
355
return "return function ( \$ms) { \n" . join ("\n" , $ res ) . "\n}; " ;
@@ -287,17 +362,13 @@ function getMatcher () {
287
362
if ($ this ->_matcher ) return $ this ->_matcher ;
288
363
289
364
// debugging purposes only (!!!)
290
- // $debug = false; //true;
291
-
292
365
// $key = md5(join('|', array_map(function ($e) { return $e['expr']; }, $this->_matchers)));
293
366
// $file = "./tmp/bh-matchers-{$key}.php";
294
367
// $constructor = @include $file;
295
368
// if (!$constructor) {
296
- // if ($debug) {
297
369
$ code = $ this ->buildMatcher ();
298
- // file_put_contents($file, "<?php\n" . $code);
299
- // file_put_contents("./bh-matcher.php", "<?php\n" . $fn);
300
- // $constructor = include("./bh-matcher.php");
370
+ // file_put_contents($file, "<?php\n" . $code);
371
+ // $constructor = include $file;
301
372
$ constructor = eval ($ code );
302
373
// }
303
374
@@ -348,18 +419,14 @@ function processBemJson ($bemJson, $blockName = null, $ignoreContent = null) {
348
419
}
349
420
350
421
$ bemJson = $ resultArr [0 ];
351
- $ blockMods = null ;
352
- if ($ bemJson instanceof Json) {
353
- $ blockMods = (!$ bemJson ->elem && isset ($ bemJson ->mods )) ? $ bemJson ->mods : $ bemJson ->blockMods ;
354
- }
355
422
356
423
$ steps = [];
357
424
$ steps [] = new Step (
358
425
$ bemJson ,
359
426
$ resultArr ,
360
427
0 ,
361
428
$ blockName ,
362
- $ blockMods
429
+ null
363
430
);
364
431
365
432
// var compiledMatcher = (this._fastMatcher || (this._fastMatcher = Function('ms', this.buildMatcher())(this._matchers)));
@@ -374,8 +441,8 @@ function processBemJson ($bemJson, $blockName = null, $ignoreContent = null) {
374
441
// js: while (node = nodes.shift()) {
375
442
while ($ step = array_shift ($ steps )) {
376
443
$ json = $ step ->json ;
377
- $ blockName = $ step ->blockName ;
378
- $ blockMods = $ step ->blockMods ;
444
+ $ blockName = $ step ->block ;
445
+ $ blockMods = $ step ->mods ;
379
446
380
447
if ($ json instanceof JsonCollection) {
381
448
$ j = 0 ;
@@ -408,21 +475,19 @@ function processBemJson ($bemJson, $blockName = null, $ignoreContent = null) {
408
475
continue ;
409
476
410
477
} elseif ($ json ->elem ) {
411
- $ blockName = $ json ->block = isset ($ json ->block ) ? $ json ->block : $ blockName ;
412
- // sync mods:
413
- // blockMods = json.blockMods = json.blockMods || blockMods
414
- $ blockMods = $ json ->blockMods = isset ($ json ->blockMods ) ? $ json ->blockMods : $ blockMods ;
415
- // sync elem mods:
416
- if (isset ($ json ->elemMods )) {
417
- $ json ->mods = $ json ->elemMods ;
478
+ $ blockName = $ json ->block = $ json ->block ?: $ blockName ;
479
+ if (!isset ($ json ->elemMods )) {
480
+ $ json ->elemMods = $ json ->mods ;
481
+ $ json ->mods = null ;
418
482
}
483
+ $ blockMods = $ json ->mods = isset ($ json ->mods ) ? $ json ->mods : $ blockMods ;
419
484
420
485
} elseif ($ json ->block ) {
421
486
$ blockName = $ json ->block ;
422
- $ blockMods = $ json ->blockMods = $ json -> mods ;
487
+ $ blockMods = $ json ->mods ;
423
488
}
424
489
425
- if ($ json && $ json -> block ) {
490
+ if ($ json instanceof Json ) {
426
491
if ($ infiniteLoopDetection ) {
427
492
$ json ->_matcherCalls ++;
428
493
$ this ->_matcherCalls ++;
@@ -441,8 +506,8 @@ function processBemJson ($bemJson, $blockName = null, $ignoreContent = null) {
441
506
if ($ subRes !== null ) {
442
507
$ json = JsonCollection::normalize ($ subRes );
443
508
$ step ->json = $ json ;
444
- $ step ->blockName = $ blockName ;
445
- $ step ->blockMods = $ blockMods ;
509
+ $ step ->block = $ blockName ;
510
+ $ step ->mods = $ blockMods ;
446
511
$ steps [] = $ step ;
447
512
$ stopProcess = true ;
448
513
}
@@ -451,34 +516,27 @@ function processBemJson ($bemJson, $blockName = null, $ignoreContent = null) {
451
516
452
517
if (!$ stopProcess && $ processContent && isset ($ json ->content ) && !is_scalar ($ json ->content )) {
453
518
$ content = $ json ->content ;
454
- //if ($content instanceof JsonCollection) {
455
-
456
- $ j = 0 ;
457
- foreach ($ content as $ i => $ child ) {
458
- if (is_scalar ($ child ) || empty ($ child )) {
459
- continue ;
460
- }
461
-
462
- $ steps [] = new Step (
463
- $ child ,
464
- $ content ,
465
- $ i ,
466
- $ blockName ,
467
- $ blockMods ,
468
- ++$ j ,
469
- $ step
470
- );
519
+
520
+ $ j = 0 ;
521
+ foreach ($ content as $ i => $ child ) {
522
+ if (is_scalar ($ child ) || empty ($ child )) {
523
+ continue ;
471
524
}
472
- $ content ->_listLength = $ j ;
473
525
474
- /*} else {
475
- // commented since 24 nov '14
476
- // throw new \Exception('Do we need it?');
477
- }*/
526
+ $ steps [] = new Step (
527
+ $ child ,
528
+ $ content ,
529
+ $ i ,
530
+ $ blockName ,
531
+ $ blockMods ,
532
+ ++$ j ,
533
+ $ step
534
+ );
535
+ }
536
+ $ content ->_listLength = $ j ;
478
537
}
479
538
}
480
539
481
- //d('processBemjson#' . ($_callId) . ' out ', $resultArr[0]);
482
540
return $ resultArr [0 ];
483
541
}
484
542
@@ -528,15 +586,13 @@ public function toHtml ($json) {
528
586
529
587
$ jsParams = false ;
530
588
if ($ json ->block ) {
531
- $ cls = static ::toBemCssClasses ($ json , $ base );
589
+ $ cls = static ::toBemCssClasses ($ json , $ base, null , $ this -> _optNobaseMods );
532
590
if ($ json ->js !== null && $ json ->js !== false ) {
533
591
$ jsParams = [];
534
592
$ jsParams [$ base ] = $ json ->js === true ? [] : $ this ->_filterNulls ($ json ->js );
535
593
}
536
594
}
537
595
538
- $ addJSInitClass = $ jsParams && !$ json ->elem ;
539
-
540
596
if ($ json ->mix ) {
541
597
foreach ($ json ->mix as $ mix ) {
542
598
if (!$ mix || $ mix ->bem === false ) {
@@ -553,18 +609,17 @@ public function toHtml ($json) {
553
609
$ mixElem = $ mix ->elem ?: ($ mix ->block ? null : ($ json ->block ? $ json ->elem : null ));
554
610
$ mixBase = $ mixBlock . ($ mixElem ? '__ ' . $ mixElem : '' );
555
611
556
- $ cls .= static ::toBemCssClasses ($ mix , $ mixBase , $ base );
612
+ $ cls .= static ::toBemCssClasses ($ mix , $ mixBase , $ base, $ this -> _optNobaseMods );
557
613
if ($ mix ->js !== null && $ mix ->js !== false ) {
558
614
$ jsParams = $ jsParams ?: [];
559
615
$ jsParams [$ mixBase ] = $ mix ->js === true ? [] : $ this ->_filterNulls ($ mix ->js );
560
616
$ hasMixJsParams = true ;
561
- if (!$ addJSInitClass ) $ addJSInitClass = ($ mixBlock && !$ mixElem );
562
617
}
563
618
}
564
619
}
565
620
566
621
if ($ jsParams ) {
567
- if ($ addJSInitClass ) $ cls .= ' i-bem ' ;
622
+ if ($ this -> _optJsCls ) $ cls .= ' ' . $ this -> _optJsCls ;
568
623
$ jsData = !$ hasMixJsParams && $ json ->js === true ?
569
624
'{" ' . $ base . '":{}} ' :
570
625
self ::attrEscape (str_replace ('[] ' , '{} ' ,
@@ -610,7 +665,7 @@ public static function attrEscape($s) {
610
665
return htmlspecialchars ($ s , ENT_QUOTES );
611
666
}
612
667
613
- public static function toBemCssClasses ($ json , $ base , $ parentBase = null ) {
668
+ public static function toBemCssClasses ($ json , $ base , $ parentBase = null , $ nobase = false ) {
614
669
$ res = '' ;
615
670
616
671
if ($ parentBase !== $ base ) {
@@ -620,14 +675,11 @@ public static function toBemCssClasses($json, $base, $parentBase = null) {
620
675
$ res .= $ base ;
621
676
}
622
677
623
- // if (mods = json.mods || json.elem && json.elemMods)
624
- $ mods = isset ($ json ->mods ) ? $ json ->mods :
625
- ($ json ->elem && isset ($ json ->elemMods ) ? $ json ->elemMods : null );
626
- if ($ mods ) {
627
- foreach ($ mods as $ k => $ mod ) {
628
- if ($ mod || $ mod === 0 ) {
629
- $ res .= ' ' . $ base . '_ ' . $ k . ($ mod === true ? '' : '_ ' . $ mod );
630
- }
678
+ // if (mods = json.elem && json.elemMods || json.mods)
679
+ $ mods = $ json ->elem && isset ($ json ->elemMods ) ? $ json ->elemMods : $ json ->mods ;
680
+ foreach ($ mods as $ k => $ mod ) {
681
+ if ($ mod || $ mod === 0 ) {
682
+ $ res .= ' ' . ($ nobase ? '' : $ base ) . '_ ' . $ k . ($ mod === true ? '' : '_ ' . $ mod );
631
683
}
632
684
}
633
685
0 commit comments