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 }