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 }