1 module awebview.jsvariable;
2 
3 import std.algorithm;
4 import std.conv;
5 import std..string;
6 import std.typecons;
7 
8 import awebview.wrapper;
9 import awebview.gui.activity;
10 
11 
12 
13 enum bool isJSExpression(T) = is(typeof((T a){
14     string str = a.jsExpr;
15     Activity act = a.activity;
16 }));
17 
18 
19 mixin template JSExpressionOperators()
20 {
21     import std.format : format;
22     import std.range : iota;
23 
24     void opAssign(JSExpr)(JSExpr expr)
25     if(isJSExpression!JSExpr)
26     in{
27         assert(expr.activity is this.activity);
28     }
29     body{
30         this.activity.runJS(this.jsExpr ~ '=' ~ expr.jsExpr ~ ';');
31     }
32 
33 
34     auto opIndex()(string name)
35     {
36         return this.jsUnaryExpression("%s." ~ name);
37     }
38 
39 
40     void opIndexAssign(JSExpr)(JSExpr expr, string name)
41     if(isJSExpression!JSExpr)
42     in{
43         assert(expr.activity is this.activity);
44     }
45     body {
46         this.activity.runJS(this.jsExpr ~ '.' ~ name ~ '=' ~ expr.jsExpr ~ ';');
47     }
48 
49 
50     void opIndexAssign(T)(T expr, string name)
51     if(is(typeof(JSValue(expr))))
52     {
53         this.opIndexAssign(JSValue(expr), name);
54     }
55 
56 
57     void opIndexAssign(JSValue value, string name)
58     {
59         if(value.isBoolean){
60             if(value.get!bool)
61                 this.activity.runJS(this.jsExpr ~ '.' ~ name ~ "= true;");
62             else
63                 this.activity.runJS(this.jsExpr ~ '.' ~ name ~ "= false;");
64         }else{
65             this.activity.carrierObject.setProperty("value", value);
66             this.activity.runJS(this.jsExpr ~ '.' ~ name ~ "=_carrierObject_.value;");
67         }
68     }
69 
70 
71     auto invoke(T...)(string name, auto ref T args) @system // dmd bug
72     {
73       static if(T.length == 0)
74         return this.jsUnaryExpression("%s." ~ name ~ "()");
75       else{
76         auto v = activity.newJSVariable();
77         foreach(i, ref e; args)
78             v[format("value%s", i)] = e;
79 
80         auto ret = this.jsBinaryExpression(v, "%1$s." ~ name ~ format("(%(%%2$s.value%s, %))", iota(args.length)));
81         return ret;
82       }
83     }
84 }
85 
86 
87 mixin template DOMOperators()
88 {
89     /**
90     .innerHTML
91     */
92     void innerHTML(string html) @property
93     {
94         this["innerHTML"] = html;
95     }
96 
97 
98     string innerHTML() @property
99     {
100         return this["innerHTML"].eval().to!string;
101     }
102 
103 
104     /**
105     See also: HTML.insertAdjacentHTML
106     */
107     void insertAdjacentHTML(string pos, string html)
108     {
109         this.invoke("insertAdjacentHTML", pos, html).run();
110     }
111 
112 
113     /**
114     append html to this, which is block element.
115     See also: jQuery.append
116     */
117     void append(string html)
118     {
119         this.insertAdjacentHTML("beforeend", html);
120     }
121 
122 
123     /**
124     prepend html to this which is block element.
125     See also: jQuery.prepend
126     */
127     void prepend(string html)
128     {
129         this.insertAdjacentHTML("afterbegin", html);
130     }
131 
132 
133     /**
134     insert html after this.
135     See also: jQuery.insertAfter
136     */
137     void insertAfter(string html)
138     {
139         this.insertAdjacentHTML("afterend", html);
140     }
141 
142 
143     /**
144     insert html before this.
145     See also: jQuery.insertBefore
146     */
147     void insertBefore(string html)
148     {
149         this.insertAdjacentHTML("beforebegin", html);
150     }
151 }
152 
153 
154 
155 mixin template JSExprDOMEagerOperators()
156 {
157     auto _jsExprObj() @property { return this.activity.jsExpression(this.jsExpr); }
158 
159     JSValue opIndex()(string name)
160     {
161         return this._jsExprObj[name].eval;
162     }
163 
164 
165     void opIndexAssign(JSExpr)(JSExpr expr, string name)
166     if(isJSExpression!JSExpr)
167     in{
168         assert(expr.activity is this.activity);
169     }
170     body {
171         this._jsExprObj[name] = expr;
172     }
173 
174 
175     void opIndexAssign(T)(T expr, string name)
176     if(is(typeof(JSValue(expr))))
177     {
178         this.opIndexAssign(JSValue(expr), name);
179     }
180 
181 
182     void opIndexAssign(JSValue value, string name)
183     {
184         this._jsExprObj[name] = value;
185     }
186 
187 
188     auto invoke(T...)(string name, auto ref T args)
189     {
190         return this._jsExprObj.invoke(name, forward!args).eval;
191     }
192 
193 
194         /**
195     .innerHTML
196     */
197     void innerHTML(string html) @property
198     {
199         this["innerHTML"] = html;
200     }
201 
202 
203     string innerHTML() @property
204     {
205         return this["innerHTML"].to!string;
206     }
207 
208 
209     /**
210     See also: HTML.insertAdjacentHTML
211     */
212     void insertAdjacentHTML(string pos, string html)
213     {
214         this.invoke("insertAdjacentHTML", pos, html);
215     }
216 
217 
218     /**
219     append html to this, which is block element.
220     See also: jQuery.append
221     */
222     void append(string html)
223     {
224         this.insertAdjacentHTML("beforeend", html);
225     }
226 
227 
228     /**
229     prepend html to this which is block element.
230     See also: jQuery.prepend
231     */
232     void prepend(string html)
233     {
234         this.insertAdjacentHTML("afterbegin", html);
235     }
236 
237 
238     /**
239     insert html after this.
240     See also: jQuery.insertAfter
241     */
242     void insertAfter(string html)
243     {
244         this.insertAdjacentHTML("afterend", html);
245     }
246 
247 
248     /**
249     insert html before this.
250     See also: jQuery.insertBefore
251     */
252     void insertBefore(string html)
253     {
254         this.insertAdjacentHTML("beforebegin", html);
255     }
256 }
257 
258 
259 
260 auto newJSVariable(Activity activity)
261 {
262     static ulong number;
263 
264     if(number == 0){
265         import std.random : rndGen;
266 
267         number = rndGen.front;
268         rndGen.popFront();
269     }else
270         ++number;
271 
272     immutable name = "jsv" ~ to!string(number);
273 
274 
275     static struct JSVariablePayload
276     {
277         ~this()
278         {
279             activity.runJS("delete " ~ _name ~ ";");
280         }
281 
282 
283         string jsExpr() const @property { return _name; }
284         Activity activity() @property { return _activity; }
285 
286       private:
287         Activity _activity;
288         string _name;
289     }
290 
291 
292     struct RefCountedJSVariable
293     {
294         RefCounted!JSVariablePayload impl;
295 
296         alias impl this;
297         mixin JSExpressionOperators!();
298     }
299 
300     activity.runJS(name ~ ` = {};`);
301     return RefCountedJSVariable(RefCounted!JSVariablePayload(activity, name));
302 }
303 
304 
305 struct JSExpressionCode
306 {
307     string jsExpr() const @property { return _code; }
308     Activity activity() @property { return _activity; }
309 
310     mixin JSExpressionOperators!();
311 
312   private:
313     Activity _activity;
314     string _code;
315 }
316 
317 
318 JSExpressionCode jsExpression(Activity activity, string jscode)
319 {
320     return JSExpressionCode(activity, jscode);
321 }
322 
323 
324 auto jsExpression(Activity activity, JSValue value)
325 {
326     auto v = activity.newJSVariable;
327     auto c = activity.carrierObject;
328 
329     c.setProperty("value", value);
330     v = activity.jsExpression("_carrierObject_.value");
331 
332     return v;
333 }
334 
335 
336 JSValue eval(JSExpr)(JSExpr expr)
337 if(isJSExpression!JSExpr)
338 {
339     return expr.activity.evalJS(expr.jsExpr);
340 }
341 
342 
343 void run(JSExpr)(JSExpr expr)
344 if(isJSExpression!JSExpr)
345 {
346     expr.activity.runJS(expr.jsExpr);
347 }
348 
349 
350 auto bindToJSVariable(JSExpr)(JSExpr expr)
351 if(isJSExpression!JSExpr)
352 {
353     auto v = expr.activity.newJSVariable;
354     v = activity.jsExpression(expr.jsExpr);
355     return v;
356 }
357 
358 
359 auto jsBinaryExpression(A, B)(A a, B b, string fmt)
360 if(isJSExpression!A && isJSExpression!B)
361 in{
362     assert(a.activity == b.activity);
363 }
364 body{
365     static struct JSBinaryExpression
366     {
367         Activity activity() @property { return _a.activity; }
368         string jsExpr() const @property { return _jsExpr; }
369 
370         mixin JSExpressionOperators!();
371 
372       private:
373         A _a;
374         B _b;
375         string _jsExpr;
376     }
377 
378 
379     return JSBinaryExpression(a, b, format(fmt, a.jsExpr, b.jsExpr));
380 }
381 
382 
383 auto jsUnaryExpression(A)(A a, string fmt)
384 if(isJSExpression!A)
385 {
386     static struct JSUnaryExpression
387     {
388         Activity activity() @property { return _a.activity; }
389         string jsExpr() const @property { return _jsExpr; }
390 
391         mixin JSExpressionOperators!();
392 
393       private:
394         A _a;
395         string _jsExpr;
396     }
397 
398     return JSUnaryExpression(a, format(fmt, a.jsExpr));
399 }