12
12
use DOMText ;
13
13
use Exception ;
14
14
use Paneon \VueToTwig \Models \Component ;
15
+ use Paneon \VueToTwig \Models \Pre ;
15
16
use Paneon \VueToTwig \Models \Property ;
16
17
use Paneon \VueToTwig \Models \Replacements ;
17
18
use Paneon \VueToTwig \Models \Slot ;
@@ -39,6 +40,11 @@ class Compiler
39
40
*/
40
41
protected $ lastCloseIf ;
41
42
43
+ /**
44
+ * @var mixed[]|null
45
+ */
46
+ protected $ selectData ;
47
+
42
48
/**
43
49
* @var LoggerInterface
44
50
*/
@@ -69,6 +75,11 @@ class Compiler
69
75
*/
70
76
protected $ properties ;
71
77
78
+ /**
79
+ * @var Pre[]
80
+ */
81
+ protected $ pre ;
82
+
72
83
/**
73
84
* @var mixed[]
74
85
*/
@@ -105,9 +116,11 @@ public function __construct(DOMDocument $document, LoggerInterface $logger)
105
116
$ this ->document = $ document ;
106
117
$ this ->logger = $ logger ;
107
118
$ this ->lastCloseIf = [];
119
+ $ this ->selectData = null ;
108
120
$ this ->components = [];
109
121
$ this ->banner = [];
110
122
$ this ->properties = [];
123
+ $ this ->pre = [];
111
124
$ this ->rawBlocks = [];
112
125
113
126
$ this ->logger ->debug ("\n--------- New Compiler Instance ---------- \n" );
@@ -183,6 +196,8 @@ public function convert(): string
183
196
184
197
$ html = $ this ->builder ->concatConvertHandler ($ html , $ this ->properties );
185
198
199
+ $ html = $ this ->replacePre ($ html );
200
+
186
201
if ($ this ->stripWhitespace ) {
187
202
$ html = $ this ->stripWhitespace ($ html );
188
203
}
@@ -211,9 +226,16 @@ public function convertNode(DOMNode $node, int $level = 0): DOMNode
211
226
if ($ this ->twigRemove ($ node )) {
212
227
return $ node ;
213
228
}
229
+ if ($ this ->handlePre ($ node )) {
230
+ return $ node ;
231
+ }
214
232
$ this ->replaceShowWithIf ($ node );
215
233
$ this ->handleIf ($ node , $ level );
216
234
$ this ->handleFor ($ node );
235
+ $ modelData = $ this ->handleModel ($ node );
236
+ if ($ modelData && $ modelData ['type ' ] === 'option ' ) {
237
+ $ this ->selectData = $ modelData ;
238
+ }
217
239
$ this ->handleHtml ($ node );
218
240
$ this ->handleText ($ node );
219
241
$ this ->stripEventHandlers ($ node );
@@ -298,6 +320,10 @@ public function convertNode(DOMNode $node, int $level = 0): DOMNode
298
320
299
321
if ($ node instanceof DOMElement) {
300
322
$ this ->handleAttributeBinding ($ node );
323
+ $ this ->handleOption ($ node );
324
+ if (isset ($ modelData )) {
325
+ $ this ->handleRadioOrCheckbox ($ node , $ modelData );
326
+ }
301
327
if ($ level === 1 ) {
302
328
foreach ($ this ->includeAttributes as $ attribute ) {
303
329
$ this ->handleRootNodeAttribute ($ node , $ attribute );
@@ -309,6 +335,10 @@ public function convertNode(DOMNode $node, int $level = 0): DOMNode
309
335
$ this ->convertNode ($ childNode , $ level + 1 );
310
336
}
311
337
338
+ if ($ node ->nodeName === 'selected ' ) {
339
+ $ this ->selectData = null ;
340
+ }
341
+
312
342
return $ node ;
313
343
}
314
344
@@ -373,16 +403,16 @@ public function registerProperties(DOMElement $scriptElement): void
373
403
$ property ->setType ($ matchType [1 ]);
374
404
}
375
405
376
- if (preg_match ('/default:\s*(?<default>[^,$]+)\s*,?/mx ' , $ definition , $ matchDefault )) {
406
+ if (preg_match ('/default:\s*(?<default>\[[^\[\]]+\]| [^,$]+)\s*,?/mx ' , $ definition , $ matchDefault )) {
377
407
$ property ->setDefault (trim ($ matchDefault ['default ' ]));
378
408
}
379
409
380
410
$ this ->properties [$ propName ] = $ property ;
381
411
}
382
412
}
383
413
384
- $ typeScriptRegexProps = '/\@Prop\s*\({(?<propOptions>.*?)}\)[^;]*?(?<propName>[a-zA-Z0-9_$]+)\!?\:\s*(?<propType>[a-zA-Z]+)[^;\@]*;/msx ' ;
385
- $ typeScriptRegexDefault = '/default\s*\:\s*(?<defaultValue> \'(?:.(?!(?<![ \\\\]) \'))*.? \'|"(?:.(?!(?<![ \\\\])"))*.?"|[a-zA-Z0-9_]+)/msx ' ;
414
+ $ typeScriptRegexProps = '/\@Prop\s*\({(?<propOptions>.*?)}\)[^;]*?(?<propName>[a-zA-Z0-9_$]+)\!?\:\s*(?<propType>[a-zA-Z\[\] ]+)[^;\@]*;/msx ' ;
415
+ $ typeScriptRegexDefault = '/default\s*\:\s*(?<defaultValue> \'(?:.(?!(?<![ \\\\]) \'))*.? \'|"(?:.(?!(?<![ \\\\])"))*.?"|[a-zA-Z0-9_]+|\[[^\[\]]+\] )/msx ' ;
386
416
if (preg_match_all ($ typeScriptRegexProps , $ content , $ typeScriptMatches , PREG_SET_ORDER )) {
387
417
$ this ->properties = [];
388
418
foreach ($ typeScriptMatches as $ typeScriptMatch ) {
@@ -396,6 +426,42 @@ public function registerProperties(DOMElement $scriptElement): void
396
426
}
397
427
}
398
428
429
+ /**
430
+ * @throws Exception
431
+ */
432
+ public function handlePre (DOMElement $ node ): bool
433
+ {
434
+ if (!$ node ->hasAttribute ('v-pre ' )) {
435
+ return false ;
436
+ }
437
+ $ node ->removeAttribute ('v-pre ' );
438
+ $ html = $ this ->document ->saveHTML ($ node );
439
+ $ parentNode = $ node ->parentNode ;
440
+ $ parentNode ->removeChild ($ node );
441
+ $ pre = new Pre ('{% verbatim %} ' . $ html . '{% endverbatim %} ' );
442
+ $ key = $ pre ->getPreContentVariableString ();
443
+ $ replacer = $ this ->document ->createTextNode ($ key );
444
+ $ parentNode ->appendChild ($ replacer );
445
+ $ this ->pre [$ key ] = $ pre ;
446
+
447
+ return true ;
448
+ }
449
+
450
+ protected function replacePre (string $ html ): string
451
+ {
452
+ if (preg_match_all (Pre::PRE_REGEX , $ html , $ matches )) {
453
+ foreach ($ matches [0 ] as $ key ) {
454
+ $ html = str_replace (
455
+ $ key ,
456
+ $ this ->pre [$ key ]->getValue (),
457
+ $ html
458
+ );
459
+ }
460
+ }
461
+
462
+ return $ html ;
463
+ }
464
+
399
465
public function replaceShowWithIf (DOMElement $ node ): void
400
466
{
401
467
if ($ node ->hasAttribute ('v-show ' )) {
@@ -553,7 +619,7 @@ private function cleanupAttributes(DOMElement $node): void
553
619
/** @var DOMAttr $attribute */
554
620
foreach ($ node ->attributes as $ attribute ) {
555
621
if (
556
- (preg_match ('/^v-([a-z]*)/ ' , $ attribute ->name , $ matches ) === 1 && $ matches [1 ] !== 'bind ' && $ matches [1 ] !== 'slot ' )
622
+ (preg_match ('/^v-([a-z]*)/ ' , $ attribute ->name , $ matches ) === 1 && $ matches [1 ] !== 'bind ' && $ matches [1 ] !== 'slot ' && $ matches [ 1 ] !== ' cloak ' )
557
623
|| preg_match ('/^[:]?ref$/ ' , $ attribute ->name ) === 1
558
624
) {
559
625
$ removeAttributes [] = $ attribute ->name ;
@@ -678,6 +744,135 @@ private function handleFor(DOMElement $node): void
678
744
$ node ->removeAttribute ('v-for ' );
679
745
}
680
746
747
+ /**
748
+ * @return mixed|void
749
+ */
750
+ private function handleModel (DOMElement $ node )
751
+ {
752
+ if (!$ node ->hasAttribute ('v-model ' )) {
753
+ return ;
754
+ }
755
+
756
+ $ modelValue = $ node ->getAttribute ('v-model ' );
757
+ $ node ->removeAttribute ('v-mode ' );
758
+
759
+ switch ($ node ->nodeName ) {
760
+ case 'textarea ' :
761
+ $ node ->setAttribute ('v-text ' , $ modelValue );
762
+
763
+ return null ;
764
+ case 'input ' :
765
+ $ typeAttribute = $ node ->getAttribute ('type ' );
766
+ if ($ typeAttribute === 'checkbox ' ) {
767
+ return [
768
+ 'value ' => $ modelValue ,
769
+ 'type ' => 'checkbox ' ,
770
+ ];
771
+ } elseif ($ typeAttribute === 'radio ' ) {
772
+ return [
773
+ 'value ' => $ modelValue ,
774
+ 'type ' => 'radio ' ,
775
+ ];
776
+ } else {
777
+ $ node ->setAttribute (':value ' , $ modelValue );
778
+ }
779
+
780
+ return null ;
781
+ case 'select ' :
782
+ return [
783
+ 'value ' => $ modelValue ,
784
+ 'multiple ' => $ node ->hasAttribute ('multiple ' ),
785
+ 'type ' => 'option ' ,
786
+ ];
787
+ default :
788
+ return null ;
789
+ }
790
+ }
791
+
792
+ /**
793
+ * @throws ReflectionException
794
+ */
795
+ private function handleOption (DOMElement $ node ): void
796
+ {
797
+ if ($ node ->tagName !== 'option ' || $ this ->selectData === null ) {
798
+ return ;
799
+ }
800
+
801
+ if ($ node ->hasAttribute ('value ' )) {
802
+ $ value = $ node ->getAttribute ('value ' );
803
+ } else {
804
+ $ value = trim ($ node ->textContent );
805
+ }
806
+
807
+ $ value = '" ' . str_replace (['__DOUBLE_CURLY_OPEN__ ' , '__DOUBLE_CURLY_CLOSE__ ' ], ['" ~ ' , '~ " ' ], $ value ) . '" ' ;
808
+
809
+ if ($ this ->selectData ['multiple ' ]) {
810
+ $ condition = $ this ->selectData ['value ' ] . ' is iterable and ' . $ value . ' in ' . $ this ->selectData ['value ' ];
811
+ } else {
812
+ $ condition = $ this ->selectData ['value ' ] . ' == ' . $ value ;
813
+ }
814
+
815
+ $ this ->addAttributeIf ($ node , $ condition , 'selected ' , 'selected ' );
816
+ }
817
+
818
+ /**
819
+ * @param mixed[] $modelData
820
+ *
821
+ * @throws ReflectionException
822
+ */
823
+ private function handleRadioOrCheckbox (DOMElement $ node , array $ modelData ): void
824
+ {
825
+ if (!$ node ->hasAttribute ('value ' )
826
+ || !$ node ->hasAttribute ('type ' )
827
+ || ($ node ->getAttribute ('type ' ) !== 'radio ' && $ node ->getAttribute ('type ' ) !== 'checkbox ' )) {
828
+ return ;
829
+ }
830
+
831
+ $ value = $ node ->getAttribute ('value ' );
832
+
833
+ $ value = '" ' . str_replace (['__DOUBLE_CURLY_OPEN__ ' , '__DOUBLE_CURLY_CLOSE__ ' ], ['" ~ ' , '~ " ' ], $ value ) . '" ' ;
834
+
835
+ if ($ modelData ['type ' ] === 'checkbox ' ) {
836
+ $ condition = '( ' . $ modelData ['value ' ] . ' is iterable and ' . $ value . ' in ' . $ modelData ['value ' ] . ') '
837
+ . ' or ( ' . $ modelData ['value ' ] . ' is not iterable and ' . $ modelData ['value ' ] . ') ' ;
838
+ } else {
839
+ $ condition = $ modelData ['value ' ] . ' == ' . $ value ;
840
+ }
841
+
842
+ $ this ->addAttributeIf ($ node , $ condition , 'checked ' , 'checked ' );
843
+ }
844
+
845
+ /**
846
+ * @throws ReflectionException
847
+ */
848
+ private function addAttributeIf (DOMElement $ node , string $ condition , string $ attributeName , string $ attributeValue ): void
849
+ {
850
+ /** @var DOMElement $clonedNode */
851
+ $ clonedNode = $ node ->cloneNode (true );
852
+ $ node ->setAttribute ($ attributeName , $ attributeValue );
853
+
854
+ if ($ clonedNode ->hasAttribute ($ attributeName )) {
855
+ $ clonedNode ->removeAttribute ($ attributeName );
856
+ }
857
+
858
+ $ node ->parentNode ->insertBefore (
859
+ $ this ->document ->createTextNode ($ this ->builder ->createIf ($ condition )),
860
+ $ node
861
+ );
862
+ $ node ->parentNode ->insertBefore (
863
+ $ this ->document ->createTextNode ($ this ->builder ->createEndIf ()),
864
+ $ node ->nextSibling
865
+ );
866
+ $ node ->parentNode ->insertBefore (
867
+ $ clonedNode ,
868
+ $ node ->nextSibling
869
+ );
870
+ $ node ->parentNode ->insertBefore (
871
+ $ this ->document ->createTextNode ($ this ->builder ->createElse ()),
872
+ $ node ->nextSibling
873
+ );
874
+ }
875
+
681
876
private function handleHtml (DOMElement $ node ): void
682
877
{
683
878
if (!$ node ->hasAttribute ('v-html ' )) {
@@ -689,7 +884,7 @@ private function handleHtml(DOMElement $node): void
689
884
while ($ node ->hasChildNodes ()) {
690
885
$ node ->removeChild ($ node ->firstChild );
691
886
}
692
- $ node ->appendChild (new DOMText (' {{ ' . $ html . '|raw}} ' ));
887
+ $ node ->appendChild (new DOMText ($ this -> builder -> prepareBindingOutput ( $ html . '|raw ' ) ));
693
888
}
694
889
695
890
private function handleText (DOMElement $ node ): void
@@ -703,7 +898,7 @@ private function handleText(DOMElement $node): void
703
898
while ($ node ->hasChildNodes ()) {
704
899
$ node ->removeChild ($ node ->firstChild );
705
900
}
706
- $ node ->appendChild (new DOMText (' {{ ' . $ text . ' }} ' ));
901
+ $ node ->appendChild (new DOMText ($ this -> builder -> prepareBindingOutput ( $ text) ));
707
902
}
708
903
709
904
/**
0 commit comments