diff --git a/experiments/modern/src/App.js b/experiments/modern/src/App.js
index 7e87be3d..6ff49c53 100644
--- a/experiments/modern/src/App.js
+++ b/experiments/modern/src/App.js
@@ -7,8 +7,6 @@ const System = require("./runtime/System");
const runtime = require("./runtime");
const interpret = require("./maki-interpreter/interpreter");
-const SkinContext = React.createContext(null);
-
async function getSkin() {
// const resp = await fetch(process.env.PUBLIC_URL + "/skins/CornerAmp_Redux.wal");
const resp = await fetch(process.env.PUBLIC_URL + "/skins/simple.wal");
@@ -43,6 +41,7 @@ function Container(props) {
}
function Layout({
+ node,
id,
background,
desktopalpha,
@@ -54,13 +53,17 @@ function Layout({
droptarget,
children,
}) {
- const data = React.useContext(SkinContext);
if (background == null) {
console.warn("Got a Layout without a background. Rendering null", id);
return null;
}
- const image = data[background];
+ const image = node.js_imageLookup(background);
+ if (image == null) {
+ console.warn("Unable to find image to render. Rendering null", background);
+ return null;
+ }
+
return (
<>
{
- getSkin().then(async ({ root, registry }) => {
+ getSkin().then(async root => {
// Execute scripts
await Utils.asyncTreeFlatMap(root, async node => {
switch (node.xmlNode.name) {
@@ -238,12 +239,17 @@ function App() {
}
case "script": {
// TODO: stop ignoring standardframe
- if (node.xmlNode.file.endsWith("standardframe.maki")) {
+ if (node.xmlNode.attributes.file.endsWith("standardframe.maki")) {
break;
}
const scriptGroup = Utils.findParentNodeOfType(node, ["group", "WinampAbstractionLayer", "WasabiXML"]);
const system = new System(scriptGroup);
- await interpret({ runtime, data: node.xmlNode.script, system, log: false });
+ await interpret({
+ runtime,
+ data: node.js_annotations.script,
+ system,
+ log: false,
+ });
return node;
}
default: {
@@ -252,19 +258,15 @@ function App() {
}
});
- setData({ root, registry });
+ setData(root);
});
}, []);
if (data == null) {
return
Loading...
;
}
- const { root, registry } = data;
+ const root = data;
- return (
-
-
-
- );
+ return ;
}
export default App;
diff --git a/experiments/modern/src/initialize.js b/experiments/modern/src/initialize.js
index 6f81da2d..5130ab46 100644
--- a/experiments/modern/src/initialize.js
+++ b/experiments/modern/src/initialize.js
@@ -4,16 +4,15 @@ const WinampAbstractionLayer = require("./runtime/WinampAbstractionLayer");
const Layout = require('./runtime/Layout');
const Layer = require('./runtime/Layer');
const Container = require('./runtime/Container');
+const JsElements = require("./runtime/JsElements");
+const JsGammaSet = require("./runtime/JsGammaSet");
+const JsGroupDef = require("./runtime/JsGroupDef");
const Group = require("./runtime/Group");
const Button = require("./runtime/Button");
const ToggleButton = require("./runtime/ToggleButton");
const Text = require("./runtime/Text");
const Status = require("./runtime/Status");
-function splitValues(str) {
- return str.split(",").map(parseFloat);
-}
-
async function loadImage(imgUrl) {
return await new Promise(resolve => {
const img = new Image();
@@ -123,12 +122,7 @@ const schema = {
const noop = (node, parent) => new MakiObject(node, parent);
const parsers = {
- groupdef: (node, parent, registry) => {
- const attributeId = node.attributes.id;
- registry.groupdefs[attributeId] = node;
-
- return new MakiObject(node, parent);
- },
+ groupdef: (node, parent) => new JsGroupDef(node, parent),
skininfo: noop,
version: noop,
name: noop,
@@ -139,39 +133,17 @@ const parsers = {
screenshot: noop,
container: (node, parent) => new Container(node, parent),
scripts: noop,
- gammaset: (node, parent, registry) => {
- const gammaId = node.attributes.id;
- if (!registry.gammasets.hasOwnProperty(gammaId)) {
- registry.gammasets[gammaId] = {};
- }
-
- return new MakiObject(node, parent);
- },
+ gammaset: (node, parent) => new JsGammaSet(node, parent),
color: noop,
layer: (node, parent) => new Layer(node, parent),
layoutstatus: noop,
hideobject: noop,
button: (node, parent) => new Button(node, parent),
- group: (node, parent, registry) => {
- if (!node.children || node.children.length === 0) {
- const groupdef = registry.groupdefs[node.attributes.id];
- if (groupdef) {
- const newNode = {
- ...node,
- ...groupdef,
- attributes: { ...node.attributes, ...groupdef.attributes },
- name: "group",
- };
- return new Group(newNode, parent);
- }
- }
-
- return new Group(node, parent);
- },
+ group: (node, parent) => new Group(node, parent),
layout: (node, parent) => new Layout(node, parent),
sendparams: noop,
- elements: noop,
- bitmap: async (node, parent, registry, zip) => {
+ elements: (node, parent) => new JsElements(node, parent),
+ bitmap: async (node, parent, zip) => {
let { file, gammagroup, h, id, w, x, y } = node.attributes;
// TODO: Escape file for regex
const img = Utils.getCaseInsensitveFile(zip, file);
@@ -187,20 +159,21 @@ const parsers = {
x = x !== undefined ? x : 0;
y = y !== undefined ? y : 0;
}
- registry.images[id.toLowerCase()] = { file, gammagroup, h, w, x, y, imgUrl };
- return new MakiObject(node, parent);
+ return new MakiObject(node, parent, {
+ id,
+ file,
+ gammagroup,
+ h,
+ w,
+ x,
+ y,
+ imgUrl,
+ });
},
eqvis: noop,
slider: noop,
- gammagroup: (node, parent, registry) => {
- const gammaId = parent.xmlNode.attributes.id;
- const attributeId = node.attributes.id;
- const attributeValues = splitValues(node.attributes.value);
- registry.gammasets[gammaId][attributeId] = attributeValues;
-
- return new MakiObject(node, parent);
- },
+ gammagroup: noop,
truetypefont: noop,
component: noop,
text: (node, parent) => new Text(node, parent),
@@ -226,17 +199,13 @@ const parsers = {
menu: noop,
albumart: noop,
playlistplus: noop,
- async script(node, parent, registry, zip) {
- const { id, file, param } = node.attributes;
- const script = await Utils.readUint8array(zip, file);
- registry.scripts.push({ parent, id, param, script });
-
- const newNode = { ...node, script, param, file };
- return new MakiObject(newNode, parent);
+ async script(node, parent, zip) {
+ const script = await Utils.readUint8array(zip, node.attributes.file);
+ return new MakiObject(node, parent, { script });
},
};
-async function parseChildren(node, registry, zip) {
+async function parseChildren(node, zip) {
if (node.xmlNode.type === "comment") {
return;
}
@@ -274,9 +243,12 @@ async function parseChildren(node, registry, zip) {
throw new Error(`Missing parser for ${childName}`);
return;
}
- const parsedChild = await childParser(child, node, registry, zip);
- if (parsedChild.xmlNode.children != null && parsedChild.xmlNode.children.length > 0) {
- await parseChildren(parsedChild, registry, zip);
+ const parsedChild = await childParser(child, node, zip);
+ if (
+ parsedChild.xmlNode.children != null &&
+ parsedChild.xmlNode.children.length > 0
+ ) {
+ await parseChildren(parsedChild, zip);
}
return parsedChild;
})
@@ -287,11 +259,43 @@ async function parseChildren(node, registry, zip) {
node.js_addChildren(filteredChildren);
}
+async function applyGroupDefs(root) {
+ await Utils.asyncTreeFlatMap(root, async node => {
+ switch (node.xmlNode.name) {
+ case "group": {
+ if (!node.children || node.children.length === 0) {
+ const groupdef = node.js_groupdefLookup(node.xmlNode.attributes.id);
+ if (!groupdef) {
+ console.warn(
+ "Unable to find groupdef. Rendering null",
+ node.xmlNode.attributes.id
+ );
+ return {};
+ }
+ node.children = groupdef.children;
+ // Do we need to copy the items instead of just changing the parent?
+ node.children.forEach(item => {
+ item.parent = node;
+ });
+ node.xmlNode.attributes = {
+ ...node.xmlNode.attributes,
+ ...groupdef.xmlNode.attributes,
+ };
+ }
+ return {};
+ }
+ default: {
+ return node;
+ }
+ }
+ });
+}
+
async function initialize(zip, skinXml) {
- const registry = { scripts: [], gammasets: {}, images: {}, groupdefs: {} };
const root = new WinampAbstractionLayer(skinXml.children[0], null);
- await parseChildren(root, registry, zip);
- return { root, registry };
+ await parseChildren(root, zip);
+ await applyGroupDefs(root);
+ return root;
}
export default initialize;
diff --git a/experiments/modern/src/runtime/GuiObject.js b/experiments/modern/src/runtime/GuiObject.js
index 4784a2b2..f66fb228 100644
--- a/experiments/modern/src/runtime/GuiObject.js
+++ b/experiments/modern/src/runtime/GuiObject.js
@@ -24,6 +24,7 @@ class GuiObject extends MakiObject {
return this.findobject(id);
}
init(newRoot) {
+ this.parent = newRoot;
newRoot.js_addChild(this);
return this;
}
diff --git a/experiments/modern/src/runtime/JsElements.js b/experiments/modern/src/runtime/JsElements.js
new file mode 100644
index 00000000..d771eaa0
--- /dev/null
+++ b/experiments/modern/src/runtime/JsElements.js
@@ -0,0 +1,15 @@
+const MakiObject = require("./MakiObject");
+
+class JsElements extends MakiObject {
+ /**
+ * getclassname()
+ *
+ * Returns the class name for the object.
+ * @ret The class name.
+ */
+ static getclassname() {
+ return "Elements";
+ }
+}
+
+module.exports = JsElements;
diff --git a/experiments/modern/src/runtime/JsGammaSet.js b/experiments/modern/src/runtime/JsGammaSet.js
new file mode 100644
index 00000000..411c8302
--- /dev/null
+++ b/experiments/modern/src/runtime/JsGammaSet.js
@@ -0,0 +1,15 @@
+const MakiObject = require("./MakiObject");
+
+class JsGammaSet extends MakiObject {
+ /**
+ * getclassname()
+ *
+ * Returns the class name for the object.
+ * @ret The class name.
+ */
+ static getclassname() {
+ return "GammaSet";
+ }
+}
+
+module.exports = JsGammaSet;
diff --git a/experiments/modern/src/runtime/JsGroupDef.js b/experiments/modern/src/runtime/JsGroupDef.js
new file mode 100644
index 00000000..397483c9
--- /dev/null
+++ b/experiments/modern/src/runtime/JsGroupDef.js
@@ -0,0 +1,15 @@
+const MakiObject = require("./MakiObject");
+
+class JsGroupDef extends MakiObject {
+ /**
+ * getclassname()
+ *
+ * Returns the class name for the object.
+ * @ret The class name.
+ */
+ static getclassname() {
+ return "GroupDef";
+ }
+}
+
+module.exports = JsGroupDef;
diff --git a/experiments/modern/src/runtime/MakiObject.js b/experiments/modern/src/runtime/MakiObject.js
index 2734fe78..66eb4b75 100644
--- a/experiments/modern/src/runtime/MakiObject.js
+++ b/experiments/modern/src/runtime/MakiObject.js
@@ -1,9 +1,11 @@
const Emitter = require("../Emitter");
+const { findElementById, findGroupDefById } = require("../utils");
class MakiObject {
- constructor(node, parent) {
+ constructor(node, parent, annotations = {}) {
this.xmlNode = node;
this.parent = parent;
+ this.js_annotations = annotations;
this.children = [];
this._emitter = new Emitter();
@@ -42,6 +44,24 @@ class MakiObject {
this._emitter.dispose();
}
+ js_imageLookup(id) {
+ const element = findElementById(this, id);
+ if (element) {
+ return element.js_annotations;
+ }
+
+ return null;
+ }
+
+ js_groupdefLookup(id) {
+ const groupdef = findGroupDefById(this, id);
+ if (groupdef) {
+ return groupdef;
+ }
+
+ return null;
+ }
+
/**
* getclassname()
*
diff --git a/experiments/modern/src/utils.js b/experiments/modern/src/utils.js
index 061db9f3..668f12a3 100644
--- a/experiments/modern/src/utils.js
+++ b/experiments/modern/src/utils.js
@@ -1,4 +1,6 @@
import { xml2js } from "xml-js";
+const JsElements = require("./runtime/JsElements");
+const JsGroupDef = require("./runtime/JsGroupDef");
export function getCaseInsensitveFile(zip, filename) {
// TODO: Escape `file` for rejex characters
@@ -104,7 +106,7 @@ export function unimplementedWarning(name) {
// Operations on trees
export function findParentNodeOfType(node, type) {
let n = node;
- while(n.parent !== null) {
+ while (n.parent) {
n = n.parent;
if ((!Array.isArray(type) && n.xmlNode.name === type) ||
(Array.isArray(type) && type.includes(n.xmlNode.name))) {
@@ -129,7 +131,7 @@ export function findDescendantByTypeAndId(node, type, id) {
}
}
- for(let i = 0; i < node.children.length; i++) {
+ for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
const descendant = findDescendantByTypeAndId(child, type, id);
if (descendant) {
@@ -139,3 +141,52 @@ export function findDescendantByTypeAndId(node, type, id) {
return null;
}
+
+function findDirectDescendantById(node, id) {
+ return node.children.find(item => item.xmlNode.attributes.id === id);
+}
+
+// Search up the tree for a node that is in `node`'s lexical scope and pred returns node
+function findInLexicalScope(node, pred) {
+ let currentNode = node;
+ while (currentNode.parent) {
+ let parent = currentNode.parent;
+ const children = parent.children;
+ for (let i = 0; i < children.length; i++) {
+ const child = children[i];
+ if (child === currentNode) {
+ break;
+ }
+
+ const item = pred(child);
+ if (item) {
+ return item;
+ }
+ }
+ currentNode = parent;
+ }
+
+ return null;
+}
+
+// Search up the tree for nodes that are in node's lexical scope.
+// return the first child of an that matches id
+export function findElementById(node, id) {
+ return findInLexicalScope(node, child => {
+ if (child instanceof JsElements) {
+ const element = findDirectDescendantById(child, id);
+ if (element) {
+ return element;
+ }
+ }
+ });
+}
+
+// Search up the tree for a node that is in node's lexical scope and matches id.
+export function findGroupDefById(node, id) {
+ return findInLexicalScope(node, child => {
+ if (child instanceof JsGroupDef && child.xmlNode.attributes.id === id) {
+ return child;
+ }
+ });
+}