1
1
<?php
2
2
/**
3
3
* CSScomb
4
- * @version: 2.11 (build 3ded73c-1208080145)
5
- * @author: Vyacheslav Oliyanchuk (miripiruni)
6
- * @web: http://csscomb.com/
4
+ *
5
+ * Tool for sorting CSS properties in specific order
6
+ *
7
+ * @version 2.12 (build e784736-1301040046)
8
+ * @author Vyacheslav Oliyanchuk (miripiruni) <mail@csscomb.com>
9
+ * @license MIT
10
+ * @web http://csscomb.com/
7
11
*/
8
-
12
+
9
13
error_reporting (E_ALL );
10
-
14
+
11
15
class csscomb{
12
16
13
17
var $ sort_order = Array (),
@@ -24,6 +28,8 @@ class csscomb{
24
28
'expressions ' => null ,
25
29
// если найдены data uri, то эта переменная станет массивом...
26
30
'datauri ' => null ,
31
+ // если найдены интерполированные переменные, то эта переменная станет массивом
32
+ 'interpolations ' => null ,
27
33
// если найдены CSS-хаки мешающие парсить, то эта переменная станет массивом...
28
34
'hacks ' => null ,
29
35
// если найдены комментарии содержащие { или } мешающие парсить,
@@ -125,18 +131,18 @@ class csscomb{
125
131
"-webkit-border-radius",
126
132
"-moz-border-radius",
127
133
"border-radius",
134
+ "-webkit-border-top-left-radius",
135
+ "-moz-border-radius-topleft",
136
+ "border-top-left-radius",
128
137
"-webkit-border-top-right-radius",
129
- "-moz-border-top-right- radius",
138
+ "-moz-border-radius-topright ",
130
139
"border-top-right-radius",
131
140
"-webkit-border-bottom-right-radius",
132
- "-moz-border-bottom-right- radius",
141
+ "-moz-border-radius-bottomright ",
133
142
"border-bottom-right-radius",
134
143
"-webkit-border-bottom-left-radius",
135
- "-moz-border-bottom-left- radius",
144
+ "-moz-border-radius-bottomleft ",
136
145
"border-bottom-left-radius",
137
- "-webkit-border-top-left-radius",
138
- "-moz-border-top-left-radius",
139
- "border-top-left-radius",
140
146
"-webkit-border-image",
141
147
"-moz-border-image",
142
148
"-o-border-image",
@@ -230,6 +236,8 @@ class csscomb{
230
236
"-ms-writing-mode",
231
237
"vertical-align",
232
238
"text-align",
239
+ "-webkit-text-align-last",
240
+ "-moz-text-align-last",
233
241
"-ms-text-align-last",
234
242
"text-align-last",
235
243
"text-decoration",
@@ -360,6 +368,7 @@ class csscomb{
360
368
"-ms-animation-direction",
361
369
"-o-animation-direction",
362
370
"animation-direction",
371
+ "pointer-events",
363
372
"unicode-bidi",
364
373
"direction",
365
374
"-webkit-columns",
@@ -559,7 +568,8 @@ class csscomb{
559
568
"-o-animation-direction",
560
569
"animation-direction",
561
570
"text-align",
562
- "text-align-last",
571
+ "-webkit-text-align-last",
572
+ "-moz-text-align-last",
563
573
"-ms-text-align-last",
564
574
"text-align-last",
565
575
"vertical-align",
@@ -592,7 +602,8 @@ class csscomb{
592
602
"tab-size",
593
603
"-webkit-hyphens",
594
604
"-moz-hyphens",
595
- "hyphens"
605
+ "hyphens",
606
+ "pointer-events"
596
607
],
597
608
[
598
609
"opacity",
@@ -624,18 +635,18 @@ class csscomb{
624
635
"-webkit-border-radius",
625
636
"-moz-border-radius",
626
637
"border-radius",
638
+ "-webkit-border-top-left-radius",
639
+ "-moz-border-radius-topleft",
640
+ "border-top-left-radius",
627
641
"-webkit-border-top-right-radius",
628
- "-moz-border-top-right- radius",
642
+ "-moz-border-radius-topright ",
629
643
"border-top-right-radius",
630
644
"-webkit-border-bottom-right-radius",
631
- "-moz-border-bottom-right- radius",
645
+ "-moz-border-radius-bottomright ",
632
646
"border-bottom-right-radius",
633
647
"-webkit-border-bottom-left-radius",
634
- "-moz-border-bottom-left- radius",
648
+ "-moz-border-radius-bottomleft ",
635
649
"border-bottom-left-radius",
636
- "-webkit-border-top-left-radius",
637
- "-moz-border-top-left-radius",
638
- "border-top-left-radius",
639
650
"-webkit-border-image",
640
651
"-moz-border-image",
641
652
"-o-border-image",
@@ -716,11 +727,25 @@ class csscomb{
716
727
717
728
/**
718
729
* @param string css
719
- * @param boolean debug
720
- * @param json custom_sort_order JSON expected
721
- * @return string
730
+ * @param boolean debug, OPTIONAL
731
+ * @param json custom_sort_order JSON expected, OPTIONAL
732
+ * @return string|false
722
733
*
723
734
* @TODO: https://github.com/miripiruni/CSScomb/issues/21
735
+ *
736
+ * Example:
737
+ *
738
+ * <code>
739
+ * require_once 'PATH_TO_CSScomb/csscomb.php';
740
+ *
741
+ * $c = new csscomb();
742
+ * $result_code = $c->csscomb(
743
+ * 'div {margin-top:0; color: red; display: inline;}',
744
+ * false,
745
+ * $MY_JSON_SORT_ORDER
746
+ * );
747
+ * </code>
748
+ *
724
749
*/
725
750
function csscomb ($ css = '' , $ debug = false , $ custom_sort_order = null ) {
726
751
$ this ->output = $ debug ? true : false ;
@@ -872,18 +897,27 @@ function preprocess() {
872
897
endwhile ;
873
898
}
874
899
875
- // 4. Закрываем сложности парсинга {}
900
+ // 4. Interpolated variables
901
+ preg_match_all ('@(\#|\@){.*?}@ismx ' , $ this ->code ['edited ' ], $ this ->code ['interpolations ' ]);
902
+ foreach ($ this ->code ['interpolations ' ][0 ] as $ key => $ value ) {
903
+ $ pos = strpos ($ this ->code ['edited ' ], $ value );
904
+ if ($ pos !== false ) {
905
+ $ this ->code ['edited ' ] = substr_replace ($ this ->code ['edited ' ],"interpolation " .$ key .'__ ' ,$ pos ,strlen ($ value ));
906
+ }
907
+ }
908
+
909
+ // 5. Закрываем сложности парсинга {}
876
910
$ this ->code ['edited ' ] = str_replace ('{} ' , '{ } ' , $ this ->code ['edited ' ]);
877
911
878
- // 5 . Закрываем сложности с отсутствующей последней ; перед }
912
+ // 6 . Закрываем сложности с отсутствующей последней ; перед }
879
913
$ this ->code ['edited ' ] = preg_replace ('@(.*?[^\s;\{\}\/\*])(\s*?})@ ' , '$1;$2 ' , $ this ->code ['edited ' ]);
880
914
// Убираем ; у последнего инлайнового комментария
881
915
// Инлайновый комментарий может идти только после фигурной скобки или ;
882
916
$ this ->code ['edited ' ] = preg_replace ('@([;\{\}]+\s*?//.*?);(\s*?})@ ' , '$1$2 ' , $ this ->code ['edited ' ]);
883
917
// Убираем ; у интерполированных переменных
884
- $ this ->code ['edited ' ] = preg_replace ('@( #\{\$.*?)[;](\s*?\})@ ' , '$1$2 ' , $ this ->code ['edited ' ]);
918
+ $ this ->code ['edited ' ] = preg_replace ('/(( #\{\$|\@\{) .*?)[;](\s*?\})/ ' , '$1$3 ' , $ this ->code ['edited ' ]);
885
919
886
- // 6 . Комментарии
920
+ // 7 . Комментарии
887
921
if (preg_match_all ('@
888
922
(
889
923
\s*
@@ -894,7 +928,7 @@ function preprocess() {
894
928
)
895
929
@ismx ' , $ this ->code ['edited ' ], $ test )) {
896
930
897
- // 6 .1. Закомментировано одно или несколько свойств: повторяющийся паттерн *:*; \s*?
931
+ // 7 .1. Закомментировано одно или несколько свойств: повторяющийся паттерн *:*; \s*?
898
932
if (preg_match_all ('@
899
933
(\s*)
900
934
/\*
@@ -943,7 +977,7 @@ function preprocess() {
943
977
}
944
978
}
945
979
946
- // 6 .2. Обрывки закомментированных деклараций: присутствует { или }
980
+ // 7 .2. Обрывки закомментированных деклараций: присутствует { или }
947
981
if (preg_match_all ('@
948
982
\s*?
949
983
/\*
@@ -978,7 +1012,7 @@ function preprocess() {
978
1012
}
979
1013
}
980
1014
981
- // 7 . Entities
1015
+ // 8 . Entities
982
1016
if (preg_match_all ('@
983
1017
\&
984
1018
\#?
@@ -1087,28 +1121,37 @@ function parse_root($css = '') {
1087
1121
*
1088
1122
*/
1089
1123
function parse_child ($ value = '' ) {
1124
+ $ block_imports = array ();
1090
1125
// 1. Ищем «детей» (вложенные селекторы)
1091
1126
preg_match_all ('@
1092
- [^\};]*?[\s]*?\{((([^\{\}]+)|(?R))*)\}
1127
+ [^};]*?
1128
+ {
1129
+ (
1130
+ (
1131
+ ([^\{\}]+)|(?R)
1132
+ )*
1133
+ )
1134
+ }
1093
1135
@ismx ' , $ value , $ nested );
1094
1136
1095
- // Убираем из выборки интерполированные переменные
1096
- foreach ($ nested [0 ] as $ nested_key => $ nested_value ) {
1097
- if (strpos ($ nested_value , '#{$ ' )) {
1098
- unset($ nested [0 ][$ nested_key ]);
1137
+ // TODO: возможно, вынести отдельной функцией, т.к. часто повторяется
1138
+ foreach ($ nested [0 ] as $ key => &$ nest ) {
1139
+ $ value = str_replace ($ nest , '' , $ value );
1140
+ if (strpos (trim ($ nest ), '@include ' ) === 0 ) {
1141
+ $ value = str_replace ($ nest , '' , $ value );
1142
+ $ old_nest = $ nested [1 ][$ key ];
1143
+ $ new_nest = $ this ->parse_child ($ nested [1 ][$ key ]);
1144
+ $ nest = str_replace ($ old_nest , $ new_nest , $ nest );
1145
+ $ block_imports [] = $ nest ;
1146
+ unset($ nested [0 ][$ key ]);
1147
+ unset($ nested [1 ][$ key ]);
1099
1148
}
1100
1149
}
1101
1150
1102
1151
// Сохраняем всех «детей» в строку для последующей замены
1103
1152
// TODO: убрать, если без этого можно обойтись
1104
1153
$ nested_string = implode ('' , $ nested [0 ]);
1105
1154
1106
- // Удаляем «детей» из общей строки
1107
- // TODO: возможно, вынести отдельной функцией, т.к. часто повторяется
1108
- foreach ($ nested [0 ] as &$ nest ) {
1109
- $ value = str_replace ($ nest , '' , $ value );
1110
- }
1111
-
1112
1155
// Рекурсия, ahoj!
1113
1156
// Сортируем содержимое «детей»
1114
1157
foreach ($ nested [1 ] as &$ child ) {
@@ -1135,20 +1178,23 @@ function parse_child($value = '') {
1135
1178
1136
1179
// Включения, следующие сразу за {
1137
1180
preg_match_all ('@
1138
- ^\s*\@[^;]+?[;]
1181
+ ( ^\s*\@[^;]+?[;])|(^\s*\.[^;:]+?[;])
1139
1182
@isx ' , $ value , $ first_imports );
1140
1183
foreach ($ first_imports [0 ] as &$ first_import ) {
1141
1184
$ value = str_replace ($ first_import , '' , $ value );
1142
1185
}
1143
1186
1144
1187
// Все остальные
1145
1188
preg_match_all ('@
1146
- [;\{\}]+( \s*\@ [^;]+?[;])
1189
+ (?<=[;}])(\s*\@[^;]+?[;])|(?<=[;}])( \s*\. [^;: ]+?[;])
1147
1190
@ismx ' , $ value , $ imports );
1148
1191
// Удаляем их из общей строки
1149
1192
foreach ($ imports [1 ] as &$ import ) {
1150
1193
$ value = str_replace ($ import , '' , $ value );
1151
1194
}
1195
+ foreach ($ imports [2 ] as &$ import ) {
1196
+ $ value = str_replace ($ import , '' , $ value );
1197
+ }
1152
1198
1153
1199
// 4. Выносим простые свойства в массив $properties
1154
1200
preg_match_all ('@
@@ -1168,7 +1214,7 @@ function parse_child($value = '') {
1168
1214
1169
1215
// 6. Склеиваем всё обратно в следующем порядке:
1170
1216
// переменные, включения, простые свойства, вложенные {}
1171
- $ value = implode ('' , $ vars [0 ]).implode ('' , $ first_imports [0 ]).implode ('' , $ imports [1 ]).implode ('' , $ props ).$ nested_string .$ value ;
1217
+ $ value = implode ('' , $ vars [0 ]).implode ('' , $ first_imports [0 ]).implode ('' , $ imports [1 ]).implode ('' , $ imports [ 2 ]). implode ( '' , $ block_imports ). implode ( '' , $ props ).$ nested_string .$ value ;
1172
1218
return $ value ;
1173
1219
}
1174
1220
@@ -1438,7 +1484,13 @@ function postprocess() {
1438
1484
}
1439
1485
}
1440
1486
1441
- // 4. Удаляем искусственно созданные 'commented__'
1487
+ // 4. Interpolated variables
1488
+ preg_match_all ('#interpolation(\d)__#ismx ' , $ this ->code ['resorted ' ], $ new_vars );
1489
+ foreach ($ new_vars [1 ] as $ key => $ value ) {
1490
+ $ this ->code ['resorted ' ] = str_replace ($ new_vars [0 ][$ key ], $ this ->code ['interpolations ' ][0 ][$ key ], $ this ->code ['resorted ' ]);
1491
+ }
1492
+
1493
+ // 5. Удаляем искусственно созданные 'commented__'
1442
1494
while (strpos ($ this ->code ['resorted ' ], 'commented__ ' ) !== FALSE ) {
1443
1495
$ this ->code ['resorted ' ] = preg_replace (
1444
1496
'#
@@ -1453,7 +1505,7 @@ function postprocess() {
1453
1505
);
1454
1506
}
1455
1507
1456
- // 5 . Удаляем искусственно созданные 'brace__'
1508
+ // 6 . Удаляем искусственно созданные 'brace__'
1457
1509
if (is_array ($ this ->code ['braces ' ])) { // если были обнаружены и вырезаны хаки
1458
1510
foreach ($ this ->code ['braces ' ] as $ key => $ val ) {
1459
1511
if (strpos ($ this ->code ['resorted ' ], 'brace__ ' .$ key .'{ ' ) !== FALSE ) {
0 commit comments