DomkitViewer.hx 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  1. package hrt.impl;
  2. #if (!hscript || !hscriptPos)
  3. #error "DomkitViewer requires --library hscript with -D hscriptPos"
  4. #end
  5. #if (macro && domkit)
  6. import haxe.macro.Expr;
  7. import haxe.macro.Context;
  8. class DomkitViewer {
  9. static function codeContains( code : haxe.macro.Expr, dynParams : Map<String,Bool> ) {
  10. return switch( code.expr ) {
  11. case EConst(CIdent(v)) if( dynParams.exists(v) ): true;
  12. case EParenthesis(e): codeContains(e,dynParams);
  13. case EBinop(_,e1,e2): codeContains(e1,dynParams) || codeContains(e2,dynParams);
  14. default: false;
  15. }
  16. }
  17. static function removeDynParamsRec( m : domkit.MarkupParser.Markup, dynParams : Map<String,Bool> ) {
  18. if( m.attributes != null ) {
  19. for( a in m.attributes )
  20. switch( a.value ) {
  21. case Code(code) if( codeContains(code,dynParams) ):
  22. m.attributes.remove(a);
  23. default:
  24. }
  25. }
  26. if( m.children != null )
  27. for( c in m.children.copy() ) {
  28. removeDynParamsRec(c, dynParams);
  29. if( c.kind == null ) m.children.remove(c);
  30. }
  31. if( m.condition != null && codeContains(m.condition.cond,dynParams) )
  32. m.condition = null;
  33. switch( m.kind ) {
  34. case For(cond) if( codeContains(cond,dynParams) ):
  35. m.kind = null;
  36. default:
  37. }
  38. }
  39. static function clearImportNames( m : domkit.MarkupParser.Markup ) {
  40. switch( m.kind ) {
  41. case Node(n) if( n != null && n.indexOf(".") >= 0 ): m.kind = Node(n.split(".").pop()); // remove root.name
  42. default:
  43. }
  44. if( m.children != null )
  45. for( c in m.children )
  46. clearImportNames(c);
  47. }
  48. static function remapCDBCode( map : Map<String, Bool>, code : domkit.MarkupParser.CodeExpr ) {
  49. switch( code.expr ) {
  50. case EConst(CIdent(name)) if( map.exists(name) ):
  51. code.expr = EField({ expr : EConst(CIdent(CDB_MODULE)), pos : code.pos }, name+"Kind");
  52. default:
  53. haxe.macro.ExprTools.iter(code, remapCDBCode.bind(map));
  54. }
  55. }
  56. static function iterCode( m : domkit.MarkupParser.Markup, f : domkit.MarkupParser.CodeExpr -> Void ) {
  57. if( m.arguments != null ) {
  58. for( a in m.arguments )
  59. switch( a.value ) {
  60. case Code(code): f(code);
  61. default:
  62. }
  63. }
  64. if( m.attributes != null ) {
  65. for( a in m.attributes )
  66. switch( a.value ) {
  67. case Code(code): f(code);
  68. default:
  69. }
  70. }
  71. if( m.condition != null )
  72. f(m.condition.cond);
  73. if( m.children != null )
  74. for( c in m.children )
  75. iterCode(c, f);
  76. switch( m.kind ) {
  77. case For(cond): f(cond);
  78. default:
  79. }
  80. }
  81. public static function loadSource( path : String, pos : Position, fields : Array<Field>, extraParams : Array<Expr> ) {
  82. var name = path.split("/").pop().split("_").join("-");
  83. var dotPos = name.lastIndexOf(".");
  84. if( dotPos >= 0 ) {
  85. path = path.substr(0, path.length - name.length + dotPos);
  86. name = name.substr(dotPos+1);
  87. }
  88. path += ".domkit";
  89. var fullPath = try Context.resolvePath(path) catch( e : Dynamic ) return null;
  90. if( fullPath == null )
  91. return null;
  92. var staticCSS = false;
  93. var hasCSS = true;
  94. while( extraParams.length > 0 ) {
  95. switch( extraParams[0].expr ) {
  96. case EConst(CIdent("staticCSS")): staticCSS = true;
  97. case EConst(CIdent("noCSS")): hasCSS = false;
  98. default:
  99. Context.error("Invalid parameter", extraParams[0].pos);
  100. return null;
  101. }
  102. extraParams.shift();
  103. }
  104. Context.registerModuleDependency(Context.getLocalModule(),fullPath);
  105. var fullData = sys.io.File.getContent(fullPath);
  106. var data = DomkitFile.parse(fullData);
  107. var p = new domkit.MarkupParser();
  108. var index = fullData.indexOf(data.dml);
  109. try {
  110. var m = p.parse(data.dml, fullPath, index);
  111. for( c in m.children ) {
  112. switch( c.kind ) {
  113. case Node(n) if( n.indexOf(":") >= 0 ): c.kind = Node(n.split(":")[0]);
  114. default:
  115. }
  116. if( c.arguments != null ) c.arguments = null;
  117. }
  118. var found = null;
  119. for( c in m.children ) {
  120. switch( c.kind ) {
  121. case Node(n) if( n == name ):
  122. found = c;
  123. break;
  124. default:
  125. }
  126. }
  127. if( found == null ) {
  128. Context.error("Could not find definition for component '"+name+"'", Context.currentPos());
  129. return null;
  130. }
  131. m.children = [found];
  132. var params = new hscript.Parser().parseString(data.params, path);
  133. var dynParams = new Map();
  134. var hasDynParam = false;
  135. switch( params.e ) {
  136. case EObject(fields):
  137. for( f in fields )
  138. if( f.name == "dynamicParams" ) {
  139. switch( f.e.e ) {
  140. case EArrayDecl(values):
  141. for( v in values )
  142. switch( v.e ) {
  143. case EConst(CString(v)):
  144. dynParams.set(v, true);
  145. hasDynParam = true;
  146. default:
  147. }
  148. default:
  149. }
  150. }
  151. default:
  152. }
  153. if( hasDynParam )
  154. removeDynParamsRec(m, dynParams);
  155. clearImportNames(m);
  156. if( data.enums != null ) {
  157. var enums : Array<{ path : String, constrs : Array<String> }> = haxe.Json.parse(data.enums);
  158. for( e in enums )
  159. if( e.path == "$cdb" ) {
  160. var map = [for( c in e.constrs ) c => true];
  161. iterCode(m,remapCDBCode.bind(map));
  162. break;
  163. }
  164. }
  165. if( hasCSS ) {
  166. fields.push({
  167. name : "__CSS",
  168. access : [AStatic],
  169. kind : FVar(null, macro hrt.impl.DomkitViewer.DomkitStyle.registerCSSSource($v{path},$v{staticCSS?fullData:null})),
  170. pos : pos,
  171. });
  172. }
  173. return { dml : m, pos : Context.makePosition({ file : fullPath, min : index, max : index + data.dml.length }) };
  174. } catch( e : domkit.Error ) {
  175. Context.error(e.message, Context.makePosition({ file : fullPath, min : e.pmin, max : e.pmax }));
  176. return null;
  177. }
  178. }
  179. public static var CDB_MODULE = "Data";
  180. public static function init() {
  181. domkit.Macros.onSourceLoad = loadSource;
  182. }
  183. }
  184. #elseif domkit
  185. import h2d.domkit.BaseComponents;
  186. import domkit.MarkupParser.Markup;
  187. class DomkitInterp extends hscript.Async.AsyncInterp {
  188. public function executeLoop( n : String, it : hscript.Expr, callb ) {
  189. var old = declared.length;
  190. declared.push({ n : n, old : locals.get(n) });
  191. var it = makeIterator(expr(it));
  192. while( it.hasNext() ) {
  193. locals.set(n,{ r : it.next() });
  194. if( !loopRun(callb) )
  195. break;
  196. }
  197. restore(old);
  198. }
  199. public function executeKeyValueLoop( vk : String, vv : String, it : hscript.Expr, callb ) {
  200. var old = declared.length;
  201. declared.push({ n : vk, old : locals.get(vk) });
  202. declared.push({ n : vv, old : locals.get(vv) });
  203. var it = makeKeyValueIterator(expr(it));
  204. while( it.hasNext() ) {
  205. var v = it.next();
  206. locals.set(vk,{ r : v.key });
  207. locals.set(vv,{ r : v.value });
  208. if( !loopRun(callb) )
  209. break;
  210. }
  211. restore(old);
  212. }
  213. }
  214. class DomkitBaseContext {
  215. public function new() {
  216. }
  217. public function loadTile( url : String ) {
  218. return hxd.res.Loader.currentInstance.load(url).toTile();
  219. }
  220. }
  221. private typedef CompMap = Map<String, Array<Dynamic> -> h2d.Object -> h2d.Object>;
  222. class DomkitViewer extends h2d.Object {
  223. var resource : hxd.res.Resource;
  224. var style : DomkitStyle;
  225. var current : h2d.Object;
  226. var currentObj : h2d.Object;
  227. var currentRoot : h2d.Object;
  228. var contexts : Array<Dynamic> = [];
  229. var variables : Map<String,Dynamic> = [];
  230. var rebuilding = false;
  231. var rootObject : h2d.Object;
  232. var componentsPaths : Array<String> = [];
  233. var loadedComponents : Array<domkit.Component<h2d.Object, h2d.Object>> = [];
  234. var compHooks : CompMap = [];
  235. var definedClasses : Array<String> = [];
  236. var loadedResources : Array<{ r : hxd.res.Resource, wasLoaded : Bool }> = [];
  237. var tmpCompMap : CompMap;
  238. public function new( style : DomkitStyle, res : hxd.res.Resource, ?parent ) {
  239. super(parent);
  240. this.style = style;
  241. this.resource = res;
  242. addContext(new DomkitBaseContext());
  243. rebuildDelay();
  244. }
  245. function loadResource( res : hxd.res.Resource ) {
  246. var loaded = false;
  247. var path = res.entry.path;
  248. for( r in @:privateAccess style.resources )
  249. if( r.entry.path == path ) {
  250. loaded = true;
  251. break;
  252. }
  253. loadedResources.push({ r : res, wasLoaded: loaded });
  254. if( !loaded ) handleErrors(res, () -> style.load(res));
  255. res.watch(rebuild);
  256. }
  257. function rebuildDelay() {
  258. if( rebuilding ) return;
  259. rebuilding = true;
  260. haxe.Timer.delay(() -> { rebuilding = false; rebuild(); },0);
  261. }
  262. public function addComponentsPath( dir : String ) {
  263. componentsPaths.push(dir);
  264. rebuildDelay();
  265. }
  266. public function addContext( ctx : Dynamic ) {
  267. contexts.push(ctx);
  268. rebuildDelay();
  269. }
  270. public function addGlobal( name : String, v : Dynamic ) {
  271. variables.set(name, v);
  272. rebuildDelay();
  273. }
  274. public function addComponentHook( name : String, make ) {
  275. compHooks.set(name, make);
  276. }
  277. #if castle
  278. public function addCDB( cdb : cdb.Types.IndexId<Dynamic,Dynamic> ) {
  279. var obj = {};
  280. var idName = null;
  281. for( c in @:privateAccess cdb.sheet.columns ) {
  282. switch( c.type ) {
  283. case TId: idName = c.name; break;
  284. default:
  285. }
  286. }
  287. for( o in cdb.all ) {
  288. var id = Reflect.field(o, idName);
  289. if( id == null || id == "" ) continue;
  290. Reflect.setField(obj, id, id);
  291. }
  292. var name = @:privateAccess cdb.name;
  293. name = name.charAt(0).toUpperCase() + name.substr(1);
  294. variables.set(name, obj);
  295. rebuildDelay();
  296. }
  297. #end
  298. override function onRemove() {
  299. super.onRemove();
  300. if( currentObj != null )
  301. currentObj.remove();
  302. unload();
  303. }
  304. function unload() {
  305. // force rewatch
  306. for( r in loadedResources ) {
  307. if( r.wasLoaded )
  308. style.load(r.r);
  309. else
  310. style.unload(r.r);
  311. }
  312. for( c in loadedComponents ) {
  313. @:privateAccess domkit.Component.COMPONENTS.remove(c.name);
  314. @:privateAccess domkit.CssStyle.CssData.COMPONENTS.remove(c);
  315. }
  316. loadedResources = [];
  317. loadedComponents = [];
  318. }
  319. public dynamic function onError( res : hxd.res.Resource, e : domkit.Error ) @:privateAccess {
  320. var text = res.entry.getText();
  321. var line = text.substr(0, e.pmin).split("\n").length;
  322. var err = res.entry.path+":"+line+": "+e.message;
  323. style.errors.remove(err);
  324. style.errors.push(err);
  325. style.refreshErrors(getScene());
  326. }
  327. function makeInterp() {
  328. var interp = new DomkitInterp();
  329. for( c in contexts )
  330. interp.setContext(c);
  331. for( name => value in variables )
  332. interp.variables.set(name, value);
  333. return interp;
  334. }
  335. public static function toStr(data:DomkitFileData) {
  336. var parts = ['<css>\n${data.css}\n</css>'];
  337. if( data.params != '' && data.params != '{}' )
  338. parts.push('<params>\n${data.params}\n</params>');
  339. if( data.enums != null )
  340. parts.push('<enums>\n${data.enums}\n</enums>');
  341. parts.push(data.dml);
  342. return parts.join('\n\n');
  343. }
  344. function rebuild() {
  345. @:privateAccess {
  346. style.errors = [];
  347. style.refreshErrors();
  348. }
  349. var root = new h2d.Flow();
  350. root.dom = domkit.Properties.create("flow",root,{ "class" : "debugRoot", layout : "stack", "content-align" : "middle middle", "fill-width" : "true", "fill-height" : "true" });
  351. unload();
  352. tmpCompMap = compHooks.copy();
  353. try {
  354. loadResource(resource);
  355. }
  356. catch(e: domkit.Error) {
  357. style.cssParser.warnings.push({ msg : e.message, pmin : e.pmin, pmax : e.pmax });
  358. }
  359. var inf = loadComponents(resource);
  360. var obj : h2d.Object = null;
  361. var mainComp = inf.comps[inf.comps.length - 1];
  362. if( mainComp != null )
  363. obj = mainComp([], root);
  364. if( inf.params != null && obj != null ) {
  365. var classes : Array<String> = Std.downcast(inf.params.classes,Array);
  366. if( classes != null ) {
  367. var checks = new h2d.Flow(root);
  368. checks.dom = domkit.Properties.create("flow",checks,{ "class" : "debugClasses" });
  369. var p = root.getProperties(checks);
  370. p.isAbsolute = true;
  371. p.verticalAlign = Top;
  372. p.horizontalAlign = Middle;
  373. p.paddingTop = 5;
  374. for( c in definedClasses.copy() )
  375. if( classes.indexOf(c) < 0 )
  376. definedClasses.remove(c);
  377. for( cl in classes ) {
  378. var c = new h2d.CheckBox(checks);
  379. c.dom = domkit.Properties.create("flow",c);
  380. c.text = cl;
  381. if( definedClasses.indexOf(cl) >= 0 )
  382. c.selected = true;
  383. c.onChange = function() {
  384. obj.dom.toggleClass(cl, c.selected);
  385. if( c.selected ) definedClasses.push(cl) else definedClasses.remove(cl);
  386. if( Reflect.field(inf.params,cl) != null )
  387. rebuild();
  388. };
  389. }
  390. for( c in definedClasses )
  391. obj.dom.addClass(c);
  392. }
  393. }
  394. if( currentObj != null ) {
  395. currentObj.remove();
  396. currentObj = null;
  397. }
  398. if( current != null ) {
  399. current.remove();
  400. style.removeObject(current);
  401. }
  402. addChild(root);
  403. style.addObject(root);
  404. current = root;
  405. currentObj = obj;
  406. var errors = @:privateAccess style.errors.copy();
  407. @:privateAccess style.onChange(); // force trigger reload (css might have changed)
  408. if( errors.length > 0 ) {
  409. @:privateAccess style.errors = errors.concat(style.errors);
  410. @:privateAccess style.refreshErrors(getScene());
  411. }
  412. }
  413. inline function error(msg,pmin,pmax) {
  414. throw new domkit.Error(msg, pmin, pmax);
  415. }
  416. function loadComponents( res : hxd.res.Resource ) {
  417. var fullText = res.entry.getText();
  418. var data = DomkitFile.parse(fullText);
  419. var inf = { comps : [], params : (null:Dynamic) };
  420. handleErrors(res, function() {
  421. var parser = new domkit.MarkupParser();
  422. parser.allowRawText = true;
  423. var eparams = parseCode(data.params, fullText.indexOf(data.params));
  424. var expr = parser.parse(data.dml,res.entry.path, fullText.indexOf(data.dml));
  425. var interp = makeInterp();
  426. if( data.enums != null ) {
  427. var enums : Array<{ path : String, constrs : Array<String> }> = haxe.Json.parse(data.enums);
  428. for( e in enums ) {
  429. var en = Type.resolveEnum(e.path);
  430. if( en == null ) continue;
  431. for( c in e.constrs ) {
  432. var f : Dynamic = try Type.createEnum(en, c) catch( e : Dynamic ) Reflect.makeVarArgs((args) -> Type.createEnum(en,c,args));
  433. if( f != null && !interp.variables.exists(c) ) interp.variables.set(c, f);
  434. }
  435. }
  436. }
  437. var mainComp = null;
  438. for( i in 0...expr.children.length ) {
  439. var m = expr.children[expr.children.length - i - 1];
  440. switch( m.kind ) {
  441. case Node(name):
  442. mainComp = domkit.Component.get(name, true);
  443. break;
  444. default:
  445. }
  446. }
  447. var prev = interp.variables.copy();
  448. var mainInst : Dynamic = null;
  449. if( mainComp != null ) {
  450. var cl = @:privateAccess mainComp.classValue;
  451. if( cl != null ) {
  452. mainInst = Type.createEmptyInstance(cl);
  453. interp.setContext(mainInst);
  454. }
  455. }
  456. var vparams = switch( eparams.e ) {
  457. case EObject(fl):
  458. [for( f in fl ) {
  459. var val : Dynamic = evalCode(interp,f.e);
  460. var forceNull = res == resource && definedClasses.indexOf(f.name) >= 0;
  461. if( forceNull ) val = null;
  462. interp.variables.set(f.name, val);
  463. if( mainInst != null ) try Reflect.setProperty(mainInst,f.name, val) catch( e : Dynamic ) {};
  464. { name : f.name, value : val };
  465. }];
  466. default: throw "assert";
  467. }
  468. interp.variables = prev;
  469. for( f in vparams )
  470. interp.variables.set(f.name, f.value);
  471. for( m in expr.children ) {
  472. switch( m.kind ) {
  473. case Node(name):
  474. if( tmpCompMap.exists(name) )
  475. error("Duplicate component "+name, m.pmin, m.pmax);
  476. var parentName = m.parent?.name ?? "flow";
  477. var compParent = resolveComponent(parentName, m.pmin);
  478. var comp = domkit.Component.get(name, true);
  479. var inst : Dynamic = null;
  480. if( comp == null ) {
  481. comp = new domkit.Component(name,null,compParent);
  482. domkit.CssStyle.CssData.registerComponent(comp);
  483. loadedComponents.push(cast comp);
  484. } else {
  485. var compClass = @:privateAccess comp.classValue;
  486. if( compClass != null ) {
  487. inst = Type.createEmptyInstance(compClass);
  488. interp.setContext(inst);
  489. for( f in vparams )
  490. try Reflect.setProperty(inst, f.name, f.value) catch( e : Dynamic ) {}
  491. }
  492. }
  493. var argNames = [];
  494. if( m.arguments != null ) {
  495. for( arg in m.arguments ) {
  496. switch( arg.value ) {
  497. case Code(code):
  498. var code = parseCode(code.split(":")[0], arg.pmin);
  499. switch( code.e ) {
  500. case EIdent(a):
  501. argNames.push(a);
  502. continue;
  503. default:
  504. }
  505. default:
  506. }
  507. error("Invalid argument decl", arg.pmin, arg.pmax);
  508. }
  509. }
  510. function make( args : Array<Dynamic>, parent : h2d.Object ) : h2d.Object {
  511. var prev = interp.variables.copy();
  512. var obj = null;
  513. handleErrors(res, function() {
  514. if( args.length > 0 && argNames.length > 0 ) {
  515. for( i => arg in argNames ) {
  516. interp.variables.set(arg, args[i]);
  517. if( inst != null )
  518. try Reflect.setProperty(inst, arg, args[i]) catch( e : Dynamic ) {};
  519. }
  520. }
  521. var fmake = tmpCompMap.get(comp.parent.name);
  522. if( fmake == null ) fmake = compHooks.get(comp.parent.name);
  523. if( fmake == null ) {
  524. fmake = comp.parent.make;
  525. if( m.parent != null )
  526. args = evalArgs(interp, m.parent.arguments);
  527. }
  528. obj = fmake(args, parent);
  529. if( obj.dom == null )
  530. obj.dom = new domkit.Properties(obj, cast comp);
  531. else
  532. @:privateAccess obj.dom.component = cast comp;
  533. interp.variables.set("this", inst ?? obj);
  534. if( inst != null )
  535. inst.dom = obj.dom;
  536. });
  537. var prevRoot = currentRoot;
  538. currentRoot = cast obj.dom.contentRoot;
  539. for( c in m.children )
  540. handleErrors(res, () -> addRec(c, interp, obj));
  541. interp.variables = prev;
  542. @:privateAccess obj.dom.contentRoot = currentRoot;
  543. currentRoot = prevRoot;
  544. if( inst != null )
  545. inst.dom = null;
  546. return obj;
  547. }
  548. tmpCompMap.set(name, make);
  549. inf.comps.push(make);
  550. default:
  551. }
  552. }
  553. inf.params = {};
  554. for( v in vparams )
  555. Reflect.setField(inf.params, v.name, v.value);
  556. });
  557. return inf;
  558. }
  559. function parseCode( codeStr : String, pos : Int ) {
  560. var parser = new hscript.Parser();
  561. return parser.parseString(codeStr, "");
  562. }
  563. function evalCode( interp : hscript.Interp, e : hscript.Expr ) : Dynamic {
  564. return @:privateAccess interp.expr(e);
  565. }
  566. public static function getParentName( expr : hscript.Expr ) {
  567. switch( expr.e ) {
  568. case EIdent(name):
  569. return name;
  570. case EBinop("-", e1, e2):
  571. var e1 = getParentName(e1);
  572. var e2 = getParentName(e2);
  573. return e1 == null || e2 == null ? null : e1+"-"+e2;
  574. default:
  575. return null;
  576. }
  577. }
  578. function handleErrors( res : hxd.res.Resource, callb ) {
  579. try {
  580. callb();
  581. } catch( e : domkit.Error ) {
  582. onError(res, e);
  583. } catch( e : hscript.Expr.Error ) {
  584. var p = e.toString().split(": ");
  585. p.shift();
  586. var msg = p.join(": ");
  587. onError(res, new domkit.Error(msg, e.pmin, e.pmax));
  588. }
  589. }
  590. function resolveComponent( fullName : String, pmin : Int ) {
  591. var name = fullName.split(".").pop();
  592. var comp = domkit.Component.get(name, true);
  593. if( comp == null ) {
  594. for( path in componentsPaths ) {
  595. var res = try hxd.res.Loader.currentInstance.load(path+"/"+name+".domkit") catch( e : hxd.res.NotFound ) continue;
  596. loadResource(res);
  597. loadComponents(res);
  598. comp = domkit.Component.get(name, true);
  599. if( comp == null ) {
  600. error(res.entry.path+" does not define component "+name, pmin, pmin + name.length);
  601. return null;
  602. }
  603. break;
  604. }
  605. }
  606. if( comp == null )
  607. error("Unknown component "+name, pmin, pmin + name.length);
  608. return comp;
  609. }
  610. function evalArgs( interp : DomkitInterp, args : Array<domkit.MarkupParser.Argument> ) : Array<Dynamic> {
  611. return [for( a in args ) {
  612. var v : Dynamic = switch( a.value ) {
  613. case RawValue(v): v;
  614. case Code(code):
  615. var code = parseCode(code, a.pmin);
  616. evalCode(interp, code);
  617. }
  618. v;
  619. }];
  620. }
  621. function addRec( e : domkit.MarkupParser.Markup, interp : DomkitInterp, parent : h2d.Object ) {
  622. var parentObj = cast(parent.dom?.contentRoot,h2d.Object) ?? parent;
  623. switch( e.kind ) {
  624. case Node(name):
  625. if( e.condition != null ) {
  626. var expr = parseCode(e.condition.cond, e.condition.pmin);
  627. if( !evalCode(interp, expr) )
  628. return;
  629. }
  630. var comp = resolveComponent(name, e.pmin+1);
  631. var args = evalArgs(interp, e.arguments);
  632. var make = tmpCompMap.get(comp.name);
  633. var obj = make != null ? make(args, parentObj) : comp.make(args, parentObj);
  634. if( obj == null )
  635. return;
  636. var p : domkit.Properties<Dynamic> = obj.dom;
  637. if( p == null )
  638. p = obj.dom = new domkit.Properties(obj, cast comp);
  639. var attributes = {};
  640. for( a in e.attributes ) {
  641. if( a.name == "id" ) {
  642. var objId = switch( a.value ) {
  643. case RawValue("true"):
  644. var name = null;
  645. for( a in e.attributes )
  646. if( a.name == "class" ) {
  647. name = switch( a.value ) { case RawValue(v): v; default: null; };
  648. break;
  649. }
  650. name;
  651. case RawValue(name) if( StringTools.endsWith(name,"[]") ):
  652. name.substr(0,name.length - 2);
  653. case RawValue(name):
  654. name;
  655. case Code(_): null;
  656. }
  657. if( objId != null )
  658. (attributes:Dynamic).id = objId;
  659. continue;
  660. }
  661. if( a.name == "__content__" ) {
  662. currentRoot = obj;
  663. continue;
  664. }
  665. switch( a.value ) {
  666. case RawValue(v):
  667. Reflect.setField(attributes,a.name,v);
  668. case Code(_):
  669. // skip (init after)
  670. }
  671. }
  672. p.initAttributes(attributes);
  673. for( a in e.attributes ) {
  674. switch( a.value ) {
  675. case RawValue(_):
  676. case Code(code):
  677. var h = comp.getHandler(domkit.Property.get(a.name));
  678. var v : Dynamic = evalCode(interp, parseCode(code, a.pmin));
  679. @:privateAccess p.initStyle(a.name, v);
  680. if( h == null ) {
  681. // might be a class field
  682. try Reflect.setProperty(obj, a.name, v) catch( e : Dynamic ) {}
  683. } else {
  684. h.apply(p.obj, v);
  685. }
  686. }
  687. }
  688. for( c in e.children )
  689. addRec(c, interp, cast p.contentRoot);
  690. case Text(text):
  691. var tf = new h2d.HtmlText(hxd.res.DefaultFont.get(), parentObj);
  692. tf.dom = domkit.Properties.create("html-text", tf);
  693. tf.text = text;
  694. case For(cond):
  695. var expr = parseCode("for"+cond+"{}", e.pmin);
  696. switch( expr.e ) {
  697. case EFor(n,it,_):
  698. interp.executeLoop(n, it, function() {
  699. for( c in e.children )
  700. addRec(c, interp, parent);
  701. });
  702. return;
  703. case EForGen(it,_):
  704. hscript.Tools.getKeyIterator(it, function(vk,vv,it) {
  705. if( vk == null ) {
  706. throw new domkit.Error("Invalid for loop", e.pmin);
  707. return;
  708. }
  709. interp.executeKeyValueLoop(vk,vv,it,function() {
  710. for( c in e.children )
  711. addRec(c, interp, parent);
  712. });
  713. });
  714. return;
  715. default:
  716. }
  717. throw new domkit.Error("Invalid for loop", e.pmin);
  718. case CodeBlock(v):
  719. throw new domkit.Error("Code block not supported", e.pmin);
  720. case Macro(id):
  721. throw new domkit.Error("Macro not supported", e.pmin);
  722. }
  723. }
  724. }
  725. class DomkitStyle extends h2d.domkit.Style {
  726. public function new() {
  727. super();
  728. }
  729. public function loadDefaults( globals : Array<hxd.res.Resource> ) {
  730. for( r in globals )
  731. load(r, true, true);
  732. for( path in CSS_SOURCES ) {
  733. var content = CONTENT.get(path);
  734. if( content == null )
  735. load(hxd.res.Loader.currentInstance.load(path));
  736. else
  737. load(new hxd.res.Resource(new hxd.fs.BytesFileSystem.BytesFileEntry(path,haxe.io.Bytes.ofString(content))));
  738. }
  739. }
  740. override function loadData( r : hxd.res.Resource ) {
  741. if( r.entry.extension != "domkit" )
  742. return super.loadData(r);
  743. var fullData = r.entry.getText();
  744. var data = DomkitFile.parse(fullData);
  745. return data.css;
  746. }
  747. static var CSS_SOURCES = [];
  748. static var CONTENT = new Map();
  749. public static function registerCSSSource( path : String, ?content : String ) {
  750. if( CSS_SOURCES.indexOf(path) >= 0 )
  751. return false;
  752. CSS_SOURCES.push(path);
  753. if( content != null ) CONTENT.set(path, content);
  754. return true;
  755. }
  756. }
  757. #end
  758. typedef DomkitFileData = { css : String, params : String, dml : String, ?enums : String };
  759. class DomkitFile {
  760. public static function parse( content : String ) : DomkitFileData {
  761. var cssText = "";
  762. var paramsText = "{}";
  763. content = StringTools.trim(content);
  764. if( StringTools.startsWith(content,"<css>") ) {
  765. var pos = content.indexOf("</css>");
  766. cssText = StringTools.trim(content.substr(5, pos - 6));
  767. content = content.substr(pos + 6);
  768. content = StringTools.trim(content);
  769. }
  770. if( StringTools.startsWith(content,"<params>") ) {
  771. var pos = content.indexOf("</params>");
  772. paramsText = StringTools.trim(content.substr(8, pos - 9));
  773. content = content.substr(pos + 9);
  774. content = StringTools.trim(content);
  775. }
  776. var enums = null;
  777. if( StringTools.startsWith(content,"<enums>") ) {
  778. var pos = content.indexOf("</enums>");
  779. enums = StringTools.trim(content.substr(7, pos - 8));
  780. content = content.substr(pos + 8);
  781. content = StringTools.trim(content);
  782. }
  783. return {
  784. css : cssText,
  785. params : paramsText,
  786. dml : content,
  787. enums : enums,
  788. }
  789. }
  790. }