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