1 module awebview.cssgrammar;
2 
3 import std.conv;
4 import std.algorithm;
5 import std.array;
6 import std.format;
7 import std..string;
8 
9 import awebview.gui.activity;
10 import awebview.wrapper;
11 
12 import pegged.grammar;
13 
14 import carbon.utils;
15 
16 mixin(grammar(q{
17 CSSCode:
18     Body < (Hash / Ignore)*
19     Hash < Selector Ignore "{" Ignore FieldList Ignore "}"
20 
21     Comment < "/*" (!"*/" .)* "*/"
22     Space < ("\r" / "\n" / "\t" / " ")+
23     Ignore < (Space / Comment)*
24 
25     FieldList < (Field / Ignore)*
26     Field < Symbol Ignore ":" Ignore Value Ignore ";"
27     Symbol < [a-zA-Z_] ([a-zA-Z0-9_] / "-")*
28     Value < (Statements / (!";" .))*
29     Statements < "{" ( (&"{" Statements) / (!"}" .) )* "}"
30 
31     Selector < (!(";" / "{") .)+
32 }));
33 
34 
35 string matchString(ParseTree p) @property
36 {
37     return p.input[p.begin .. p.end];
38 }
39 
40 
41 void foreachAll(ParseTree p, scope bool delegate(ParseTree) dg)
42 {
43     auto res = dg(p);
44     if(res)
45         return;
46 
47     foreach(e; p.children)
48         foreachAll(e, dg);
49 }
50 
51 
52 void foreachMatch(alias pred)(ParseTree p, scope bool delegate(ParseTree) dg)
53 {
54     if(pred(p))
55         if(dg(p))
56             return;
57 
58     foreach(e; p.children)
59         foreachMatch!pred(e, dg);
60 }
61 
62 
63 bool getFirstMatch(alias pred)(ParseTree p, out ParseTree dst)
64 {
65     if(pred(p)){
66         dst = p;
67         return true;
68     }
69 
70     foreach(e; p.children){
71         if(getFirstMatch!pred(e, dst))
72             return true;
73     }
74 
75     return false;
76 }
77 
78 
79 ParseTree firstMatch(alias pred)(ParseTree p)
80 {
81     ParseTree dst;
82     getFirstMatch!pred(p, dst);
83     return dst;
84 }
85 
86 
87 string generateResizeStatements(Activity activity)
88 {
89     static string[] getCSSList(Activity activity)
90     {
91         auto len = activity.evalJS(`(function() {
92             var ss = document.styleSheets;
93             var hrefs = [];
94             for(var i = 0, len = ss.length; i < len; ++i){
95                 var stylesheet = ss[i];
96                 if(stylesheet.href && stylesheet.href !== ''){
97                     hrefs.push(stylesheet.href);
98                 }
99             }
100 
101             _carrierObject_.value = hrefs;
102             _carrierObject_.idx = 0;
103             return hrefs.length;
104         })()`).get!uint;
105 
106         string[] res;
107         foreach(i; 0 .. len){
108             res ~= activity.evalJS(q{_carrierObject_.value[_carrierObject_.idx++]}).to!string;
109         }
110 
111         return res;
112     }
113 
114 
115     static string getCSS(string href)
116     {
117         WebURL url = href;
118         if(url.scheme == "file"){
119             import std.file : readText;
120             string path = url.path.to!string;
121             if(path[0] == '/')
122                 path = path[1 .. $];
123 
124             return readText(path);
125         }
126         else{
127             import std.net.curl : get;
128             return get(href.to!string).dup;
129         }
130     }
131 
132 
133     static string[2][] getJSExprFieldOfHash(ParseTree p)
134     {
135         string[2][] res;
136         if(p.name == "CSSCode.Hash"){
137             p.foreachMatch!(p => p.name == "CSSCode.Field")((p){
138                 string sym = p.firstMatch!(a => a.name == "CSSCode.Symbol")().matchString;
139                 string val = p.firstMatch!(a => a.name == "CSSCode.Value")().matchString;
140 
141                 if(val.startsWith("jsExpr(") && val.endsWith(")"))
142                     res ~= [sym, val[7 .. $-1]];
143 
144                 return true;
145             });
146         }
147 
148         return res;
149     }
150 
151 
152     import std.algorithm : map;
153     import std.array : array;
154 
155     auto app = appender!string();
156 
157     foreach(stylesheetName; getCSSList(activity)){
158         auto stylesheet = getCSS(stylesheetName);
159         auto ptree = CSSCode(stylesheet);
160 
161         if(!ptree.successful)
162             continue;
163 
164         ptree.foreachMatch!(p => p.name == "CSSCode.Hash")((p){
165             auto sel = toLiteral(matchString(p.children[0]));
166             auto exprs = getJSExprFieldOfHash(p);
167 
168             foreach(e; exprs)
169                 app.formattedWrite(`cssResize(document,window,%s,%s,%s);`, sel, toLiteral(e[0]), toLiteral(e[1]));
170 
171             return true;
172         });
173     }
174 
175     return app.data;
176 }