------------------------------------------------------------
commit 613d0a0164252587138498eec4d7ec6d391949e3
Author: Breck Yunits <breck7@gmail.com> Date: Mon Apr 8 14:24:00 2024 -0400 Create CNAME diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..b80a049 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +simoji.treenotation.org \ No newline at end of file ------------------------------------------------------------
commit 2f0f7f85896c3eb16a4309bca915d9d59ef15eb4
Author: Breck Yunits <breck@Brecks-MacBook-Pro.local> Date: Mon Apr 8 14:23:23 2024 -0400 Update subdomain diff --git a/cheatSheet.html b/cheatSheet.html index 76ecba5..adbe8ac 100644 --- a/cheatSheet.html +++ b/cheatSheet.html @@ -2,7 +2,7 @@ <title>Simoji Quickstart Guide</title> <script>/* This HTML was generated by 📜 Scroll v68.0.0. http://scroll.pub */</script> <style>@media print {.doNotPrint {display: none !important;}}</style> -<link rel="canonical" href="https://simoji.pub/cheatSheet.html"></link> +<link rel="canonical" href="https://simoji.treenotation.org/cheatSheet.html"></link> <meta charset="iso-8859-1"></meta> <meta name="viewport" content="width=device-width,initial-scale=1"></meta> <meta name="description" content="Design quick simulations with Emojis"></meta> @@ -395,7 +395,7 @@ experiment <p class="scrollParagraph">You can drop new Agents onto your board using the Agent Palette on the right side of your screen.</p> </div> <div class="scrollSection"><h3 class="scrollParagraph">Tree Notation</h3> -<p class="scrollParagraph">Simoji the language is a <a href="https://treenotation.org">Tree Language</a>. There are no visible syntax characters. Indentation is used for parent/child relationships. Here is the <a href="https://jtree.treenotation.org/designer#url%20https%3A%2F%2Fsimoji.pub%2Fsimoji.grammar">grammar</a>.</p> +<p class="scrollParagraph">Simoji the language is a <a href="https://treenotation.org">Tree Language</a>. There are no visible syntax characters. Indentation is used for parent/child relationships. Here is the <a href="https://jtree.treenotation.org/designer#url%20https%3A%2F%2Fsimoji.treenotation.org%2Fsimoji.grammar">grammar</a>.</p> </div> <div class="scrollSection"><h3 class="scrollParagraph">Keyboard shortcuts</h3> <table class="scrollTable"><thead><tr><th>Combo</th> @@ -437,7 +437,7 @@ experiment <p class="scrollParagraph">At the top of the page you should see a link that you can copy and paste to share your sim. When you update your simulation code that link will update.</p> </div> <div class="scrollSection"><h4 class="scrollParagraph">Loading a Simulation from a URL</h4> -<p class="scrollParagraph">You can load any simulation from a publicly accessible URL by prefixing it with: <code><a href="https://simoji.pub/#url" target="<em>blank">https://simoji.pub/#url</a> </code>. For example: <a href="https://simoji.pub/#url%20https://simoji.pub/examples/eatTheBacon.simoji" target="</em>blank">https://simoji.pub/#url%20https://simoji.pub/examples/eatTheBacon.simoji</a></p> +<p class="scrollParagraph">You can load any simulation from a publicly accessible URL by prefixing it with: <code><a href="https://simoji.treenotation.org/#url" target="<em>blank">https://simoji.treenotation.org/#url</a> </code>. For example: <a href="https://simoji.treenotation.org/#url%20https://simoji.treenotation.org/examples/eatTheBacon.simoji" target="</em>blank">https://simoji.treenotation.org/#url%20https://simoji.treenotation.org/examples/eatTheBacon.simoji</a></p> </div> <div class="scrollSection"><h3 class="scrollParagraph">Getting Involved</h3> <p class="scrollParagraph">The source code for Simoji and all development happens on <a href="https://github.com/publicdomaincompany/simoji">Github</a>.</p> @@ -450,7 +450,7 @@ experiment if (event.key === "ArrowLeft") getLinks()[0].click() else if (event.key === "ArrowRight") - getLinks()[1].click() +].click() });</script></div> </div><p class="scrollViewSource doNotPrint"> <a href="cheatSheet.scroll">View source</a> diff --git a/cheatSheet.scroll b/cheatSheet.scroll index 8e36fd8..70301e6 100644 --- a/cheatSheet.scroll +++ b/cheatSheet.scroll @@ -95,7 +95,7 @@ code # Tree Notation * Simoji the language is a Tree Language. There are no visible syntax characters. Indentation is used for parent/child relationships. Here is the grammar. https://treenotation.org Tree Language - https://jtree.treenotation.org/designer#url%20https%3A%2F%2Fsimoji.pub%2Fdist%2Fsimoji.grammar grammar + https://jtree.treenotation.org/designer#url%20https%3A%2F%2Fsimoji.treenotation.org%2Fdist%2Fsimoji.grammar grammar # Keyboard shortcuts pipeTable @@ -115,7 +115,7 @@ pipeTable * At the top of the page you should see a link that you can copy and paste to share your sim. When you update your simulation code that link will update. ## Loading a Simulation from a URL -* You can load any simulation from a publicly accessible URL by prefixing it with: `https://simoji.pub/#url `. For example: https://simoji.pub/#url%20https://simoji.pub/examples/eatTheBacon.simoji +* You can load any simulation from a publicly accessible URL by prefixing it with: `https://simoji.treenotation.org/#url `. For example: https://simoji.treenotation.org/#url%20https://simoji.treenotation.org/examples/eatTheBacon.simoji # Getting Involved * The source code for Simoji and all development happens on Github. diff --git a/header.scroll b/header.scroll index aeb8e25..9307c25 100644 --- a/header.scroll +++ b/header.scroll @@ -4,5 +4,5 @@ gazetteCss gazetteHeader description Design quick simulations with Emojis git https://github.com/breck7/simoji -baseUrl https://simoji.pub/ +baseUrl https://simoji.treenotation.org/ email breck7+simoji@gmail.com \ No newline at end of file diff --git a/index.html b/index.html index b156da4..ce9012b 100644 --- a/index.html +++ b/index.html @@ -11,7 +11,7 @@ <script src="./dist/constants.js"></script> <meta property="og:title" content="Simoji - Write simulations with Emojis"></meta> <meta property="og:description" content="Simoji is a language and web app for making shareable simulations."></meta> - <meta property="og:image" content="https://simoji.pub/screenshot.png"></meta> + <meta property="og:image" content="https://simoji.treenotation.org/screenshot.png"></meta> <meta name="twitter:card" content="summary_large_image"></meta> <script> lodash = _ diff --git a/readme.html b/readme.html index 7192464..d462338 100644 --- a/readme.html +++ b/readme.html @@ -2,7 +2,7 @@ <title>Simoji - Write simulations with Emojis</title> <script>/* This HTML was generated by 📜 Scroll v68.0.0. http://scroll.pub */</script> <style>@media print {.doNotPrint {display: none !important;}}</style> -<link rel="canonical" href="https://simoji.pub/readme.html"></link> +<link rel="canonical" href="https://simoji.treenotation.org/readme.html"></link> <meta charset="iso-8859-1"></meta> <meta name="viewport" content="width=device-width,initial-scale=1"></meta> <meta name="description" content="Design quick simulations with Emojis"></meta> @@ -365,7 +365,7 @@ h4.scrollQuestion { if (event.key === "ArrowLeft") getLinks()[0].click() else if (event.key === "ArrowRight") - getLinks()[1].click() +].click() });</script></div> </div><p class="scrollViewSource doNotPrint"> <a href="readme.scroll">View source</a> diff --git a/releaseNotes.html b/releaseNotes.html index 3c4164e..0be01a8 100644 --- a/releaseNotes.html +++ b/releaseNotes.html @@ -2,7 +2,7 @@ <title>Simoji Release Notes</title> <script>/* This HTML was generated by 📜 Scroll v68.0.0. http://scroll.pub */</script> <style>@media print {.doNotPrint {display: none !important;}}</style> -<link rel="canonical" href="https://simoji.pub/releaseNotes.html"></link> +<link rel="canonical" href="https://simoji.treenotation.org/releaseNotes.html"></link> <meta charset="iso-8859-1"></meta> <meta name="viewport" content="width=device-width,initial-scale=1"></meta> <meta name="description" content="Design quick simulations with Emojis"></meta> @@ -327,7 +327,7 @@ h4.scrollQuestion { if (event.key === "ArrowLeft") getLinks()[0].click() else if (event.key === "ArrowRight") - getLinks()[1].click() +].click() });</script></div> </div><p class="scrollViewSource doNotPrint"> <a href="releaseNotes.scroll">View source</a> ------------------------------------------------------------
commit 2ebccf12438e8f68ce612fbd495189aeb7cc58f7
Author: Breck Yunits <breck7@gmail.com> Date: Fri Mar 8 16:41:40 2024 -0500 Delete CNAME diff --git a/CNAME b/CNAME deleted file mode 100644 index 382921c..0000000 --- a/CNAME +++ /dev/null @@ -1 +0,0 @@ -simoji.pub \ No newline at end of file ------------------------------------------------------------
commit a8a62bf723d736f6deb0a06ebd5ac8656d1753d9
Author: Breck Yunits <breck7@gmail.com> Date: Wed Apr 12 12:26:21 2023 -1000 Version 3 - new coordinate system (#8) * checkpoint * checkpoint * checkpoint * Code cleanup * checkpoint * Test cleanup * checkpoint * checkpoint * checkpoint * checkpoint * checkpoint * checkpoint * checkpoint * checkpoint * checkpoint * checkpoint * checkpoint * checkpoint * checkpoint * checkpoint * checkpoint * checkpoint * Turn off auto-snapshot * Drop agents in the center of click * update examples * Fill update * checkpoint * Collission fixes * Quad tree fixes * Fix draw * checkpoint * Switch to unit vectors * checkpoint * checkpoint * checkpoint * Remove game of life for now * Version 3 --------- Co-authored-by: Breck Yunits <breck@Brecks-MacBook-Pro.local> diff --git a/.gitignore b/.gitignore index bb49e81..e4e0162 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ node_modules ignore/ coverage/ .nyc_output/ -.DS_Store \ No newline at end of file +.DS_Store +package-lock.json \ No newline at end of file diff --git a/BrowserGlue.js b/BrowserGlue.js index 2f632de..d075d38 100644 --- a/BrowserGlue.js +++ b/BrowserGlue.js @@ -3,7 +3,7 @@ const { TreeNode } = require("jtree/products/TreeNode.js") const { HandGrammarProgram } = require("jtree/products/GrammarLanguage.js") const { ExampleSims } = require("./components/ExampleSims.js") const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") -const { Keywords, LocalStorageKeys, UrlKeys } = require("./components/Types.js") +const { LocalStorageKeys, UrlKeys } = require("./components/Types.js") class BrowserGlue extends AbstractTreeComponentParser { async fetchAndLoadSimCodeFromUrlCommand(url) { @@ -43,7 +43,7 @@ class BrowserGlue extends AbstractTreeComponentParser { } async fetchSimGrammarAndExamplesAndInit() { - const grammar = await fetch("simoji.grammar") + const grammar = await fetch("dist/simoji.grammar") const grammarCode = await grammar.text() const result = await fetch("examples") diff --git a/build.js b/build.js index db8350d..d2453b2 100755 --- a/build.js +++ b/build.js @@ -1,9 +1,13 @@ #!/usr/bin/env node +const path = require("path") const { readFile } = require("fs") const { Disk } = require("jtree/products/Disk.node.js") const { TypeScriptRewriter } = require("jtree/products/TypeScriptRewriter.js") const { getExamples } = require("./examples") +const { Server } = require("./server.js") + +const distFolder = path.join(__dirname, "dist") const libPaths = `node_modules/jtree/treeComponentFramework/sweepercraft/lib/mousetrap.min.js node_modules/mathjs/lib/browser/math.js @@ -21,13 +25,14 @@ node_modules/jtree/products/stump.browser.js node_modules/jtree/products/hakon.browser.js node_modules/jtree/products/TreeComponentFramework.browser.js`.split("\n") -const libCode = libPaths.map(path => Disk.read(__dirname + "/" + path)).join("\n\n") +const libCode = libPaths.map(libPath => Disk.read(path.join(__dirname, libPath))).join("\n\n") -Disk.write(__dirname + "/dist/libs.js", libCode) +Disk.write(path.join(distFolder, "simoji.grammar"), new Server().grammar) +Disk.write(path.join(distFolder, "libs.js"), libCode) -const ourPaths = Disk.getFiles(__dirname + "/components").filter(path => !path.includes(".test")) -ourPaths.unshift(__dirname + "/yodash.js") -ourPaths.push(__dirname + "/BrowserGlue.js") +const ourPaths = Disk.getFiles(path.join(__dirname, "components")).filter(path => !path.includes(".test")) +ourPaths.unshift(path.join(__dirname, "yodash.js")) +ourPaths.push(path.join(__dirname, "BrowserGlue.js")) const simCode = ourPaths .map(path => { @@ -41,11 +46,11 @@ const simCode = ourPaths }) .join("\n\n") -Disk.write(__dirname + "/dist/simoji.js", simCode) +Disk.write(path.join(distFolder, "simoji.js"), simCode) const SimConstants = { - grammar: Disk.read(__dirname + "/simoji.grammar"), + grammar: Disk.read(path.join(distFolder, "simoji.grammar")), examples: getExamples() } -Disk.write(__dirname + "/dist/constants.js", `const SimConstants = ` + JSON.stringify(SimConstants)) +Disk.write(path.join(distFolder, "constants.js"), `const SimConstants = ` + JSON.stringify(SimConstants)) diff --git a/cheatSheet.scroll b/cheatSheet.scroll index 2232ecc..8e36fd8 100644 --- a/cheatSheet.scroll +++ b/cheatSheet.scroll @@ -45,7 +45,6 @@ code * Events are blocks of commands that execute during the running of the experiments. Probabilities can be assigned so blocks run stochastically. - onTick - onHit -- onTouch - onDeath * Some events can also happen on the board level: @@ -96,7 +95,7 @@ code # Tree Notation * Simoji the language is a Tree Language. There are no visible syntax characters. Indentation is used for parent/child relationships. Here is the grammar. https://treenotation.org Tree Language - https://jtree.treenotation.org/designer#url%20https%3A%2F%2Fsimoji.pub%2Fsimoji.grammar grammar + https://jtree.treenotation.org/designer#url%20https%3A%2F%2Fsimoji.pub%2Fdist%2Fsimoji.grammar grammar # Keyboard shortcuts pipeTable diff --git a/components/Agent.js b/components/Agent.js index ee0e7bf..852f15b 100644 --- a/components/Agent.js +++ b/components/Agent.js @@ -1,25 +1,52 @@ -const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") const { yodash } = require("../yodash.js") const { TreeNode } = require("jtree/products/TreeNode.js") -const { Keywords, Directions } = require("./Types.js") +const { Keywords } = require("./Types.js") const SelectedClass = "selected" +const classCache = {} +const getClassCache = (program, words) => { + const key = words.join(" ") + if (!classCache[key]) classCache[key] = yodash.flatten(yodash.pick(program, words)) + return classCache[key] +} + class Agent extends TreeNode { get name() { return this._name ?? this.icon } - angle = Directions.South + _direction = { x: 0, y: 1 } + + get direction() { + if (this.angle) { + const vectors = { + North: [0, -1], + East: [1, 0], + South: [0, 1], + West: [-1, 0], + Northeast: [Math.cos(Math.PI / 4), Math.sin((Math.PI * 3) / 4)], + Southeast: [Math.cos((Math.PI * 3) / 4), Math.sin(Math.PI / 4)], + Southwest: [-Math.cos((Math.PI * 3) / 4), Math.sin(Math.PI * (5 / 8))], + Northwest: [-Math.cos(Math.PI * (5 / 8)), Math.sin((Math.PI * -3) / 4)] + } + this._direction = vectors[this.angle] + this.angle = "" + } + return this._direction + } + + set direction(newDirection) { + this._direction = newDirection + } getCommandBlocks(eventName) { - return this.definitionWithBehaviors.findNodes(eventName) + return this.definitionWithClasses.findNodes(eventName) } - get definitionWithBehaviors() { - if (!this.behaviors.length) return this.board.simojiProgram.getNode(this.firstWord) - const behaviors = yodash.flatten(yodash.pick(this.board.simojiProgram, [this.firstWord, ...this.behaviors])) - return behaviors + get definitionWithClasses() { + if (!this.classes.length) return this.board.simojiProgram.getNode(this.firstWord) + return getClassCache(this.board.simojiProgram, [this.firstWord, ...this.classes]) } skip(probability) { @@ -31,59 +58,6 @@ class Agent extends TreeNode { return !!this.element } - handleNeighbors() { - if (!this.stillExists) return - this.getCommandBlocks(Keywords.onNeighbors).forEach(neighborConditions => { - if (this.skip(neighborConditions.getWord(1))) return - - const { neighorCount } = this - - neighborConditions.forEach(conditionAndCommandsBlock => { - const [emoji, operator, count] = conditionAndCommandsBlock.words - const actual = neighorCount[emoji] - if (!yodash.compare(actual ?? 0, operator, count)) return - conditionAndCommandsBlock.forEach(command => this._executeCommand(this, command)) - - if (this.getIndex() === -1) return {} - }) - }) - } - - handleTouches(agentPositionMap) { - if (!this.stillExists) return - this.getCommandBlocks(Keywords.onTouch).forEach(touchMap => { - if (this.skip(touchMap.getWord(1))) return - - for (let pos of yodash.positionsAdjacentTo(this.position)) { - const hits = agentPositionMap.get(yodash.makePositionHash(pos)) ?? [] - for (let target of hits) { - const targetId = target.firstWord - const commandBlock = touchMap.getNode(targetId) - if (commandBlock) { - commandBlock.forEach(command => this._executeCommand(target, command)) - if (this.getIndex() === -1) return - } - } - } - }) - } - - handleOverlaps(targets) { - if (!this.stillExists) return - this.getCommandBlocks(Keywords.onHit).forEach(hitMap => { - if (this.skip(hitMap.getWord(1))) return - targets.forEach(target => { - const targetId = target.firstWord - const commandBlock = hitMap.getNode(targetId) - if (commandBlock) commandBlock.forEach(command => this._executeCommand(target, command)) - }) - }) - } - - get overlappingAgents() { - return (this.board.agentPositionMap.get(this.positionHash) ?? []).filter(node => node !== this) - } - _executeCommand(target, instruction) { const commandName = instruction.firstWord if (this[commandName]) this[commandName](target, instruction) @@ -111,19 +85,6 @@ class Agent extends TreeNode { if (this.health === 0) this.onDeathCommand() } - get neighorCount() { - const { agentPositionMap } = this.board - const neighborCounts = {} - yodash.positionsAdjacentTo(this.position).forEach(pos => { - const agents = agentPositionMap.get(yodash.makePositionHash(pos)) ?? [] - agents.forEach(agent => { - if (!neighborCounts[agent.name]) neighborCounts[agent.name] = 0 - neighborCounts[agent.name]++ - }) - }) - return neighborCounts - } - onDeathCommand() { this._executeCommandBlocks(Keywords.onDeath) } @@ -133,101 +94,141 @@ class Agent extends TreeNode { } _replaceWith(newObject) { - this.parent.appendLine(`${newObject} ${this.positionHash}`) - + this.parent.insertInbounds(newObject, this.x, this.y) this.remove() } _move() { if (this.owner) return this - const { angle } = this - if (angle.includes(Directions.North)) this.moveNorth() - else if (angle.includes(Directions.South)) this.moveSouth() - if (angle.includes(Directions.East)) this.moveEast() - else if (angle.includes(Directions.West)) this.moveWest() + const { direction, speed } = this + + this.top = Math.max(this.top + direction.y * speed, 0) + this.left = Math.max(this.left + direction.x * speed, 0) if (this.holding) { this.holding.forEach(node => { - node.position = { right: this.left, down: this.top } + node.setPosition({ x: this.x, y: this.y }) }) } } - moveSouth() { - this.top++ + speed = 1 + + get x() { + return this.left } - moveNorth() { - this.top-- + get y() { + return this.top } - moveWest() { - this.left-- + get w() { + return this.width } - moveEast() { - this.left++ + get h() { + return this.height } + width = 10 + height = 10 + get top() { - return this.position.down + return this._y ?? this.position.y } set top(value) { if (value > this.maxDown) value = this.maxDown if (value < 0) value = 0 - this.position = { - down: value, - right: this.left - } - } - - set position(value) { - if (this.board.isSolidAgent(value)) return this.bouncy ? this.bounce() : this - const newLine = this.getLine() - .split(" ") - .map(part => (part.includes("⬇️") ? value.down + "⬇️" : part.includes("➡️") ? value.right + "➡️" : part)) - .join(" ") - return this.setLine(newLine) + this.setPosition({ + y: value, + x: this.left + }) } get board() { return this.parent } + setPosition(newPosition) { + if (!this.board.canGoHere(newPosition.x, newPosition.y, this.width, this.height)) + return this.bouncy ? this.bounce() : this + + this._x = newPosition.x + this._y = newPosition.y + // Todo: do we need to update the string? + return this.setLine([this.firstWord, newPosition.x, newPosition.y].join(" ")) + } + + handleCollisions(targetAgents) { + if (!this.stillExists) return + this.getCommandBlocks(Keywords.onHit).forEach(hitMap => { + if (this.skip(hitMap.getWord(1))) return + targetAgents.forEach(targetAgent => { + const targetId = targetAgent.firstWord + const commandBlock = hitMap.getNode(targetId) + if (commandBlock) commandBlock.forEach(command => this._executeCommand(targetAgent, command)) + }) + }) + } + + get symbol() { + return this.firstWord + } + + get collidingAgents() { + return this.board.objectsCollidingWith(this.x, this.y, this.width, this.height).filter(node => node !== this) + } + + get neighorCount() { + return this.board.getNeighborCount(this) + } + get maxRight() { - return this.board.cols + return this.board.width - this.width - 1 } get maxDown() { - return this.board.rows + return this.board.height - this.height - 1 } set left(value) { if (value > this.maxRight) value = this.maxRight if (value < 0) value = 0 - this.position = { - down: this.top, - right: value - } + this.setPosition({ + y: this.top, + x: value + }) } get left() { - return this.position.right + return this._x ?? this.position.x } - get position() { - return yodash.parsePosition(this.words) + get bounds() { + return { + x: this.x, + y: this.y, + w: this.width, + h: this.height + } } - get positionHash() { - return yodash.makePositionHash(this.position) + get position() { + return { + x: parseInt(this.words[1]), + y: parseInt(this.words[2]) + } } get gridSize() { - return this.parent.gridSize + return 1 + } + + get agentSize() { + return this.size ?? this.gridSize } get selected() { @@ -251,12 +252,12 @@ class Agent extends TreeNode { // DOM operations nuke() { - this.element.remove() + if (this.element) this.element.remove() this.destroy() } get element() { - return document.getElementById(`agent${this._getUid()}`) + return document.getElementById(this.id) } _updateHtml() { @@ -266,16 +267,24 @@ class Agent extends TreeNode { } get inlineStyle() { - const { gridSize, health } = this + const { health, width, height } = this const opacity = health === undefined ? "" : `opacity:${this.health / this.startHealth};` - return `top:${this.top * gridSize}px;left:${ - this.left * gridSize - }px;font-size:${gridSize}px;line-height: ${gridSize}px;${opacity};${this.style ?? ""}` + return `top:${this.top}px;left:${this.left}px;font-size:${height}px;line-height:${height}px;${opacity};${ + this.style ?? "" + }` + } + + get id() { + return `agent${this.agentNumber}` + } + + get agentNumber() { + return this._getUid() } toElement() { const elem = document.createElement("div") - elem.setAttribute("id", `agent${this._getUid()}`) + elem.setAttribute("id", this.id) elem.innerHTML = this.html ?? this.icon elem.classList.add("Agent") if (this.selected) elem.classList.add(SelectedClass) @@ -283,11 +292,22 @@ class Agent extends TreeNode { return elem } - toStumpCode() { - return `div ${this.html ?? this.icon} - id agent${this._getUid()} - class Agent ${this.selected ? SelectedClass : ""} - style ${this.inlineStyle}` + toggleSelectCommand() { + const { root } = this + root.selection.includes(this) ? this.unselectCommand() : this.selectCommand() + + root.ensureRender() + return this + } + + unselectCommand() { + this.unselect() + this.root.selection = this.root.selection.filter(node => node !== this) + } + + selectCommand() { + this.root.selection.push(this) + this.select() } needsUpdate(lastRenderedTime = 0) { @@ -305,7 +325,7 @@ class Agent extends TreeNode { } kickIt(target) { - target.angle = this.angle + target.direction = this.direction target.tickStack = new TreeNode(`1 move move @@ -350,7 +370,8 @@ class Agent extends TreeNode { }) } bounce() { - this.angle = yodash.flipAngle(this.angle) + const { x, y } = this.direction + this.direction = { x: -x, y: -y } } decrease(target, command) { @@ -366,7 +387,8 @@ class Agent extends TreeNode { } turnRandomly() { - this.angle = yodash.getRandomAngle(this.board.randomNumberGenerator) + const rng = this.board.randomNumberGenerator + this.direction = { x: 2 * rng() - 1, y: 2 * rng() - 1 } return this } @@ -374,7 +396,9 @@ class Agent extends TreeNode { const targetId = instruction.getWord(1) const kind = this[targetId] ?? targetId // can define a custom target const targets = this.board.agentTypeMap.get(kind) - if (targets) this.angle = yodash.getBestAngle(targets, this.position) + if (!targets) return this + this.target = yodash.getClosest(targets, this) + this.direction = yodash.unitVector(this, this.target) return this } @@ -382,7 +406,10 @@ class Agent extends TreeNode { const targetId = instruction.getWord(1) const kind = this[targetId] ?? targetId // can define a custom target const targets = this.board.agentTypeMap.get(kind) - if (targets) this.angle = yodash.flipAngle(yodash.getBestAngle(targets, this.position)) + if (!targets) return this + this.target = yodash.getClosest(targets, this) + const bestUnitVector = yodash.unitVector(this, this.target) + this.direction = { x: -bestUnitVector.x, y: -bestUnitVector.y } return this } @@ -391,33 +418,64 @@ class Agent extends TreeNode { } spawn(subject, command) { - const position = command.getWordsFrom(2).length ? command.getWordsFrom(2).join(" ") : subject.positionHash + const position = command.getWordsFrom(2).length ? command.getWordsFrom(2).join(" ") : `${subject.x} ${subject.y}` this.board.appendLine(`${command.getWord(1)} ${position}`) } + get midpoint() { + return { x: this.x + this.width / 2, y: this.y + this.height / 2 } + } + + emit(subject, command) { + const { midpoint } = this + const position = command.getWordsFrom(2).length ? command.getWordsFrom(2).join(" ") : `${midpoint.x} ${midpoint.y}` + const agent = this.board.appendLine(`${command.getWord(1)} ${position}`) + agent.direction = this.direction + } + move() { if (this.selected) return return this._move() } moveToEmptySpot() { - while (this.overlappingAgents.length) { + while (this.collidingAgents.length) { this.move() } } + grow() { + this.width++ + this.height++ + this.markDirty() + } + + shrink() { + if (!this.width || !this.height) return + this.width-- + this.height-- + this.markDirty() + } + jitter() { this.turnRandomly() this.move() } + _lastPulse + pulse() { + if (this._lastPulse) this.shrink() + else this.grow() + this._lastPulse = !this._lastPulse + } + learn(target, command) { - this.behaviors.push(command.getWord(1)) + this.classes.push(command.getWord(1)) } unlearn(target, command) { - const behaviorName = command.getWord(1) - this.behaviors = this.behaviors.filter(name => name !== behaviorName) + const className = command.getWord(1) + this.classes = this.classes.filter(name => name !== className) } } diff --git a/components/Board.js b/components/Board.js index 8b6bf93..5cb5bf9 100644 --- a/components/Board.js +++ b/components/Board.js @@ -2,6 +2,7 @@ const { TreeNode } = require("jtree/products/TreeNode.js") const { yodash } = require("../yodash.js") const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") const { GridComponent } = require("./Grid.js") +const { CollisionDetector } = require("./CollisionDetector.js") const { Agent } = require("./Agent.js") const { Keywords, ParserTypes } = require("./Types.js") @@ -60,15 +61,17 @@ class BoardComponent extends AbstractTreeComponentParser { } get gridSize() { - return parseInt(this.getWord(1)) + return 1 } - get rows() { - return parseInt(this.getWord(2)) + get width() { + if (this._width === undefined) this._width = parseInt(this.getWord(2)) + return this._width } - get cols() { - return parseInt(this.getWord(3)) + get height() { + if (this._height === undefined) this._height = parseInt(this.getWord(3)) + return this._height } get populationCsv() { @@ -107,31 +110,28 @@ class BoardComponent extends AbstractTreeComponentParser { ) } - insertAgentAtCommand(right, down) { + insertAgentAtCommand(xCenter, yCenter) { const root = this.root - const board = this - const positionHash = down + " " + right - board.resetAgentPositionMap() - const { agentPositionMap } = board - const existingObjects = agentPositionMap.get(positionHash) ?? [] - if (existingObjects.length) return root.toggleSelectCommand(existingObjects) const { agentToInsert } = root - if (!agentToInsert) return + this.clearCollisionDetector() - //if (parent.findNodes(agentToInsert).length > MAX_ITEMS) return true + const { agentWidth, agentHeight } = this.getAgentHeightAndWidth(agentToInsert) + + const x = xCenter - Math.floor(agentWidth / 2) + const y = yCenter - Math.floor(agentHeight / 2) - board.prependLine(`${agentToInsert} ${positionHash}`) - board.renderAndGetRenderReport() - board.resetAgentPositionMap() + //if (parent.findNodes(agentToInsert).length > MAX_ITEMS) return true - if (!root.isSnapshotOn) root.snapShotCommand() + this.prependLine(`${agentToInsert} ${x} ${y}`) + this.renderAndGetRenderReport() + this.clearCollisionDetector() const allCode = new TreeNode(root.simCode) let targetNode = root.boards.length === 1 ? allCode : allCode.findNodes("experiment")[this.boardIndex] if (this.tick) targetNode = targetNode.appendLine(`atTime ${this.tick}`) - targetNode.appendLine(`insertAt ${agentToInsert} ${down} ${right}`) + targetNode.appendLine(`insertAt ${agentToInsert} ${x} ${y}`) root.onSourceCodeChange(allCode) } @@ -150,14 +150,10 @@ class BoardComponent extends AbstractTreeComponentParser { tick = 0 boardLoop(render = true) { this.runAtTimeEvents() - this.agents.forEach(node => node.onTick()) - this.resetAgentPositionMap() - this.handleOverlaps() - this.handleTouches() - this.handleNeighbors() - + this.clearCollisionDetector() + this.handleCollisions() this.executeBoardCommands(Keywords.onTick) this.handleExtinctions() @@ -180,6 +176,20 @@ class BoardComponent extends AbstractTreeComponentParser { return report } + treeComponentDidMount() { + const that = this + if (this.isNodeJs()) return + jQuery(this.getStumpNode().getShadow().element).on("click", ".Agent", function (evt) { + const agent = evt.target + const id = parseInt(jQuery(agent).attr("id").replace("agent", "")) + that.getAgent(id).toggleSelectCommand() + }) + } + + getAgent(uid) { + return this.agents.find(agent => agent._getUid() === uid) + } + appendAgents(agents) { if (!agents.length) return this @@ -208,53 +218,95 @@ class BoardComponent extends AbstractTreeComponentParser { return setTime ? parseInt(setTime) : 10 } - occupiedSpots = new Set() - runInjectCommand(command) { this[command.parserId](command) } + getAgentHeightAndWidth(agentSymbol) { + const item = new this.agentMap[agentSymbol]() + return { agentWidth: item.width, agentHeight: item.height } + } + + insertInbounds(agentSymbol, x, y) { + const { agentWidth, agentHeight } = this.getAgentHeightAndWidth(agentSymbol) + const xOver = x + agentWidth - this.width + const yOver = y + agentHeight - this.height + if (xOver > 0) x = x - xOver - 10 + if (yOver > 0) y = y - yOver - 10 + if (x < 0) x = 0 + if (y < 0) y = 0 + this.appendLine(`${agentSymbol} ${x} ${y}`) + } + + // todo: origin + insertClusteredRandomAgents(amount, agentSymbol, x = 0, y = 0) { + const { agentWidth, agentHeight } = this.getAgentHeightAndWidth(agentSymbol) + const spots = this.collisionDetector.findClusteredNonOverlappingSquares( + agentWidth, + agentHeight, + amount, + x, + y, + (amount * agentWidth) / 4 + ) + return spots.map(spot => `${agentSymbol} ${spot.x} ${spot.y}`).join("\n") + } + insertClusterParser(commandNode) { this.concat( - yodash.insertClusteredRandomAgents( - this.randomNumberGenerator, + this.insertClusteredRandomAgents( parseInt(commandNode.getWord(1)), commandNode.getWord(2), - this.rows, - this.cols, - this.occupiedSpots, commandNode.getWord(3), commandNode.getWord(4) ) ) + this.clearCollisionDetector() } insertAtParser(commandNode) { - this.appendLine(`${commandNode.getWord(1)} ${commandNode.getWord(3)} ${commandNode.getWord(2)}`) - // TODO: update occupied spots cache? + this.appendLine(`${commandNode.getWord(1)} ${commandNode.getWord(2)} ${commandNode.getWord(3)}`) + this.clearCollisionDetector() } rectangleDrawParser(commandNode) { - const newLines = yodash.makeRectangle(...yodash.parseInts(commandNode.words.slice(1), 1)) + // todo: need a typed words method in jtree + // rectangle 🙂 width height x y 🙂 + const [command, agentSymbol, width, height, x, y, fillSymbol, spacing] = commandNode.words + + const { agentWidth, agentHeight } = this.getAgentHeightAndWidth(agentSymbol) + + const options = { + agentSymbol, + width: parseInt(width), + height: parseInt(height), + x: x ? parseInt(x) : 0, + y: y ? parseInt(y) : 0, + fillSymbol, + spacing: spacing || 0, + agentHeight, + agentWidth + } + + const newLines = this.makeRectangle(options) this.concat(newLines) - // TODO: update occupied spots cache? + this.clearCollisionDetector() } pasteDrawParser(commandNode) { const newSpots = new TreeNode(commandNode.childrenToString()) - yodash.updateOccupiedSpots(newSpots, this.occupiedSpots) this.concat(newSpots) + this.clearCollisionDetector() } fillParser(commandNode) { - this.concat(yodash.fill(this.rows, this.cols, this.occupiedSpots, commandNode.getWord(1))) + this.concat(this.fill(commandNode.getWord(1))) + this.clearCollisionDetector() } drawParser(commandNode) { - const { occupiedSpots } = this - const spots = yodash.draw(commandNode.childrenToString()) - yodash.updateOccupiedSpots(spots, occupiedSpots) - this.concat(spots) + this.concat(this.draw(commandNode.childrenToString())) + this.clearCollisionDetector() } get seed() { @@ -269,21 +321,19 @@ class BoardComponent extends AbstractTreeComponentParser { } insertParser(commandNode) { - const { rows, cols, occupiedSpots } = this - const emoji = commandNode.getWord(2) + const { width, height } = this + const agentSymbol = commandNode.getWord(2) let amount = commandNode.getWord(1) + const { agentWidth, agentHeight } = this.getAgentHeightAndWidth(agentSymbol) + const maxCells = (width * height) / (agentWidth * agentHeight) + amount = amount.includes("%") ? yodash.parsePercent(amount) * maxCells : parseInt(amount) + + const spots = this.collisionDetector.findNonOverlappingSquares(agentWidth, agentHeight, amount) + + const newAgents = spots.map(spot => `${agentSymbol} ${spot.x + " " + spot.y}`).join("\n") - const availableSpots = yodash.getAllAvailableSpots(rows, cols, occupiedSpots) - amount = amount.includes("%") ? yodash.parsePercent(amount) * (rows * cols) : parseInt(amount) - const newAgents = yodash - .sampleFrom(availableSpots, amount, this.randomNumberGenerator) - .map(spot => { - const { hash } = spot - occupiedSpots.add(hash) - return `${emoji} ${hash}` - }) - .join("\n") this.concat(newAgents) + this.clearCollisionDetector() } handleExtinctions() { @@ -306,37 +356,10 @@ class BoardComponent extends AbstractTreeComponentParser { }) } - isSolidAgent(position) { - if (!this._solidsSet) this.resetAgentPositionMap() - const hash = yodash.makePositionHash(position) - if (this._solidsSet.has(hash)) return true - - return false - } - get agents() { return this.topDownArray.filter(node => node instanceof Agent) } - get agentPositionMap() { - if (!this._agentPositionMap) this.resetAgentPositionMap() - return this._agentPositionMap - } - - resetAgentPositionMap() { - const map = new Map() - const solidsSet = new Set() - this.agents.forEach(agent => { - const { positionHash } = agent - if (agent.solid) solidsSet.add(positionHash) - if (!map.has(positionHash)) map.set(positionHash, []) - map.get(positionHash).push(agent) - }) - this._solidsSet = solidsSet - this._agentPositionMap = map - this.occupiedSpots = new Set(map.keys()) - } - get agentTypeMap() { const map = new Map() this.agents.forEach(node => { @@ -347,19 +370,24 @@ class BoardComponent extends AbstractTreeComponentParser { return map } - handleOverlaps() { - this.agentPositionMap.forEach(nodes => { - if (nodes.length > 1) nodes.forEach(node => node.handleOverlaps(nodes)) - }) + clearCollisionDetector() { + delete this._collisionDetector + delete this._solidCollisionDetector } - handleTouches() { - const agentPositionMap = this.agentPositionMap - this.agents.forEach(node => node.handleTouches(agentPositionMap)) + _collisionDetector + get collisionDetector() { + if (!this._collisionDetector) this._collisionDetector = new CollisionDetector(this.agents, this.width, this.height) + return this._collisionDetector } - handleNeighbors() { - this.agents.forEach(node => node.handleNeighbors()) + handleCollisions() { + const collisions = this.collisionDetector.detectCollisions() + collisions.forEach(collision => { + const [agentA, agentB] = collision + agentA.handleCollisions([agentB]) + agentB.handleCollisions([agentA]) + }) } get boardIndex() { @@ -443,14 +471,7 @@ class BoardComponent extends AbstractTreeComponentParser { // Commands available to users: spawn(command) { - this.appendLine( - `${command.getWord(1)} ${yodash.getRandomLocationHash( - this.rows, - this.cols, - undefined, - this.randomNumberGenerator - )}` - ) + this.appendLine(`${command.getWord(1)} ${this.getRandomLocationHash()}`) } alert(command) { @@ -474,6 +495,159 @@ class BoardComponent extends AbstractTreeComponentParser { log(command) { this.root.log(command.content) } + + isRectOccupied(x, y, width, height) { + return !this.collisionDetector.isSpotAvailable(x, y, width, height) + } + + objectsCollidingWith(x, y, width, height) { + return this.collisionDetector.getCollidingAgents(x, y, width, height) + } + + _solidCollisionDetector + get solidCollisionDetector() { + if (!this._solidCollisionDetector) + this._solidCollisionDetector = new CollisionDetector( + this.agents.filter(agent => agent.solid), + this.width, + this.height + ) + return this._solidCollisionDetector + } + + canGoHere(x, y, width, height) { + const blockersHere = this.solidCollisionDetector.getCollidingAgents(x, y, width, height) + if (blockersHere.length) return false + + return true + } + + get collidingAgents() { + const agents = this.agents + const collidingAgents = [] + for (let agent of agents) { + const { position, agentSize } = agent + const agentsHere = this.objectsCollidingWith(position.x, position.y, agentSize).filter(a => a !== agent) + if (agentsHere.length) collidingAgents.push(...agentsHere) + } + return collidingAgents + } + + makePositionHash(positionType) { + return `${positionType.x + " " + positionType.y}` + } + + getRandomLocationHash(size = 1) { + const { x, y } = this.getRandomLocation() + if (this.isRectOccupied(x, y, size, size)) return this.getRandomLocationHash() + return this.makePositionHash({ x, y }) + } + + getRandomLocation() { + const { randomNumberGenerator, height, width } = this + const maxRight = width + const maxBottom = height + const x = Math.round(randomNumberGenerator() * maxRight) + const y = Math.round(randomNumberGenerator() * maxBottom) + return { x, y } + } + + draw(str) { + const lines = str.split("\n") + const output = [] + let agentWidth + let agentHeight + for (let index = 0; index < lines.length; index++) { + const words = lines[index].split(" ") + for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { + const agentSymbol = words[wordIndex] + if (agentSymbol && !agentWidth) { + // Draw assumes everything being drawn is a square with sides N. + agentWidth = this.getAgentHeightAndWidth(agentSymbol).agentWidth + agentHeight = agentWidth + } + + if (agentSymbol !== "") + output.push(`${agentSymbol} ${this.makePositionHash({ y: index * agentHeight, x: wordIndex * agentHeight })}`) + } + } + return output.join("\n") + } + + makeRectangle(options) { + const { width, height, agentSymbol, x, y, fillSymbol, spacing, agentHeight, agentWidth } = options + + if (width < 1 || height < 1) return "" + + if (isNaN(x)) x = 20 + if (isNaN(y)) y = 20 + + const cells = [] + let row = 0 + while (row < height) { + let col = 0 + while (col < width) { + const isPerimeter = row === 0 || row === height - 1 || col === 0 || col === width - 1 + if (!fillSymbol && !isPerimeter) { + col++ + continue + } + + cells.push( + `${isPerimeter ? agentSymbol : fillSymbol} ${x + col * (agentWidth + spacing)} ${ + y + row * (agentHeight + spacing) + }` + ) + col++ + } + row++ + } + return cells.join("\n") + } + + fill(agentSymbol) { + let { width, height } = this + const { agentWidth, agentHeight } = this.getAgentHeightAndWidth(agentSymbol) + const board = [] + let y = 0 + while (y < height - agentHeight) { + let x = 0 + while (x < width - agentWidth) { + if (this.isRectOccupied(x, y, agentWidth, agentHeight)) { + x += agentWidth + continue + } + board.push(`${agentSymbol} ${x} ${y}`) + x += agentWidth + } + y += agentHeight + } + return board.join("\n") + } + + getNeighborCount(rect) { + const { position, agentSize } = rect + const neighborCounts = {} + this.positionsAdjacentToRect(position.right, position.down, agentSize).forEach(pos => { + const agents = this.objectsCollidingWith(pos.right, pos.down, agentSize) + agents.forEach(agent => { + if (!neighborCounts[agent.name]) neighborCounts[agent.name] = 0 + neighborCounts[agent.name]++ + }) + }) + return neighborCounts + } + + positionsAdjacentToRect(x, y, size) { + const positions = [] + for (let row = y - size; row <= y + size; row++) { + for (let col = x - size; col <= x + size; col++) { + if (row === y && col === x) continue + positions.push({ right: col, down: row }) + } + } + return positions + } } class BoardStyleComponent extends AbstractTreeComponentParser { diff --git a/components/Board.test.node.js b/components/Board.test.node.js new file mode 100644 index 0000000..9c10972 --- /dev/null +++ b/components/Board.test.node.js @@ -0,0 +1,41 @@ +#!/usr/bin/env node + +const { BoardComponent } = require("./Board.js") +const { TestRacer } = require("jtree/products/TestRacer.js") + +const testTree = {} + +testTree.makeRectangle = areEqual => { + const expected = `😀 0 0 +😀 1 0 +😀 0 1 +😀 1 1` + + const options = { + agentSymbol: "😀", + width: 2, + height: 2, + x: 0, + y: 0, + fillSymbol: false, + spacing: 0, + agentHeight: 1, + agentWidth: 1 + } + + const board = new BoardComponent() + areEqual(board.makeRectangle(options), expected) + + options.agentSymbol = "🚪" + options.height = 1 + options.x = 1 + options.y = 1 + areEqual( + board.makeRectangle(options), + `🚪 1 1 +🚪 2 1` + ) +} + +if (!module.parent) TestRacer.testSingleFile(__filename, testTree) +module.exports = { testTree } diff --git a/components/CollisionDetector.js b/components/CollisionDetector.js new file mode 100644 index 0000000..9e97a25 --- /dev/null +++ b/components/CollisionDetector.js @@ -0,0 +1,237 @@ +class Bounds { + constructor(x, y, w, h) { + this.x = x + this.y = y + this.w = w + this.h = h + } + + intersects(range) { + return !( + range.x >= this.x + this.w || + range.y >= this.y + this.h || + range.x + range.w <= this.x || + range.y + range.h <= this.y + ) + } +} + +class Quadtree { + constructor(bounds, capacity, maxDepth = 10) { + this.bounds = new Bounds(bounds.x, bounds.y, bounds.w, bounds.h) + this.capacity = capacity + this.maxDepth = maxDepth + this.agents = [] + } + + get agentCount() { + let count = 0 + + if (this.isLeaf()) { + count += this.agents.length + } else { + for (const child of this.children) { + count += child.agentCount + } + } + + return count + } + + insert(agent, depth = 0) { + if (!this.bounds.intersects(agent)) return false + + if (!this.divided && (this.agents.length < this.capacity || depth >= this.maxDepth)) { + this.agents.push(agent) + return true + } else { + if (!this.divided) this.divide() + for (const child of this.children) { + if (child.insert(agent, depth + 1)) return true + } + } + return false + } + + isLeaf() { + return !this.divided + } + + get northWest() { + return this.children[0] + } + + get northEast() { + return this.children[1] + } + + get southWest() { + return this.children[2] + } + + get southEast() { + return this.children[3] + } + + prettyPrint(depth = 0) { + let output = "" + + if (this.isLeaf()) { + output += "-".repeat(depth - 1) + output += `[${this.bounds.x}, ${this.bounds.y}, ${this.bounds.w}, ${this.bounds.h}]: ` + output += this.agents.map(agent => agent.id).join(", ") + output += "\n" + } else { + output += this.northWest.prettyPrint(depth + 1) + output += this.northEast.prettyPrint(depth + 1) + output += this.southWest.prettyPrint(depth + 1) + output += this.southEast.prettyPrint(depth + 1) + } + + return output + } + + get divided() { + return !!this.children + } + + divide() { + const { x, y } = this.bounds + const { bounds, capacity } = this + const w = bounds.w / 2 + const h = bounds.h / 2 + + this.children = [ + new Quadtree({ x, y, w, h }, capacity), + new Quadtree({ x: x + w, y, w, h }, capacity), + new Quadtree({ x, y: y + h, w, h }, capacity), + new Quadtree({ x: x + w, y: y + h, w, h }, capacity) + ] + + for (const agent of this.agents) this.insert(agent) + delete this.agents + } + + query(range, found = []) { + if (!this.bounds.intersects(range)) return found + + if (!this.divided) { + for (const agent of this.agents) if (range.intersects(agent)) found.push(agent) + } else { + for (const child of this.children) child.query(range, found) + } + + return found + } +} + +class CollisionDetector { + constructor(agents, worldWidth, worldHeight) { + this.agents = agents + this.width = worldWidth + this.height = worldHeight + this.quadtree = new Quadtree({ x: 0, y: 0, w: worldWidth, h: worldHeight }, 4) + for (const agent of this.agents) this.addAgent(agent) + } + + addAgent(agent) { + this.quadtree.insert(agent) + return this + } + + isSpotAvailable(x, y, width, height) { + const searchBounds = new Bounds(x, y, width, height) + + const nearbyAgents = this.quadtree.query(searchBounds) + + for (const agent of nearbyAgents) { + if (x < agent.x + agent.width && x + width > agent.x && y < agent.y + agent.height && y + height > agent.y) { + return false + } + } + return true + } + + findNonOverlappingSquares(width, height, N) { + const nonOverlappingSquares = [] + const availableCells = [] + + // Divide the world into cells + for (let x = 0; x < this.width - width; x += width) { + for (let y = 0; y < this.height - height; y += height) { + if (this.isSpotAvailable(x, y, width, height)) availableCells.push({ x, y }) + } + } + + // Randomly select non-overlapping cells + while (nonOverlappingSquares.length < N && availableCells.length) { + const randomIndex = Math.floor(Math.random() * availableCells.length) + nonOverlappingSquares.push(availableCells[randomIndex]) + availableCells.splice(randomIndex, 1) + } + return nonOverlappingSquares + } + + findClusteredNonOverlappingSquares(width, height, N, centerX, centerY, clusterRadius) { + const nonOverlappingSquares = [] + const availableCells = [] + + // Divide the world into cells and filter by clusterRadius + for (let x = 0; x < this.width - width; x += width) { + for (let y = 0; y < this.height - height; y += height) { + const dx = x - centerX + const dy = y - centerY + const distance = Math.sqrt(dx * dx + dy * dy) + + if (distance <= clusterRadius && this.isSpotAvailable(x, y, width, height)) { + availableCells.push({ x: x, y: y }) + } + } + } + + // Randomly select non-overlapping cells + while (nonOverlappingSquares.length < N && availableCells.length > 0) { + const randomIndex = Math.floor(Math.random() * availableCells.length) + nonOverlappingSquares.push(availableCells[randomIndex]) + availableCells.splice(randomIndex, 1) + } + + return nonOverlappingSquares + } + + getCollidingAgents(x, y, width, height) { + const collidingAgents = [] + const queryBounds = new Bounds(x, y, width, height) + const nearbyAgents = this.quadtree.query(queryBounds) + + for (const agent of nearbyAgents) { + if (queryBounds.intersects(agent)) collidingAgents.push(agent) + } + + return collidingAgents + } + + detectCollisions() { + let collissions = [] + + for (const agentA of this.agents) { + const searchBounds = new Bounds( + agentA.x - agentA.width, + agentA.y - agentA.height, + agentA.width * 2, + agentA.height * 2 + ) + const nearbyAgents = this.quadtree.query(searchBounds) + for (const agentB of nearbyAgents) { + if (agentA !== agentB && this.checkCollision(agentA, agentB)) collissions.push([agentA, agentB]) + } + } + return collissions + } + + checkCollision(a, b) { + return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y + } +} + +module.exports = { CollisionDetector } diff --git a/components/CollisionDetector.test.node.js b/components/CollisionDetector.test.node.js new file mode 100755 index 0000000..610ccf3 --- /dev/null +++ b/components/CollisionDetector.test.node.js @@ -0,0 +1,107 @@ +#!/usr/bin/env node + +const { CollisionDetector } = require("./CollisionDetector.js") +const { TestRacer } = require("jtree/products/TestRacer.js") + +const testTree = {} + +class MockWorld { + constructor(width, height) { + this.width = width + this.height = height + } +} + +class MockAgent { + constructor(id, position) { + this.id = id + this.x = position.x + this.y = position.y + this.width = position.width + this.height = position.height + } + + get w() { + return this.width + } + + get h() { + return this.height + } +} + +testTree.empty = areEqual => { + const world = new MockWorld(500, 500) + const agents = [] + const collisionDetector = new CollisionDetector(agents, world.width, world.height) + areEqual(collisionDetector.detectCollisions().length, 0) // Should not detect any collisions +} + +testTree.miss = areEqual => { + const world = new MockWorld(500, 500) + const agents = [ + new MockAgent(1, { x: 50, y: 50, width: 10, height: 10 }), + new MockAgent(2, { x: 200, y: 200, width: 10, height: 10 }) + ] + const collisionDetector = new CollisionDetector(agents, world.width, world.height) + areEqual(collisionDetector.detectCollisions().length, 0) // Should not detect any collisions +} + +testTree.hit = areEqual => { + const world = new MockWorld(500, 500) + const agents = [ + new MockAgent(1, { x: 50, y: 50, width: 10, height: 10 }), + new MockAgent(2, { x: 55, y: 55, width: 10, height: 10 }) + ] + const collisionDetector = new CollisionDetector(agents, world.width, world.height) + areEqual(collisionDetector.detectCollisions().length, 2, "one hit") // Should detect a collision between agents 1 and 2 +} + +testTree.multiple = areEqual => { + const world = new MockWorld(500, 500) + const agents = [ + new MockAgent(1, { x: 50, y: 50, width: 10, height: 10 }), + new MockAgent(2, { x: 55, y: 55, width: 10, height: 10 }), + new MockAgent(3, { x: 200, y: 200, width: 10, height: 10 }), + new MockAgent(4, { x: 205, y: 200, width: 10, height: 10 }), + new MockAgent(5, { x: 400, y: 400, width: 10, height: 10 }) + ] + const collisionDetector = new CollisionDetector(agents, world.width, world.height) + areEqual(collisionDetector.detectCollisions().length, 4, "2 hits") // Should detect collisions between agents 1 and 2, and agents 3 and 4 +} + +testTree.get = areEqual => { + const world = new MockWorld(500, 500) + const agents = [ + new MockAgent(1, { x: 50, y: 50, width: 10, height: 10 }), + new MockAgent(2, { x: 55, y: 55, width: 10, height: 10 }), + new MockAgent(3, { x: 200, y: 200, width: 10, height: 10 }) + ] + const collisionDetector = new CollisionDetector(agents, world.width, world.height) + const collidingAgents = collisionDetector.getCollidingAgents(45, 45, 20, 20) + areEqual(collidingAgents.length, 2, "query works") // Should return an array containing the agents 1 and 2, as they collide with the specified rectangle +} + +testTree.big = areEqual => { + const world = new MockWorld(500, 500) + const agents = [ + new MockAgent(1, { x: 50, y: 50, width: 10, height: 10 }), + new MockAgent(2, { x: 55, y: 55, width: 10, height: 10 }), + new MockAgent(3, { x: 200, y: 200, width: 10, height: 10 }), + new MockAgent(4, { x: 250, y: 250, width: 10, height: 10 }), + new MockAgent(5, { x: 300, y: 300, width: 10, height: 10 }), + new MockAgent(6, { x: 30, y: 30, width: 10, height: 10 }), + new MockAgent(7, { x: 15, y: 15, width: 10, height: 10 }), + new MockAgent(8, { x: 100, y: 100, width: 10, height: 10 }), + new MockAgent(9, { x: 75, y: 75, width: 10, height: 10 }), + new MockAgent(10, { x: 350, y: 350, width: 10, height: 10 }), + new MockAgent(11, { x: 400, y: 400, width: 10, height: 10 }), + new MockAgent(12, { x: 450, y: 450, width: 10, height: 10 }) + ] + const collisionDetector = new CollisionDetector(agents, world.width, world.height) + const collidingAgents = collisionDetector.getCollidingAgents(45, 45, 30, 30) + areEqual(collidingAgents.length, 2) // Should return an array containing the agents 1 and 2, as they collide with the specified rectangle +} + +if (!module.parent) TestRacer.testSingleFile(__filename, testTree) +module.exports = { testTree } diff --git a/components/Examples.js b/components/Examples.js index 1796308..c4e6555 100644 --- a/components/Examples.js +++ b/components/Examples.js @@ -17,10 +17,6 @@ const Categories = new TreeNode(`🦠 Epidemiology basketball 💰 Business startupIdeas -👾 Game of Life - gameOfLife - gospersGliderGun - gameOfLifeAdvanced 🦋 Biology moths 🕹 Games diff --git a/components/Grid.js b/components/Grid.js index f80db79..59564d3 100644 --- a/components/Grid.js +++ b/components/Grid.js @@ -1,34 +1,25 @@ -const { yodash } = require("../yodash.js") const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") class GridComponent extends AbstractTreeComponentParser { - gridClickCommand(down, right) { - return this.parent.insertAgentAtCommand(right, down) + gridClickCommand(x, y) { + return this.parent.insertAgentAtCommand(x, y) } - makeBlock(down, right, gridSize) { - return `\n div - class block - style width:${gridSize}px;height:${gridSize}px;top:${down * gridSize}px;left:${right * gridSize}px; - clickCommand gridClickCommand ${yodash.makePositionHash({ right, down })}` + treeComponentDidMount() { + const that = this + if (this.isNodeJs()) return super.treeComponentDidMount() + + jQuery(`.${GridComponent.name}`).on("click", function (evt) { + const { offsetX, offsetY } = evt + const x = offsetX + const y = offsetY + that.gridClickCommand(x, y) + }) } toStumpCode() { - const { cols, rows, gridSize } = this.parent - let blocks = "" - let rs = rows - while (rs >= 0) { - let cs = cols - while (cs >= 0) { - blocks = this.makeBlock(rs, cs, gridSize) + blocks - cs-- - } - rs-- - } - return ( - `div - class ${GridComponent.name}` + blocks - ) + return `div + class ${GridComponent.name}` } } diff --git a/components/SimEditor.js b/components/SimEditor.js index 7c718f0..8f4149d 100644 --- a/components/SimEditor.js +++ b/components/SimEditor.js @@ -2,7 +2,7 @@ const { TreeNode } = require("jtree/products/TreeNode.js") const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") // prettier-ignore -/*NODE_JS_ONLY*/ const simojiParser = require("jtree/products/GrammarCompiler.js").GrammarCompiler.compileGrammarFileAtPathAndReturnRootParser( __dirname + "/../simoji.grammar") +/*NODE_JS_ONLY*/ const simojiParser = require("jtree/products/GrammarCompiler.js").GrammarCompiler.compileGrammarFileAtPathAndReturnRootParser( __dirname + "/../dist/simoji.grammar") class CodeMirrorShim { setSize() {} diff --git a/components/SimojiApp.js b/components/SimojiApp.js index f5c0b04..93cd76c 100644 --- a/components/SimojiApp.js +++ b/components/SimojiApp.js @@ -20,16 +20,10 @@ const { BottomBarComponent } = require("./BottomBar.js") const { RightBarComponent } = require("./RightBar.js") const { EditorHandleComponent } = require("./EditorHandle.js") const { TitleComponent } = require("./Title.js") -const { Keywords, LocalStorageKeys, UrlKeys, Directions, ParserTypes } = require("./Types.js") - -const MIN_GRID_SIZE = 10 -const MAX_GRID_SIZE = 200 -const DEFAULT_GRID_SIZE = 20 -const MIN_GRID_COLUMNS = 10 -const MIN_GRID_ROWS = 10 +const { Keywords, LocalStorageKeys, UrlKeys, ParserTypes } = require("./Types.js") // prettier-ignore -/*NODE_JS_ONLY*/ const simojiParser = require("jtree/products/GrammarCompiler.js").GrammarCompiler.compileGrammarFileAtPathAndReturnRootParser( __dirname + "/../simoji.grammar") +/*NODE_JS_ONLY*/ const simojiParser = require("jtree/products/GrammarCompiler.js").GrammarCompiler.compileGrammarFileAtPathAndReturnRootParser( __dirname + "/../dist/simoji.grammar") class githubTriangleComponent extends AbstractTreeComponentParser { githubLink = `https://github.com/breck7/simoji` @@ -87,20 +81,15 @@ class SimojiApp extends AbstractTreeComponentParser { } makeGrid(simojiProgram, windowWidth, windowHeight) { - const setSize = simojiProgram.get(Keywords.size) - const gridSize = Math.min(Math.max(setSize ? parseInt(setSize) : DEFAULT_GRID_SIZE, MIN_GRID_SIZE), MAX_GRID_SIZE) - const chromeWidth = this.leftStartPosition + SIZES.RIGHT_BAR_WIDTH + SIZES.BOARD_MARGIN - const maxAvailableCols = Math.floor((windowWidth - chromeWidth) / gridSize) - 1 - const maxAvailableRows = Math.floor((windowHeight - SIZES.CHROME_HEIGHT - SIZES.TITLE_HEIGHT) / gridSize) - 1 + const width = windowWidth - chromeWidth - 1 + const height = windowHeight - SIZES.CHROME_HEIGHT - SIZES.TITLE_HEIGHT - 1 - const setCols = simojiProgram.get(Keywords.columns) - const cols = Math.max(1, setCols ? parseInt(setCols) : Math.max(MIN_GRID_COLUMNS, maxAvailableCols)) + const setWidth = simojiProgram.get(Keywords.width) + const setHeight = simojiProgram.get(Keywords.height) + // todo: use the set values if present - const setRows = simojiProgram.get(Keywords.rows) - const rows = Math.max(1, setRows ? parseInt(setRows) : Math.max(MIN_GRID_ROWS, maxAvailableRows)) - - return { gridSize, cols, rows } + return { width, height } } verbose = true @@ -140,11 +129,11 @@ class SimojiApp extends AbstractTreeComponentParser { _appendExperiment(program, index) { const { windowWidth, windowHeight } = this - const { gridSize, cols, rows } = this.makeGrid(program, windowWidth, windowHeight) + const { width, height } = this.makeGrid(program, windowWidth, windowHeight) const styleNode = program.getNode(Keywords.style) ?? undefined const board = this.appendLineAndChildren( - `${BoardComponent.name} ${gridSize} ${rows} ${cols} ${index}`, + `${BoardComponent.name} 1 ${width} ${height} ${index}`, `leftStartPosition ${this.leftStartPosition} ${GridComponent.name} ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : ""}`.trim() @@ -251,7 +240,6 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : const previousTick = maxTick - 2 this.pauseAllCommand() if (previousTick < 0) return - if (!this.isSnapshotOn) this.snapShotCommand() this.loadNewSim(this.simCode) this.boards.forEach(board => board.skipToThisManyTicksIfNotPaused(previousTick)) console.log(`Running to tick ${previousTick} from ${maxTick}`) @@ -331,25 +319,6 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : this.renderAndGetRenderReport() } - toggleSelectCommand(objects) { - objects.forEach(object => { - this.selection.includes(object) ? this.unselectCommand(object) : this.selectCommand(object) - }) - - this.ensureRender() - return this - } - - unselectCommand(object) { - object.unselect() - this.selection = this.selection.filter(node => node !== object) - } - - selectCommand(object) { - this.selection.push(object) - object.select() - } - async downloadCsvCommand() { let extension = "csv" let type = "text/csv" @@ -427,27 +396,25 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : selection = [] - moveSelection(direction) { + moveSelection(x, y) { const { selection } = this if (!selection.length) return this selection.forEach(node => { - node.angle = direction + node.direction = { x, y } node._move() }) - this.boards.forEach(board => board.resetAgentPositionMap()) - this.ensureRender() } deleteSelectionCommand() { this.selection.forEach(node => node.nuke()) this.selection = [] - this.boards.forEach(board => board.resetAgentPositionMap()) + // todo: update any state? } get isSnapshotOn() { - // technically also needs rows and column settings + // technically also needs width and height settings return new TreeNode(this.simCode).has(Keywords.seed) } @@ -459,13 +426,13 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : // todo: buggy. we should rename the board class to experiment, or rename experiment keyword to board. const board = boards[0] newCode.set(Keywords.seed, board.seed.toString()) - newCode.set(Keywords.rows, board.rows.toString()) - newCode.set(Keywords.columns, board.cols.toString()) + newCode.set(Keywords.height, board.height.toString()) + newCode.set(Keywords.width, board.width.toString()) newCode.findNodes(Keywords.experiment).forEach((experiment, index) => { const board = boards[index] experiment.set(Keywords.seed, board.seed.toString()) - experiment.set(Keywords.rows, board.rows.toString()) - experiment.set(Keywords.columns, board.cols.toString()) + experiment.set(Keywords.height, board.height.toString()) + experiment.set(Keywords.width, board.width.toString()) }) this.editor.setCodeMirrorValue(newCode.toString()) @@ -501,10 +468,10 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : o: () => this.openReportInOhayoCommand(), r: () => this.resetAllCommand(), s: () => this.snapShotCommand(), - up: () => this.moveSelection(Directions.North), - down: () => this.moveSelection(Directions.South), - right: () => this.moveSelection(Directions.East), - left: () => this.moveSelection(Directions.West), + up: () => this.moveSelection(0, -1), + down: () => this.moveSelection(0, 1), + right: () => this.moveSelection(1, 0), + left: () => this.moveSelection(-1, 0), escape: () => this.clearSelectionCommand(), "command+a": () => this.selectAllCommand(), "?": () => this.toggleHelpCommand(), diff --git a/components/SimojiApp.test.node.js b/components/SimojiApp.test.node.js index c6ad791..d925005 100755 --- a/components/SimojiApp.test.node.js +++ b/components/SimojiApp.test.node.js @@ -1,12 +1,13 @@ #!/usr/bin/env node const { TreeNode } = require("jtree/products/TreeNode.js") +const { TestRacer } = require("jtree/products/TestRacer.js") const { GrammarCompiler } = require("jtree/products/GrammarCompiler.js") const { Disk } = require("jtree/products/Disk.node.js") const grammarNode = require("jtree/products/grammar.nodejs.js") const { SimojiApp } = require("./SimojiApp.js") -const grammarPath = __dirname + "/../simoji.grammar" +const grammarPath = __dirname + "/../dist/simoji.grammar" const examplesPath = __dirname + "/../examples/" const simojiParser = GrammarCompiler.compileGrammarFileAtPathAndReturnRootParser(grammarPath) const testTree = {} @@ -67,11 +68,5 @@ insert 10 😃 areEqual(app.simojiPrograms[0].getAllErrors().length, 2, "invalid programs dont crash") } +if (!module.parent) TestRacer.testSingleFile(__filename, testTree) module.exports = { testTree } -const runTree = testTree => { - const tap = require("tap") - Object.keys(testTree).forEach(key => { - testTree[key](tap.equal) - }) -} -if (module && !module.parent) runTree(testTree) diff --git a/components/Types.js b/components/Types.js index 631356f..7e19a23 100644 --- a/components/Types.js +++ b/components/Types.js @@ -2,15 +2,12 @@ const Keywords = {} Keywords.experiment = "experiment" Keywords.seed = "seed" -Keywords.size = "size" -Keywords.rows = "rows" -Keywords.columns = "columns" +Keywords.height = "height" +Keywords.width = "width" Keywords.report = "report" Keywords.ticksPerSecond = "ticksPerSecond" Keywords.style = "style" -Keywords.onNeighbors = "onNeighbors" -Keywords.onTouch = "onTouch" Keywords.onHit = "onHit" Keywords.onTick = "onTick" Keywords.onDeath = "onDeath" @@ -29,13 +26,6 @@ UrlKeys.simoji = "simoji" UrlKeys.example = "example" UrlKeys.url = "url" -const Directions = {} - -Directions.North = "North" -Directions.East = "East" -Directions.South = "South" -Directions.West = "West" - const ParserTypes = {} ParserTypes.agentDefinitionParser = "agentDefinitionParser" @@ -43,4 +33,4 @@ ParserTypes.experimentParser = "experimentParser" ParserTypes.settingDefinitionParser = "settingDefinitionParser" ParserTypes.abstractInjectCommandParser = "abstractInjectCommandParser" -module.exports = { Keywords, LocalStorageKeys, UrlKeys, Directions, ParserTypes } +module.exports = { Keywords, LocalStorageKeys, UrlKeys, ParserTypes } diff --git a/dev.html b/dev.html index 0d4b5c2..5fb04cb 100644 --- a/dev.html +++ b/dev.html @@ -28,6 +28,7 @@ <script src="./components/Board.js"></script> <script src="./components/Examples.js"></script> <script src="./components/Grid.js"></script> + <script src="./components/CollisionDetector.js"></script> <script src="./components/HelpModal.js"></script> <script src="./components/Title.js"></script> <script src="./components/AgentPalette.js"></script> diff --git a/dist/constants.js b/dist/constants.js index f86e503..833b084 100644 --- a/dist/constants.js +++ b/dist/constants.js @@ -1 +1 @@ -const SimConstants = {"grammar":"anyCell\nbooleanCell\nstringCell\n highlightScope string\nsettingValueCell\n highlightScope constant.numeric\ncssCell\n highlightScope string\njavascriptCell\n highlightScope string\nhtmlCell\n highlightScope string\nemojiCell\n highlightScope string\nohayoCell\n highlightScope string\nblankCell\ncodeCell\n highlightScope comment\ncommentCell\n highlightScope comment\nkeywordCell\n highlightScope keyword\ntextCell\n highlightScope string\nintegerCell\n highlightScope constant.numeric\nbehaviorNameCell\n highlightScope keyword\nconditionalOperatorCell\n highlightScope keyword\n enum < > = <= >=\npositionCell\n highlightScope constant.numeric\nneighborCountCell\n extends integerCell\n min 0\n max 8\nintegerOrPercentCell\n highlightScope constant.numeric\nprobabilityCell\n description A number between 0 and 1\n highlightScope constant.numeric\npropertyNameCell\n highlightScope keyword\nangleCell\n enum North South East West NorthWest NorthEast SouthWest SouthEast\n highlightScope constant.numeric\nerrorParser\n baseParser errorParser\nsimojiParser\n extensions simoji\n description A Tree Language that compiles to a TreeComponentFramework app.\n root\n inScope abstractIgnoreParser abstractSetupParser abstractInjectCommandParser onTickParser onExtinctParser behaviorDefinitionParser experimentParser settingDefinitionParser\n catchAllParser agentDefinitionParser\n compilesTo javascript\n example\n 🦋\n onTick .1\n turnRandomly\n move\n onTick .2\n turnToward 💡\n move\n 💡\n \n insert 10 🦋\n insert 2 💡\n javascript\n get agentTypes() {\n return this.filter(node => node.parserId === \"agentDefinitionParser\")\n }\nexperimentParser\n cruxFromId\n cells keywordCell\n inScope abstractIgnoreParser abstractSetupParser abstractInjectCommandParser onTickParser onExtinctParser settingDefinitionParser\n catchAllCellType stringCell\nabstractSetupParser\natTimeParser\n cruxFromId\n description Run commands at a certain tick.\n cells keywordCell integerCell\n extends abstractSetupParser\n inScope abstractInjectCommandParser\nabstractSetupNumberParser\n cells keywordCell integerCell\n extends abstractSetupParser\n javascript\n compile() {\n return \"\"\n }\nsizeParser\n description Size of a grid cell in pixels. Min is 10. Max is 200.\n extends abstractSetupNumberParser\n cruxFromId\nrowsParser\n description Number of rows in the grid. Default is based on screen size.\n extends abstractSetupNumberParser\n cruxFromId\ncolumnsParser\n description Number of columns in the grid. Default is based on screen size.\n extends abstractSetupNumberParser\n cruxFromId\nseedParser\n description If you'd like reproducible runs set a seed for the random number generator.\n extends abstractSetupNumberParser\n cruxFromId\nticksPerSecondParser\n description Time in milliseconds of one step.\n extends abstractSetupNumberParser\n cruxFromId\nreportParser\n cruxFromId\n description Define a custom report template.\n catchAllParser ohayoLineParser\n extends abstractSetupParser\n cells keywordCell\n javascript\n compile() {\n return \"\"\n }\nstyleParser\n description Optional CSS to load in BoardStyleComponent\n extends abstractSetupParser\n cells keywordCell\n cruxFromId\n catchAllParser styleLineParser\n javascript\n compile() {\n return \"\"\n }\nquestionParser\n cruxFromId\n description What are you trying to figure out?\n cells keywordCell\n catchAllCellType stringCell\n extends abstractSetupParser\nabstractInjectCommandParser\nfillParser\n description Fill all blank cells with this agent.\n extends abstractInjectCommandParser\n cells keywordCell emojiCell\n cruxFromId\ndrawParser\n extends abstractInjectCommandParser\n cells keywordCell\n cruxFromId\n catchAllParser drawLineParser\ninsertParser\n extends abstractInjectCommandParser\n cells keywordCell integerOrPercentCell emojiCell\n cruxFromId\ninsertAtParser\n extends insertParser\n description Insert at X Y\n cells keywordCell emojiCell positionCell positionCell\n cruxFromId\ninsertClusterParser\n extends insertParser\n cruxFromId\n catchAllCellType integerCell\nrectangleDrawParser\n extends abstractInjectCommandParser\n cells keywordCell emojiCell integerCell integerCell\n catchAllCellType integerCell\n crux rectangle\npasteDrawParser\n extends abstractInjectCommandParser\n cells keywordCell\n crux paste\n catchAllParser pasteLineParser\ndrawLineParser\n catchAllCellType emojiCell\npasteLineParser\n catchAllCellType anyCell\n catchAllParser pasteLineParser\nagentDefinitionParser\n inScope abstractIgnoreParser abstractEventParser abstractAgentAttributeParser behaviorAttributeParser\n cells keywordCell\n catchAllParser errorParser\n compiler\n stringTemplate \n javascript\n compile() {\n const root = this.root\n const name = root.agentKeywordMap[this.firstWord]\n const normal = super.compile()\n const behaviors = this.filter(node => node.parserId === \"behaviorAttributeParser\")\n .map(behavior => `\"${behavior.getLine()}\"`)\n .join(\",\")\n return `class ${name} extends Agent {\n icon = \"${this.firstWord}\"\n behaviors = [${behaviors}]\n ${normal}\n }`\n }\nabstractCommandParser\n cells keywordCell\nabstractSubjectObjectCommandParser\n extends abstractCommandParser\nreplaceWithCommandParser\n extends abstractSubjectObjectCommandParser\n crux replaceWith\n cells keywordCell emojiCell\nkickItCommandParser\n extends abstractSubjectObjectCommandParser\n crux kickIt\nshootCommandParser\n extends abstractSubjectObjectCommandParser\n crux shoot\npickItUpCommandParser\n extends abstractSubjectObjectCommandParser\n crux pickItUp\nspawnCommandParser\n crux spawn\n extends abstractCommandParser\n cells keywordCell emojiCell\n catchAllCellType positionCell\nmoveToEmptySpotCommandParser\n crux moveToEmptySpot\n extends abstractCommandParser\n cells keywordCell\nremoveCommandParser\n description Remove this agent from the board.\n crux remove\n extends abstractCommandParser\n cells keywordCell\njavascriptCommandParser\n description An escape hatch so you can write custom javascript in a pinch.\n extends abstractCommandParser\n crux javascript\n catchAllParser javascriptLineParser\n cells keywordCell\nalertCommandParser\n extends abstractCommandParser\n crux alert\n catchAllCellType stringCell\nlogCommandParser\n extends abstractCommandParser\n crux log\n catchAllCellType stringCell\nnarrateCommandParser\n extends abstractCommandParser\n crux narrate\n catchAllCellType stringCell\npauseCommandParser\n extends abstractCommandParser\n crux pause\ndecreaseCommandParser\n extends abstractCommandParser\n description Decrease a property by 1.\n crux decrease\n cells keywordCell propertyNameCell\nincreaseCommandParser\n extends abstractCommandParser\n description Increase a property by 1.\n crux increase\n cells keywordCell propertyNameCell\nmoveCommandParser\n extends abstractCommandParser\n crux move\nturnRandomlyCommandParser\n extends abstractCommandParser\n crux turnRandomly\njitterCommandParser\n extends abstractCommandParser\n crux jitter\nturnTowardCommandParser\n description Turn to the closest agent of a certain type.\n extends abstractCommandParser\n crux turnToward\n cells keywordCell emojiCell\nturnFromCommandParser\n description Turn away from the closest agent of a certain type.\n extends abstractCommandParser\n crux turnFrom\n cells keywordCell emojiCell\nlearnCommandParser\n crux learn\n extends abstractCommandParser\n cells keywordCell behaviorNameCell\nunlearnCommandParser\n crux unlearn\n extends abstractCommandParser\n cells keywordCell behaviorNameCell\nabstractAgentAttributeParser\n cells keywordCell\nstringAttributeParser\n extends abstractAgentAttributeParser\n pattern ^\\w+ .+$\n catchAllCellType stringCell\n javascript\n compile() {\n return `${this.firstWord} = \"${this.getWord(1)}\"`\n }\nangleParser\n extends stringAttributeParser\n cells keywordCell angleCell\n cruxFromId\nagentStyleParser\n description Provide custom CSS for an agent type.\n extends stringAttributeParser\n cells keywordCell cssCell\n crux style\nagentHtmlParser\n description Provide custom HTML for each rendered agent.\n extends stringAttributeParser\n cells keywordCell htmlCell\n crux html\nabstractBooleanAttributeParser\n description A boolean attribute.\n extends abstractAgentAttributeParser\n javascript\n compile() {\n return `${this.firstWord} = true`\n }\nnoPaletteParser\n extends abstractBooleanAttributeParser\n cruxFromId\n description Don't show this agent in the palette.\nsolidTraitParser\n description If set other agents won't pass through these.\n extends abstractBooleanAttributeParser\n crux solid\nbouncyTraitParser\n description If set other agents will bounce off this after a collision.\n extends abstractBooleanAttributeParser\n crux bouncy\nabstractIntegerAttributeParser\n extends abstractAgentAttributeParser\n description An integer attribute.\n cells keywordCell integerCell\n javascript\n compile() {\n return `${this.firstWord} = ${this.getWord(1)}`\n }\ncustomIntegerAttributeParser\n pattern ^\\w+ \\d+$\n extends abstractIntegerAttributeParser\nhealthParser\n extends abstractIntegerAttributeParser\n cruxFromId\nsettingDefinitionParser\n description Define a configurable input.\n cells keywordCell settingValueCell\n pattern ^\\w+Setting .+$\nohayoLineParser\n description Data visualization code written for Ohayo.\n catchAllCellType ohayoCell\nstyleLineParser\n catchAllCellType cssCell\n catchAllParser styleLineParser\ntargetEmojiParser\n inScope abstractCommandParser\n cells emojiCell\nabstractEventParser\n cells keywordCell\n catchAllCellType probabilityCell\n javascript\n compile() {\n return ``\n }\nabstractInteractionEventParser\n extends abstractEventParser\n catchAllParser targetEmojiParser\nonHitParser\n extends abstractInteractionEventParser\n cruxFromId\n description Define what happens when this agent collides with other agents.\nonTouchParser\n extends abstractInteractionEventParser\n cruxFromId\n description Define what happens when this agent is adjacent to other agents.\nonNeighborsParser\n description Define what happens when a certain amount of neighbors are nearby.\n extends abstractInteractionEventParser\n inScope emojiAndNeighborConditionParser\n cruxFromId\nonDeathParser\n extends abstractEventParser\n cruxFromId\n inScope abstractCommandParser\n description Define what happens when this agent runs out of health.\nonTickParser\n extends abstractEventParser\n cruxFromId\n inScope abstractCommandParser\n description Define what happens each tick.\nemojiAndNeighborConditionParser\n inScope abstractCommandParser\n pattern ^.+ (<|>|=|<=|>=)+ .+$\n cells emojiCell conditionalOperatorCell neighborCountCell\nonExtinctParser\n cruxFromId\n inScope abstractCommandParser\n cells keywordCell emojiCell\n description Define what happens when a type of agent goes extinct from the board.\n javascript\n compile() {\n return \"\"\n }\nabstractIgnoreParser\n tags doNotSynthesize\n javascript\n compile () {\n return \"\"\n }\ncommentParser\n extends abstractIgnoreParser\n catchAllCellType commentCell\n cruxFromId\n catchAllParser commentLineParser\ncommentAliasParser\n description Alternate alias for a comment.\n crux #\n extends commentParser\nblankLineParser\n extends abstractIgnoreParser\n description Blank lines compile do nothing.\n cells blankCell\n pattern ^$\ncommentLineParser\n catchAllCellType commentCell\njavascriptLineParser\n catchAllCellType javascriptCell\nbehaviorAttributeParser\n cells behaviorNameCell\n pattern ^.*Behavior$\n javascript\n compile() {\n return \"\"\n }\nbehaviorDefinitionParser\n inScope abstractIgnoreParser abstractEventParser\n cells behaviorNameCell\n pattern ^.*Behavior$\n catchAllParser errorParser\n javascript\n compile() {\n return \"\"\n }","examples":"ants\n comment\n https://ccl.northwestern.edu/netlogo/models/Ants\n \n ⛰\n onTick 0.05\n spawn 🐜\n 🐜\n onTick\n jitter\n onHit\n 🥖\n pickItUp\n 🥖\n \n insert 3 🥖\n insert 1 ⛰\nbasketball\n question Is it better to shoot wildly or to bring it close to the basket?\n \n experiment Shoot rarely\n shotProbabilitySetting .02\n \n experiment\n shotProbabilitySetting .2\n \n experiment\n shotProbabilitySetting .4\n \n experiment Shoot right away\n shotProbabilitySetting .8\n \n 🏀\n onHit\n 🥅⛹️‍♂️\n narrate Blue scores!\n spawn 🏀 9⬇️ 15➡️\n spawn 🔵 18⬇️ 1➡️\n remove\n 🥅⛹️‍♀️\n narrate Red scores!\n spawn 🏀 9⬇️ 15➡️\n spawn 🔴 17⬇️ 1➡️\n remove\n \n \n moveEastToBlankSpotBehavior\n onTick\n moveToEmptySpot\n unlearn moveEastToBlankSpotBehavior\n \n \n 🔵\n angle East\n moveEastToBlankSpotBehavior\n 🔴\n angle East\n moveEastToBlankSpotBehavior\n \n \n ticksPerSecond 30\n \n hasBallBehavior\n comment Sprint toward net\n onTick .5\n turnToward net\n move\n narrate breaks toward the net.\n comment Shoot\n onTick shotProbabilitySetting\n turnToward net\n shoot\n narrate shoots!\n learn noBallBehavior\n unlearn hasBallBehavior\n comment Pass\n onTick .02\n turnToward team\n shoot\n narrate passes the ball!\n learn noBallBehavior\n unlearn hasBallBehavior\n \n noBallBehavior\n onTick .3\n turnToward 🏀\n move\n onHit\n 🏀\n pickItUp\n narrate has the ball\n learn hasBallBehavior\n unlearn noBallBehavior\n onTick .05\n turnFrom opponent\n move\n onTick .1\n turnFrom opponent\n jitter\n \n # Blue Team\n ⛹️‍♂️\n net 🥅⛹️‍♂️\n team ⛹️‍♂️\n opponent ⛹️‍♀️\n noBallBehavior\n \n # Red Team\n ⛹️‍♀️\n net 🥅⛹️‍♀️\n team ⛹️‍♀️\n opponent ⛹️‍♂️\n noBallBehavior\n \n # Baskets\n 🥅⛹️‍♂️\n html 🥅\n 🥅⛹️‍♀️\n html 🥅\n paste\n 🥅⛹️‍♂️ 8⬇️ 2➡️\n 🥅⛹️‍♀️ 8⬇️ 29➡️\n 🏀 9⬇️ 15➡️\n \n # Court\n 🪵\n solid\n rectangle 🪵 30 15 1 1\n \n size 30\n \n # Red Team\n paste\n ⛹️‍♀️ 9⬇️ 6➡️\n ⛹️‍♀️ 5⬇️ 6➡️\n ⛹️‍♀️ 11⬇️ 11➡️\n ⛹️‍♀️ 8⬇️ 11➡️\n ⛹️‍♀️ 5⬇️ 11➡️\n \n # Blue Team\n paste\n ⛹️‍♂️ 8⬇️ 25➡️\n ⛹️‍♂️ 6⬇️ 25➡️\n ⛹️‍♂️ 11⬇️ 20➡️\n ⛹️‍♂️ 7⬇️ 20➡️\n ⛹️‍♂️ 4⬇️ 20➡️\n \ncity\n ⛪️\n comment church\n 🏟\n comment stadium\n 🏥\n comment hospital\n 🏭\n comment factory\n 🏦\n comment bank\n 🏛\n comment courthouse\n 🏫\n comment school\n 🏡\n comment house\n 🏘\n comment houses\n 🎡\n comment park\n 🏪\n comment store\n 🚗\n onTick\n move\n move\n angle West\n 🚓\n onTick\n move\n move\n angle West\n 🚋\n comment subway\n onTick\n move\n move\n move\n angle West\n ⬜️\n comment road\n 🟩\n ⛳️\n size 20\n \n rectangle ⬜️ 10 20 0 0\n rectangle ⬜️ 10 1 0 10\n paste\n 🏛 9⬇️ 8➡️\n 🏭 1⬇️ 4➡️\n 🏭 1⬇️ 3➡️\n 🏭 1⬇️ 2➡️\n 🏭 1⬇️ 1➡️\n 🏡 18⬇️ 5➡️\n 🏡 18⬇️ 6➡️\n 🏡 18⬇️ 7➡️\n 🏡 18⬇️ 8➡️\ncops\n 🚗\n onTick .5\n jitter\n move\n 🚓\n onHit\n 🚗\n alert Got em!\n pause\n onTick .1\n turnToward 🚗\n move\n onTick .1\n move\n \n size 20\n ticksPerSecond 30\n \n paste\n 🚓 1⬇️ 1➡️\n 🚗 15⬇️ 5➡️\ncovid19\n 🦠\n \n question How long will the pandemic last?\n \n # Given someone has never been infected, what are the odds they get infected?\n succeptibilitySetting .95\n # What are odds of reinfection?\n reinfectionRateSetting .002\n \n # 1 is no lockdowns. 0 is total lockdown.\n freedomOfMovementSetting 1\n \n # What is starting population size?\n urbanPopulationSetting 150\n # What is starting rural population?\n ruralPopulationSetting 30\n # What is starting infected size?\n startingInfectedSetting 3\n \n # How many places can one get the vaccine?\n vaccineCentersSetting 5\n # How likely are people to seek the vaccine?\n vaccinationDesirabilitySetting .3\n # Given someone was vaxed, what are the odds they get infected?\n vaxSucceptibilitySetting .5\n \n experiment High Vaccination Rate, High Vaccine Efficacy\n vaccinationDesirabilitySetting .8\n vaxSucceptibilitySetting .05\n \n \n experiment High Vaccination Rate, Low Vaccine Efficacy\n vaccinationDesirabilitySetting .8\n vaxSucceptibilitySetting .75\n \n experiment Lockdown\n freedomOfMovementSetting .3\n \n experiment High Reinfection Rate\n reinfectionRateSetting .2\n \n \n insert startingInfectedSetting 🧟\n insert vaccineCentersSetting 💉\n \n insertCluster urbanPopulationSetting 🙍\n insert ruralPopulationSetting 🙍\n \n \n 🧟\n health 100\n onTick .03\n log recovered\n replaceWith 🦸‍♂️\n onTick\n decrease health\n jitter\n onDeath\n replaceWith 🪦\n \n 🦸‍♂️\n comment Recovered\n onTick\n jitter\n onTouch reinfectionRateSetting\n 🧟\n replaceWith 🧟\n \n lifeBehavior\n onTick freedomOfMovementSetting\n jitter\n \n seekVaccineBehavior\n onTick vaccinationDesirabilitySetting\n turnToward 💉\n move\n \n \n 🙍\n lifeBehavior\n seekVaccineBehavior\n onTouch innateImmunitySetting\n 🧟\n replaceWith 🧟\n 💉\n replaceWith 🧑🏽‍🚒\n \n \n 💉\n \n \n 🧑🏽‍🚒\n lifeBehavior\n onTouch vaxSucceptibilitySetting\n 🧟\n replaceWith 🧟\n \n \n \n \n onExtinct 🧟\n log No more cases.\n pause\n \n \n 🪦\n \n size 15\n ticksPerSecond 10\n \n report\n roughjs.line\n columns.keep 🧟\n roughjs.line Active Cases\n columns.keep 🪦\n roughjs.line Cumulative Deaths\n \n \n comment\n See Also\n - http://covidsim.eu/\n - http://modelingcommons.org/browse/one_model/6282#model_tabs_browse_info\n - https://github.com/maplerainresearch/covid19-sim-mesa/blob/master/model.py\n - https://www.frontiersin.org/articles/10.3389/fpubh.2020.563247/full\n - https://ncase.me/covid-19/\n - https://en.wikipedia.org/wiki/List_of_COVID-19_simulation_models\n \n \ncovid19simple\n question What is the effect of population density on pandemic duration?\n \n experiment\n insertCluster 100 🙍\n insertCluster 100 🙍\n insertCluster 100 🙍\n insertCluster 30 🙍\n insertCluster 30 🙍\n insertCluster 10 🙍\n \n experiment\n insert 200 🙍\n \n experiment\n insertCluster 200 🙍\n \n experiment\n insertCluster 200 🙍\n insert 200 🙍\n \n \n 🦠\n health 10\n onTick\n decrease health\n onDeath\n remove\n \n 🧟\n health 100\n onTick .03\n log recovered\n replaceWith 🦸‍♂️\n onTick\n decrease health\n jitter\n onDeath\n replaceWith 🪦\n \n 🦸‍♂️\n comment immune\n onTick\n jitter\n \n 🙍\n onTick\n jitter\n onTouch\n 🦠\n replaceWith 🧟\n 🧟\n replaceWith 🧟\n \n insert 1 🦠\n \n onExtinct 🧟\n log No more cases.\n pause\n \n \n 🪦\n \n size 15\n ticksPerSecond 10\n \n report\n roughjs.line\n columns.keep 🧟\n roughjs.line Active Cases\n columns.keep 🪦\n roughjs.line Cumulative Deaths\n \n \n comment\n See Also\n - http://covidsim.eu/\n - http://modelingcommons.org/browse/one_model/6282#model_tabs_browse_info\n - https://github.com/maplerainresearch/covid19-sim-mesa/blob/master/model.py\n - https://www.frontiersin.org/articles/10.3389/fpubh.2020.563247/full\n - https://ncase.me/covid-19/\n - https://en.wikipedia.org/wiki/List_of_COVID-19_simulation_models\n \neatTheBacon\n 🐕\n onTick .2\n turnToward 🥓\n move\n onTick .2\n jitter\n 🥓\n onTouch\n 🐕\n remove\n 🥦\n \n \n insert 1 🐕\n insert 3 🥓\n insert 10 🥦\n \nelevators\n 🛗\n onTick\n move\n move\n angle South\n bouncy\n onHit\n 🚶🏻\n pickItUp\n 🚶🏻\n angle West\n onTick\n move\n bouncy\n 🌾\n 🚪\n onTick .001\n spawn 🚶🏻\n 🪵\n solid\n 🚗\n \n \n size 15\n \n rectangle 🪵 20➡️ 47⬇️ 5 1\n rectangle 🌾 40➡️ 1⬇️ 0 48\n rectangle 🚪 1➡️ 45⬇️ 15 2\n paste\n 🛗 6⬇️ 19➡️\n 🛗 4⬇️ 22➡️\n 🛗 3⬇️ 20➡️\n 🛗 10⬇️ 13➡️\n 🛗 4⬇️ 9➡️\n 🛗 3⬇️ 11➡️\n 🚗 47⬇️ 30➡️\n 🚗 47⬇️ 28➡️\nfire\n question How fast do fires spread?\n \n 🌲\n onHit\n ⚡️\n replaceWith 🔥\n onTouch\n 🔥\n replaceWith 🔥\n \n ⚡️\n health 10\n onTick\n decrease health\n onDeath\n remove\n \n 🔥\n health 50\n onTick\n decrease health\n onDeath\n replaceWith ⬛️\n \n \n ⬛️\n comment Burnt forest\n html 🌲\n style filter:grayscale(100%);\n \n \n insert 50% 🌲\n onTick .3\n spawn ⚡️\nfireAdvanced\n question What is the effect of forest density on fire risk?\n \n experiment\n treeDensitySetting 10%\n \n experiment\n treeDensitySetting 20%\n \n experiment\n treeDensitySetting 40%\n \n experiment\n treeDensitySetting 80%\n \n catchFireSetting .3\n fireSpreadSetting .7\n fireLifetimeSetting 10\n lightningFrequencySetting .1\n \n 🌲\n onHit catchFireSetting\n ⚡️\n replaceWith 🔥\n onTouch fireSpreadSetting\n 🔥\n replaceWith 🔥\n \n ⚡️\n health 10\n onTick\n decrease health\n onDeath\n remove\n \n 🔥\n health fireLifetimeSetting\n onTick\n decrease health\n onDeath\n replaceWith ⬛️\n \n \n ⬛️\n comment Burnt forest\n html 🌲\n style filter:grayscale(100%);\n \n \n insert treeDensitySetting 🌲\n onTick lightningFrequencySetting\n spawn ⚡️\n \ngameOfLife\n question Can simple rules produce complex effects?\n \n ⬛️\n onNeighbors\n ⬛️ < 2\n replaceWith ◻️\n ⬛️ > 3\n replaceWith ◻️\n \n ◻️\n onNeighbors\n ⬛️ = 3\n replaceWith ⬛️\n \n insert 10% ⬛️\n fill ◻️\n size 15\ngameOfLifeAdvanced\n # Conway's Game of Life\n \n experiment\n neighborSetting 2\n \n experiment\n neighborSetting 3\n \n experiment\n neighborSetting 4\n \n experiment\n neighborSetting 5\n \n ⬛️\n onNeighbors\n ⬛️ < 2\n replaceWith ◻️\n ⬛️ > neighborSetting\n replaceWith ◻️\n \n ◻️\n onNeighbors\n ⬛️ = 3\n replaceWith ⬛️\n \n insert 10% ⬛️\n fill ◻️\n size 15\ngospersGliderGun\n ⬛️\n onNeighbors\n ⬛️ < 2\n replaceWith ◻️\n ⬛️ > 3\n replaceWith ◻️\n \n ◻️\n onNeighbors\n ⬛️ = 3\n replaceWith ⬛️\n \n # Gosper's Glider Gun\n \n draw\n ⬛️ \n ⬛️ ⬛️ \n ⬛️ ⬛️ ⬛️ ⬛️ \n ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️\n ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️\n ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ \n ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ \n ⬛️ ⬛️ \n ⬛️ ⬛️ \n \n \n fill ◻️\n \nmoths\n question Can you move the moths from one light to the other?\n \n 🦋\n onTick .1\n jitter\n move\n onTick .2\n turnToward 💡\n move\n move\n 💡\n \n ticksPerSecond 10\n size 20\n style\n .BoardComponent {background:black;}\n \n insert 10 🦋\n insert 2 💡\n \n comment\n http://www.netlogoweb.org/launch#http://www.netlogoweb.org/assets/modelslib/Sample%20Models/Biology/Moths.nlogo\nninjas\n 🤺\n health 100\n onTick\n decrease health\n jitter\n \n 🥷\n health 100\n onTick\n decrease health\n jitter\n \n insert 50 🤺\n insert 50 🥷\npong\n \n \n 🏐\n bouncy\n onTick\n move\n angle West\n 🏓\n angle East\n onHit\n 🏐\n kickIt\n 🏸\n angle West\n onHit\n 🏐\n kickIt\n 🪵\n solid\n \n size 20\n ticksPerSecond 10\n \n rectangle 🪵 30 15 5 5\n paste\n 🏓 13⬇️ 6➡️\n 🏸 13⬇️ 33➡️\n 🏐 13⬇️ 19➡️\n \n \npoolTable\n comment\n Needs balls to collide. Acceleration.\n \n 🎱\n bouncy\n onHit\n 🎱\n kickIt\n 🏐\n kickIt\n \n 🪵\n solid\n \n 🏐\n bouncy\n onTick .1\n turnRandomly\n kickIt\n onTick .5\n kickIt\n onHit\n 🎱\n kickIt\n angle West\n \n rectangle 🪵 40 20 0 7\n paste\n 🏐 17⬇️ 32➡️\n 🎱 12⬇️ 7➡️\n 🎱 14⬇️ 7➡️\n 🎱 16⬇️ 7➡️\n 🎱 18⬇️ 7➡️\n 🎱 20⬇️ 7➡️\n 🎱 22⬇️ 7➡️\n 🎱 21⬇️ 8➡️\n 🎱 19⬇️ 8➡️\n 🎱 17⬇️ 8➡️\n 🎱 15⬇️ 8➡️\n 🎱 13⬇️ 8➡️\n 🎱 20⬇️ 9➡️\n 🎱 18⬇️ 9➡️\n 🎱 16⬇️ 9➡️\n 🎱 14⬇️ 9➡️\n 🎱 15⬇️ 10➡️\n 🎱 17⬇️ 10➡️\n 🎱 19⬇️ 10➡️\n 🎱 18⬇️ 11➡️\n 🎱 16⬇️ 11➡️\n 🎱 17⬇️ 12➡️\nsoccer\n \n \n ⚽️\n onHit\n 🥅\n pause\n alert GOAAAAAAAAALLLL!\n bouncy\n \n ⛹️‍♂️\n onTick\n jitter\n onHit\n ⚽️\n kickIt\n \n ⛹️‍♀️\n onTick\n jitter\n onHit\n ⚽️\n kickIt\n 🥅\n 🪵\n solid\n \n size 20\n ticksPerSecond 10\n \n rectangle 🪵 30 15 5 5\n \n paste\n 🥅 13⬇️ 6➡️\n 🥅 13⬇️ 33➡️\n ⚽️ 13⬇️ 19➡️\n \n paste\n ⛹️‍♀️ 17⬇️ 14➡️\n ⛹️‍♀️ 17⬇️ 17➡️\n ⛹️‍♀️ 13⬇️ 17➡️\n ⛹️‍♀️ 13⬇️ 14➡️\n ⛹️‍♀️ 8⬇️ 14➡️\n ⛹️‍♀️ 8⬇️ 17➡️\n ⛹️‍♀️ 10⬇️ 14➡️\n ⛹️‍♀️ 9⬇️ 10➡️\n ⛹️‍♀️ 13⬇️ 8➡️\n ⛹️‍♀️ 13⬇️ 10➡️\n ⛹️‍♀️ 17⬇️ 10➡️\n \n paste\n ⛹️‍♂️ 13⬇️ 31➡️\n ⛹️‍♂️ 17⬇️ 28➡️\n ⛹️‍♂️ 13⬇️ 28➡️\n ⛹️‍♂️ 8⬇️ 29➡️\n ⛹️‍♂️ 8⬇️ 25➡️\n ⛹️‍♂️ 10⬇️ 25➡️\n ⛹️‍♂️ 13⬇️ 25➡️\n ⛹️‍♂️ 17⬇️ 25➡️\n ⛹️‍♂️ 17⬇️ 21➡️\n ⛹️‍♂️ 8⬇️ 21➡️\n ⛹️‍♂️ 13⬇️ 21➡️\n \nstartupIdeas\n question What is the effect of ideas vs ideas with revenue?\n \n 👨‍💼🔖\n comment person with an idea\n onTick\n jitter\n \n 👨‍💼💰\n comment peron with an idea\n that is making money\n onTick\n jitter\n \n 👨‍\n onTick .1\n jitter\n onTick .1\n turnToward 👨‍💼💰\n move\n \n \n size 10\n insert 200 👨‍\n insert 30 👨‍💼🔖\n insert 3 👨‍💼💰\n \n \nstore\n 🚶🏻\n onTick\n move\n angle North\n 🛒\n 🚪\n onTick .1\n spawn 🚶🏻\n 🪵\n solid\n \n size 25\n \n rectangle 🪵 30 15 3 3\n paste\n 🚪 16⬇️ 17➡️\n \nvirus\n question What might the spread of a simple virus look like?\n \n 🧟\n health 100\n onTick .9\n decrease health\n jitter\n onTick .01\n log recovered\n replaceWith 🦸‍♂️\n onDeath\n replaceWith 🪦\n \n 🙍\n onTick\n jitter\n onTouch\n 🧟\n replaceWith 🧟\n \n 🦸‍♂️\n onTick\n jitter\n \n insert 10% 🙍\n insert 1 🧟\n \n 🪦\n \n onExtinct 🧟\n log No more cases.\n pause\nwaves\n 🌊\n onTick\n move\n angle South\n \n size 25\n ticksPerSecond 5\n \n rectangle 🌊 100 1 0\n rectangle 🌊 100 1 0 6\n rectangle 🌊 100 1 0 11\nzombies\n question Can you protect the family from the zombies?\n \n 🧟‍♂️\n noPalette\n onTick\n jitter\n onHit\n 🪃\n replaceWith 🪦\n 💣\n replaceWith 🪦\n 👨‍👩‍👧‍👦\n pause\n alert TheyGotYou!\n \n 🧱\n solid\n \n 🔫\n onTick .1\n spawn 🪃\n \n 🪃\n noPalette\n angle West\n onTick\n move\n \n 💣\n \n 🪦\n noPalette\n comment Dead zombie\n \n 👨‍👩‍👧‍👦\n noPalette\n \n size 30\n ticksPerSecond 10\n \n insertCluster 30 🧟‍♂️ 1 1\n paste\n 👨‍👩‍👧‍👦 12⬇️ 11➡️"} \ No newline at end of file +const SimConstants = {"grammar":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nanyCell\nbooleanCell\nstringCell\n highlightScope string\nsettingValueCell\n highlightScope constant.numeric\ncssCell\n highlightScope string\njavascriptCell\n highlightScope string\nhtmlCell\n highlightScope string\nemojiCell\n highlightScope string\nohayoCell\n highlightScope string\nblankCell\ncodeCell\n highlightScope comment\ncommentCell\n highlightScope comment\nkeywordCell\n highlightScope keyword\ntextCell\n highlightScope string\nintegerCell\n highlightScope constant.numeric\nclassNameCell\n highlightScope keyword\nconditionalOperatorCell\n highlightScope keyword\n enum < > = <= >=\npositionCell\n highlightScope constant.numeric\nneighborCountCell\n extends integerCell\n min 0\n max 8\nintegerOrPercentCell\n highlightScope constant.numeric\nprobabilityCell\n description A number between 0 and 1\n highlightScope constant.numeric\npropertyNameCell\n highlightScope keyword\nangleCell\n enum North South East West NorthWest NorthEast SouthWest SouthEast\n highlightScope constant.numeric\njavascriptLineParser\n catchAllCellType javascriptCell\nabstractCommandParser\n cells keywordCell\nabstractSubjectObjectCommandParser\n extends abstractCommandParser\nreplaceWithCommandParser\n extends abstractSubjectObjectCommandParser\n crux replaceWith\n cells keywordCell emojiCell\nkickItCommandParser\n extends abstractSubjectObjectCommandParser\n crux kickIt\nshootCommandParser\n extends abstractSubjectObjectCommandParser\n crux shoot\npickItUpCommandParser\n extends abstractSubjectObjectCommandParser\n crux pickItUp\nspawnCommandParser\n crux spawn\n extends abstractCommandParser\n cells keywordCell emojiCell\n catchAllCellType positionCell\nemitCommandParser\n crux emit\n extends abstractCommandParser\n cells keywordCell emojiCell\nremoveCommandParser\n description Remove this agent from the board.\n crux remove\n extends abstractCommandParser\n cells keywordCell\njavascriptCommandParser\n description An escape hatch so you can write custom javascript in a pinch.\n extends abstractCommandParser\n crux javascript\n catchAllParser javascriptLineParser\n cells keywordCell\nalertCommandParser\n extends abstractCommandParser\n crux alert\n catchAllCellType stringCell\nlogCommandParser\n extends abstractCommandParser\n crux log\n catchAllCellType stringCell\nnarrateCommandParser\n extends abstractCommandParser\n crux narrate\n catchAllCellType stringCell\npauseCommandParser\n extends abstractCommandParser\n crux pause\ndecreaseCommandParser\n extends abstractCommandParser\n description Decrease a property by 1.\n crux decrease\n cells keywordCell propertyNameCell\nincreaseCommandParser\n extends abstractCommandParser\n description Increase a property by 1.\n crux increase\n cells keywordCell propertyNameCell\ngrowCommandParser\n extends abstractCommandParser\n crux grow\nshrinkCommandParser\n extends abstractCommandParser\n crux shrink\npulseCommandParser\n extends abstractCommandParser\n crux pulse\nlearnCommandParser\n crux learn\n extends abstractCommandParser\n cells keywordCell classNameCell\nunlearnCommandParser\n crux unlearn\n extends abstractCommandParser\n cells keywordCell classNameCell\nmoveCommandParser\n extends abstractCommandParser\n crux move\nmoveToEmptySpotCommandParser\n crux moveToEmptySpot\n extends abstractCommandParser\n cells keywordCell\nturnRandomlyCommandParser\n extends abstractCommandParser\n crux turnRandomly\njitterCommandParser\n extends abstractCommandParser\n crux jitter\nturnTowardCommandParser\n description Turn to the closest agent of a certain type.\n extends abstractCommandParser\n crux turnToward\n cells keywordCell emojiCell\nturnFromCommandParser\n description Turn away from the closest agent of a certain type.\n extends abstractCommandParser\n crux turnFrom\n cells keywordCell emojiCell\nagentDefinitionParser\n inScope abstractIgnoreParser abstractEventParser abstractAgentAttributeParser belongsToClassParser\n cells keywordCell\n catchAllParser errorParser\n compiler\n stringTemplate \n javascript\n compile() {\n const root = this.root\n const name = root.agentKeywordMap[this.firstWord]\n const normal = super.compile()\n const classIds = this.filter(node => node.parserId === \"belongsToClassParser\").map(node => node.getLine())\n const props = classIds.map(id => this.root.getNode(id).properties).join(\"\\n\\n\")\n return `class ${name} extends Agent {\n icon = \"${this.firstWord}\"\n classes = [${classIds.map(id => `\"${id}\"`).join(\",\")}]\n ${props}\n ${normal}\n }`\n }\nabstractAgentAttributeParser\n cells keywordCell\nstringAttributeParser\n extends abstractAgentAttributeParser\n pattern ^\\w+ .+$\n catchAllCellType stringCell\n javascript\n compile() {\n return `${this.firstWord} = \"${this.content}\"`\n }\nangleParser\n extends stringAttributeParser\n cells keywordCell angleCell\n cruxFromId\nagentStyleParser\n description Provide custom CSS for an agent type.\n extends stringAttributeParser\n cells keywordCell cssCell\n crux style\nagentHtmlParser\n description Provide custom HTML for each rendered agent.\n extends stringAttributeParser\n cells keywordCell htmlCell\n crux html\nabstractBooleanAttributeParser\n description A boolean attribute.\n extends abstractAgentAttributeParser\n javascript\n compile() {\n return `${this.firstWord} = true`\n }\nnoPaletteParser\n extends abstractBooleanAttributeParser\n cruxFromId\n description Don't show this agent in the palette.\nsolidTraitParser\n description If set other agents won't pass through these.\n extends abstractBooleanAttributeParser\n crux solid\nbouncyTraitParser\n description If set other agents will bounce off this after a collision.\n extends abstractBooleanAttributeParser\n crux bouncy\nabstractIntegerAttributeParser\n extends abstractAgentAttributeParser\n description An integer attribute.\n cells keywordCell integerCell\n javascript\n compile() {\n return `${this.firstWord} = ${this.getWord(1)}`\n }\ncustomIntegerAttributeParser\n pattern ^\\w+ \\d+$\n extends abstractIntegerAttributeParser\nhealthParser\n extends abstractIntegerAttributeParser\n cruxFromId\nagentWidthParser\n extends abstractIntegerAttributeParser\n description Width of the agent.\n crux width\nagentHeightParser\n extends abstractIntegerAttributeParser\n description Height of the agent.\n crux height\nspeedParser\n extends abstractIntegerAttributeParser\n description Movement speed. Default is 1\n crux speed\nsettingDefinitionParser\n description Define a configurable input.\n cells keywordCell settingValueCell\n pattern ^\\w+Setting .+$\nstyleLineParser\n catchAllCellType cssCell\n catchAllParser styleLineParser\nabstractSetupParser\nstyleParser\n description Optional CSS to load in BoardStyleComponent\n extends abstractSetupParser\n cells keywordCell\n cruxFromId\n catchAllParser styleLineParser\n javascript\n compile() {\n return \"\"\n }\nquestionParser\n cruxFromId\n description What are you trying to figure out?\n cells keywordCell\n catchAllCellType stringCell\n extends abstractSetupParser\natTimeParser\n cruxFromId\n description Run commands at a certain tick.\n cells keywordCell integerCell\n extends abstractSetupParser\n inScope abstractInjectCommandParser\nabstractSetupNumberParser\n cells keywordCell integerCell\n extends abstractSetupParser\n javascript\n compile() {\n return \"\"\n }\nheightParser\n description Height of the grid. Default is based on screen size.\n extends abstractSetupNumberParser\n crux height\nwidthParser\n description Width of the grid. Default is based on screen size.\n extends abstractSetupNumberParser\n crux width\nseedParser\n description If you'd like reproducible runs set a seed for the random number generator.\n extends abstractSetupNumberParser\n cruxFromId\nticksPerSecondParser\n description Time in milliseconds of one step.\n extends abstractSetupNumberParser\n cruxFromId\nreportParser\n cruxFromId\n description Define a custom report template.\n catchAllParser ohayoLineParser\n extends abstractSetupParser\n cells keywordCell\n javascript\n compile() {\n return \"\"\n }\nbelongsToClassParser\n cells classNameCell\n pattern ^.*Class$\n javascript\n compile() {\n return \"\"\n }\nclassDefinitionParser\n inScope abstractIgnoreParser abstractEventParser abstractAgentAttributeParser\n cells classNameCell\n pattern ^.*Class$\n catchAllParser errorParser\n javascript\n compile() {\n return \"\"\n }\n get properties() {\n return this.filter(node => node.doesExtend(\"abstractAgentAttributeParser\")).map(node => node.compile()).join(\"\\n\")\n }\ncommentLineParser\n catchAllCellType commentCell\nabstractIgnoreParser\n tags doNotSynthesize\n javascript\n compile () {\n return \"\"\n }\ncommentParser\n extends abstractIgnoreParser\n catchAllCellType commentCell\n cruxFromId\n catchAllParser commentLineParser\ncommentAliasParser\n description Alternate alias for a comment.\n crux #\n extends commentParser\nblankLineParser\n extends abstractIgnoreParser\n description Blank lines compile do nothing.\n cells blankCell\n pattern ^$\nabstractInjectCommandParser\ninsertParser\n extends abstractInjectCommandParser\n cells keywordCell integerOrPercentCell emojiCell\n cruxFromId\ninsertAtParser\n extends insertParser\n description Insert at X Y\n cells keywordCell emojiCell positionCell positionCell\n cruxFromId\ninsertClusterParser\n extends insertParser\n cruxFromId\n catchAllCellType integerCell\nfillParser\n description Fill all blank cells with this agent.\n extends abstractInjectCommandParser\n cells keywordCell emojiCell\n cruxFromId\ndrawParser\n extends abstractInjectCommandParser\n cells keywordCell\n cruxFromId\n catchAllParser drawLineParser\nrectangleDrawParser\n extends abstractInjectCommandParser\n example\n rectangle 🙂 width height x y 🙂\n cells keywordCell emojiCell integerCell integerCell\n catchAllCellType integerCell\n crux rectangle\npasteDrawParser\n extends abstractInjectCommandParser\n cells keywordCell\n crux paste\n catchAllParser pasteLineParser\ndrawLineParser\n catchAllCellType emojiCell\npasteLineParser\n catchAllCellType anyCell\n catchAllParser pasteLineParser\ntargetEmojiParser\n inScope abstractCommandParser\n cells emojiCell\nabstractEventParser\n cells keywordCell\n catchAllCellType probabilityCell\n javascript\n compile() {\n return ``\n }\nabstractInteractionEventParser\n extends abstractEventParser\n catchAllParser targetEmojiParser\nonHitParser\n extends abstractInteractionEventParser\n cruxFromId\n description Define what happens when this agent collides with other agents.\nonDeathParser\n extends abstractEventParser\n cruxFromId\n inScope abstractCommandParser\n description Define what happens when this agent runs out of health.\nonTickParser\n extends abstractEventParser\n cruxFromId\n inScope abstractCommandParser\n description Define what happens each tick.\nemojiAndNeighborConditionParser\n inScope abstractCommandParser\n pattern ^.+ (<|>|=|<=|>=)+ .+$\n cells emojiCell conditionalOperatorCell neighborCountCell\nonExtinctParser\n cruxFromId\n inScope abstractCommandParser\n cells keywordCell emojiCell\n description Define what happens when a type of agent goes extinct from the board.\n javascript\n compile() {\n return \"\"\n }\nexperimentParser\n cruxFromId\n cells keywordCell\n inScope abstractIgnoreParser abstractSetupParser abstractInjectCommandParser onTickParser onExtinctParser settingDefinitionParser\n catchAllCellType stringCell\nohayoLineParser\n description Data visualization code written for Ohayo.\n catchAllCellType ohayoCell\nerrorParser\n baseParser errorParser\nsimojiParser\n extensions simoji\n description A Tree Language that compiles to a TreeComponentFramework app.\n root\n inScope abstractIgnoreParser abstractSetupParser abstractInjectCommandParser onTickParser onExtinctParser classDefinitionParser experimentParser settingDefinitionParser\n catchAllParser agentDefinitionParser\n compilesTo javascript\n example\n 🦋\n onTick .1\n turnRandomly\n move\n onTick .2\n turnToward 💡\n move\n 💡\n \n insert 10 🦋\n insert 2 💡\n javascript\n get agentTypes() {\n return this.filter(node => node.parserId === \"agentDefinitionParser\")\n }","examples":"ants\n comment\n https://ccl.northwestern.edu/netlogo/models/Ants\n \n ⛰\n onTick 0.05\n spawn 🐜\n 🐜\n onTick\n jitter\n onHit\n 🥖\n pickItUp\n 🥖\n \n insert 3 🥖\n insert 1 ⛰\nbasketball\n question Is it better to shoot wildly or to bring it close to the basket?\n \n experiment Shoot rarely\n shotProbabilitySetting .02\n \n experiment\n shotProbabilitySetting .2\n \n experiment\n shotProbabilitySetting .4\n \n experiment Shoot right away\n shotProbabilitySetting .8\n \n thingClass\n width 30\n height 30\n \n personClass\n width 40\n height 40\n speed 20\n \n 🏀\n width 20\n height 20\n speed 30\n onHit\n 🥅⛹️‍♂️\n narrate Blue scores!\n spawn 🏀 15 9\n spawn 🔵 1 18\n remove\n 🥅⛹️‍♀️\n narrate Red scores!\n spawn 🏀 15 9\n spawn 🔴 1 17\n remove\n \n \n moveEastToBlankSpotClass\n onTick\n moveToEmptySpot\n unlearn moveEastToBlankSpotClass\n \n \n 🔵\n angle East\n moveEastToBlankSpotClass\n 🔴\n angle East\n moveEastToBlankSpotClass\n \n \n ticksPerSecond 30\n \n hasBallClass\n comment Sprint toward net\n onTick .5\n turnToward net\n move\n narrate breaks toward the net.\n comment Shoot\n onTick shotProbabilitySetting\n turnToward net\n shoot\n narrate shoots!\n learn noBallClass\n unlearn hasBallClass\n comment Pass\n onTick .02\n turnToward team\n shoot\n narrate passes the ball!\n learn noBallClass\n unlearn hasBallClass\n \n noBallClass\n onTick .3\n turnToward 🏀\n move\n onHit\n 🏀\n pickItUp\n narrate has the ball\n learn hasBallClass\n unlearn noBallClass\n onTick .05\n turnFrom opponent\n move\n onTick .1\n turnFrom opponent\n jitter\n \n # Blue Team\n ⛹️‍♂️\n personClass\n net 🥅⛹️‍♂️\n team ⛹️‍♂️\n opponent ⛹️‍♀️\n noBallClass\n \n # Red Team\n ⛹️‍♀️\n personClass\n net 🥅⛹️‍♀️\n team ⛹️‍♀️\n opponent ⛹️‍♂️\n noBallClass\n \n # Baskets\n 🥅⛹️‍♂️\n thingClass\n html 🥅\n 🥅⛹️‍♀️\n thingClass\n html 🥅\n \n paste\n 🥅⛹️‍♂️ 50 180\n 🥅⛹️‍♀️ 800 180\n 🏀 425 180\n \n # Court\n 🪵\n thingClass\n solid\n rectangle 🪵 30 15 1 1\n \n \n insertAt ⛹️‍♂️ 719 140\n insertAt ⛹️‍♂️ 733 242\n insertAt ⛹️‍♂️ 727 341\n insertAt ⛹️‍♂️ 645 172\n insertAt ⛹️‍♂️ 626 271\n insertAt ⛹️‍♀️ 167 142\n insertAt ⛹️‍♀️ 154 286\n insertAt ⛹️‍♀️ 307 139\n insertAt ⛹️‍♀️ 309 227\n insertAt ⛹️‍♀️ 320 341\ncity\n ⛪️\n comment church\n 🏟\n comment stadium\n 🏥\n comment hospital\n 🏭\n comment factory\n 🏦\n comment bank\n 🏛\n comment courthouse\n 🏫\n comment school\n 🏡\n comment house\n 🏘\n comment houses\n 🎡\n comment park\n 🏪\n comment store\n 🚗\n onTick\n move\n move\n angle West\n 🚓\n onTick\n move\n move\n angle West\n 🚋\n comment subway\n onTick\n move\n move\n move\n angle West\n ⬜️\n comment road\n 🟩\n ⛳️\n \n rectangle ⬜️ 10 20 0 0\n rectangle ⬜️ 10 1 0 10\n paste\n 🏛 8 9\n 🏭 4 1\n 🏭 3 1\n 🏭 2 1\n 🏭 1 1\n 🏡 5 18\n 🏡 6 18\n 🏡 7 18\n 🏡 8 18\ncops\n 🚗\n onTick .5\n jitter\n move\n 🚓\n onHit\n 🚗\n alert Got em!\n pause\n onTick .1\n turnToward 🚗\n move\n onTick .1\n move\n \n ticksPerSecond 30\n \n paste\n 🚓 1 1\n 🚗 5 15\ncovid19\n 🦠\n thingClass\n \n question How long will the pandemic last?\n \n # Given someone has never been infected, what are the odds they get infected?\n succeptibilitySetting .95\n # What are odds of reinfection?\n reinfectionRateSetting .002\n \n # 1 is no lockdowns. 0 is total lockdown.\n freedomOfMovementSetting 1\n \n # What is starting population size?\n urbanPopulationSetting 150\n # What is starting rural population?\n ruralPopulationSetting 30\n # What is starting infected size?\n startingInfectedSetting 3\n \n # How many places can one get the vaccine?\n vaccineCentersSetting 5\n # How likely are people to seek the vaccine?\n vaccinationDesirabilitySetting .3\n # Given someone was vaxed, what are the odds they get infected?\n vaxSucceptibilitySetting .5\n \n experiment High Vaccination Rate, High Vaccine Efficacy\n vaccinationDesirabilitySetting .8\n vaxSucceptibilitySetting .05\n \n \n experiment High Vaccination Rate, Low Vaccine Efficacy\n vaccinationDesirabilitySetting .8\n vaxSucceptibilitySetting .75\n \n experiment Lockdown\n freedomOfMovementSetting .1\n \n experiment High Reinfection Rate\n reinfectionRateSetting .3\n \n \n insert startingInfectedSetting 🧟\n insert vaccineCentersSetting 💉\n \n insertCluster urbanPopulationSetting 🙍\n insert ruralPopulationSetting 🙍\n \n thingClass\n width 20\n height 20\n speed 5\n \n 🧟\n thingClass\n health 100\n onTick .03\n log recovered\n replaceWith 🦸‍♂️\n onTick\n decrease health\n jitter\n onDeath\n replaceWith 🪦\n \n 🦸‍♂️\n thingClass\n comment Recovered\n onTick\n jitter\n onHit reinfectionRateSetting\n 🧟\n replaceWith 🧟\n \n aliveClass\n onTick freedomOfMovementSetting\n jitter\n \n vaccineSeekerClass\n onTick vaccinationDesirabilitySetting\n turnToward 💉\n move\n \n \n 🙍\n thingClass\n aliveClass\n vaccineSeekerClass\n onHit innateImmunitySetting\n 🧟\n replaceWith 🧟\n 💉\n replaceWith 🧑🏽‍🚒\n \n \n 💉\n thingClass\n \n \n 🧑🏽‍🚒\n thingClass\n aliveClass\n onHit vaxSucceptibilitySetting\n 🧟\n replaceWith 🧟\n \n \n \n \n onExtinct 🧟\n log No more cases.\n pause\n \n \n 🪦\n thingClass\n \n \n ticksPerSecond 10\n \n report\n roughjs.line\n columns.keep 🧟\n roughjs.line Active Cases\n columns.keep 🪦\n roughjs.line Cumulative Deaths\n \n \n comment\n See Also\n - http://covidsim.eu/\n - http://modelingcommons.org/browse/one_model/6282#model_tabs_browse_info\n - https://github.com/maplerainresearch/covid19-sim-mesa/blob/master/model.py\n - https://www.frontiersin.org/articles/10.3389/fpubh.2020.563247/full\n - https://ncase.me/covid-19/\n - https://en.wikipedia.org/wiki/List_of_COVID-19_simulation_models\n \n \ncovid19simple\n question What is the effect of population density on pandemic duration?\n \n experiment\n insertCluster 100 🙍\n insertCluster 100 🙍\n insertCluster 100 🙍\n insertCluster 30 🙍\n insertCluster 30 🙍\n insertCluster 10 🙍\n \n experiment\n insert 200 🙍\n \n experiment\n insertCluster 200 🙍\n \n experiment\n insertCluster 200 🙍\n insert 200 🙍\n \n \n 🦠\n health 10\n onTick\n decrease health\n onDeath\n remove\n \n 🧟\n health 100\n onTick .03\n log recovered\n replaceWith 🦸‍♂️\n onTick\n decrease health\n jitter\n onDeath\n replaceWith 🪦\n \n 🦸‍♂️\n comment immune\n onTick\n jitter\n \n 🙍\n onTick\n jitter\n onHit\n 🦠\n replaceWith 🧟\n 🧟\n replaceWith 🧟\n \n insert 1 🦠\n \n onExtinct 🧟\n log No more cases.\n pause\n \n \n 🪦\n \n ticksPerSecond 10\n \n report\n roughjs.line\n columns.keep 🧟\n roughjs.line Active Cases\n columns.keep 🪦\n roughjs.line Cumulative Deaths\n \n \n comment\n See Also\n - http://covidsim.eu/\n - http://modelingcommons.org/browse/one_model/6282#model_tabs_browse_info\n - https://github.com/maplerainresearch/covid19-sim-mesa/blob/master/model.py\n - https://www.frontiersin.org/articles/10.3389/fpubh.2020.563247/full\n - https://ncase.me/covid-19/\n - https://en.wikipedia.org/wiki/List_of_COVID-19_simulation_models\n \neatTheBacon\n 🐕\n onTick .2\n turnToward 🥓\n move\n onTick .2\n jitter\n 🥓\n onHit\n 🐕\n remove\n 🥦\n \n \n insert 1 🐕\n insert 3 🥓\n insert 10 🥦\n \nelevators\n 🛗\n onTick\n move\n move\n angle South\n bouncy\n onHit\n 🚶🏻\n pickItUp\n 🚶🏻\n angle West\n onTick\n move\n bouncy\n 🌾\n 🚪\n onTick .001\n spawn 🚶🏻\n 🪵\n solid\n 🚗\n \n \n rectangle 🪵 20 47 5 1\n rectangle 🌾 40 1 0 48\n rectangle 🚪 1 45 15 2\n paste\n 🛗 19 6\n 🛗 22 4\n 🛗 20 3\n 🛗 13 10\n 🛗 9 4\n 🛗 11 3\n 🚗 30 47\n 🚗 28 47\nfire\n question How fast do fires spread?\n \n 🌲\n width 20\n height 20\n onHit\n ⚡️\n replaceWith ⬛️\n replaceWith 🔥\n onHit\n 🔥\n replaceWith ⬛️\n replaceWith 🔥\n \n ⚡️\n health 10\n onTick\n decrease health\n onDeath\n remove\n \n 🔥\n width 40\n height 40\n health 50\n onTick\n jitter\n decrease health\n shrink\n \n ⬛️\n comment Burnt forest\n html 🌲\n style filter:grayscale(100%);\n \n \n insert 50% 🌲\n onTick .3\n spawn ⚡️\nfireAdvanced\n question What is the effect of forest density on fire risk?\n \n experiment\n treeDensitySetting 10%\n \n experiment\n treeDensitySetting 20%\n \n experiment\n treeDensitySetting 40%\n \n experiment\n treeDensitySetting 80%\n \n catchFireSetting .3\n fireSpreadSetting .7\n fireLifetimeSetting 10\n lightningFrequencySetting .1\n \n 🌲\n width 20\n height 20\n onHit catchFireSetting\n ⚡️\n replaceWith 🔥\n onHit fireSpreadSetting\n 🔥\n replaceWith 🔥\n \n ⚡️\n health 10\n onTick\n decrease health\n onDeath\n remove\n \n 🔥\n width 40\n height 40\n health fireLifetimeSetting\n onTick\n decrease health\n jitter\n shrink\n onDeath\n replaceWith ⬛️\n \n \n ⬛️\n comment Burnt forest\n html 🌲\n style filter:grayscale(100%);\n \n \n insert treeDensitySetting 🌲\n onTick lightningFrequencySetting\n spawn ⚡️\n \nmoths\n question Can you move the moths from one light to the other?\n \n 🦋\n speed 10\n height 30\n width 30\n onTick .5\n jitter\n move\n onTick .2\n turnToward 💡\n move\n move\n 💡\n height 100\n width 100\n \n ticksPerSecond 10\n style\n .BoardComponent {background:black;}\n \n insert 10 🦋\n insert 2 💡\n \n comment\n http://www.netlogoweb.org/launch#http://www.netlogoweb.org/assets/modelslib/Sample%20Models/Biology/Moths.nlogo\nninjas\n 🤺\n health 100\n onTick\n decrease health\n jitter\n \n 🥷\n health 100\n onTick\n decrease health\n jitter\n \n insert 50 🤺\n insert 50 🥷\npong\n \n 🏐\n bouncy\n speed 20\n onTick\n move\n angle West\n 🏓\n width 30\n height 30\n angle East\n onHit\n 🏐\n kickIt\n \n 🏸\n width 30\n height 30\n angle West\n onHit\n 🏐\n kickIt\n 🪵\n solid\n \n ticksPerSecond 10\n \n rectangle 🪵 50 15 50 50\n \n \n insertAt 🏐 273 125\n insertAt 🏓 90 120\n insertAt 🏸 510 121\npoolTable\n comment\n Needs balls to collide. Acceleration.\n \n 🎱\n bouncy\n onHit\n 🎱\n kickIt\n turnRandomly\n 🏐\n kickIt\n \n 🪵\n solid\n \n 🏐\n bouncy\n speed 10\n onTick .1\n kickIt\n onTick .5\n kickIt\n onHit\n 🎱\n turnRandomly\n kickIt\n move\n turnRandomly\n angle West\n \n rectangle 🪵 40 20 10 70\n paste\n 🏐 320 170\n 🎱 70 120\n 🎱 70 140\n 🎱 70 160\n 🎱 70 180\n 🎱 70 200\n 🎱 70 220\n 🎱 80 210\n 🎱 80 190\n 🎱 80 170\n 🎱 80 150\n 🎱 80 130\n 🎱 90 200\n 🎱 90 180\n 🎱 90 160\n 🎱 90 140\n 🎱 100 150\n 🎱 100 170\n 🎱 100 190\n 🎱 110 180\n 🎱 110 160\n 🎱 120 170\nsoccer\n thingClass\n width 40\n height 40\n speed 20\n \n ⚽️\n width 20\n height 20\n speed 30\n onHit\n 🥅\n pause\n alert GOAAAAAAAAALLLL!\n bouncy\n \n ⛹️‍♂️\n thingClass\n onTick\n jitter\n onHit\n ⚽️\n kickIt\n \n ⛹️‍♀️\n thingClass\n onTick\n jitter\n onHit\n ⚽️\n kickIt\n \n 🥅\n thingClass\n 🪵\n thingClass\n solid\n \n ticksPerSecond 10\n \n rectangle 🪵 30 15 5 5\n \n \n seed 1681189626875\n height 862\n width 1354\n insertAt 🥅 1095 293\n insertAt 🥅 98 294\n insertAt ⛹️‍♂️ 993 303\n insertAt ⛹️‍♂️ 923 114\n insertAt ⛹️‍♂️ 923 244\n insertAt ⛹️‍♂️ 954 449\n insertAt ⛹️‍♂️ 844 129\n insertAt ⛹️‍♂️ 842 246\n insertAt ⛹️‍♂️ 849 337\n insertAt ⛹️‍♂️ 866 454\n insertAt ⛹️‍♂️ 722 148\n insertAt ⛹️‍♂️ 705 273\n insertAt ⛹️‍♂️ 722 423\n insertAt ⛹️‍♀️ 443 168\n insertAt ⛹️‍♀️ 496 285\n insertAt ⛹️‍♀️ 481 441\n insertAt ⛹️‍♀️ 317 137\n insertAt ⛹️‍♀️ 321 253\n insertAt ⛹️‍♀️ 342 334\n insertAt ⛹️‍♀️ 333 471\n insertAt ⛹️‍♀️ 196 505\n insertAt ⛹️‍♀️ 182 395\n insertAt ⛹️‍♀️ 212 192\n insertAt ⛹️‍♀️ 161 318\n insertAt ⚽️ 618 322\nstartupIdeas\n question What is the effect of ideas vs ideas with revenue?\n \n personClass\n width 25\n height 25\n speed 10\n \n 👨‍💼🔖\n comment person with an idea\n personClass\n onTick\n jitter\n \n 👨‍💼💰\n width 50\n height 50\n comment peron with an idea\n that is making money\n personClass\n onTick\n jitter\n \n 👨‍\n personClass\n onTick .1\n jitter\n onTick .1\n turnToward 👨‍💼💰\n move\n \n \n insert 200 👨‍\n insert 30 👨‍💼🔖\n insert 3 👨‍💼💰\n \n \nstore\n bigClass\n width 20\n height 20\n \n 🚶🏻\n bigClass\n speed 10\n onTick\n move\n onTick .4\n turnRandomly\n move\n onTick .5\n turnToward 🛒\n onHit\n 🛒\n pickItUp\n angle South\n 🛒\n bigClass\n 🚪\n bigClass\n onTick .1\n spawn 🚶🏻\n 🪵\n bigClass\n solid\n \n rectangle 🪵 30 15 30 30\n paste\n 🚪 170 120\n \n \n \n insertAt 🛒 432 95\n insertAt 🛒 435 212\ntalking\n 🙂\n width 30\n height 30\n onTick .1\n turnToward 🙂\n emit 💬\n \n \n 💬\n health 5\n speed 10\n onHit\n 🙂\n decrease health\n decrease health\n decrease health\n decrease health\n decrease health\n decrease health\n onTick\n move\n decrease health\n onDeath\n remove\n \n \n rectangle 🙂 5 5 100 100 🙂 20\n \n textClass\n width 20\n height 20\n \n 1\n html Imagine a world with 25 people\n textClass\n \n insertAt 1 70 60\nvirus\n question What might the spread of a simple virus look like?\n \n thingClass\n width 20\n height 20\n speed 20\n \n 🧟\n thingClass\n health 100\n onTick .9\n decrease health\n jitter\n onTick .01\n log recovered\n replaceWith 🦸‍♂️\n onDeath\n replaceWith 🪦\n \n 🙍\n thingClass\n onTick\n jitter\n onHit\n 🧟\n replaceWith 🧟\n \n 🦸‍♂️\n # Recovered\n thingClass\n onTick\n jitter\n \n insert 10% 🙍\n insert 1 🧟\n \n 🪦\n thingClass\n \n onExtinct 🧟\n log No more cases.\n pause\nwaves\n 🌊\n onTick\n move\n angle South\n \n ticksPerSecond 5\n \n rectangle 🌊 100 1 0\n rectangle 🌊 100 1 0 6\n rectangle 🌊 100 1 0 11\nzombies\n question Can you protect the family from the zombies?\n \n bigClass\n width 40\n height 40\n \n 🧟‍♂️\n bigClass\n speed 10\n noPalette\n onTick\n jitter\n onHit\n 🪃\n replaceWith 🪦\n 💣\n replaceWith 🪦\n 👨‍👩‍👧‍👦\n pause\n alert TheyGotYou!\n \n 🧱\n bigClass\n solid\n \n 🔫\n bigClass\n onTick .1\n spawn 🪃\n \n 🪃\n speed 50\n bigClass\n width 50\n height 50\n noPalette\n angle West\n onTick\n move\n \n 💣\n bigClass\n \n 🪦\n bigClass\n noPalette\n comment Dead zombie\n \n 👨‍👩‍👧‍👦\n bigClass\n noPalette\n \n ticksPerSecond 10\n \n insertCluster 30 🧟‍♂️ 1 1\n paste\n 👨‍👩‍👧‍👦 400 400"} \ No newline at end of file diff --git a/simoji.grammar b/dist/simoji.grammar similarity index 85% rename from simoji.grammar rename to dist/simoji.grammar index a941896..2f5b0b9 100644 --- a/simoji.grammar +++ b/dist/simoji.grammar @@ -1,3 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + anyCell booleanCell stringCell @@ -25,7 +53,7 @@ textCell highlightScope string integerCell highlightScope constant.numeric -behaviorNameCell +classNameCell highlightScope keyword conditionalOperatorCell highlightScope keyword @@ -46,155 +74,8 @@ propertyNameCell angleCell enum North South East West NorthWest NorthEast SouthWest SouthEast highlightScope constant.numeric -errorParser - baseParser errorParser -simojiParser - extensions simoji - description A Tree Language that compiles to a TreeComponentFramework app. - root - inScope abstractIgnoreParser abstractSetupParser abstractInjectCommandParser onTickParser onExtinctParser behaviorDefinitionParser experimentParser settingDefinitionParser - catchAllParser agentDefinitionParser - compilesTo javascript - example - 🦋 - onTick .1 - turnRandomly - move - onTick .2 - turnToward 💡 - move - 💡 - - insert 10 🦋 - insert 2 💡 - javascript - get agentTypes() { - return this.filter(node => node.parserId === "agentDefinitionParser") - } -experimentParser - cruxFromId - cells keywordCell - inScope abstractIgnoreParser abstractSetupParser abstractInjectCommandParser onTickParser onExtinctParser settingDefinitionParser - catchAllCellType stringCell -abstractSetupParser -atTimeParser - cruxFromId - description Run commands at a certain tick. - cells keywordCell integerCell - extends abstractSetupParser - inScope abstractInjectCommandParser -abstractSetupNumberParser - cells keywordCell integerCell - extends abstractSetupParser - javascript - compile() { - return "" - } -sizeParser - description Size of a grid cell in pixels. Min is 10. Max is 200. - extends abstractSetupNumberParser - cruxFromId -rowsParser - description Number of rows in the grid. Default is based on screen size. - extends abstractSetupNumberParser - cruxFromId -columnsParser - description Number of columns in the grid. Default is based on screen size. - extends abstractSetupNumberParser - cruxFromId -seedParser - description If you'd like reproducible runs set a seed for the random number generator. - extends abstractSetupNumberParser - cruxFromId -ticksPerSecondParser - description Time in milliseconds of one step. - extends abstractSetupNumberParser - cruxFromId -reportParser - cruxFromId - description Define a custom report template. - catchAllParser ohayoLineParser - extends abstractSetupParser - cells keywordCell - javascript - compile() { - return "" - } -styleParser - description Optional CSS to load in BoardStyleComponent - extends abstractSetupParser - cells keywordCell - cruxFromId - catchAllParser styleLineParser - javascript - compile() { - return "" - } -questionParser - cruxFromId - description What are you trying to figure out? - cells keywordCell - catchAllCellType stringCell - extends abstractSetupParser -abstractInjectCommandParser -fillParser - description Fill all blank cells with this agent. - extends abstractInjectCommandParser - cells keywordCell emojiCell - cruxFromId -drawParser - extends abstractInjectCommandParser - cells keywordCell - cruxFromId - catchAllParser drawLineParser -insertParser - extends abstractInjectCommandParser - cells keywordCell integerOrPercentCell emojiCell - cruxFromId -insertAtParser - extends insertParser - description Insert at X Y - cells keywordCell emojiCell positionCell positionCell - cruxFromId -insertClusterParser - extends insertParser - cruxFromId - catchAllCellType integerCell -rectangleDrawParser - extends abstractInjectCommandParser - cells keywordCell emojiCell integerCell integerCell - catchAllCellType integerCell - crux rectangle -pasteDrawParser - extends abstractInjectCommandParser - cells keywordCell - crux paste - catchAllParser pasteLineParser -drawLineParser - catchAllCellType emojiCell -pasteLineParser - catchAllCellType anyCell - catchAllParser pasteLineParser -agentDefinitionParser - inScope abstractIgnoreParser abstractEventParser abstractAgentAttributeParser behaviorAttributeParser - cells keywordCell - catchAllParser errorParser - compiler - stringTemplate - javascript - compile() { - const root = this.root - const name = root.agentKeywordMap[this.firstWord] - const normal = super.compile() - const behaviors = this.filter(node => node.parserId === "behaviorAttributeParser") - .map(behavior => `"${behavior.getLine()}"`) - .join(",") - return `class ${name} extends Agent { - icon = "${this.firstWord}" - behaviors = [${behaviors}] - ${normal} - }` - } +javascriptLineParser + catchAllCellType javascriptCell abstractCommandParser cells keywordCell abstractSubjectObjectCommandParser @@ -217,10 +98,10 @@ spawnCommandParser extends abstractCommandParser cells keywordCell emojiCell catchAllCellType positionCell -moveToEmptySpotCommandParser - crux moveToEmptySpot +emitCommandParser + crux emit extends abstractCommandParser - cells keywordCell + cells keywordCell emojiCell removeCommandParser description Remove this agent from the board. crux remove @@ -257,9 +138,30 @@ increaseCommandParser description Increase a property by 1. crux increase cells keywordCell propertyNameCell +growCommandParser + extends abstractCommandParser + crux grow +shrinkCommandParser + extends abstractCommandParser + crux shrink +pulseCommandParser + extends abstractCommandParser + crux pulse +learnCommandParser + crux learn + extends abstractCommandParser + cells keywordCell classNameCell +unlearnCommandParser + crux unlearn + extends abstractCommandParser + cells keywordCell classNameCell moveCommandParser extends abstractCommandParser crux move +moveToEmptySpotCommandParser + crux moveToEmptySpot + extends abstractCommandParser + cells keywordCell turnRandomlyCommandParser extends abstractCommandParser crux turnRandomly @@ -276,14 +178,26 @@ turnFromCommandParser extends abstractCommandParser crux turnFrom cells keywordCell emojiCell -learnCommandParser - crux learn - extends abstractCommandParser - cells keywordCell behaviorNameCell -unlearnCommandParser - crux unlearn - extends abstractCommandParser - cells keywordCell behaviorNameCell +agentDefinitionParser + inScope abstractIgnoreParser abstractEventParser abstractAgentAttributeParser belongsToClassParser + cells keywordCell + catchAllParser errorParser + compiler + stringTemplate + javascript + compile() { + const root = this.root + const name = root.agentKeywordMap[this.firstWord] + const normal = super.compile() + const classIds = this.filter(node => node.parserId === "belongsToClassParser").map(node => node.getLine()) + const props = classIds.map(id => this.root.getNode(id).properties).join("\n\n") + return `class ${name} extends Agent { + icon = "${this.firstWord}" + classes = [${classIds.map(id => `"${id}"`).join(",")}] + ${props} + ${normal} + }` + } abstractAgentAttributeParser cells keywordCell stringAttributeParser @@ -292,7 +206,7 @@ stringAttributeParser catchAllCellType stringCell javascript compile() { - return `${this.firstWord} = "${this.getWord(1)}"` + return `${this.firstWord} = "${this.content}"` } angleParser extends stringAttributeParser @@ -341,16 +255,163 @@ customIntegerAttributeParser healthParser extends abstractIntegerAttributeParser cruxFromId +agentWidthParser + extends abstractIntegerAttributeParser + description Width of the agent. + crux width +agentHeightParser + extends abstractIntegerAttributeParser + description Height of the agent. + crux height +speedParser + extends abstractIntegerAttributeParser + description Movement speed. Default is 1 + crux speed settingDefinitionParser description Define a configurable input. cells keywordCell settingValueCell pattern ^\w+Setting .+$ -ohayoLineParser - description Data visualization code written for Ohayo. - catchAllCellType ohayoCell styleLineParser catchAllCellType cssCell catchAllParser styleLineParser +abstractSetupParser +styleParser + description Optional CSS to load in BoardStyleComponent + extends abstractSetupParser + cells keywordCell + cruxFromId + catchAllParser styleLineParser + javascript + compile() { + return "" + } +questionParser + cruxFromId + description What are you trying to figure out? + cells keywordCell + catchAllCellType stringCell + extends abstractSetupParser +atTimeParser + cruxFromId + description Run commands at a certain tick. + cells keywordCell integerCell + extends abstractSetupParser + inScope abstractInjectCommandParser +abstractSetupNumberParser + cells keywordCell integerCell + extends abstractSetupParser + javascript + compile() { + return "" + } +heightParser + description Height of the grid. Default is based on screen size. + extends abstractSetupNumberParser + crux height +widthParser + description Width of the grid. Default is based on screen size. + extends abstractSetupNumberParser + crux width +seedParser + description If you'd like reproducible runs set a seed for the random number generator. + extends abstractSetupNumberParser + cruxFromId +ticksPerSecondParser + description Time in milliseconds of one step. + extends abstractSetupNumberParser + cruxFromId +reportParser + cruxFromId + description Define a custom report template. + catchAllParser ohayoLineParser + extends abstractSetupParser + cells keywordCell + javascript + compile() { + return "" + } +belongsToClassParser + cells classNameCell + pattern ^.*Class$ + javascript + compile() { + return "" + } +classDefinitionParser + inScope abstractIgnoreParser abstractEventParser abstractAgentAttributeParser + cells classNameCell + pattern ^.*Class$ + catchAllParser errorParser + javascript + compile() { + return "" + } + get properties() { + return this.filter(node => node.doesExtend("abstractAgentAttributeParser")).map(node => node.compile()).join("\n") + } +commentLineParser + catchAllCellType commentCell +abstractIgnoreParser + tags doNotSynthesize + javascript + compile () { + return "" + } +commentParser + extends abstractIgnoreParser + catchAllCellType commentCell + cruxFromId + catchAllParser commentLineParser +commentAliasParser + description Alternate alias for a comment. + crux # + extends commentParser +blankLineParser + extends abstractIgnoreParser + description Blank lines compile do nothing. + cells blankCell + pattern ^$ +abstractInjectCommandParser +insertParser + extends abstractInjectCommandParser + cells keywordCell integerOrPercentCell emojiCell + cruxFromId +insertAtParser + extends insertParser + description Insert at X Y + cells keywordCell emojiCell positionCell positionCell + cruxFromId +insertClusterParser + extends insertParser + cruxFromId + catchAllCellType integerCell +fillParser + description Fill all blank cells with this agent. + extends abstractInjectCommandParser + cells keywordCell emojiCell + cruxFromId +drawParser + extends abstractInjectCommandParser + cells keywordCell + cruxFromId + catchAllParser drawLineParser +rectangleDrawParser + extends abstractInjectCommandParser + example + rectangle 🙂 width height x y 🙂 + cells keywordCell emojiCell integerCell integerCell + catchAllCellType integerCell + crux rectangle +pasteDrawParser + extends abstractInjectCommandParser + cells keywordCell + crux paste + catchAllParser pasteLineParser +drawLineParser + catchAllCellType emojiCell +pasteLineParser + catchAllCellType anyCell + catchAllParser pasteLineParser targetEmojiParser inScope abstractCommandParser cells emojiCell @@ -368,15 +429,6 @@ onHitParser extends abstractInteractionEventParser cruxFromId description Define what happens when this agent collides with other agents. -onTouchParser - extends abstractInteractionEventParser - cruxFromId - description Define what happens when this agent is adjacent to other agents. -onNeighborsParser - description Define what happens when a certain amount of neighbors are nearby. - extends abstractInteractionEventParser - inScope emojiAndNeighborConditionParser - cruxFromId onDeathParser extends abstractEventParser cruxFromId @@ -400,43 +452,36 @@ onExtinctParser compile() { return "" } -abstractIgnoreParser - tags doNotSynthesize - javascript - compile () { - return "" - } -commentParser - extends abstractIgnoreParser - catchAllCellType commentCell +experimentParser cruxFromId - catchAllParser commentLineParser -commentAliasParser - description Alternate alias for a comment. - crux # - extends commentParser -blankLineParser - extends abstractIgnoreParser - description Blank lines compile do nothing. - cells blankCell - pattern ^$ -commentLineParser - catchAllCellType commentCell -javascriptLineParser - catchAllCellType javascriptCell -behaviorAttributeParser - cells behaviorNameCell - pattern ^.*Behavior$ - javascript - compile() { - return "" - } -behaviorDefinitionParser - inScope abstractIgnoreParser abstractEventParser - cells behaviorNameCell - pattern ^.*Behavior$ - catchAllParser errorParser + cells keywordCell + inScope abstractIgnoreParser abstractSetupParser abstractInjectCommandParser onTickParser onExtinctParser settingDefinitionParser + catchAllCellType stringCell +ohayoLineParser + description Data visualization code written for Ohayo. + catchAllCellType ohayoCell +errorParser + baseParser errorParser +simojiParser + extensions simoji + description A Tree Language that compiles to a TreeComponentFramework app. + root + inScope abstractIgnoreParser abstractSetupParser abstractInjectCommandParser onTickParser onExtinctParser classDefinitionParser experimentParser settingDefinitionParser + catchAllParser agentDefinitionParser + compilesTo javascript + example + 🦋 + onTick .1 + turnRandomly + move + onTick .2 + turnToward 💡 + move + 💡 + + insert 10 🦋 + insert 2 💡 javascript - compile() { - return "" + get agentTypes() { + return this.filter(node => node.parserId === "agentDefinitionParser") } \ No newline at end of file diff --git a/dist/simoji.js b/dist/simoji.js index 3b2c6a6..34a55e6 100644 --- a/dist/simoji.js +++ b/dist/simoji.js @@ -3,25 +3,6 @@ const yodash = {} - -yodash.parseInts = (arr, start) => arr.map((item, index) => (index >= start ? parseInt(item) : item)) - -yodash.getRandomAngle = randomNumberGenerator => { - const r1 = randomNumberGenerator() - const r2 = randomNumberGenerator() - if (r1 > 0.5) return r2 > 0.5 ? Directions.North : Directions.South - return r2 > 0.5 ? Directions.West : Directions.East -} - -yodash.flipAngle = angle => { - let newAngle = "" - if (angle.includes(Directions.North)) newAngle += Directions.South - else if (angle.includes(Directions.South)) newAngle += Directions.North - if (angle.includes(Directions.East)) newAngle += Directions.West - else if (angle.includes(Directions.West)) newAngle += Directions.East - return newAngle -} - yodash.compare = (left, operator, right) => { if (operator === "=") return left == right if (operator === "<") return left < right @@ -32,14 +13,16 @@ yodash.compare = (left, operator, right) => { return false } +// Todo: why do we do this? Very confusing. Caught me by surprise. +// Is it because sometimes the class name is not valid JS? yodash.compileAgentClassDeclarationsAndMap = program => { - const clone = program.clone() - clone.filter(node => node.parserId !== ParserTypes.agentDefinitionParser).forEach(node => node.destroy()) - clone.agentKeywordMap = {} - clone.agentTypes.forEach((node, index) => (clone.agentKeywordMap[node.firstWord] = `simAgent${index}`)) - const compiled = clone.compile() - const agentMap = Object.keys(clone.agentKeywordMap) - .map(key => `"${key}":${clone.agentKeywordMap[key]}`) + const agentKeywordMap = {} + program.agentKeywordMap = agentKeywordMap // confusing + const agentDefs = program.filter(node => node.parserId === ParserTypes.agentDefinitionParser) + agentDefs.forEach((node, index) => (agentKeywordMap[node.firstWord] = `simAgent${index}`)) + const compiled = agentDefs.map(node => node.compile()).join("\n") + const agentMap = Object.keys(agentKeywordMap) + .map(key => `"${key}":${agentKeywordMap[key]}`) .join(",") return `${compiled} const map = {${agentMap}}; @@ -68,189 +51,39 @@ yodash.patchExperimentAndReplaceSymbols = (program, experiment) => { return withVarsReplaced } -yodash.getBestAngle = (targets, position) => { +yodash.getClosest = (targets, subject) => { let closest = Infinity let target - targets.forEach(candidate => { - const pos = candidate.position - const distance = math.distance([pos.down, pos.right], [position.down, position.right]) + targets.forEach(agent => { + if (agent === subject) return + const distance = math.distance([agent.y, agent.x], [subject.y, subject.x]) if (distance < closest) { closest = distance - target = candidate + target = agent } }) - const heading = target.position - return yodash.angle(position.down, position.right, heading.down, heading.right) -} - -yodash.angle = (cx, cy, ex, ey) => { - const dy = ey - cy - const dx = ex - cx - let theta = Math.atan2(dy, dx) // range (-PI, PI] - theta *= 180 / Math.PI // rads to degs, range (-180, 180] - //if (theta < 0) theta = 360 + theta; // range [0, 360) - let angle = "" - - if (Math.abs(theta) > 90) angle += Directions.North - else angle += Directions.South - if (theta < 0) angle += Directions.West - else angle += Directions.East - return angle -} - -yodash.getRandomLocation = (rows, cols, randomNumberGenerator) => { - const maxRight = cols - const maxBottom = rows - const right = Math.round(randomNumberGenerator() * maxRight) - const down = Math.round(randomNumberGenerator() * maxBottom) - return { right, down } + return target } -yodash.getRandomLocationHash = (rows, cols, occupiedSpots, randomNumberGenerator) => { - const { right, down } = yodash.getRandomLocation(rows, cols, randomNumberGenerator) - const hash = yodash.makePositionHash({ right, down }) - if (occupiedSpots && occupiedSpots.has(hash)) - return yodash.getRandomLocationHash(rows, cols, occupiedSpots, randomNumberGenerator) - return hash -} - -yodash.fill = (rows, cols, occupiedSpots, emoji) => { - const board = [] - while (rows >= 0) { - let col = cols - while (col >= 0) { - const hash = yodash.makePositionHash({ right: col, down: rows }) - col-- - if (occupiedSpots.has(hash)) continue - board.push(`${emoji} ${hash}`) - } - rows-- +yodash.unitVector = (objA, objB) => { + // calculate direction vector (delta) + const delta = { + x: objB.x - objA.x, + y: objB.y - objA.y } - return board.join("\n") -} - -yodash.positionsAdjacentTo = position => { - let { right, down } = position - const positions = [] - down-- - positions.push({ down, right }) - right-- - positions.push({ down, right }) - right++ - right++ - positions.push({ down, right }) - down++ - positions.push({ down, right }) - right-- - right-- - positions.push({ down, right }) - down++ - positions.push({ down, right }) - right++ - positions.push({ down, right }) - right++ - positions.push({ down, right }) - return positions -} -yodash.makePositionHash = position => `${position.down + "⬇️ " + position.right + "➡️"}` - -yodash.makeRectangle = (character = "🧱", width = 20, height = 20, startRight = 0, startDown = 0) => { - if (width < 1 || height < 1) { - return "" - } - const cells = [] - let row = 0 - while (row < height) { - let col = 0 - while (col < width) { - const isPerimeter = row === 0 || row === height - 1 || col === 0 || col === width - 1 - if (isPerimeter) - cells.push( - `${character} ${yodash.makePositionHash({ - down: startDown + row, - right: startRight + col - })}` - ) - col++ - } - row++ - } - return cells.join("\n") -} + // calculate magnitude of delta (distance between two points) + const magDelta = Math.sqrt(delta.x * delta.x + delta.y * delta.y) -yodash.parsePosition = words => { + // calculate unit vector (normalize direction vector by dividing by magnitude) return { - down: parseInt(words.find(word => word.includes("⬇️")).slice(0, -1)), - right: parseInt(words.find(word => word.includes("➡️")).slice(0, -1)) - } -} - -yodash.draw = str => { - const lines = str.split("\n") - const output = [] - for (let index = 0; index < lines.length; index++) { - const words = lines[index].split(" ") - for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { - const word = words[wordIndex] - if (word !== "") output.push(`${word} ${yodash.makePositionHash({ down: index, right: wordIndex })}`) - } - } - return output.join("\n") -} - -yodash.updateOccupiedSpots = (board, occupiedSpots) => { - new TreeNode(board).forEach(line => { - occupiedSpots.add(yodash.makePositionHash(yodash.parsePosition(line.words))) - }) -} - -yodash.getAllAvailableSpots = (rows, cols, occupiedSpots, rowStart = 0, colStart = 0) => { - const availablePositions = [] - let down = rows - while (down >= rowStart) { - let right = cols - while (right >= colStart) { - const hash = yodash.makePositionHash({ right, down }) - if (!occupiedSpots.has(hash)) availablePositions.push({ right, down, hash }) - right-- - } - down-- + x: delta.x / magDelta, + y: delta.y / magDelta } - return availablePositions } yodash.parsePercent = str => parseFloat(str.replace("%", "")) / 100 -yodash.insertClusteredRandomAgents = ( - randomNumberGenerator, - amount, - char, - rows, - cols, - occupiedSpots, - originRow, - originColumn -) => { - const availableSpots = yodash.getAllAvailableSpots(rows, cols, occupiedSpots) - const spots = yodash.sampleFrom(availableSpots, amount * 10, randomNumberGenerator) - const origin = originColumn - ? { down: parseInt(originRow), right: parseInt(originColumn) } - : yodash.getRandomLocation(rows, cols, randomNumberGenerator) - const sortedByDistance = lodash.sortBy(spots, spot => - math.distance([origin.down, origin.right], [spot.down, spot.right]) - ) - - return sortedByDistance - .slice(0, amount) - .map(spot => { - const { hash } = spot - occupiedSpots.add(hash) - return `${char} ${hash}` - }) - .join("\n") -} - yodash.getRandomNumberGenerator = seed => () => { const semiRand = Math.sin(seed++) * 10000 return semiRand - Math.floor(semiRand) @@ -376,24 +209,51 @@ window.AbstractContextMenuComponent = AbstractContextMenuComponent - const SelectedClass = "selected" +const classCache = {} +const getClassCache = (program, words) => { + const key = words.join(" ") + if (!classCache[key]) classCache[key] = yodash.flatten(yodash.pick(program, words)) + return classCache[key] +} + class Agent extends TreeNode { get name() { return this._name ?? this.icon } - angle = Directions.South + _direction = { x: 0, y: 1 } + + get direction() { + if (this.angle) { + const vectors = { + North: [0, -1], + East: [1, 0], + South: [0, 1], + West: [-1, 0], + Northeast: [Math.cos(Math.PI / 4), Math.sin((Math.PI * 3) / 4)], + Southeast: [Math.cos((Math.PI * 3) / 4), Math.sin(Math.PI / 4)], + Southwest: [-Math.cos((Math.PI * 3) / 4), Math.sin(Math.PI * (5 / 8))], + Northwest: [-Math.cos(Math.PI * (5 / 8)), Math.sin((Math.PI * -3) / 4)] + } + this._direction = vectors[this.angle] + this.angle = "" + } + return this._direction + } + + set direction(newDirection) { + this._direction = newDirection + } getCommandBlocks(eventName) { - return this.definitionWithBehaviors.findNodes(eventName) + return this.definitionWithClasses.findNodes(eventName) } - get definitionWithBehaviors() { - if (!this.behaviors.length) return this.board.simojiProgram.getNode(this.firstWord) - const behaviors = yodash.flatten(yodash.pick(this.board.simojiProgram, [this.firstWord, ...this.behaviors])) - return behaviors + get definitionWithClasses() { + if (!this.classes.length) return this.board.simojiProgram.getNode(this.firstWord) + return getClassCache(this.board.simojiProgram, [this.firstWord, ...this.classes]) } skip(probability) { @@ -405,59 +265,6 @@ class Agent extends TreeNode { return !!this.element } - handleNeighbors() { - if (!this.stillExists) return - this.getCommandBlocks(Keywords.onNeighbors).forEach(neighborConditions => { - if (this.skip(neighborConditions.getWord(1))) return - - const { neighorCount } = this - - neighborConditions.forEach(conditionAndCommandsBlock => { - const [emoji, operator, count] = conditionAndCommandsBlock.words - const actual = neighorCount[emoji] - if (!yodash.compare(actual ?? 0, operator, count)) return - conditionAndCommandsBlock.forEach(command => this._executeCommand(this, command)) - - if (this.getIndex() === -1) return {} - }) - }) - } - - handleTouches(agentPositionMap) { - if (!this.stillExists) return - this.getCommandBlocks(Keywords.onTouch).forEach(touchMap => { - if (this.skip(touchMap.getWord(1))) return - - for (let pos of yodash.positionsAdjacentTo(this.position)) { - const hits = agentPositionMap.get(yodash.makePositionHash(pos)) ?? [] - for (let target of hits) { - const targetId = target.firstWord - const commandBlock = touchMap.getNode(targetId) - if (commandBlock) { - commandBlock.forEach(command => this._executeCommand(target, command)) - if (this.getIndex() === -1) return - } - } - } - }) - } - - handleOverlaps(targets) { - if (!this.stillExists) return - this.getCommandBlocks(Keywords.onHit).forEach(hitMap => { - if (this.skip(hitMap.getWord(1))) return - targets.forEach(target => { - const targetId = target.firstWord - const commandBlock = hitMap.getNode(targetId) - if (commandBlock) commandBlock.forEach(command => this._executeCommand(target, command)) - }) - }) - } - - get overlappingAgents() { - return (this.board.agentPositionMap.get(this.positionHash) ?? []).filter(node => node !== this) - } - _executeCommand(target, instruction) { const commandName = instruction.firstWord if (this[commandName]) this[commandName](target, instruction) @@ -485,19 +292,6 @@ class Agent extends TreeNode { if (this.health === 0) this.onDeathCommand() } - get neighorCount() { - const { agentPositionMap } = this.board - const neighborCounts = {} - yodash.positionsAdjacentTo(this.position).forEach(pos => { - const agents = agentPositionMap.get(yodash.makePositionHash(pos)) ?? [] - agents.forEach(agent => { - if (!neighborCounts[agent.name]) neighborCounts[agent.name] = 0 - neighborCounts[agent.name]++ - }) - }) - return neighborCounts - } - onDeathCommand() { this._executeCommandBlocks(Keywords.onDeath) } @@ -507,101 +301,141 @@ class Agent extends TreeNode { } _replaceWith(newObject) { - this.parent.appendLine(`${newObject} ${this.positionHash}`) - + this.parent.insertInbounds(newObject, this.x, this.y) this.remove() } _move() { if (this.owner) return this - const { angle } = this - if (angle.includes(Directions.North)) this.moveNorth() - else if (angle.includes(Directions.South)) this.moveSouth() - if (angle.includes(Directions.East)) this.moveEast() - else if (angle.includes(Directions.West)) this.moveWest() + const { direction, speed } = this + + this.top = Math.max(this.top + direction.y * speed, 0) + this.left = Math.max(this.left + direction.x * speed, 0) if (this.holding) { this.holding.forEach(node => { - node.position = { right: this.left, down: this.top } + node.setPosition({ x: this.x, y: this.y }) }) } } - moveSouth() { - this.top++ + speed = 1 + + get x() { + return this.left } - moveNorth() { - this.top-- + get y() { + return this.top } - moveWest() { - this.left-- + get w() { + return this.width } - moveEast() { - this.left++ + get h() { + return this.height } + width = 10 + height = 10 + get top() { - return this.position.down + return this._y ?? this.position.y } set top(value) { if (value > this.maxDown) value = this.maxDown if (value < 0) value = 0 - this.position = { - down: value, - right: this.left - } - } - - set position(value) { - if (this.board.isSolidAgent(value)) return this.bouncy ? this.bounce() : this - const newLine = this.getLine() - .split(" ") - .map(part => (part.includes("⬇️") ? value.down + "⬇️" : part.includes("➡️") ? value.right + "➡️" : part)) - .join(" ") - return this.setLine(newLine) + this.setPosition({ + y: value, + x: this.left + }) } get board() { return this.parent } + setPosition(newPosition) { + if (!this.board.canGoHere(newPosition.x, newPosition.y, this.width, this.height)) + return this.bouncy ? this.bounce() : this + + this._x = newPosition.x + this._y = newPosition.y + // Todo: do we need to update the string? + return this.setLine([this.firstWord, newPosition.x, newPosition.y].join(" ")) + } + + handleCollisions(targetAgents) { + if (!this.stillExists) return + this.getCommandBlocks(Keywords.onHit).forEach(hitMap => { + if (this.skip(hitMap.getWord(1))) return + targetAgents.forEach(targetAgent => { + const targetId = targetAgent.firstWord + const commandBlock = hitMap.getNode(targetId) + if (commandBlock) commandBlock.forEach(command => this._executeCommand(targetAgent, command)) + }) + }) + } + + get symbol() { + return this.firstWord + } + + get collidingAgents() { + return this.board.objectsCollidingWith(this.x, this.y, this.width, this.height).filter(node => node !== this) + } + + get neighorCount() { + return this.board.getNeighborCount(this) + } + get maxRight() { - return this.board.cols + return this.board.width - this.width - 1 } get maxDown() { - return this.board.rows + return this.board.height - this.height - 1 } set left(value) { if (value > this.maxRight) value = this.maxRight if (value < 0) value = 0 - this.position = { - down: this.top, - right: value - } + this.setPosition({ + y: this.top, + x: value + }) } get left() { - return this.position.right + return this._x ?? this.position.x } - get position() { - return yodash.parsePosition(this.words) + get bounds() { + return { + x: this.x, + y: this.y, + w: this.width, + h: this.height + } } - get positionHash() { - return yodash.makePositionHash(this.position) + get position() { + return { + x: parseInt(this.words[1]), + y: parseInt(this.words[2]) + } } get gridSize() { - return this.parent.gridSize + return 1 + } + + get agentSize() { + return this.size ?? this.gridSize } get selected() { @@ -625,12 +459,12 @@ class Agent extends TreeNode { // DOM operations nuke() { - this.element.remove() + if (this.element) this.element.remove() this.destroy() } get element() { - return document.getElementById(`agent${this._getUid()}`) + return document.getElementById(this.id) } _updateHtml() { @@ -640,16 +474,24 @@ class Agent extends TreeNode { } get inlineStyle() { - const { gridSize, health } = this + const { health, width, height } = this const opacity = health === undefined ? "" : `opacity:${this.health / this.startHealth};` - return `top:${this.top * gridSize}px;left:${ - this.left * gridSize - }px;font-size:${gridSize}px;line-height: ${gridSize}px;${opacity};${this.style ?? ""}` + return `top:${this.top}px;left:${this.left}px;font-size:${height}px;line-height:${height}px;${opacity};${ + this.style ?? "" + }` + } + + get id() { + return `agent${this.agentNumber}` + } + + get agentNumber() { + return this._getUid() } toElement() { const elem = document.createElement("div") - elem.setAttribute("id", `agent${this._getUid()}`) + elem.setAttribute("id", this.id) elem.innerHTML = this.html ?? this.icon elem.classList.add("Agent") if (this.selected) elem.classList.add(SelectedClass) @@ -657,11 +499,22 @@ class Agent extends TreeNode { return elem } - toStumpCode() { - return `div ${this.html ?? this.icon} - id agent${this._getUid()} - class Agent ${this.selected ? SelectedClass : ""} - style ${this.inlineStyle}` + toggleSelectCommand() { + const { root } = this + root.selection.includes(this) ? this.unselectCommand() : this.selectCommand() + + root.ensureRender() + return this + } + + unselectCommand() { + this.unselect() + this.root.selection = this.root.selection.filter(node => node !== this) + } + + selectCommand() { + this.root.selection.push(this) + this.select() } needsUpdate(lastRenderedTime = 0) { @@ -679,7 +532,7 @@ class Agent extends TreeNode { } kickIt(target) { - target.angle = this.angle + target.direction = this.direction target.tickStack = new TreeNode(`1 move move @@ -724,7 +577,8 @@ class Agent extends TreeNode { }) } bounce() { - this.angle = yodash.flipAngle(this.angle) + const { x, y } = this.direction + this.direction = { x: -x, y: -y } } decrease(target, command) { @@ -740,7 +594,8 @@ class Agent extends TreeNode { } turnRandomly() { - this.angle = yodash.getRandomAngle(this.board.randomNumberGenerator) + const rng = this.board.randomNumberGenerator + this.direction = { x: 2 * rng() - 1, y: 2 * rng() - 1 } return this } @@ -748,7 +603,9 @@ class Agent extends TreeNode { const targetId = instruction.getWord(1) const kind = this[targetId] ?? targetId // can define a custom target const targets = this.board.agentTypeMap.get(kind) - if (targets) this.angle = yodash.getBestAngle(targets, this.position) + if (!targets) return this + this.target = yodash.getClosest(targets, this) + this.direction = yodash.unitVector(this, this.target) return this } @@ -756,7 +613,10 @@ class Agent extends TreeNode { const targetId = instruction.getWord(1) const kind = this[targetId] ?? targetId // can define a custom target const targets = this.board.agentTypeMap.get(kind) - if (targets) this.angle = yodash.flipAngle(yodash.getBestAngle(targets, this.position)) + if (!targets) return this + this.target = yodash.getClosest(targets, this) + const bestUnitVector = yodash.unitVector(this, this.target) + this.direction = { x: -bestUnitVector.x, y: -bestUnitVector.y } return this } @@ -765,33 +625,64 @@ class Agent extends TreeNode { } spawn(subject, command) { - const position = command.getWordsFrom(2).length ? command.getWordsFrom(2).join(" ") : subject.positionHash + const position = command.getWordsFrom(2).length ? command.getWordsFrom(2).join(" ") : `${subject.x} ${subject.y}` this.board.appendLine(`${command.getWord(1)} ${position}`) } + get midpoint() { + return { x: this.x + this.width / 2, y: this.y + this.height / 2 } + } + + emit(subject, command) { + const { midpoint } = this + const position = command.getWordsFrom(2).length ? command.getWordsFrom(2).join(" ") : `${midpoint.x} ${midpoint.y}` + const agent = this.board.appendLine(`${command.getWord(1)} ${position}`) + agent.direction = this.direction + } + move() { if (this.selected) return return this._move() } moveToEmptySpot() { - while (this.overlappingAgents.length) { + while (this.collidingAgents.length) { this.move() } } + grow() { + this.width++ + this.height++ + this.markDirty() + } + + shrink() { + if (!this.width || !this.height) return + this.width-- + this.height-- + this.markDirty() + } + jitter() { this.turnRandomly() this.move() } + _lastPulse + pulse() { + if (this._lastPulse) this.shrink() + else this.grow() + this._lastPulse = !this._lastPulse + } + learn(target, command) { - this.behaviors.push(command.getWord(1)) + this.classes.push(command.getWord(1)) } unlearn(target, command) { - const behaviorName = command.getWord(1) - this.behaviors = this.behaviors.filter(name => name !== behaviorName) + const className = command.getWord(1) + this.classes = this.classes.filter(name => name !== className) } } @@ -841,6 +732,7 @@ window.AgentPaletteComponent = AgentPaletteComponent + let nodeJsPrefix = "" // prettier-ignore @@ -895,15 +787,17 @@ class BoardComponent extends AbstractTreeComponentParser { } get gridSize() { - return parseInt(this.getWord(1)) + return 1 } - get rows() { - return parseInt(this.getWord(2)) + get width() { + if (this._width === undefined) this._width = parseInt(this.getWord(2)) + return this._width } - get cols() { - return parseInt(this.getWord(3)) + get height() { + if (this._height === undefined) this._height = parseInt(this.getWord(3)) + return this._height } get populationCsv() { @@ -942,31 +836,28 @@ class BoardComponent extends AbstractTreeComponentParser { ) } - insertAgentAtCommand(right, down) { + insertAgentAtCommand(xCenter, yCenter) { const root = this.root - const board = this - const positionHash = down + " " + right - board.resetAgentPositionMap() - const { agentPositionMap } = board - const existingObjects = agentPositionMap.get(positionHash) ?? [] - if (existingObjects.length) return root.toggleSelectCommand(existingObjects) const { agentToInsert } = root - if (!agentToInsert) return + this.clearCollisionDetector() - //if (parent.findNodes(agentToInsert).length > MAX_ITEMS) return true + const { agentWidth, agentHeight } = this.getAgentHeightAndWidth(agentToInsert) + + const x = xCenter - Math.floor(agentWidth / 2) + const y = yCenter - Math.floor(agentHeight / 2) - board.prependLine(`${agentToInsert} ${positionHash}`) - board.renderAndGetRenderReport() - board.resetAgentPositionMap() + //if (parent.findNodes(agentToInsert).length > MAX_ITEMS) return true - if (!root.isSnapshotOn) root.snapShotCommand() + this.prependLine(`${agentToInsert} ${x} ${y}`) + this.renderAndGetRenderReport() + this.clearCollisionDetector() const allCode = new TreeNode(root.simCode) let targetNode = root.boards.length === 1 ? allCode : allCode.findNodes("experiment")[this.boardIndex] if (this.tick) targetNode = targetNode.appendLine(`atTime ${this.tick}`) - targetNode.appendLine(`insertAt ${agentToInsert} ${down} ${right}`) + targetNode.appendLine(`insertAt ${agentToInsert} ${x} ${y}`) root.onSourceCodeChange(allCode) } @@ -985,14 +876,10 @@ class BoardComponent extends AbstractTreeComponentParser { tick = 0 boardLoop(render = true) { this.runAtTimeEvents() - this.agents.forEach(node => node.onTick()) - this.resetAgentPositionMap() - this.handleOverlaps() - this.handleTouches() - this.handleNeighbors() - + this.clearCollisionDetector() + this.handleCollisions() this.executeBoardCommands(Keywords.onTick) this.handleExtinctions() @@ -1015,6 +902,20 @@ class BoardComponent extends AbstractTreeComponentParser { return report } + treeComponentDidMount() { + const that = this + if (this.isNodeJs()) return + jQuery(this.getStumpNode().getShadow().element).on("click", ".Agent", function (evt) { + const agent = evt.target + const id = parseInt(jQuery(agent).attr("id").replace("agent", "")) + that.getAgent(id).toggleSelectCommand() + }) + } + + getAgent(uid) { + return this.agents.find(agent => agent._getUid() === uid) + } + appendAgents(agents) { if (!agents.length) return this @@ -1043,53 +944,95 @@ class BoardComponent extends AbstractTreeComponentParser { return setTime ? parseInt(setTime) : 10 } - occupiedSpots = new Set() - runInjectCommand(command) { this[command.parserId](command) } + getAgentHeightAndWidth(agentSymbol) { + const item = new this.agentMap[agentSymbol]() + return { agentWidth: item.width, agentHeight: item.height } + } + + insertInbounds(agentSymbol, x, y) { + const { agentWidth, agentHeight } = this.getAgentHeightAndWidth(agentSymbol) + const xOver = x + agentWidth - this.width + const yOver = y + agentHeight - this.height + if (xOver > 0) x = x - xOver - 10 + if (yOver > 0) y = y - yOver - 10 + if (x < 0) x = 0 + if (y < 0) y = 0 + this.appendLine(`${agentSymbol} ${x} ${y}`) + } + + // todo: origin + insertClusteredRandomAgents(amount, agentSymbol, x = 0, y = 0) { + const { agentWidth, agentHeight } = this.getAgentHeightAndWidth(agentSymbol) + const spots = this.collisionDetector.findClusteredNonOverlappingSquares( + agentWidth, + agentHeight, + amount, + x, + y, + (amount * agentWidth) / 4 + ) + return spots.map(spot => `${agentSymbol} ${spot.x} ${spot.y}`).join("\n") + } + insertClusterParser(commandNode) { this.concat( - yodash.insertClusteredRandomAgents( - this.randomNumberGenerator, + this.insertClusteredRandomAgents( parseInt(commandNode.getWord(1)), commandNode.getWord(2), - this.rows, - this.cols, - this.occupiedSpots, commandNode.getWord(3), commandNode.getWord(4) ) ) + this.clearCollisionDetector() } insertAtParser(commandNode) { - this.appendLine(`${commandNode.getWord(1)} ${commandNode.getWord(3)} ${commandNode.getWord(2)}`) - // TODO: update occupied spots cache? + this.appendLine(`${commandNode.getWord(1)} ${commandNode.getWord(2)} ${commandNode.getWord(3)}`) + this.clearCollisionDetector() } rectangleDrawParser(commandNode) { - const newLines = yodash.makeRectangle(...yodash.parseInts(commandNode.words.slice(1), 1)) + // todo: need a typed words method in jtree + // rectangle 🙂 width height x y 🙂 + const [command, agentSymbol, width, height, x, y, fillSymbol, spacing] = commandNode.words + + const { agentWidth, agentHeight } = this.getAgentHeightAndWidth(agentSymbol) + + const options = { + agentSymbol, + width: parseInt(width), + height: parseInt(height), + x: x ? parseInt(x) : 0, + y: y ? parseInt(y) : 0, + fillSymbol, + spacing: spacing || 0, + agentHeight, + agentWidth + } + + const newLines = this.makeRectangle(options) this.concat(newLines) - // TODO: update occupied spots cache? + this.clearCollisionDetector() } pasteDrawParser(commandNode) { const newSpots = new TreeNode(commandNode.childrenToString()) - yodash.updateOccupiedSpots(newSpots, this.occupiedSpots) this.concat(newSpots) + this.clearCollisionDetector() } fillParser(commandNode) { - this.concat(yodash.fill(this.rows, this.cols, this.occupiedSpots, commandNode.getWord(1))) + this.concat(this.fill(commandNode.getWord(1))) + this.clearCollisionDetector() } drawParser(commandNode) { - const { occupiedSpots } = this - const spots = yodash.draw(commandNode.childrenToString()) - yodash.updateOccupiedSpots(spots, occupiedSpots) - this.concat(spots) + this.concat(this.draw(commandNode.childrenToString())) + this.clearCollisionDetector() } get seed() { @@ -1104,21 +1047,19 @@ class BoardComponent extends AbstractTreeComponentParser { } insertParser(commandNode) { - const { rows, cols, occupiedSpots } = this - const emoji = commandNode.getWord(2) + const { width, height } = this + const agentSymbol = commandNode.getWord(2) let amount = commandNode.getWord(1) + const { agentWidth, agentHeight } = this.getAgentHeightAndWidth(agentSymbol) + const maxCells = (width * height) / (agentWidth * agentHeight) + amount = amount.includes("%") ? yodash.parsePercent(amount) * maxCells : parseInt(amount) + + const spots = this.collisionDetector.findNonOverlappingSquares(agentWidth, agentHeight, amount) + + const newAgents = spots.map(spot => `${agentSymbol} ${spot.x + " " + spot.y}`).join("\n") - const availableSpots = yodash.getAllAvailableSpots(rows, cols, occupiedSpots) - amount = amount.includes("%") ? yodash.parsePercent(amount) * (rows * cols) : parseInt(amount) - const newAgents = yodash - .sampleFrom(availableSpots, amount, this.randomNumberGenerator) - .map(spot => { - const { hash } = spot - occupiedSpots.add(hash) - return `${emoji} ${hash}` - }) - .join("\n") this.concat(newAgents) + this.clearCollisionDetector() } handleExtinctions() { @@ -1141,37 +1082,10 @@ class BoardComponent extends AbstractTreeComponentParser { }) } - isSolidAgent(position) { - if (!this._solidsSet) this.resetAgentPositionMap() - const hash = yodash.makePositionHash(position) - if (this._solidsSet.has(hash)) return true - - return false - } - get agents() { return this.topDownArray.filter(node => node instanceof Agent) } - get agentPositionMap() { - if (!this._agentPositionMap) this.resetAgentPositionMap() - return this._agentPositionMap - } - - resetAgentPositionMap() { - const map = new Map() - const solidsSet = new Set() - this.agents.forEach(agent => { - const { positionHash } = agent - if (agent.solid) solidsSet.add(positionHash) - if (!map.has(positionHash)) map.set(positionHash, []) - map.get(positionHash).push(agent) - }) - this._solidsSet = solidsSet - this._agentPositionMap = map - this.occupiedSpots = new Set(map.keys()) - } - get agentTypeMap() { const map = new Map() this.agents.forEach(node => { @@ -1182,19 +1096,24 @@ class BoardComponent extends AbstractTreeComponentParser { return map } - handleOverlaps() { - this.agentPositionMap.forEach(nodes => { - if (nodes.length > 1) nodes.forEach(node => node.handleOverlaps(nodes)) - }) + clearCollisionDetector() { + delete this._collisionDetector + delete this._solidCollisionDetector } - handleTouches() { - const agentPositionMap = this.agentPositionMap - this.agents.forEach(node => node.handleTouches(agentPositionMap)) + _collisionDetector + get collisionDetector() { + if (!this._collisionDetector) this._collisionDetector = new CollisionDetector(this.agents, this.width, this.height) + return this._collisionDetector } - handleNeighbors() { - this.agents.forEach(node => node.handleNeighbors()) + handleCollisions() { + const collisions = this.collisionDetector.detectCollisions() + collisions.forEach(collision => { + const [agentA, agentB] = collision + agentA.handleCollisions([agentB]) + agentB.handleCollisions([agentA]) + }) } get boardIndex() { @@ -1278,14 +1197,7 @@ class BoardComponent extends AbstractTreeComponentParser { // Commands available to users: spawn(command) { - this.appendLine( - `${command.getWord(1)} ${yodash.getRandomLocationHash( - this.rows, - this.cols, - undefined, - this.randomNumberGenerator - )}` - ) + this.appendLine(`${command.getWord(1)} ${this.getRandomLocationHash()}`) } alert(command) { @@ -1309,6 +1221,159 @@ class BoardComponent extends AbstractTreeComponentParser { log(command) { this.root.log(command.content) } + + isRectOccupied(x, y, width, height) { + return !this.collisionDetector.isSpotAvailable(x, y, width, height) + } + + objectsCollidingWith(x, y, width, height) { + return this.collisionDetector.getCollidingAgents(x, y, width, height) + } + + _solidCollisionDetector + get solidCollisionDetector() { + if (!this._solidCollisionDetector) + this._solidCollisionDetector = new CollisionDetector( + this.agents.filter(agent => agent.solid), + this.width, + this.height + ) + return this._solidCollisionDetector + } + + canGoHere(x, y, width, height) { + const blockersHere = this.solidCollisionDetector.getCollidingAgents(x, y, width, height) + if (blockersHere.length) return false + + return true + } + + get collidingAgents() { + const agents = this.agents + const collidingAgents = [] + for (let agent of agents) { + const { position, agentSize } = agent + const agentsHere = this.objectsCollidingWith(position.x, position.y, agentSize).filter(a => a !== agent) + if (agentsHere.length) collidingAgents.push(...agentsHere) + } + return collidingAgents + } + + makePositionHash(positionType) { + return `${positionType.x + " " + positionType.y}` + } + + getRandomLocationHash(size = 1) { + const { x, y } = this.getRandomLocation() + if (this.isRectOccupied(x, y, size, size)) return this.getRandomLocationHash() + return this.makePositionHash({ x, y }) + } + + getRandomLocation() { + const { randomNumberGenerator, height, width } = this + const maxRight = width + const maxBottom = height + const x = Math.round(randomNumberGenerator() * maxRight) + const y = Math.round(randomNumberGenerator() * maxBottom) + return { x, y } + } + + draw(str) { + const lines = str.split("\n") + const output = [] + let agentWidth + let agentHeight + for (let index = 0; index < lines.length; index++) { + const words = lines[index].split(" ") + for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { + const agentSymbol = words[wordIndex] + if (agentSymbol && !agentWidth) { + // Draw assumes everything being drawn is a square with sides N. + agentWidth = this.getAgentHeightAndWidth(agentSymbol).agentWidth + agentHeight = agentWidth + } + + if (agentSymbol !== "") + output.push(`${agentSymbol} ${this.makePositionHash({ y: index * agentHeight, x: wordIndex * agentHeight })}`) + } + } + return output.join("\n") + } + + makeRectangle(options) { + const { width, height, agentSymbol, x, y, fillSymbol, spacing, agentHeight, agentWidth } = options + + if (width < 1 || height < 1) return "" + + if (isNaN(x)) x = 20 + if (isNaN(y)) y = 20 + + const cells = [] + let row = 0 + while (row < height) { + let col = 0 + while (col < width) { + const isPerimeter = row === 0 || row === height - 1 || col === 0 || col === width - 1 + if (!fillSymbol && !isPerimeter) { + col++ + continue + } + + cells.push( + `${isPerimeter ? agentSymbol : fillSymbol} ${x + col * (agentWidth + spacing)} ${ + y + row * (agentHeight + spacing) + }` + ) + col++ + } + row++ + } + return cells.join("\n") + } + + fill(agentSymbol) { + let { width, height } = this + const { agentWidth, agentHeight } = this.getAgentHeightAndWidth(agentSymbol) + const board = [] + let y = 0 + while (y < height - agentHeight) { + let x = 0 + while (x < width - agentWidth) { + if (this.isRectOccupied(x, y, agentWidth, agentHeight)) { + x += agentWidth + continue + } + board.push(`${agentSymbol} ${x} ${y}`) + x += agentWidth + } + y += agentHeight + } + return board.join("\n") + } + + getNeighborCount(rect) { + const { position, agentSize } = rect + const neighborCounts = {} + this.positionsAdjacentToRect(position.right, position.down, agentSize).forEach(pos => { + const agents = this.objectsCollidingWith(pos.right, pos.down, agentSize) + agents.forEach(agent => { + if (!neighborCounts[agent.name]) neighborCounts[agent.name] = 0 + neighborCounts[agent.name]++ + }) + }) + return neighborCounts + } + + positionsAdjacentToRect(x, y, size) { + const positions = [] + for (let row = y - size; row <= y + size; row++) { + for (let col = x - size; col <= x + size; col++) { + if (row === y && col === x) continue + positions.push({ right: col, down: row }) + } + } + return positions + } } class BoardStyleComponent extends AbstractTreeComponentParser { @@ -1351,6 +1416,245 @@ class BottomBarComponent extends AbstractTreeComponentParser { window.BottomBarComponent = BottomBarComponent +class Bounds { + constructor(x, y, w, h) { + this.x = x + this.y = y + this.w = w + this.h = h + } + + intersects(range) { + return !( + range.x >= this.x + this.w || + range.y >= this.y + this.h || + range.x + range.w <= this.x || + range.y + range.h <= this.y + ) + } +} + +class Quadtree { + constructor(bounds, capacity, maxDepth = 10) { + this.bounds = new Bounds(bounds.x, bounds.y, bounds.w, bounds.h) + this.capacity = capacity + this.maxDepth = maxDepth + this.agents = [] + } + + get agentCount() { + let count = 0 + + if (this.isLeaf()) { + count += this.agents.length + } else { + for (const child of this.children) { + count += child.agentCount + } + } + + return count + } + + insert(agent, depth = 0) { + if (!this.bounds.intersects(agent)) return false + + if (!this.divided && (this.agents.length < this.capacity || depth >= this.maxDepth)) { + this.agents.push(agent) + return true + } else { + if (!this.divided) this.divide() + for (const child of this.children) { + if (child.insert(agent, depth + 1)) return true + } + } + return false + } + + isLeaf() { + return !this.divided + } + + get northWest() { + return this.children[0] + } + + get northEast() { + return this.children[1] + } + + get southWest() { + return this.children[2] + } + + get southEast() { + return this.children[3] + } + + prettyPrint(depth = 0) { + let output = "" + + if (this.isLeaf()) { + output += "-".repeat(depth - 1) + output += `[${this.bounds.x}, ${this.bounds.y}, ${this.bounds.w}, ${this.bounds.h}]: ` + output += this.agents.map(agent => agent.id).join(", ") + output += "\n" + } else { + output += this.northWest.prettyPrint(depth + 1) + output += this.northEast.prettyPrint(depth + 1) + output += this.southWest.prettyPrint(depth + 1) + output += this.southEast.prettyPrint(depth + 1) + } + + return output + } + + get divided() { + return !!this.children + } + + divide() { + const { x, y } = this.bounds + const { bounds, capacity } = this + const w = bounds.w / 2 + const h = bounds.h / 2 + + this.children = [ + new Quadtree({ x, y, w, h }, capacity), + new Quadtree({ x: x + w, y, w, h }, capacity), + new Quadtree({ x, y: y + h, w, h }, capacity), + new Quadtree({ x: x + w, y: y + h, w, h }, capacity) + ] + + for (const agent of this.agents) this.insert(agent) + delete this.agents + } + + query(range, found = []) { + if (!this.bounds.intersects(range)) return found + + if (!this.divided) { + for (const agent of this.agents) if (range.intersects(agent)) found.push(agent) + } else { + for (const child of this.children) child.query(range, found) + } + + return found + } +} + +class CollisionDetector { + constructor(agents, worldWidth, worldHeight) { + this.agents = agents + this.width = worldWidth + this.height = worldHeight + this.quadtree = new Quadtree({ x: 0, y: 0, w: worldWidth, h: worldHeight }, 4) + for (const agent of this.agents) this.addAgent(agent) + } + + addAgent(agent) { + this.quadtree.insert(agent) + return this + } + + isSpotAvailable(x, y, width, height) { + const searchBounds = new Bounds(x, y, width, height) + + const nearbyAgents = this.quadtree.query(searchBounds) + + for (const agent of nearbyAgents) { + if (x < agent.x + agent.width && x + width > agent.x && y < agent.y + agent.height && y + height > agent.y) { + return false + } + } + return true + } + + findNonOverlappingSquares(width, height, N) { + const nonOverlappingSquares = [] + const availableCells = [] + + // Divide the world into cells + for (let x = 0; x < this.width - width; x += width) { + for (let y = 0; y < this.height - height; y += height) { + if (this.isSpotAvailable(x, y, width, height)) availableCells.push({ x, y }) + } + } + + // Randomly select non-overlapping cells + while (nonOverlappingSquares.length < N && availableCells.length) { + const randomIndex = Math.floor(Math.random() * availableCells.length) + nonOverlappingSquares.push(availableCells[randomIndex]) + availableCells.splice(randomIndex, 1) + } + return nonOverlappingSquares + } + + findClusteredNonOverlappingSquares(width, height, N, centerX, centerY, clusterRadius) { + const nonOverlappingSquares = [] + const availableCells = [] + + // Divide the world into cells and filter by clusterRadius + for (let x = 0; x < this.width - width; x += width) { + for (let y = 0; y < this.height - height; y += height) { + const dx = x - centerX + const dy = y - centerY + const distance = Math.sqrt(dx * dx + dy * dy) + + if (distance <= clusterRadius && this.isSpotAvailable(x, y, width, height)) { + availableCells.push({ x: x, y: y }) + } + } + } + + // Randomly select non-overlapping cells + while (nonOverlappingSquares.length < N && availableCells.length > 0) { + const randomIndex = Math.floor(Math.random() * availableCells.length) + nonOverlappingSquares.push(availableCells[randomIndex]) + availableCells.splice(randomIndex, 1) + } + + return nonOverlappingSquares + } + + getCollidingAgents(x, y, width, height) { + const collidingAgents = [] + const queryBounds = new Bounds(x, y, width, height) + const nearbyAgents = this.quadtree.query(queryBounds) + + for (const agent of nearbyAgents) { + if (queryBounds.intersects(agent)) collidingAgents.push(agent) + } + + return collidingAgents + } + + detectCollisions() { + let collissions = [] + + for (const agentA of this.agents) { + const searchBounds = new Bounds( + agentA.x - agentA.width, + agentA.y - agentA.height, + agentA.width * 2, + agentA.height * 2 + ) + const nearbyAgents = this.quadtree.query(searchBounds) + for (const agentB of nearbyAgents) { + if (agentA !== agentB && this.checkCollision(agentA, agentB)) collissions.push([agentA, agentB]) + } + } + return collissions + } + + checkCollision(a, b) { + return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y + } +} + +window.CollisionDetector = CollisionDetector + + class EditorHandleComponent extends AbstractTreeComponentParser { @@ -1425,10 +1729,6 @@ const Categories = new TreeNode(`🦠 Epidemiology basketball 💰 Business startupIdeas -👾 Game of Life - gameOfLife - gospersGliderGun - gameOfLifeAdvanced 🦋 Biology moths 🕹 Games @@ -1491,35 +1791,26 @@ window.ExampleMenuComponent = ExampleMenuComponent - class GridComponent extends AbstractTreeComponentParser { - gridClickCommand(down, right) { - return this.parent.insertAgentAtCommand(right, down) + gridClickCommand(x, y) { + return this.parent.insertAgentAtCommand(x, y) } - makeBlock(down, right, gridSize) { - return `\n div - class block - style width:${gridSize}px;height:${gridSize}px;top:${down * gridSize}px;left:${right * gridSize}px; - clickCommand gridClickCommand ${yodash.makePositionHash({ right, down })}` + treeComponentDidMount() { + const that = this + if (this.isNodeJs()) return super.treeComponentDidMount() + + jQuery(`.${GridComponent.name}`).on("click", function (evt) { + const { offsetX, offsetY } = evt + const x = offsetX + const y = offsetY + that.gridClickCommand(x, y) + }) } toStumpCode() { - const { cols, rows, gridSize } = this.parent - let blocks = "" - let rs = rows - while (rs >= 0) { - let cs = cols - while (cs >= 0) { - blocks = this.makeBlock(rs, cs, gridSize) + blocks - cs-- - } - rs-- - } - return ( - `div - class ${GridComponent.name}` + blocks - ) + return `div + class ${GridComponent.name}` } } @@ -1853,12 +2144,6 @@ window.SimEditorComponent = SimEditorComponent - -const MIN_GRID_SIZE = 10 -const MAX_GRID_SIZE = 200 -const DEFAULT_GRID_SIZE = 20 -const MIN_GRID_COLUMNS = 10 -const MIN_GRID_ROWS = 10 // prettier-ignore @@ -1918,20 +2203,15 @@ class SimojiApp extends AbstractTreeComponentParser { } makeGrid(simojiProgram, windowWidth, windowHeight) { - const setSize = simojiProgram.get(Keywords.size) - const gridSize = Math.min(Math.max(setSize ? parseInt(setSize) : DEFAULT_GRID_SIZE, MIN_GRID_SIZE), MAX_GRID_SIZE) - const chromeWidth = this.leftStartPosition + SIZES.RIGHT_BAR_WIDTH + SIZES.BOARD_MARGIN - const maxAvailableCols = Math.floor((windowWidth - chromeWidth) / gridSize) - 1 - const maxAvailableRows = Math.floor((windowHeight - SIZES.CHROME_HEIGHT - SIZES.TITLE_HEIGHT) / gridSize) - 1 + const width = windowWidth - chromeWidth - 1 + const height = windowHeight - SIZES.CHROME_HEIGHT - SIZES.TITLE_HEIGHT - 1 - const setCols = simojiProgram.get(Keywords.columns) - const cols = Math.max(1, setCols ? parseInt(setCols) : Math.max(MIN_GRID_COLUMNS, maxAvailableCols)) + const setWidth = simojiProgram.get(Keywords.width) + const setHeight = simojiProgram.get(Keywords.height) + // todo: use the set values if present - const setRows = simojiProgram.get(Keywords.rows) - const rows = Math.max(1, setRows ? parseInt(setRows) : Math.max(MIN_GRID_ROWS, maxAvailableRows)) - - return { gridSize, cols, rows } + return { width, height } } verbose = true @@ -1971,11 +2251,11 @@ class SimojiApp extends AbstractTreeComponentParser { _appendExperiment(program, index) { const { windowWidth, windowHeight } = this - const { gridSize, cols, rows } = this.makeGrid(program, windowWidth, windowHeight) + const { width, height } = this.makeGrid(program, windowWidth, windowHeight) const styleNode = program.getNode(Keywords.style) ?? undefined const board = this.appendLineAndChildren( - `${BoardComponent.name} ${gridSize} ${rows} ${cols} ${index}`, + `${BoardComponent.name} 1 ${width} ${height} ${index}`, `leftStartPosition ${this.leftStartPosition} ${GridComponent.name} ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : ""}`.trim() @@ -2082,7 +2362,6 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : const previousTick = maxTick - 2 this.pauseAllCommand() if (previousTick < 0) return - if (!this.isSnapshotOn) this.snapShotCommand() this.loadNewSim(this.simCode) this.boards.forEach(board => board.skipToThisManyTicksIfNotPaused(previousTick)) console.log(`Running to tick ${previousTick} from ${maxTick}`) @@ -2162,25 +2441,6 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : this.renderAndGetRenderReport() } - toggleSelectCommand(objects) { - objects.forEach(object => { - this.selection.includes(object) ? this.unselectCommand(object) : this.selectCommand(object) - }) - - this.ensureRender() - return this - } - - unselectCommand(object) { - object.unselect() - this.selection = this.selection.filter(node => node !== object) - } - - selectCommand(object) { - this.selection.push(object) - object.select() - } - async downloadCsvCommand() { let extension = "csv" let type = "text/csv" @@ -2258,27 +2518,25 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : selection = [] - moveSelection(direction) { + moveSelection(x, y) { const { selection } = this if (!selection.length) return this selection.forEach(node => { - node.angle = direction + node.direction = { x, y } node._move() }) - this.boards.forEach(board => board.resetAgentPositionMap()) - this.ensureRender() } deleteSelectionCommand() { this.selection.forEach(node => node.nuke()) this.selection = [] - this.boards.forEach(board => board.resetAgentPositionMap()) + // todo: update any state? } get isSnapshotOn() { - // technically also needs rows and column settings + // technically also needs width and height settings return new TreeNode(this.simCode).has(Keywords.seed) } @@ -2290,13 +2548,13 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : // todo: buggy. we should rename the board class to experiment, or rename experiment keyword to board. const board = boards[0] newCode.set(Keywords.seed, board.seed.toString()) - newCode.set(Keywords.rows, board.rows.toString()) - newCode.set(Keywords.columns, board.cols.toString()) + newCode.set(Keywords.height, board.height.toString()) + newCode.set(Keywords.width, board.width.toString()) newCode.findNodes(Keywords.experiment).forEach((experiment, index) => { const board = boards[index] experiment.set(Keywords.seed, board.seed.toString()) - experiment.set(Keywords.rows, board.rows.toString()) - experiment.set(Keywords.columns, board.cols.toString()) + experiment.set(Keywords.height, board.height.toString()) + experiment.set(Keywords.width, board.width.toString()) }) this.editor.setCodeMirrorValue(newCode.toString()) @@ -2332,10 +2590,10 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : o: () => this.openReportInOhayoCommand(), r: () => this.resetAllCommand(), s: () => this.snapShotCommand(), - up: () => this.moveSelection(Directions.North), - down: () => this.moveSelection(Directions.South), - right: () => this.moveSelection(Directions.East), - left: () => this.moveSelection(Directions.West), + up: () => this.moveSelection(0, -1), + down: () => this.moveSelection(0, 1), + right: () => this.moveSelection(1, 0), + left: () => this.moveSelection(-1, 0), escape: () => this.clearSelectionCommand(), "command+a": () => this.selectAllCommand(), "?": () => this.toggleHelpCommand(), @@ -2462,15 +2720,12 @@ const Keywords = {} Keywords.experiment = "experiment" Keywords.seed = "seed" -Keywords.size = "size" -Keywords.rows = "rows" -Keywords.columns = "columns" +Keywords.height = "height" +Keywords.width = "width" Keywords.report = "report" Keywords.ticksPerSecond = "ticksPerSecond" Keywords.style = "style" -Keywords.onNeighbors = "onNeighbors" -Keywords.onTouch = "onTouch" Keywords.onHit = "onHit" Keywords.onTick = "onTick" Keywords.onDeath = "onDeath" @@ -2489,13 +2744,6 @@ UrlKeys.simoji = "simoji" UrlKeys.example = "example" UrlKeys.url = "url" -const Directions = {} - -Directions.North = "North" -Directions.East = "East" -Directions.South = "South" -Directions.West = "West" - const ParserTypes = {} ParserTypes.agentDefinitionParser = "agentDefinitionParser" @@ -2509,8 +2757,6 @@ window.LocalStorageKeys = LocalStorageKeys window.UrlKeys = UrlKeys -window.Directions = Directions - window.ParserTypes = ParserTypes @@ -2559,7 +2805,7 @@ class BrowserGlue extends AbstractTreeComponentParser { } async fetchSimGrammarAndExamplesAndInit() { - const grammar = await fetch("simoji.grammar") + const grammar = await fetch("dist/simoji.grammar") const grammarCode = await grammar.text() const result = await fetch("examples") diff --git a/examples.js b/examples.js index b27db20..af6adec 100644 --- a/examples.js +++ b/examples.js @@ -1,4 +1,3 @@ -const stamp = require("jtree/products/stamp.nodejs.js") const { Disk } = require("jtree/products/Disk.node.js") const getExamples = () => diff --git a/examples/basketball.simoji b/examples/basketball.simoji index ce0e75d..68f54c6 100644 --- a/examples/basketball.simoji +++ b/examples/basketball.simoji @@ -12,37 +12,49 @@ experiment experiment Shoot right away shotProbabilitySetting .8 +thingClass + width 30 + height 30 + +personClass + width 40 + height 40 + speed 20 + 🏀 + width 20 + height 20 + speed 30 onHit 🥅⛹️‍♂️ narrate Blue scores! - spawn 🏀 9⬇️ 15➡️ - spawn 🔵 18⬇️ 1➡️ + spawn 🏀 15 9 + spawn 🔵 1 18 remove 🥅⛹️‍♀️ narrate Red scores! - spawn 🏀 9⬇️ 15➡️ - spawn 🔴 17⬇️ 1➡️ + spawn 🏀 15 9 + spawn 🔴 1 17 remove -moveEastToBlankSpotBehavior +moveEastToBlankSpotClass onTick moveToEmptySpot - unlearn moveEastToBlankSpotBehavior + unlearn moveEastToBlankSpotClass 🔵 angle East - moveEastToBlankSpotBehavior + moveEastToBlankSpotClass 🔴 angle East - moveEastToBlankSpotBehavior + moveEastToBlankSpotClass ticksPerSecond 30 -hasBallBehavior +hasBallClass comment Sprint toward net onTick .5 turnToward net @@ -53,17 +65,17 @@ hasBallBehavior turnToward net shoot narrate shoots! - learn noBallBehavior - unlearn hasBallBehavior + learn noBallClass + unlearn hasBallClass comment Pass onTick .02 turnToward team shoot narrate passes the ball! - learn noBallBehavior - unlearn hasBallBehavior + learn noBallClass + unlearn hasBallClass -noBallBehavior +noBallClass onTick .3 turnToward 🏀 move @@ -71,8 +83,8 @@ noBallBehavior 🏀 pickItUp narrate has the ball - learn hasBallBehavior - unlearn noBallBehavior + learn hasBallClass + unlearn noBallClass onTick .05 turnFrom opponent move @@ -82,47 +94,47 @@ noBallBehavior # Blue Team ⛹️‍♂️ + personClass net 🥅⛹️‍♂️ team ⛹️‍♂️ opponent ⛹️‍♀️ - noBallBehavior + noBallClass # Red Team ⛹️‍♀️ + personClass net 🥅⛹️‍♀️ team ⛹️‍♀️ opponent ⛹️‍♂️ - noBallBehavior + noBallClass # Baskets 🥅⛹️‍♂️ + thingClass html 🥅 🥅⛹️‍♀️ + thingClass html 🥅 + paste - 🥅⛹️‍♂️ 8⬇️ 2➡️ - 🥅⛹️‍♀️ 8⬇️ 29➡️ - 🏀 9⬇️ 15➡️ + 🥅⛹️‍♂️ 50 180 + 🥅⛹️‍♀️ 800 180 + 🏀 425 180 # Court 🪵 + thingClass solid rectangle 🪵 30 15 1 1 -size 30 - -# Red Team -paste - ⛹️‍♀️ 9⬇️ 6➡️ - ⛹️‍♀️ 5⬇️ 6➡️ - ⛹️‍♀️ 11⬇️ 11➡️ - ⛹️‍♀️ 8⬇️ 11➡️ - ⛹️‍♀️ 5⬇️ 11➡️ -# Blue Team -paste - ⛹️‍♂️ 8⬇️ 25➡️ - ⛹️‍♂️ 6⬇️ 25➡️ - ⛹️‍♂️ 11⬇️ 20➡️ - ⛹️‍♂️ 7⬇️ 20➡️ - ⛹️‍♂️ 4⬇️ 20➡️ +insertAt ⛹️‍♂️ 719 140 +insertAt ⛹️‍♂️ 733 242 +insertAt ⛹️‍♂️ 727 341 +insertAt ⛹️‍♂️ 645 172 +insertAt ⛹️‍♂️ 626 271 +insertAt ⛹️‍♀️ 167 142 +insertAt ⛹️‍♀️ 154 286 +insertAt ⛹️‍♀️ 307 139 +insertAt ⛹️‍♀️ 309 227 +insertAt ⛹️‍♀️ 320 341 \ No newline at end of file diff --git a/examples/city.simoji b/examples/city.simoji index ab7991b..c267c44 100644 --- a/examples/city.simoji +++ b/examples/city.simoji @@ -41,17 +41,16 @@ comment road 🟩 ⛳️ -size 20 rectangle ⬜️ 10 20 0 0 rectangle ⬜️ 10 1 0 10 paste - 🏛 9⬇️ 8➡️ - 🏭 1⬇️ 4➡️ - 🏭 1⬇️ 3➡️ - 🏭 1⬇️ 2➡️ - 🏭 1⬇️ 1➡️ - 🏡 18⬇️ 5➡️ - 🏡 18⬇️ 6➡️ - 🏡 18⬇️ 7➡️ - 🏡 18⬇️ 8➡️ \ No newline at end of file + 🏛 8 9 + 🏭 4 1 + 🏭 3 1 + 🏭 2 1 + 🏭 1 1 + 🏡 5 18 + 🏡 6 18 + 🏡 7 18 + 🏡 8 18 \ No newline at end of file diff --git a/examples/cops.simoji b/examples/cops.simoji index d26b541..64c0332 100644 --- a/examples/cops.simoji +++ b/examples/cops.simoji @@ -13,9 +13,8 @@ onTick .1 move -size 20 ticksPerSecond 30 paste - 🚓 1⬇️ 1➡️ - 🚗 15⬇️ 5➡️ \ No newline at end of file + 🚓 1 1 + 🚗 5 15 \ No newline at end of file diff --git a/examples/covid19.simoji b/examples/covid19.simoji index a5d6615..71af120 100644 --- a/examples/covid19.simoji +++ b/examples/covid19.simoji @@ -1,4 +1,5 @@ 🦠 + thingClass question How long will the pandemic last? @@ -34,10 +35,10 @@ experiment High Vaccination Rate, Low Vaccine Efficacy vaxSucceptibilitySetting .75 experiment Lockdown - freedomOfMovementSetting .3 + freedomOfMovementSetting .1 experiment High Reinfection Rate - reinfectionRateSetting .2 + reinfectionRateSetting .3 insert startingInfectedSetting 🧟 @@ -46,8 +47,13 @@ insert vaccineCentersSetting 💉 insertCluster urbanPopulationSetting 🙍 insert ruralPopulationSetting 🙍 +thingClass + width 20 + height 20 + speed 5 🧟 + thingClass health 100 onTick .03 log recovered @@ -59,27 +65,29 @@ insert ruralPopulationSetting 🙍 replaceWith 🪦 🦸‍♂️ + thingClass comment Recovered onTick jitter - onTouch reinfectionRateSetting + onHit reinfectionRateSetting 🧟 replaceWith 🧟 -lifeBehavior +aliveClass onTick freedomOfMovementSetting jitter -seekVaccineBehavior +vaccineSeekerClass onTick vaccinationDesirabilitySetting turnToward 💉 move 🙍 - lifeBehavior - seekVaccineBehavior - onTouch innateImmunitySetting + thingClass + aliveClass + vaccineSeekerClass + onHit innateImmunitySetting 🧟 replaceWith 🧟 💉 @@ -87,11 +95,13 @@ seekVaccineBehavior 💉 + thingClass 🧑🏽‍🚒 - lifeBehavior - onTouch vaxSucceptibilitySetting + thingClass + aliveClass + onHit vaxSucceptibilitySetting 🧟 replaceWith 🧟 @@ -104,8 +114,9 @@ onExtinct 🧟 🪦 + thingClass + -size 15 ticksPerSecond 10 report diff --git a/examples/covid19simple.simoji b/examples/covid19simple.simoji index 3e656da..a5687e4 100644 --- a/examples/covid19simple.simoji +++ b/examples/covid19simple.simoji @@ -45,7 +45,7 @@ experiment 🙍 onTick jitter - onTouch + onHit 🦠 replaceWith 🧟 🧟 @@ -60,7 +60,6 @@ onExtinct 🧟 🪦 -size 15 ticksPerSecond 10 report diff --git a/examples/eatTheBacon.simoji b/examples/eatTheBacon.simoji index d364cdd..4842f9e 100644 --- a/examples/eatTheBacon.simoji +++ b/examples/eatTheBacon.simoji @@ -5,7 +5,7 @@ onTick .2 jitter 🥓 - onTouch + onHit 🐕 remove 🥦 diff --git a/examples/elevators.simoji b/examples/elevators.simoji index d039866..a732dd5 100644 --- a/examples/elevators.simoji +++ b/examples/elevators.simoji @@ -21,17 +21,15 @@ 🚗 -size 15 - -rectangle 🪵 20➡️ 47⬇️ 5 1 -rectangle 🌾 40➡️ 1⬇️ 0 48 -rectangle 🚪 1➡️ 45⬇️ 15 2 +rectangle 🪵 20 47 5 1 +rectangle 🌾 40 1 0 48 +rectangle 🚪 1 45 15 2 paste - 🛗 6⬇️ 19➡️ - 🛗 4⬇️ 22➡️ - 🛗 3⬇️ 20➡️ - 🛗 10⬇️ 13➡️ - 🛗 4⬇️ 9➡️ - 🛗 3⬇️ 11➡️ - 🚗 47⬇️ 30➡️ - 🚗 47⬇️ 28➡️ \ No newline at end of file + 🛗 19 6 + 🛗 22 4 + 🛗 20 3 + 🛗 13 10 + 🛗 9 4 + 🛗 11 3 + 🚗 30 47 + 🚗 28 47 \ No newline at end of file diff --git a/examples/fire.simoji b/examples/fire.simoji index a4fb7ea..f6075e5 100644 --- a/examples/fire.simoji +++ b/examples/fire.simoji @@ -1,11 +1,15 @@ question How fast do fires spread? 🌲 + width 20 + height 20 onHit ⚡️ + replaceWith ⬛️ replaceWith 🔥 - onTouch + onHit 🔥 + replaceWith ⬛️ replaceWith 🔥 ⚡️ @@ -16,12 +20,13 @@ question How fast do fires spread? remove 🔥 + width 40 + height 40 health 50 onTick + jitter decrease health - onDeath - replaceWith ⬛️ - + shrink ⬛️ comment Burnt forest diff --git a/examples/fireAdvanced.simoji b/examples/fireAdvanced.simoji index 3adb1c8..0e1413d 100644 --- a/examples/fireAdvanced.simoji +++ b/examples/fireAdvanced.simoji @@ -18,10 +18,12 @@ fireLifetimeSetting 10 lightningFrequencySetting .1 🌲 + width 20 + height 20 onHit catchFireSetting ⚡️ replaceWith 🔥 - onTouch fireSpreadSetting + onHit fireSpreadSetting 🔥 replaceWith 🔥 @@ -33,9 +35,13 @@ lightningFrequencySetting .1 remove 🔥 + width 40 + height 40 health fireLifetimeSetting onTick decrease health + jitter + shrink onDeath replaceWith ⬛️ diff --git a/examples/gameOfLife.simoji b/examples/gameOfLife.simoji deleted file mode 100644 index 75f90be..0000000 --- a/examples/gameOfLife.simoji +++ /dev/null @@ -1,17 +0,0 @@ -question Can simple rules produce complex effects? - -⬛️ - onNeighbors - ⬛️ < 2 - replaceWith ◻️ - ⬛️ > 3 - replaceWith ◻️ - -◻️ - onNeighbors - ⬛️ = 3 - replaceWith ⬛️ - -insert 10% ⬛️ -fill ◻️ -size 15 \ No newline at end of file diff --git a/examples/gameOfLifeAdvanced.simoji b/examples/gameOfLifeAdvanced.simoji deleted file mode 100644 index 76d6e5c..0000000 --- a/examples/gameOfLifeAdvanced.simoji +++ /dev/null @@ -1,29 +0,0 @@ -# Conway's Game of Life - -experiment - neighborSetting 2 - -experiment - neighborSetting 3 - -experiment - neighborSetting 4 - -experiment - neighborSetting 5 - -⬛️ - onNeighbors - ⬛️ < 2 - replaceWith ◻️ - ⬛️ > neighborSetting - replaceWith ◻️ - -◻️ - onNeighbors - ⬛️ = 3 - replaceWith ⬛️ - -insert 10% ⬛️ -fill ◻️ -size 15 \ No newline at end of file diff --git a/examples/gospersGliderGun.simoji b/examples/gospersGliderGun.simoji deleted file mode 100644 index 5264867..0000000 --- a/examples/gospersGliderGun.simoji +++ /dev/null @@ -1,27 +0,0 @@ -⬛️ - onNeighbors - ⬛️ < 2 - replaceWith ◻️ - ⬛️ > 3 - replaceWith ◻️ - -◻️ - onNeighbors - ⬛️ = 3 - replaceWith ⬛️ - -# Gosper's Glider Gun - -draw - ⬛️ - ⬛️ ⬛️ - ⬛️ ⬛️ ⬛️ ⬛️ - ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ - ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ - ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ - ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ - ⬛️ ⬛️ - ⬛️ ⬛️ - - -fill ◻️ diff --git a/examples/moths.simoji b/examples/moths.simoji index 2832769..99a9776 100644 --- a/examples/moths.simoji +++ b/examples/moths.simoji @@ -1,7 +1,10 @@ question Can you move the moths from one light to the other? 🦋 - onTick .1 + speed 10 + height 30 + width 30 + onTick .5 jitter move onTick .2 @@ -9,9 +12,10 @@ question Can you move the moths from one light to the other? move move 💡 + height 100 + width 100 ticksPerSecond 10 -size 20 style .BoardComponent {background:black;} diff --git a/examples/pong.simoji b/examples/pong.simoji index 93e9aa6..b2a952e 100644 --- a/examples/pong.simoji +++ b/examples/pong.simoji @@ -1,16 +1,21 @@ - 🏐 bouncy + speed 20 onTick move angle West 🏓 + width 30 + height 30 angle East onHit 🏐 kickIt + 🏸 + width 30 + height 30 angle West onHit 🏐 @@ -18,12 +23,11 @@ 🪵 solid -size 20 ticksPerSecond 10 -rectangle 🪵 30 15 5 5 -paste - 🏓 13⬇️ 6➡️ - 🏸 13⬇️ 33➡️ - 🏐 13⬇️ 19➡️ +rectangle 🪵 50 15 50 50 + +insertAt 🏐 273 125 +insertAt 🏓 90 120 +insertAt 🏸 510 121 \ No newline at end of file diff --git a/examples/poolTable.simoji b/examples/poolTable.simoji index bfcb487..07c2a52 100644 --- a/examples/poolTable.simoji +++ b/examples/poolTable.simoji @@ -6,6 +6,7 @@ comment onHit 🎱 kickIt + turnRandomly 🏐 kickIt @@ -14,37 +15,40 @@ comment 🏐 bouncy + speed 10 onTick .1 - turnRandomly kickIt onTick .5 kickIt onHit 🎱 + turnRandomly kickIt + move + turnRandomly angle West -rectangle 🪵 40 20 0 7 +rectangle 🪵 40 20 10 70 paste - 🏐 17⬇️ 32➡️ - 🎱 12⬇️ 7➡️ - 🎱 14⬇️ 7➡️ - 🎱 16⬇️ 7➡️ - 🎱 18⬇️ 7➡️ - 🎱 20⬇️ 7➡️ - 🎱 22⬇️ 7➡️ - 🎱 21⬇️ 8➡️ - 🎱 19⬇️ 8➡️ - 🎱 17⬇️ 8➡️ - 🎱 15⬇️ 8➡️ - 🎱 13⬇️ 8➡️ - 🎱 20⬇️ 9➡️ - 🎱 18⬇️ 9➡️ - 🎱 16⬇️ 9➡️ - 🎱 14⬇️ 9➡️ - 🎱 15⬇️ 10➡️ - 🎱 17⬇️ 10➡️ - 🎱 19⬇️ 10➡️ - 🎱 18⬇️ 11➡️ - 🎱 16⬇️ 11➡️ - 🎱 17⬇️ 12➡️ \ No newline at end of file + 🏐 320 170 + 🎱 70 120 + 🎱 70 140 + 🎱 70 160 + 🎱 70 180 + 🎱 70 200 + 🎱 70 220 + 🎱 80 210 + 🎱 80 190 + 🎱 80 170 + 🎱 80 150 + 🎱 80 130 + 🎱 90 200 + 🎱 90 180 + 🎱 90 160 + 🎱 90 140 + 🎱 100 150 + 🎱 100 170 + 🎱 100 190 + 🎱 110 180 + 🎱 110 160 + 🎱 120 170 \ No newline at end of file diff --git a/examples/soccer.simoji b/examples/soccer.simoji index f9d1c14..1403d66 100644 --- a/examples/soccer.simoji +++ b/examples/soccer.simoji @@ -1,6 +1,12 @@ - +thingClass + width 40 + height 40 + speed 20 ⚽️ + width 20 + height 20 + speed 30 onHit 🥅 pause @@ -8,6 +14,7 @@ bouncy ⛹️‍♂️ + thingClass onTick jitter onHit @@ -15,47 +22,49 @@ kickIt ⛹️‍♀️ + thingClass onTick jitter onHit ⚽️ kickIt + 🥅 + thingClass 🪵 + thingClass solid -size 20 ticksPerSecond 10 rectangle 🪵 30 15 5 5 -paste - 🥅 13⬇️ 6➡️ - 🥅 13⬇️ 33➡️ - ⚽️ 13⬇️ 19➡️ - -paste - ⛹️‍♀️ 17⬇️ 14➡️ - ⛹️‍♀️ 17⬇️ 17➡️ - ⛹️‍♀️ 13⬇️ 17➡️ - ⛹️‍♀️ 13⬇️ 14➡️ - ⛹️‍♀️ 8⬇️ 14➡️ - ⛹️‍♀️ 8⬇️ 17➡️ - ⛹️‍♀️ 10⬇️ 14➡️ - ⛹️‍♀️ 9⬇️ 10➡️ - ⛹️‍♀️ 13⬇️ 8➡️ - ⛹️‍♀️ 13⬇️ 10➡️ - ⛹️‍♀️ 17⬇️ 10➡️ - -paste - ⛹️‍♂️ 13⬇️ 31➡️ - ⛹️‍♂️ 17⬇️ 28➡️ - ⛹️‍♂️ 13⬇️ 28➡️ - ⛹️‍♂️ 8⬇️ 29➡️ - ⛹️‍♂️ 8⬇️ 25➡️ - ⛹️‍♂️ 10⬇️ 25➡️ - ⛹️‍♂️ 13⬇️ 25➡️ - ⛹️‍♂️ 17⬇️ 25➡️ - ⛹️‍♂️ 17⬇️ 21➡️ - ⛹️‍♂️ 8⬇️ 21➡️ - ⛹️‍♂️ 13⬇️ 21➡️ + +seed 1681189626875 +height 862 +width 1354 +insertAt 🥅 1095 293 +insertAt 🥅 98 294 +insertAt ⛹️‍♂️ 993 303 +insertAt ⛹️‍♂️ 923 114 +insertAt ⛹️‍♂️ 923 244 +insertAt ⛹️‍♂️ 954 449 +insertAt ⛹️‍♂️ 844 129 +insertAt ⛹️‍♂️ 842 246 +insertAt ⛹️‍♂️ 849 337 +insertAt ⛹️‍♂️ 866 454 +insertAt ⛹️‍♂️ 722 148 +insertAt ⛹️‍♂️ 705 273 +insertAt ⛹️‍♂️ 722 423 +insertAt ⛹️‍♀️ 443 168 +insertAt ⛹️‍♀️ 496 285 +insertAt ⛹️‍♀️ 481 441 +insertAt ⛹️‍♀️ 317 137 +insertAt ⛹️‍♀️ 321 253 +insertAt ⛹️‍♀️ 342 334 +insertAt ⛹️‍♀️ 333 471 +insertAt ⛹️‍♀️ 196 505 +insertAt ⛹️‍♀️ 182 395 +insertAt ⛹️‍♀️ 212 192 +insertAt ⛹️‍♀️ 161 318 +insertAt ⚽️ 618 322 \ No newline at end of file diff --git a/examples/startupIdeas.simoji b/examples/startupIdeas.simoji index 4635d1a..6beaaeb 100644 --- a/examples/startupIdeas.simoji +++ b/examples/startupIdeas.simoji @@ -1,17 +1,27 @@ question What is the effect of ideas vs ideas with revenue? +personClass + width 25 + height 25 + speed 10 + 👨‍💼🔖 comment person with an idea + personClass onTick jitter 👨‍💼💰 + width 50 + height 50 comment peron with an idea that is making money + personClass onTick jitter 👨‍ + personClass onTick .1 jitter onTick .1 @@ -19,7 +29,6 @@ question What is the effect of ideas vs ideas with revenue? move -size 10 insert 200 👨‍ insert 30 👨‍💼🔖 insert 3 👨‍💼💰 diff --git a/examples/store.simoji b/examples/store.simoji index 075ffed..65140a1 100644 --- a/examples/store.simoji +++ b/examples/store.simoji @@ -1,16 +1,36 @@ +bigClass + width 20 + height 20 + 🚶🏻 + bigClass + speed 10 onTick move - angle North + onTick .4 + turnRandomly + move + onTick .5 + turnToward 🛒 + onHit + 🛒 + pickItUp + angle South 🛒 + bigClass 🚪 + bigClass onTick .1 spawn 🚶🏻 🪵 + bigClass solid -size 25 - -rectangle 🪵 30 15 3 3 +rectangle 🪵 30 15 30 30 paste - 🚪 16⬇️ 17➡️ + 🚪 170 120 + + + +insertAt 🛒 432 95 +insertAt 🛒 435 212 \ No newline at end of file diff --git a/examples/talking.simoji b/examples/talking.simoji new file mode 100644 index 0000000..f45b59a --- /dev/null +++ b/examples/talking.simoji @@ -0,0 +1,37 @@ +🙂 + width 30 + height 30 + onTick .1 + turnToward 🙂 + emit 💬 + + +💬 + health 5 + speed 10 + onHit + 🙂 + decrease health + decrease health + decrease health + decrease health + decrease health + decrease health + onTick + move + decrease health + onDeath + remove + + +rectangle 🙂 5 5 100 100 🙂 20 + +textClass + width 20 + height 20 + +1 + html Imagine a world with 25 people + textClass + +insertAt 1 70 60 \ No newline at end of file diff --git a/examples/virus.simoji b/examples/virus.simoji index 19da8a3..64a2c90 100644 --- a/examples/virus.simoji +++ b/examples/virus.simoji @@ -1,6 +1,12 @@ question What might the spread of a simple virus look like? +thingClass + width 20 + height 20 + speed 20 + 🧟 + thingClass health 100 onTick .9 decrease health @@ -12,13 +18,16 @@ question What might the spread of a simple virus look like? replaceWith 🪦 🙍 + thingClass onTick jitter - onTouch + onHit 🧟 replaceWith 🧟 🦸‍♂️ + # Recovered + thingClass onTick jitter @@ -26,6 +35,7 @@ insert 10% 🙍 insert 1 🧟 🪦 + thingClass onExtinct 🧟 log No more cases. diff --git a/examples/waves.simoji b/examples/waves.simoji index 0b2493b..d536cd4 100644 --- a/examples/waves.simoji +++ b/examples/waves.simoji @@ -3,7 +3,6 @@ move angle South -size 25 ticksPerSecond 5 rectangle 🌊 100 1 0 diff --git a/examples/zombies.simoji b/examples/zombies.simoji index 09473ee..8d88cb0 100644 --- a/examples/zombies.simoji +++ b/examples/zombies.simoji @@ -1,6 +1,12 @@ question Can you protect the family from the zombies? +bigClass + width 40 + height 40 + 🧟‍♂️ + bigClass + speed 10 noPalette onTick jitter @@ -14,31 +20,39 @@ question Can you protect the family from the zombies? alert TheyGotYou! 🧱 + bigClass solid 🔫 + bigClass onTick .1 spawn 🪃 🪃 + speed 50 + bigClass + width 50 + height 50 noPalette angle West onTick move 💣 + bigClass 🪦 + bigClass noPalette comment Dead zombie 👨‍👩‍👧‍👦 + bigClass noPalette -size 30 ticksPerSecond 10 insertCluster 30 🧟‍♂️ 1 1 paste - 👨‍👩‍👧‍👦 12⬇️ 11➡️ + 👨‍👩‍👧‍👦 400 400 diff --git a/grammar/actions.grammar b/grammar/actions.grammar new file mode 100644 index 0000000..5baf345 --- /dev/null +++ b/grammar/actions.grammar @@ -0,0 +1,87 @@ +javascriptLineParser + catchAllCellType javascriptCell +abstractCommandParser + cells keywordCell + +abstractSubjectObjectCommandParser + extends abstractCommandParser +replaceWithCommandParser + extends abstractSubjectObjectCommandParser + crux replaceWith + cells keywordCell emojiCell +kickItCommandParser + extends abstractSubjectObjectCommandParser + crux kickIt +shootCommandParser + extends abstractSubjectObjectCommandParser + crux shoot +pickItUpCommandParser + extends abstractSubjectObjectCommandParser + crux pickItUp + +spawnCommandParser + crux spawn + extends abstractCommandParser + cells keywordCell emojiCell + catchAllCellType positionCell +emitCommandParser + crux emit + extends abstractCommandParser + cells keywordCell emojiCell +removeCommandParser + description Remove this agent from the board. + crux remove + extends abstractCommandParser + cells keywordCell +javascriptCommandParser + description An escape hatch so you can write custom javascript in a pinch. + extends abstractCommandParser + crux javascript + catchAllParser javascriptLineParser + cells keywordCell + +alertCommandParser + extends abstractCommandParser + crux alert + catchAllCellType stringCell +logCommandParser + extends abstractCommandParser + crux log + catchAllCellType stringCell +narrateCommandParser + extends abstractCommandParser + crux narrate + catchAllCellType stringCell +pauseCommandParser + extends abstractCommandParser + crux pause + +decreaseCommandParser + extends abstractCommandParser + description Decrease a property by 1. + crux decrease + cells keywordCell propertyNameCell +increaseCommandParser + extends abstractCommandParser + description Increase a property by 1. + crux increase + cells keywordCell propertyNameCell + +growCommandParser + extends abstractCommandParser + crux grow +shrinkCommandParser + extends abstractCommandParser + crux shrink +pulseCommandParser + extends abstractCommandParser + crux pulse + +learnCommandParser + crux learn + extends abstractCommandParser + cells keywordCell classNameCell +unlearnCommandParser + crux unlearn + extends abstractCommandParser + cells keywordCell classNameCell \ No newline at end of file diff --git a/grammar/agents.grammar b/grammar/agents.grammar new file mode 100644 index 0000000..e3ba2fd --- /dev/null +++ b/grammar/agents.grammar @@ -0,0 +1,20 @@ +agentDefinitionParser + inScope abstractIgnoreParser abstractEventParser abstractAgentAttributeParser belongsToClassParser + cells keywordCell + catchAllParser errorParser + compiler + stringTemplate + javascript + compile() { + const root = this.root + const name = root.agentKeywordMap[this.firstWord] + const normal = super.compile() + const classIds = this.filter(node => node.parserId === "belongsToClassParser").map(node => node.getLine()) + const props = classIds.map(id => this.root.getNode(id).properties).join("\n\n") + return `class ${name} extends Agent { + icon = "${this.firstWord}" + classes = [${classIds.map(id => `"${id}"`).join(",")}] + ${props} + ${normal} + }` + } diff --git a/grammar/attributes.grammar b/grammar/attributes.grammar new file mode 100644 index 0000000..adb1a53 --- /dev/null +++ b/grammar/attributes.grammar @@ -0,0 +1,73 @@ +abstractAgentAttributeParser + cells keywordCell +stringAttributeParser + extends abstractAgentAttributeParser + pattern ^\w+ .+$ + catchAllCellType stringCell + javascript + compile() { + return `${this.firstWord} = "${this.content}"` + } +angleParser + extends stringAttributeParser + cells keywordCell angleCell + cruxFromId +agentStyleParser + description Provide custom CSS for an agent type. + extends stringAttributeParser + cells keywordCell cssCell + crux style +agentHtmlParser + description Provide custom HTML for each rendered agent. + extends stringAttributeParser + cells keywordCell htmlCell + crux html +abstractBooleanAttributeParser + description A boolean attribute. + extends abstractAgentAttributeParser + javascript + compile() { + return `${this.firstWord} = true` + } +noPaletteParser + extends abstractBooleanAttributeParser + cruxFromId + description Don't show this agent in the palette. +solidTraitParser + description If set other agents won't pass through these. + extends abstractBooleanAttributeParser + crux solid +bouncyTraitParser + description If set other agents will bounce off this after a collision. + extends abstractBooleanAttributeParser + crux bouncy +abstractIntegerAttributeParser + extends abstractAgentAttributeParser + description An integer attribute. + cells keywordCell integerCell + javascript + compile() { + return `${this.firstWord} = ${this.getWord(1)}` + } +customIntegerAttributeParser + pattern ^\w+ \d+$ + extends abstractIntegerAttributeParser +healthParser + extends abstractIntegerAttributeParser + cruxFromId +agentWidthParser + extends abstractIntegerAttributeParser + description Width of the agent. + crux width +agentHeightParser + extends abstractIntegerAttributeParser + description Height of the agent. + crux height +speedParser + extends abstractIntegerAttributeParser + description Movement speed. Default is 1 + crux speed +settingDefinitionParser + description Define a configurable input. + cells keywordCell settingValueCell + pattern ^\w+Setting .+$ \ No newline at end of file diff --git a/grammar/board.grammar b/grammar/board.grammar new file mode 100644 index 0000000..87d9587 --- /dev/null +++ b/grammar/board.grammar @@ -0,0 +1,19 @@ +styleLineParser + catchAllCellType cssCell + catchAllParser styleLineParser +styleParser + description Optional CSS to load in BoardStyleComponent + extends abstractSetupParser + cells keywordCell + cruxFromId + catchAllParser styleLineParser + javascript + compile() { + return "" + } +questionParser + cruxFromId + description What are you trying to figure out? + cells keywordCell + catchAllCellType stringCell + extends abstractSetupParser \ No newline at end of file diff --git a/grammar/cells.grammar b/grammar/cells.grammar new file mode 100644 index 0000000..4861079 --- /dev/null +++ b/grammar/cells.grammar @@ -0,0 +1,49 @@ +anyCell +booleanCell +stringCell + highlightScope string +settingValueCell + highlightScope constant.numeric +cssCell + highlightScope string +javascriptCell + highlightScope string +htmlCell + highlightScope string +emojiCell + highlightScope string +ohayoCell + highlightScope string +blankCell +codeCell + highlightScope comment +commentCell + highlightScope comment +keywordCell + highlightScope keyword +textCell + highlightScope string +integerCell + highlightScope constant.numeric + +classNameCell + highlightScope keyword +conditionalOperatorCell + highlightScope keyword + enum < > = <= >= +positionCell + highlightScope constant.numeric +neighborCountCell + extends integerCell + min 0 + max 8 +integerOrPercentCell + highlightScope constant.numeric +probabilityCell + description A number between 0 and 1 + highlightScope constant.numeric +propertyNameCell + highlightScope keyword +angleCell + enum North South East West NorthWest NorthEast SouthWest SouthEast + highlightScope constant.numeric diff --git a/grammar/classes.grammar b/grammar/classes.grammar new file mode 100644 index 0000000..2edb0fd --- /dev/null +++ b/grammar/classes.grammar @@ -0,0 +1,19 @@ +belongsToClassParser + cells classNameCell + pattern ^.*Class$ + javascript + compile() { + return "" + } +classDefinitionParser + inScope abstractIgnoreParser abstractEventParser abstractAgentAttributeParser + cells classNameCell + pattern ^.*Class$ + catchAllParser errorParser + javascript + compile() { + return "" + } + get properties() { + return this.filter(node => node.doesExtend("abstractAgentAttributeParser")).map(node => node.compile()).join("\n") + } \ No newline at end of file diff --git a/grammar/comments.grammar b/grammar/comments.grammar new file mode 100644 index 0000000..c073f09 --- /dev/null +++ b/grammar/comments.grammar @@ -0,0 +1,11 @@ +commentLineParser + catchAllCellType commentCell +commentParser + extends abstractIgnoreParser + catchAllCellType commentCell + cruxFromId + catchAllParser commentLineParser +commentAliasParser + description Alternate alias for a comment. + crux # + extends commentParser \ No newline at end of file diff --git a/grammar/draw.grammar b/grammar/draw.grammar new file mode 100644 index 0000000..9720498 --- /dev/null +++ b/grammar/draw.grammar @@ -0,0 +1,52 @@ +atTimeParser + cruxFromId + description Run commands at a certain tick. + cells keywordCell integerCell + extends abstractSetupParser + inScope abstractInjectCommandParser +abstractInjectCommandParser + +insertParser + extends abstractInjectCommandParser + cells keywordCell integerOrPercentCell emojiCell + cruxFromId +insertAtParser + extends insertParser + description Insert at X Y + cells keywordCell emojiCell positionCell positionCell + cruxFromId +insertClusterParser + extends insertParser + cruxFromId + catchAllCellType integerCell + +fillParser + description Fill all blank cells with this agent. + extends abstractInjectCommandParser + cells keywordCell emojiCell + cruxFromId + +drawLineParser + catchAllCellType emojiCell +drawParser + extends abstractInjectCommandParser + cells keywordCell + cruxFromId + catchAllParser drawLineParser + +rectangleDrawParser + extends abstractInjectCommandParser + example + rectangle 🙂 width height x y 🙂 + cells keywordCell emojiCell integerCell integerCell + catchAllCellType integerCell + crux rectangle + +pasteLineParser + catchAllCellType anyCell + catchAllParser pasteLineParser +pasteDrawParser + extends abstractInjectCommandParser + cells keywordCell + crux paste + catchAllParser pasteLineParser diff --git a/grammar/events.grammar b/grammar/events.grammar new file mode 100644 index 0000000..18c2b49 --- /dev/null +++ b/grammar/events.grammar @@ -0,0 +1,40 @@ +targetEmojiParser + inScope abstractCommandParser + cells emojiCell +abstractEventParser + cells keywordCell + catchAllCellType probabilityCell + javascript + compile() { + return `` + } +abstractInteractionEventParser + extends abstractEventParser + catchAllParser targetEmojiParser +onHitParser + extends abstractInteractionEventParser + cruxFromId + description Define what happens when this agent collides with other agents. +onDeathParser + extends abstractEventParser + cruxFromId + inScope abstractCommandParser + description Define what happens when this agent runs out of health. +onTickParser + extends abstractEventParser + cruxFromId + inScope abstractCommandParser + description Define what happens each tick. +emojiAndNeighborConditionParser + inScope abstractCommandParser + pattern ^.+ (<|>|=|<=|>=)+ .+$ + cells emojiCell conditionalOperatorCell neighborCountCell +onExtinctParser + cruxFromId + inScope abstractCommandParser + cells keywordCell emojiCell + description Define what happens when a type of agent goes extinct from the board. + javascript + compile() { + return "" + } \ No newline at end of file diff --git a/grammar/experiment.grammar b/grammar/experiment.grammar new file mode 100644 index 0000000..5bb8d5c --- /dev/null +++ b/grammar/experiment.grammar @@ -0,0 +1,28 @@ +experimentParser + cruxFromId + cells keywordCell + inScope abstractIgnoreParser abstractSetupParser abstractInjectCommandParser onTickParser onExtinctParser settingDefinitionParser + catchAllCellType stringCell +abstractSetupNumberParser + cells keywordCell integerCell + extends abstractSetupParser + javascript + compile() { + return "" + } +heightParser + description Height of the grid. Default is based on screen size. + extends abstractSetupNumberParser + crux height +widthParser + description Width of the grid. Default is based on screen size. + extends abstractSetupNumberParser + crux width +seedParser + description If you'd like reproducible runs set a seed for the random number generator. + extends abstractSetupNumberParser + cruxFromId +ticksPerSecondParser + description Time in milliseconds of one step. + extends abstractSetupNumberParser + cruxFromId \ No newline at end of file diff --git a/grammar/movement.grammar b/grammar/movement.grammar new file mode 100644 index 0000000..2e6bc0a --- /dev/null +++ b/grammar/movement.grammar @@ -0,0 +1,23 @@ +moveCommandParser + extends abstractCommandParser + crux move +moveToEmptySpotCommandParser + crux moveToEmptySpot + extends abstractCommandParser + cells keywordCell +turnRandomlyCommandParser + extends abstractCommandParser + crux turnRandomly +jitterCommandParser + extends abstractCommandParser + crux jitter +turnTowardCommandParser + description Turn to the closest agent of a certain type. + extends abstractCommandParser + crux turnToward + cells keywordCell emojiCell +turnFromCommandParser + description Turn away from the closest agent of a certain type. + extends abstractCommandParser + crux turnFrom + cells keywordCell emojiCell diff --git a/grammar/report.grammar b/grammar/report.grammar new file mode 100644 index 0000000..f7cb323 --- /dev/null +++ b/grammar/report.grammar @@ -0,0 +1,13 @@ +ohayoLineParser + description Data visualization code written for Ohayo. + catchAllCellType ohayoCell +reportParser + cruxFromId + description Define a custom report template. + catchAllParser ohayoLineParser + extends abstractSetupParser + cells keywordCell + javascript + compile() { + return "" + } \ No newline at end of file diff --git a/grammar/root.grammar b/grammar/root.grammar new file mode 100644 index 0000000..5b23b41 --- /dev/null +++ b/grammar/root.grammar @@ -0,0 +1,37 @@ +errorParser + baseParser errorParser +abstractSetupParser +abstractIgnoreParser + tags doNotSynthesize + javascript + compile () { + return "" + } +simojiParser + extensions simoji + description A Tree Language that compiles to a TreeComponentFramework app. + root + inScope abstractIgnoreParser abstractSetupParser abstractInjectCommandParser onTickParser onExtinctParser classDefinitionParser experimentParser settingDefinitionParser + catchAllParser agentDefinitionParser + compilesTo javascript + example + 🦋 + onTick .1 + turnRandomly + move + onTick .2 + turnToward 💡 + move + 💡 + + insert 10 🦋 + insert 2 💡 + javascript + get agentTypes() { + return this.filter(node => node.parserId === "agentDefinitionParser") + } +blankLineParser + extends abstractIgnoreParser + description Blank lines compile do nothing. + cells blankCell + pattern ^$ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 4135f6f..0000000 --- a/package-lock.json +++ /dev/null @@ -1,9840 +0,0 @@ -{ - "name": "simoji", - "version": "2.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "simoji", - "version": "2.0.0", - "dependencies": { - "jquery": "^3.6.0", - "jtree": "^74.0.0", - "lodash": "^4.17.21", - "mathjs": "^9.4.4", - "minimist": "^1.2.5" - }, - "bin": { - "simoji": "cli.js" - }, - "devDependencies": { - "tap": "^15.0.9" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz", - "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz", - "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.14.8", - "@babel/helper-compilation-targets": "^7.14.5", - "@babel/helper-module-transforms": "^7.14.8", - "@babel/helpers": "^7.14.8", - "@babel/parser": "^7.14.8", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.8", - "@babel/types": "^7.14.8", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/@babel/core/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@babel/generator": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.8.tgz", - "integrity": "sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.14.8", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz", - "integrity": "sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.14.5", - "@babel/helper-validator-option": "^7.14.5", - "browserslist": "^4.16.6", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", - "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", - "dev": true, - "dependencies": { - "@babel/helper-get-function-arity": "^7.14.5", - "@babel/template": "^7.14.5", - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", - "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", - "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz", - "integrity": "sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", - "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz", - "integrity": "sha512-RyE+NFOjXn5A9YU1dkpeBaduagTlZ0+fccnIcAGbv1KGUlReBj7utF7oEth8IdIBQPcux0DDgW5MFBH2xu9KcA==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.14.5", - "@babel/helper-replace-supers": "^7.14.5", - "@babel/helper-simple-access": "^7.14.8", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/helper-validator-identifier": "^7.14.8", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.8", - "@babel/types": "^7.14.8" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", - "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", - "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", - "dev": true, - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.14.5", - "@babel/helper-optimise-call-expression": "^7.14.5", - "@babel/traverse": "^7.14.5", - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz", - "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.14.8" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", - "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", - "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.8.tgz", - "integrity": "sha512-ZRDmI56pnV+p1dH6d+UN6GINGz7Krps3+270qqI9UJ4wxYThfAIcI5i7j5vXC4FJ3Wap+S9qcebxeYiqn87DZw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.8", - "@babel/types": "^7.14.8" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.8.tgz", - "integrity": "sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", - "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", - "dependencies": { - "regenerator-runtime": "^0.13.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", - "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.8.tgz", - "integrity": "sha512-kexHhzCljJcFNn1KYAQ6A5wxMRzq9ebYpEDV4+WdNyr3i7O44tanbDOR/xjiG2F3sllan+LgwK+7OMk0EmydHg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.14.8", - "@babel/helper-function-name": "^7.14.5", - "@babel/helper-hoist-variables": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/parser": "^7.14.8", - "@babel/types": "^7.14.8", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/@babel/traverse/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@babel/types": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.8.tgz", - "integrity": "sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.8", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "dependencies": { - "default-require-extensions": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" - }, - "node_modules/asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/async-hook-domain": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-2.0.3.tgz", - "integrity": "sha512-MadiLLDEZRZzZwcm0dgS+K99qXZ4H2saAUwUgwzFulbAkXrKi3AX5FvWS3FFTQtLMwrqcGqAJe6o12KrObejQA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bind-obj-methods": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bind-obj-methods/-/bind-obj-methods-3.0.0.tgz", - "integrity": "sha512-nLEaaz3/sEzNSyPWRsN9HNsqwk1AUyECtGj+XwGdIi3xABnEqecvXtIJ0wehQXuuER5uZ/5fTs2usONgYjG+iw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", - "dev": true, - "dependencies": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, - "dependencies": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001245", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001245.tgz", - "integrity": "sha512-768fM9j1PKXpOCKws6eTo3RHmvTUsG9UrpT4WoREFeZgJBTi4/X9g565azS/rVUGtqb8nt7FjLeF5u4kukERnA==", - "dev": true - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "dependencies": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "node_modules/complex.js": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.15.tgz", - "integrity": "sha512-gDBvQU8IG139ZBQTSo2qvDFP+lANMGluM779csXOr6ny1NUtA3wkUnCFjlDNH/moAVfXtvClYt6G0zarFbtz5w==", - "engines": { - "node": "*" - } - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "node_modules/coveralls": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.1.tgz", - "integrity": "sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww==", - "dev": true, - "dependencies": { - "js-yaml": "^3.13.1", - "lcov-parse": "^1.0.0", - "log-driver": "^1.2.7", - "minimist": "^1.2.5", - "request": "^2.88.2" - }, - "bin": { - "coveralls": "bin/coveralls.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" - }, - "node_modules/default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "dev": true, - "dependencies": { - "strip-bom": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/electron-to-chromium": { - "version": "1.3.780", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.780.tgz", - "integrity": "sha512-2KQ9OYm9WMUNpAPA/4aerURl3hwRc9tNlpsiEj3Y8Gf7LVf26NzyLIX2v0hSagQwrS9+cWab+28A2GPKDoVNRA==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-latex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", - "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/events-to-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", - "integrity": "sha1-LUH1Y+H+QA7Uli/hpNXGp1Od9/Y=", - "dev": true - }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true, - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/findit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findit/-/findit-2.0.0.tgz", - "integrity": "sha1-ZQnwEmr0wXhVHPqZOU4DLhOk1W4=", - "dev": true - }, - "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "dependencies": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", - "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==", - "engines": { - "node": "*" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true - }, - "node_modules/fs-exists-cached": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-exists-cached/-/fs-exists-cached-1.0.0.tgz", - "integrity": "sha1-zyVVTKBQ3EmuZla0HeQiWJidy84=", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/function-loop": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/function-loop/-/function-loop-2.0.1.tgz", - "integrity": "sha512-ktIR+O6i/4h+j/ZhZJNdzeI4i9lEPeEK6UPR2EVyTVBqOwcU3Za9xYKLH64ZR9HmcROyRrOkizNyjjtWJzDDkQ==", - "dev": true - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dev": true, - "dependencies": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "engines": { - "node": ">=8" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "dependencies": { - "append-transform": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", - "dev": true, - "dependencies": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^3.3.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-1.4.0.tgz", - "integrity": "sha512-VDcSunT+wcccoG46FtzuBAyQKlzhHjli4q31e1fIHGOsRspqNUFjVzGb+7eIFDlTvqLygxapDHPHS0ouT2o/tw==", - "dev": true, - "dependencies": { - "cliui": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/javascript-natural-sort": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", - "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" - }, - "node_modules/jquery": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", - "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "node_modules/jtree": { - "version": "74.0.0", - "resolved": "https://registry.npmjs.org/jtree/-/jtree-74.0.0.tgz", - "integrity": "sha512-jKyvI60geVzmtfqahfG4kszQp2zg50N7JvtLTxaRamZpqi3J+a+3UduSEZvzBN8k7qSvcR7rA0TBbXSBinCOPw==", - "dependencies": { - "express": "^4.18.2", - "glob": "^9.3.4", - "mkdirp": "^2.1.6", - "prettier": "^2.8.7", - "recursive-readdir-sync": "^1.0.6", - "superagent": "^8.0.9" - }, - "engines": { - "node": ">=16.0" - } - }, - "node_modules/jtree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/jtree/node_modules/glob": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.4.tgz", - "integrity": "sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==", - "dependencies": { - "fs.realpath": "^1.0.0", - "minimatch": "^8.0.2", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jtree/node_modules/minimatch": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.3.tgz", - "integrity": "sha512-tEEvU9TkZgnFDCtpnrEYnPsjT7iUx42aXfs4bzmQ5sMA09/6hZY0jeZcGkXyDagiBOvkUjNo8Viom+Me6+2x7g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jtree/node_modules/minipass": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", - "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/lcov-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", - "integrity": "sha1-6w1GtUER68VhrLTECO+TY73I9+A=", - "dev": true, - "bin": { - "lcov-parse": "bin/cli.js" - } - }, - "node_modules/libtap": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/libtap/-/libtap-1.1.1.tgz", - "integrity": "sha512-Fye8fh1+G7E8qqmjQaY+pXGxy7HM0S6bqCCJFLa16+g2jODBByxbJFDpjbDNF69wfRVyvJ+foLZc1WTIv7dx+g==", - "dev": true, - "dependencies": { - "async-hook-domain": "^2.0.1", - "bind-obj-methods": "^3.0.0", - "diff": "^4.0.2", - "function-loop": "^2.0.1", - "minipass": "^3.1.1", - "own-or": "^1.0.0", - "own-or-env": "^1.0.1", - "signal-exit": "^3.0.2", - "stack-utils": "^2.0.1", - "tap-parser": "^10.0.1", - "tap-yaml": "^1.0.0", - "tcompare": "^5.0.1", - "trivial-deferred": "^1.0.1", - "yapool": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "node_modules/log-driver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", - "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", - "dev": true, - "engines": { - "node": ">=0.8.6" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mathjs": { - "version": "9.4.4", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-9.4.4.tgz", - "integrity": "sha512-5EEJXnWOzLDgMHSFyw623nH+MTBZxquWwXtrzTsingOouJJ6UZG2VNO1lwH31IMt9aMno1axO6TYleIP4YSDaQ==", - "dependencies": { - "@babel/runtime": "^7.14.6", - "complex.js": "^2.0.15", - "decimal.js": "^10.3.1", - "escape-latex": "^1.2.0", - "fraction.js": "^4.1.1", - "javascript-natural-sort": "^0.7.1", - "seedrandom": "^3.0.5", - "tiny-emitter": "^2.1.0", - "typed-function": "^2.0.0" - }, - "bin": { - "mathjs": "bin/cli.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "node_modules/minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mkdirp": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", - "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "dependencies": { - "process-on-spawn": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/node-releases": { - "version": "1.1.73", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", - "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "dev": true, - "dependencies": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "bin": { - "nyc": "bin/nyc.js" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "dev": true, - "bin": { - "opener": "bin/opener-bin.js" - } - }, - "node_modules/own-or": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/own-or/-/own-or-1.0.0.tgz", - "integrity": "sha1-Tod/vtqaLsgAD7wLyuOWRe6L+Nw=", - "dev": true - }, - "node_modules/own-or-env": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-or-env/-/own-or-env-1.0.1.tgz", - "integrity": "sha512-y8qULRbRAlL6x2+M0vIe7jJbJx/kmUTzYonRAa2ayesR2qWLswninkVyeJe4x3IEXhdgoNodzjQRKAoEs6Fmrw==", - "dev": true, - "dependencies": { - "own-or": "^1.0.0" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.6.3.tgz", - "integrity": "sha512-RAmB+n30SlN+HnNx6EbcpoDy9nwdpcGPnEKrJnu6GZoDWBdIjo1UQMVtW2ybtC7LC2oKLcMq8y5g8WnKLiod9g==", - "dependencies": { - "lru-cache": "^7.14.1", - "minipass": "^4.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", - "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prettier": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", - "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, - "dependencies": { - "fromentries": "^1.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/react": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", - "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", - "dev": true, - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/recursive-readdir-sync": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/recursive-readdir-sync/-/recursive-readdir-sync-1.0.6.tgz", - "integrity": "sha1-Hb9tMvPFu4083pemxYjVR6nhPVY=" - }, - "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, - "node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, - "dependencies": { - "es6-error": "^4.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/seedrandom": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", - "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" - }, - "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "dependencies": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/superagent": { - "version": "8.0.9", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz", - "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==", - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.1.2", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" - }, - "engines": { - "node": ">=6.4.0 <13 || >=14" - } - }, - "node_modules/superagent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/superagent/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/superagent/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/superagent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/superagent/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tap": { - "version": "15.0.9", - "resolved": "https://registry.npmjs.org/tap/-/tap-15.0.9.tgz", - "integrity": "sha512-bqY5SxEqYKRd37PIUfKBf9HMs/hklyl/fGXkuStr9rYTIGa0/icpSLsm6IVOmx2qT0/TliPNJ6OvS5kddJYHdg==", - "bundleDependencies": [ - "@types/react", - "glob", - "import-jsx", - "ink", - "minipass", - "rimraf", - "signal-exit", - "tap-parser", - "tap-yaml", - "treport" - ], - "dev": true, - "dependencies": { - "@types/react": "^16.9.23", - "chokidar": "^3.3.0", - "coveralls": "^3.0.11", - "findit": "^2.0.0", - "foreground-child": "^2.0.0", - "fs-exists-cached": "^1.0.0", - "glob": "^7.1.6", - "import-jsx": "^4.0.0", - "ink": "^2.7.1", - "isexe": "^2.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "jackspeak": "^1.4.0", - "libtap": "^1.1.1", - "minipass": "^3.1.1", - "mkdirp": "^1.0.4", - "nyc": "^15.1.0", - "opener": "^1.5.1", - "react": "^16.12.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.0", - "source-map-support": "^0.5.16", - "tap-mocha-reporter": "^5.0.0", - "tap-parser": "^10.0.1", - "tap-yaml": "^1.0.0", - "tcompare": "^5.0.6", - "treport": "^2.0.2", - "which": "^2.0.2" - }, - "bin": { - "tap": "bin/run.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tap-mocha-reporter": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/tap-mocha-reporter/-/tap-mocha-reporter-5.0.1.tgz", - "integrity": "sha512-1knFWOwd4khx/7uSEnUeaP9IPW3w+sqTgJMhrwah6t46nZ8P25atOKAjSvVDsT67lOPu0nfdOqUwoyKn+3E5pA==", - "dev": true, - "dependencies": { - "color-support": "^1.1.0", - "debug": "^4.1.1", - "diff": "^4.0.1", - "escape-string-regexp": "^2.0.0", - "glob": "^7.0.5", - "tap-parser": "^10.0.0", - "tap-yaml": "^1.0.0", - "unicode-length": "^2.0.2" - }, - "bin": { - "tap-mocha-reporter": "index.js" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/tap-mocha-reporter/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/tap-mocha-reporter/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/tap-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-10.1.0.tgz", - "integrity": "sha512-FujQeciDaOiOvaIVGS1Rpb0v4R6XkOjvWCWowlz5oKuhPkEJ8U6pxgqt38xuzYhPt8dWEnfHn2jqpZdJEkW7pA==", - "dev": true, - "dependencies": { - "events-to-array": "^1.0.1", - "minipass": "^3.0.0", - "tap-yaml": "^1.0.0" - }, - "bin": { - "tap-parser": "bin/cmd.js" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/tap-yaml": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tap-yaml/-/tap-yaml-1.0.0.tgz", - "integrity": "sha512-Rxbx4EnrWkYk0/ztcm5u3/VznbyFJpyXO12dDBHKWiDVxy7O2Qw6MRrwO5H6Ww0U5YhRY/4C/VzWmFPhBQc4qQ==", - "dev": true, - "dependencies": { - "yaml": "^1.5.0" - } - }, - "node_modules/tap/node_modules/@babel/code-frame": { - "version": "7.12.13", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.12.13" - } - }, - "node_modules/tap/node_modules/@babel/compat-data": { - "version": "7.14.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/@babel/core": { - "version": "7.14.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.0", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-module-transforms": "^7.14.0", - "@babel/helpers": "^7.14.0", - "@babel/parser": "^7.14.0", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/tap/node_modules/@babel/generator": { - "version": "7.14.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.14.1", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "node_modules/tap/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.12.13", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/tap/node_modules/@babel/helper-compilation-targets": { - "version": "7.13.16", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.13.15", - "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.14.5", - "semver": "^6.3.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/tap/node_modules/@babel/helper-function-name": { - "version": "7.12.13", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "node_modules/tap/node_modules/@babel/helper-get-function-arity": { - "version": "7.12.13", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/tap/node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.13.12", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.13.12" - } - }, - "node_modules/tap/node_modules/@babel/helper-module-imports": { - "version": "7.13.12", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.13.12" - } - }, - "node_modules/tap/node_modules/@babel/helper-module-transforms": { - "version": "7.14.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-simple-access": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.14.0", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0" - } - }, - "node_modules/tap/node_modules/@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/tap/node_modules/@babel/helper-plugin-utils": { - "version": "7.13.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/@babel/helper-replace-supers": { - "version": "7.13.12", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.12" - } - }, - "node_modules/tap/node_modules/@babel/helper-simple-access": { - "version": "7.13.12", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.13.12" - } - }, - "node_modules/tap/node_modules/@babel/helper-split-export-declaration": { - "version": "7.12.13", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/tap/node_modules/@babel/helper-validator-identifier": { - "version": "7.14.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/@babel/helper-validator-option": { - "version": "7.12.17", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/@babel/helpers": { - "version": "7.14.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0" - } - }, - "node_modules/tap/node_modules/@babel/highlight": { - "version": "7.14.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "node_modules/tap/node_modules/@babel/parser": { - "version": "7.14.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/tap/node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.13.8", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.13.8", - "@babel/helper-compilation-targets": "^7.13.8", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/tap/node_modules/@babel/plugin-syntax-jsx": { - "version": "7.12.13", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/tap/node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/tap/node_modules/@babel/plugin-transform-destructuring": { - "version": "7.13.17", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/tap/node_modules/@babel/plugin-transform-parameters": { - "version": "7.13.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/tap/node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.13.12", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-jsx": "^7.12.13", - "@babel/types": "^7.13.12" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/tap/node_modules/@babel/template": { - "version": "7.12.13", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "node_modules/tap/node_modules/@babel/traverse": { - "version": "7.14.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.0", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.14.0", - "@babel/types": "^7.14.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "node_modules/tap/node_modules/@babel/types": { - "version": "7.14.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.0", - "to-fast-properties": "^2.0.0" - } - }, - "node_modules/tap/node_modules/@types/prop-types": { - "version": "15.7.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/@types/react": { - "version": "16.14.6", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/tap/node_modules/@types/scheduler": { - "version": "0.16.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/@types/yoga-layout": { - "version": "1.9.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/ansi-escapes": { - "version": "4.3.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tap/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/ansicolors": { - "version": "0.3.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/arrify": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/astral-regex": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/auto-bind": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tap/node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/tap/node_modules/browserslist": { - "version": "4.16.6", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/tap/node_modules/caller-callsite": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "callsites": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/caller-path": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "caller-callsite": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/callsites": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/caniuse-lite": { - "version": "1.0.30001223", - "dev": true, - "inBundle": true, - "license": "CC-BY-4.0" - }, - "node_modules/tap/node_modules/cardinal": { - "version": "2.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - }, - "bin": { - "cdl": "bin/cdl.js" - } - }, - "node_modules/tap/node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/ci-info": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/cli-cursor": { - "version": "3.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/cli-truncate": { - "version": "2.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tap/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/tap/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/colorette": { - "version": "1.2.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/commondir": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/convert-source-map": { - "version": "1.7.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/tap/node_modules/csstype": { - "version": "3.0.8", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/debug": { - "version": "4.3.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/tap/node_modules/electron-to-chromium": { - "version": "1.3.727", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/tap/node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/escalade": { - "version": "3.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tap/node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/tap/node_modules/esprima": { - "version": "4.0.1", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/events-to-array": { - "version": "1.1.2", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/tap/node_modules/find-cache-dir": { - "version": "3.3.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/tap/node_modules/find-up": { - "version": "4.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/tap/node_modules/gensync": { - "version": "1.0.0-beta.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/tap/node_modules/glob": { - "version": "7.1.7", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/tap/node_modules/globals": { - "version": "11.12.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/import-jsx": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.5.5", - "@babel/plugin-proposal-object-rest-spread": "^7.5.5", - "@babel/plugin-transform-destructuring": "^7.5.0", - "@babel/plugin-transform-react-jsx": "^7.3.0", - "caller-path": "^2.0.0", - "find-cache-dir": "^3.2.0", - "make-dir": "^3.0.2", - "resolve-from": "^3.0.0", - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tap/node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/tap/node_modules/inherits": { - "version": "2.0.4", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/tap/node_modules/ink": { - "version": "2.7.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.2.1", - "arrify": "^2.0.1", - "auto-bind": "^4.0.0", - "chalk": "^3.0.0", - "cli-cursor": "^3.1.0", - "cli-truncate": "^2.1.0", - "is-ci": "^2.0.0", - "lodash.throttle": "^4.1.1", - "log-update": "^3.0.0", - "prop-types": "^15.6.2", - "react-reconciler": "^0.24.0", - "scheduler": "^0.18.0", - "signal-exit": "^3.0.2", - "slice-ansi": "^3.0.0", - "string-length": "^3.1.0", - "widest-line": "^3.1.0", - "wrap-ansi": "^6.2.0", - "yoga-layout-prebuilt": "^1.9.3" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "@types/react": ">=16.8.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/tap/node_modules/ink/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/tap/node_modules/ink/node_modules/chalk": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/ink/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/tap/node_modules/ink/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/ink/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/ink/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/is-ci": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/tap/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/js-tokens": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/jsesc": { - "version": "2.5.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/json5": { - "version": "2.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tap/node_modules/locate-path": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/lodash.throttle": { - "version": "4.1.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/log-update": { - "version": "3.4.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^3.2.0", - "cli-cursor": "^2.1.0", - "wrap-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tap/node_modules/log-update/node_modules/ansi-escapes": { - "version": "3.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/log-update/node_modules/ansi-regex": { - "version": "4.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tap/node_modules/log-update/node_modules/cli-cursor": { - "version": "2.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/log-update/node_modules/emoji-regex": { - "version": "7.0.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/log-update/node_modules/mimic-fn": { - "version": "1.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/log-update/node_modules/onetime": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/log-update/node_modules/restore-cursor": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/log-update/node_modules/string-width": { - "version": "3.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tap/node_modules/log-update/node_modules/strip-ansi": { - "version": "5.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tap/node_modules/log-update/node_modules/wrap-ansi": { - "version": "5.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tap/node_modules/loose-envify": { - "version": "1.4.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/tap/node_modules/make-dir": { - "version": "3.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tap/node_modules/mimic-fn": { - "version": "2.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tap/node_modules/minimatch": { - "version": "3.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tap/node_modules/minimist": { - "version": "1.2.5", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/minipass": { - "version": "3.1.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tap/node_modules/ms": { - "version": "2.1.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/node-releases": { - "version": "1.1.71", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/object-assign": { - "version": "4.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tap/node_modules/once": { - "version": "1.4.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/tap/node_modules/onetime": { - "version": "5.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tap/node_modules/p-limit": { - "version": "2.3.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tap/node_modules/p-locate": { - "version": "4.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/p-try": { - "version": "2.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tap/node_modules/path-exists": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/path-is-absolute": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tap/node_modules/pkg-dir": { - "version": "4.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/prop-types": { - "version": "15.7.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, - "node_modules/tap/node_modules/punycode": { - "version": "2.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tap/node_modules/react-is": { - "version": "16.13.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/react-reconciler": { - "version": "0.24.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.18.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "react": "^16.0.0" - } - }, - "node_modules/tap/node_modules/redeyed": { - "version": "2.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "esprima": "~4.0.0" - } - }, - "node_modules/tap/node_modules/resolve-from": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/restore-cursor": { - "version": "3.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/rimraf": { - "version": "3.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/tap/node_modules/safe-buffer": { - "version": "5.1.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/scheduler": { - "version": "0.18.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "node_modules/tap/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/tap/node_modules/signal-exit": { - "version": "3.0.3", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/tap/node_modules/slice-ansi": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/tap/node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/tap/node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/source-map": { - "version": "0.5.7", - "dev": true, - "inBundle": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tap/node_modules/string-length": { - "version": "3.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "astral-regex": "^1.0.0", - "strip-ansi": "^5.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/string-length/node_modules/ansi-regex": { - "version": "4.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/tap/node_modules/string-length/node_modules/astral-regex": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/string-length/node_modules/strip-ansi": { - "version": "5.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tap/node_modules/string-width": { - "version": "4.2.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/string-width/node_modules/ansi-regex": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/tap-parser": { - "version": "10.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "events-to-array": "^1.0.1", - "minipass": "^3.0.0", - "tap-yaml": "^1.0.0" - }, - "bin": { - "tap-parser": "bin/cmd.js" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/tap/node_modules/tap-yaml": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yaml": "^1.5.0" - } - }, - "node_modules/tap/node_modules/to-fast-properties": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tap/node_modules/treport": { - "version": "2.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "cardinal": "^2.1.1", - "chalk": "^3.0.0", - "import-jsx": "^4.0.0", - "ink": "^2.6.0", - "ms": "^2.1.2", - "string-length": "^3.1.0", - "tap-parser": "^10.0.1", - "unicode-length": "^2.0.2" - }, - "peerDependencies": { - "react": "^16.8.6" - } - }, - "node_modules/tap/node_modules/treport/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/tap/node_modules/treport/node_modules/chalk": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/treport/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/tap/node_modules/treport/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/treport/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/treport/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/type-fest": { - "version": "0.21.3", - "dev": true, - "inBundle": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tap/node_modules/unicode-length": { - "version": "2.0.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.0.0", - "strip-ansi": "^3.0.1" - } - }, - "node_modules/tap/node_modules/unicode-length/node_modules/ansi-regex": { - "version": "2.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tap/node_modules/unicode-length/node_modules/strip-ansi": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tap/node_modules/widest-line": { - "version": "3.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/wrap-ansi": { - "version": "6.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/tap/node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/tap/node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/tap/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tap/node_modules/wrappy": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/tap/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/tap/node_modules/yaml": { - "version": "1.10.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/tap/node_modules/yoga-layout-prebuilt": { - "version": "1.10.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@types/yoga-layout": "1.9.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tcompare": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-5.0.6.tgz", - "integrity": "sha512-OvO7omN/wkdsKzmOqr3sQFfLbghs/2X5mwSkcfgRiXZshfPnTsAs3IRf1RixR/Pff26qG/r9ogcZMpV0YdeGXg==", - "dev": true, - "dependencies": { - "diff": "^4.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/trivial-deferred": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trivial-deferred/-/trivial-deferred-1.0.1.tgz", - "integrity": "sha1-N21NKdlR1jaKb3oK6FwvTV4GWPM=", - "dev": true - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.0.0.tgz", - "integrity": "sha512-Hhy1Iwo/e4AtLZNK10ewVVcP2UEs408DS35ubP825w/YgSBK1KVLwALvvIG4yX75QJrxjCpcWkzkVRB0BwwYlA==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/unicode-length": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unicode-length/-/unicode-length-2.0.2.tgz", - "integrity": "sha512-Ph/j1VbS3/r77nhoY2WU0GWGjVYOHL3xpKp0y/Eq2e5r0mT/6b649vm7KFO6RdAdrZkYLdxphYVgvODxPB+Ebg==", - "dev": true, - "dependencies": { - "punycode": "^2.0.0", - "strip-ansi": "^3.0.1" - } - }, - "node_modules/unicode-length/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unicode-length/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/yapool": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yapool/-/yapool-1.0.0.tgz", - "integrity": "sha1-9pPymjFbUNmp2iZGp6ZkXJaYW2o=", - "dev": true - }, - "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/yargs/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/yargs/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.14.5" - } - }, - "@babel/compat-data": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz", - "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==", - "dev": true - }, - "@babel/core": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz", - "integrity": "sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.14.8", - "@babel/helper-compilation-targets": "^7.14.5", - "@babel/helper-module-transforms": "^7.14.8", - "@babel/helpers": "^7.14.8", - "@babel/parser": "^7.14.8", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.8", - "@babel/types": "^7.14.8", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.8.tgz", - "integrity": "sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg==", - "dev": true, - "requires": { - "@babel/types": "^7.14.8", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz", - "integrity": "sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.14.5", - "@babel/helper-validator-option": "^7.14.5", - "browserslist": "^4.16.6", - "semver": "^6.3.0" - } - }, - "@babel/helper-function-name": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", - "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.14.5", - "@babel/template": "^7.14.5", - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", - "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz", - "integrity": "sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz", - "integrity": "sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-module-imports": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", - "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-module-transforms": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz", - "integrity": "sha512-RyE+NFOjXn5A9YU1dkpeBaduagTlZ0+fccnIcAGbv1KGUlReBj7utF7oEth8IdIBQPcux0DDgW5MFBH2xu9KcA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.14.5", - "@babel/helper-replace-supers": "^7.14.5", - "@babel/helper-simple-access": "^7.14.8", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/helper-validator-identifier": "^7.14.8", - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.8", - "@babel/types": "^7.14.8" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", - "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-replace-supers": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", - "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.14.5", - "@babel/helper-optimise-call-expression": "^7.14.5", - "@babel/traverse": "^7.14.5", - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-simple-access": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz", - "integrity": "sha512-TrFN4RHh9gnWEU+s7JloIho2T76GPwRHhdzOWLqTrMnlas8T9O7ec+oEDNsRXndOmru9ymH9DFrEOxpzPoSbdg==", - "dev": true, - "requires": { - "@babel/types": "^7.14.8" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", - "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", - "dev": true, - "requires": { - "@babel/types": "^7.14.5" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", - "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", - "dev": true - }, - "@babel/helpers": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.8.tgz", - "integrity": "sha512-ZRDmI56pnV+p1dH6d+UN6GINGz7Krps3+270qqI9UJ4wxYThfAIcI5i7j5vXC4FJ3Wap+S9qcebxeYiqn87DZw==", - "dev": true, - "requires": { - "@babel/template": "^7.14.5", - "@babel/traverse": "^7.14.8", - "@babel/types": "^7.14.8" - } - }, - "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.8.tgz", - "integrity": "sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA==", - "dev": true - }, - "@babel/runtime": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", - "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/template": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.14.5.tgz", - "integrity": "sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.14.5", - "@babel/types": "^7.14.5" - } - }, - "@babel/traverse": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.8.tgz", - "integrity": "sha512-kexHhzCljJcFNn1KYAQ6A5wxMRzq9ebYpEDV4+WdNyr3i7O44tanbDOR/xjiG2F3sllan+LgwK+7OMk0EmydHg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.14.8", - "@babel/helper-function-name": "^7.14.5", - "@babel/helper-hoist-variables": "^7.14.5", - "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/parser": "^7.14.8", - "@babel/types": "^7.14.8", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.8.tgz", - "integrity": "sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.8", - "to-fast-properties": "^2.0.0" - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "requires": { - "default-require-extensions": "^3.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "async-hook-domain": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/async-hook-domain/-/async-hook-domain-2.0.3.tgz", - "integrity": "sha512-MadiLLDEZRZzZwcm0dgS+K99qXZ4H2saAUwUgwzFulbAkXrKi3AX5FvWS3FFTQtLMwrqcGqAJe6o12KrObejQA==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bind-obj-methods": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bind-obj-methods/-/bind-obj-methods-3.0.0.tgz", - "integrity": "sha512-nLEaaz3/sEzNSyPWRsN9HNsqwk1AUyECtGj+XwGdIi3xABnEqecvXtIJ0wehQXuuER5uZ/5fTs2usONgYjG+iw==", - "dev": true - }, - "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, - "requires": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001245", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001245.tgz", - "integrity": "sha512-768fM9j1PKXpOCKws6eTo3RHmvTUsG9UrpT4WoREFeZgJBTi4/X9g565azS/rVUGtqb8nt7FjLeF5u4kukERnA==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - } - } - }, - "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true - }, - "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "complex.js": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.15.tgz", - "integrity": "sha512-gDBvQU8IG139ZBQTSo2qvDFP+lANMGluM779csXOr6ny1NUtA3wkUnCFjlDNH/moAVfXtvClYt6G0zarFbtz5w==" - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { - "safe-buffer": "5.2.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } - }, - "content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" - }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "coveralls": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.1.tgz", - "integrity": "sha512-+dxnG2NHncSD1NrqbSM3dn/lE57O6Qf/koe9+I7c+wzkqRmEvcp0kgJdxKInzYzkICKkFMZsX3Vct3++tsF9ww==", - "dev": true, - "requires": { - "js-yaml": "^3.13.1", - "lcov-parse": "^1.0.0", - "log-driver": "^1.2.7", - "minimist": "^1.2.5", - "request": "^2.88.2" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" - }, - "default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "dev": true, - "requires": { - "strip-bom": "^4.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "electron-to-chromium": { - "version": "1.3.780", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.780.tgz", - "integrity": "sha512-2KQ9OYm9WMUNpAPA/4aerURl3hwRc9tNlpsiEj3Y8Gf7LVf26NzyLIX2v0hSagQwrS9+cWab+28A2GPKDoVNRA==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "escape-latex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", - "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" - }, - "events-to-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/events-to-array/-/events-to-array-1.1.2.tgz", - "integrity": "sha1-LUH1Y+H+QA7Uli/hpNXGp1Od9/Y=", - "dev": true - }, - "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - } - }, - "find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "findit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findit/-/findit-2.0.0.tgz", - "integrity": "sha1-ZQnwEmr0wXhVHPqZOU4DLhOk1W4=", - "dev": true - }, - "foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "requires": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fraction.js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", - "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" - }, - "fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true - }, - "fs-exists-cached": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-exists-cached/-/fs-exists-cached-1.0.0.tgz", - "integrity": "sha1-zyVVTKBQ3EmuZla0HeQiWJidy84=", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "function-loop": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/function-loop/-/function-loop-2.0.1.tgz", - "integrity": "sha512-ktIR+O6i/4h+j/ZhZJNdzeI4i9lEPeEK6UPR2EVyTVBqOwcU3Za9xYKLH64ZR9HmcROyRrOkizNyjjtWJzDDkQ==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dev": true, - "requires": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - } - }, - "hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==" - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "requires": { - "append-transform": "^2.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - } - }, - "istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", - "dev": true, - "requires": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^3.3.3" - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jackspeak": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-1.4.0.tgz", - "integrity": "sha512-VDcSunT+wcccoG46FtzuBAyQKlzhHjli4q31e1fIHGOsRspqNUFjVzGb+7eIFDlTvqLygxapDHPHS0ouT2o/tw==", - "dev": true, - "requires": { - "cliui": "^4.1.0" - } - }, - "javascript-natural-sort": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", - "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" - }, - "jquery": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", - "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jtree": { - "version": "74.0.0", - "resolved": "https://registry.npmjs.org/jtree/-/jtree-74.0.0.tgz", - "integrity": "sha512-jKyvI60geVzmtfqahfG4kszQp2zg50N7JvtLTxaRamZpqi3J+a+3UduSEZvzBN8k7qSvcR7rA0TBbXSBinCOPw==", - "requires": { - "express": "^4.18.2", - "glob": "^9.3.4", - "mkdirp": "^2.1.6", - "prettier": "^2.8.7", - "recursive-readdir-sync": "^1.0.6", - "superagent": "^8.0.9" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "requires": { - "balanced-match": "^1.0.0" - } - }, - "glob": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.4.tgz", - "integrity": "sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==", - "requires": { - "fs.realpath": "^1.0.0", - "minimatch": "^8.0.2", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" - } - }, - "minimatch": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.3.tgz", - "integrity": "sha512-tEEvU9TkZgnFDCtpnrEYnPsjT7iUx42aXfs4bzmQ5sMA09/6hZY0jeZcGkXyDagiBOvkUjNo8Viom+Me6+2x7g==", - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "minipass": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", - "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==" - } - } - }, - "lcov-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", - "integrity": "sha1-6w1GtUER68VhrLTECO+TY73I9+A=", - "dev": true - }, - "libtap": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/libtap/-/libtap-1.1.1.tgz", - "integrity": "sha512-Fye8fh1+G7E8qqmjQaY+pXGxy7HM0S6bqCCJFLa16+g2jODBByxbJFDpjbDNF69wfRVyvJ+foLZc1WTIv7dx+g==", - "dev": true, - "requires": { - "async-hook-domain": "^2.0.1", - "bind-obj-methods": "^3.0.0", - "diff": "^4.0.2", - "function-loop": "^2.0.1", - "minipass": "^3.1.1", - "own-or": "^1.0.0", - "own-or-env": "^1.0.1", - "signal-exit": "^3.0.2", - "stack-utils": "^2.0.1", - "tap-parser": "^10.0.1", - "tap-yaml": "^1.0.0", - "tcompare": "^5.0.1", - "trivial-deferred": "^1.0.1", - "yapool": "^1.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "log-driver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", - "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==" - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "mathjs": { - "version": "9.4.4", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-9.4.4.tgz", - "integrity": "sha512-5EEJXnWOzLDgMHSFyw623nH+MTBZxquWwXtrzTsingOouJJ6UZG2VNO1lwH31IMt9aMno1axO6TYleIP4YSDaQ==", - "requires": { - "@babel/runtime": "^7.14.6", - "complex.js": "^2.0.15", - "decimal.js": "^10.3.1", - "escape-latex": "^1.2.0", - "fraction.js": "^4.1.1", - "javascript-natural-sort": "^0.7.1", - "seedrandom": "^3.0.5", - "tiny-emitter": "^2.1.0", - "typed-function": "^2.0.0" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "mkdirp": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", - "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, - "node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "requires": { - "process-on-spawn": "^1.0.0" - } - }, - "node-releases": { - "version": "1.1.73", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", - "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "dev": true, - "requires": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "dev": true - }, - "own-or": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/own-or/-/own-or-1.0.0.tgz", - "integrity": "sha1-Tod/vtqaLsgAD7wLyuOWRe6L+Nw=", - "dev": true - }, - "own-or-env": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-or-env/-/own-or-env-1.0.1.tgz", - "integrity": "sha512-y8qULRbRAlL6x2+M0vIe7jJbJx/kmUTzYonRAa2ayesR2qWLswninkVyeJe4x3IEXhdgoNodzjQRKAoEs6Fmrw==", - "dev": true, - "requires": { - "own-or": "^1.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-scurry": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.6.3.tgz", - "integrity": "sha512-RAmB+n30SlN+HnNx6EbcpoDy9nwdpcGPnEKrJnu6GZoDWBdIjo1UQMVtW2ybtC7LC2oKLcMq8y5g8WnKLiod9g==", - "requires": { - "lru-cache": "^7.14.1", - "minipass": "^4.0.2" - }, - "dependencies": { - "minipass": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", - "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==" - } - } - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "prettier": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", - "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==" - }, - "process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, - "requires": { - "fromentries": "^1.2.0" - } - }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "react": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", - "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", - "dev": true, - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "recursive-readdir-sync": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/recursive-readdir-sync/-/recursive-readdir-sync-1.0.6.tgz", - "integrity": "sha1-Hb9tMvPFu4083pemxYjVR6nhPVY=" - }, - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" - }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, - "requires": { - "es6-error": "^4.0.1" - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "seedrandom": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", - "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "requires": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "superagent": { - "version": "8.0.9", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz", - "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==", - "requires": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.1.2", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "tap": { - "version": "15.0.9", - "resolved": "https://registry.npmjs.org/tap/-/tap-15.0.9.tgz", - "integrity": "sha512-bqY5SxEqYKRd37PIUfKBf9HMs/hklyl/fGXkuStr9rYTIGa0/icpSLsm6IVOmx2qT0/TliPNJ6OvS5kddJYHdg==", - "dev": true, - "requires": { - "@types/react": "^16.9.23", - "chokidar": "^3.3.0", - "coveralls": "^3.0.11", - "findit": "^2.0.0", - "foreground-child": "^2.0.0", - "fs-exists-cached": "^1.0.0", - "glob": "^7.1.6", - "import-jsx": "^4.0.0", - "ink": "^2.7.1", - "isexe": "^2.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "jackspeak": "^1.4.0", - "libtap": "^1.1.1", - "minipass": "^3.1.1", - "mkdirp": "^1.0.4", - "nyc": "^15.1.0", - "opener": "^1.5.1", - "react": "^16.12.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.0", - "source-map-support": "^0.5.16", - "tap-mocha-reporter": "^5.0.0", - "tap-parser": "^10.0.1", - "tap-yaml": "^1.0.0", - "tcompare": "^5.0.6", - "treport": "^2.0.2", - "which": "^2.0.2" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.13", - "bundled": true, - "dev": true, - "requires": { - "@babel/highlight": "^7.12.13" - } - }, - "@babel/compat-data": { - "version": "7.14.0", - "bundled": true, - "dev": true - }, - "@babel/core": { - "version": "7.14.0", - "bundled": true, - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.0", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-module-transforms": "^7.14.0", - "@babel/helpers": "^7.14.0", - "@babel/parser": "^7.14.0", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - } - }, - "@babel/generator": { - "version": "7.14.1", - "bundled": true, - "dev": true, - "requires": { - "@babel/types": "^7.14.1", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.12.13", - "bundled": true, - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.13.16", - "bundled": true, - "dev": true, - "requires": { - "@babel/compat-data": "^7.13.15", - "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.14.5", - "semver": "^6.3.0" - } - }, - "@babel/helper-function-name": { - "version": "7.12.13", - "bundled": true, - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.12.13", - "bundled": true, - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.13.12", - "bundled": true, - "dev": true, - "requires": { - "@babel/types": "^7.13.12" - } - }, - "@babel/helper-module-imports": { - "version": "7.13.12", - "bundled": true, - "dev": true, - "requires": { - "@babel/types": "^7.13.12" - } - }, - "@babel/helper-module-transforms": { - "version": "7.14.0", - "bundled": true, - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-simple-access": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.14.0", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "bundled": true, - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.13.0", - "bundled": true, - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.13.12", - "bundled": true, - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.12" - } - }, - "@babel/helper-simple-access": { - "version": "7.13.12", - "bundled": true, - "dev": true, - "requires": { - "@babel/types": "^7.13.12" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "bundled": true, - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.14.0", - "bundled": true, - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.12.17", - "bundled": true, - "dev": true - }, - "@babel/helpers": { - "version": "7.14.0", - "bundled": true, - "dev": true, - "requires": { - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0" - } - }, - "@babel/highlight": { - "version": "7.14.0", - "bundled": true, - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.14.1", - "bundled": true, - "dev": true - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.13.8", - "bundled": true, - "dev": true, - "requires": { - "@babel/compat-data": "^7.13.8", - "@babel/helper-compilation-targets": "^7.13.8", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.13.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.12.13", - "bundled": true, - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "bundled": true, - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.13.17", - "bundled": true, - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.13.0", - "bundled": true, - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.13.0" - } - }, - "@babel/plugin-transform-react-jsx": { - "version": "7.13.12", - "bundled": true, - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-jsx": "^7.12.13", - "@babel/types": "^7.13.12" - } - }, - "@babel/template": { - "version": "7.12.13", - "bundled": true, - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/traverse": { - "version": "7.14.0", - "bundled": true, - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.0", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.14.0", - "@babel/types": "^7.14.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.14.1", - "bundled": true, - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.0", - "to-fast-properties": "^2.0.0" - } - }, - "@types/prop-types": { - "version": "15.7.3", - "bundled": true, - "dev": true - }, - "@types/react": { - "version": "16.14.6", - "bundled": true, - "dev": true, - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "@types/scheduler": { - "version": "0.16.1", - "bundled": true, - "dev": true - }, - "@types/yoga-layout": { - "version": "1.9.2", - "bundled": true, - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "bundled": true, - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-styles": { - "version": "3.2.1", - "bundled": true, - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "ansicolors": { - "version": "0.3.2", - "bundled": true, - "dev": true - }, - "arrify": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "astral-regex": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "auto-bind": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "browserslist": { - "version": "4.16.6", - "bundled": true, - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - } - }, - "caller-callsite": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "callsites": "^2.0.0" - } - }, - "caller-path": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "caller-callsite": "^2.0.0" - } - }, - "callsites": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001223", - "bundled": true, - "dev": true - }, - "cardinal": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "requires": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - } - }, - "chalk": { - "version": "2.4.2", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "ci-info": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-truncate": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - } - }, - "color-convert": { - "version": "1.9.3", - "bundled": true, - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "bundled": true, - "dev": true - }, - "colorette": { - "version": "1.2.2", - "bundled": true, - "dev": true - }, - "commondir": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "convert-source-map": { - "version": "1.7.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "csstype": { - "version": "3.0.8", - "bundled": true, - "dev": true - }, - "debug": { - "version": "4.3.1", - "bundled": true, - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "electron-to-chromium": { - "version": "1.3.727", - "bundled": true, - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "bundled": true, - "dev": true - }, - "escalade": { - "version": "3.1.1", - "bundled": true, - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "bundled": true, - "dev": true - }, - "esprima": { - "version": "4.0.1", - "bundled": true, - "dev": true - }, - "events-to-array": { - "version": "1.1.2", - "bundled": true, - "dev": true - }, - "find-cache-dir": { - "version": "3.3.1", - "bundled": true, - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, - "find-up": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "bundled": true, - "dev": true - }, - "glob": { - "version": "7.1.7", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "globals": { - "version": "11.12.0", - "bundled": true, - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "import-jsx": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "@babel/core": "^7.5.5", - "@babel/plugin-proposal-object-rest-spread": "^7.5.5", - "@babel/plugin-transform-destructuring": "^7.5.0", - "@babel/plugin-transform-react-jsx": "^7.3.0", - "caller-path": "^2.0.0", - "find-cache-dir": "^3.2.0", - "make-dir": "^3.0.2", - "resolve-from": "^3.0.0", - "rimraf": "^3.0.0" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "bundled": true, - "dev": true - }, - "ink": { - "version": "2.7.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "arrify": "^2.0.1", - "auto-bind": "^4.0.0", - "chalk": "^3.0.0", - "cli-cursor": "^3.1.0", - "cli-truncate": "^2.1.0", - "is-ci": "^2.0.0", - "lodash.throttle": "^4.1.1", - "log-update": "^3.0.0", - "prop-types": "^15.6.2", - "react-reconciler": "^0.24.0", - "scheduler": "^0.18.0", - "signal-exit": "^3.0.2", - "slice-ansi": "^3.0.0", - "string-length": "^3.1.0", - "widest-line": "^3.1.0", - "wrap-ansi": "^6.2.0", - "yoga-layout-prebuilt": "^1.9.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "bundled": true, - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "bundled": true, - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "bundled": true, - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "is-ci": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "jsesc": { - "version": "2.5.2", - "bundled": true, - "dev": true - }, - "json5": { - "version": "2.2.0", - "bundled": true, - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "locate-path": { - "version": "5.0.0", - "bundled": true, - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash.throttle": { - "version": "4.1.1", - "bundled": true, - "dev": true - }, - "log-update": { - "version": "3.4.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-escapes": "^3.2.0", - "cli-cursor": "^2.1.0", - "wrap-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-escapes": { - "version": "3.2.0", - "bundled": true, - "dev": true - }, - "ansi-regex": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "emoji-regex": { - "version": "7.0.3", - "bundled": true, - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "mimic-fn": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "onetime": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "string-width": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - } - } - }, - "loose-envify": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "make-dir": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "bundled": true, - "dev": true - }, - "minipass": { - "version": "3.1.3", - "bundled": true, - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "bundled": true, - "dev": true - }, - "node-releases": { - "version": "1.1.71", - "bundled": true, - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "bundled": true, - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "bundled": true, - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "bundled": true, - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "prop-types": { - "version": "15.7.2", - "bundled": true, - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, - "punycode": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "react-is": { - "version": "16.13.1", - "bundled": true, - "dev": true - }, - "react-reconciler": { - "version": "0.24.0", - "bundled": true, - "dev": true, - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.18.0" - } - }, - "redeyed": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "requires": { - "esprima": "~4.0.0" - } - }, - "resolve-from": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "rimraf": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true - }, - "scheduler": { - "version": "0.18.0", - "bundled": true, - "dev": true, - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "semver": { - "version": "6.3.0", - "bundled": true, - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "bundled": true, - "dev": true - }, - "slice-ansi": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "bundled": true, - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "bundled": true, - "dev": true - } - } - }, - "source-map": { - "version": "0.5.7", - "bundled": true, - "dev": true - }, - "string-length": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "astral-regex": "^1.0.0", - "strip-ansi": "^5.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "string-width": { - "version": "4.2.2", - "bundled": true, - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "supports-color": { - "version": "5.5.0", - "bundled": true, - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "tap-parser": { - "version": "10.1.0", - "bundled": true, - "dev": true, - "requires": { - "events-to-array": "^1.0.1", - "minipass": "^3.0.0", - "tap-yaml": "^1.0.0" - } - }, - "tap-yaml": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "yaml": "^1.5.0" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "treport": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "cardinal": "^2.1.1", - "chalk": "^3.0.0", - "import-jsx": "^4.0.0", - "ink": "^2.6.0", - "ms": "^2.1.2", - "string-length": "^3.1.0", - "tap-parser": "^10.0.1", - "unicode-length": "^2.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "bundled": true, - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "bundled": true, - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "bundled": true, - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "type-fest": { - "version": "0.21.3", - "bundled": true, - "dev": true - }, - "unicode-length": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "punycode": "^2.0.0", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "widest-line": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^4.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "bundled": true, - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "bundled": true, - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "yallist": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "yaml": { - "version": "1.10.2", - "bundled": true, - "dev": true - }, - "yoga-layout-prebuilt": { - "version": "1.10.0", - "bundled": true, - "dev": true, - "requires": { - "@types/yoga-layout": "1.9.2" - } - } - } - }, - "tap-mocha-reporter": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/tap-mocha-reporter/-/tap-mocha-reporter-5.0.1.tgz", - "integrity": "sha512-1knFWOwd4khx/7uSEnUeaP9IPW3w+sqTgJMhrwah6t46nZ8P25atOKAjSvVDsT67lOPu0nfdOqUwoyKn+3E5pA==", - "dev": true, - "requires": { - "color-support": "^1.1.0", - "debug": "^4.1.1", - "diff": "^4.0.1", - "escape-string-regexp": "^2.0.0", - "glob": "^7.0.5", - "tap-parser": "^10.0.0", - "tap-yaml": "^1.0.0", - "unicode-length": "^2.0.2" - }, - "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "tap-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-10.1.0.tgz", - "integrity": "sha512-FujQeciDaOiOvaIVGS1Rpb0v4R6XkOjvWCWowlz5oKuhPkEJ8U6pxgqt38xuzYhPt8dWEnfHn2jqpZdJEkW7pA==", - "dev": true, - "requires": { - "events-to-array": "^1.0.1", - "minipass": "^3.0.0", - "tap-yaml": "^1.0.0" - } - }, - "tap-yaml": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tap-yaml/-/tap-yaml-1.0.0.tgz", - "integrity": "sha512-Rxbx4EnrWkYk0/ztcm5u3/VznbyFJpyXO12dDBHKWiDVxy7O2Qw6MRrwO5H6Ww0U5YhRY/4C/VzWmFPhBQc4qQ==", - "dev": true, - "requires": { - "yaml": "^1.5.0" - } - }, - "tcompare": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/tcompare/-/tcompare-5.0.6.tgz", - "integrity": "sha512-OvO7omN/wkdsKzmOqr3sQFfLbghs/2X5mwSkcfgRiXZshfPnTsAs3IRf1RixR/Pff26qG/r9ogcZMpV0YdeGXg==", - "dev": true, - "requires": { - "diff": "^4.0.2" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "trivial-deferred": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trivial-deferred/-/trivial-deferred-1.0.1.tgz", - "integrity": "sha1-N21NKdlR1jaKb3oK6FwvTV4GWPM=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typed-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.0.0.tgz", - "integrity": "sha512-Hhy1Iwo/e4AtLZNK10ewVVcP2UEs408DS35ubP825w/YgSBK1KVLwALvvIG4yX75QJrxjCpcWkzkVRB0BwwYlA==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "unicode-length": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unicode-length/-/unicode-length-2.0.2.tgz", - "integrity": "sha512-Ph/j1VbS3/r77nhoY2WU0GWGjVYOHL3xpKp0y/Eq2e5r0mT/6b649vm7KFO6RdAdrZkYLdxphYVgvODxPB+Ebg==", - "dev": true, - "requires": { - "punycode": "^2.0.0", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - }, - "yapool": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yapool/-/yapool-1.0.0.tgz", - "integrity": "sha1-9pPymjFbUNmp2iZGp6ZkXJaYW2o=", - "dev": true - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } -} diff --git a/package.json b/package.json index b59e123..5e797c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "simoji", - "version": "2.0.0", + "version": "3.0.0", "description": "Design your own simulations using Emojis", "main": "Simoji.js", "directories": { @@ -26,10 +26,8 @@ "jtree": "^74.0.0", "lodash": "^4.17.21", "mathjs": "^9.4.4", - "minimist": "^1.2.5" - }, - "devDependencies": { - "tap": "^15.0.9" + "minimist": "^1.2.5", + "tap": "^16.3.4" }, "scripts": { "build": "./build.js", diff --git a/releaseNotes.scroll b/releaseNotes.scroll index ba810a3..cc48c0a 100644 --- a/releaseNotes.scroll +++ b/releaseNotes.scroll @@ -4,6 +4,11 @@ startColumns * Here's a list of the notable changes in Simoji. +# 3.0.0 4-12-2023 +* A big rewrite to a new grid system. +- 🎉 new collision engine and coordinate system. agents can change size and move in any direction (unit vectors over cardinality). +- ⚠️ breaking: "neighbors" concept is gone. game of life demos removed. use old version to use those. + # 2.0.0 07-30-2021 - 🎉 fill command - 🎉 kickIt behaves a little smarter diff --git a/server.js b/server.js index 657e641..d3b68bf 100755 --- a/server.js +++ b/server.js @@ -1,11 +1,12 @@ #!/usr/bin/env node +const path = require("path") const express = require("express") +const { Disk } = require("jtree/products/Disk.node.js") const { readFile } = require("fs") const { TypeScriptRewriter } = require("jtree/products/TypeScriptRewriter.js") -const stamp = require("jtree/products/stamp.nodejs.js") -const { TreeNode } = require("jtree/products/TreeNode.js") const { getExamples } = require("./examples") +const grammarParser = require("jtree/products/grammar.nodejs.js") class Server { start(port = 80) { @@ -29,13 +30,30 @@ class Server { res.send(getExamples()) }) + app.get("/examples", (req, res) => { + res.send(getExamples()) + }) + + app.get("/dist/simoji.grammar", (req, res) => { + res.send(this.grammar) + }) + app.use(express.static(__dirname + "/")) app.listen(port, () => { console.log(`Running Simoji Dev Server. cmd+dblclick: http://localhost:${port}/dev.html`) }) } + + get grammar() { + const asOneFile = Disk.getFiles(path.join(__dirname, "grammar")) + .filter(file => file.endsWith(".grammar")) + .map(filePath => Disk.read(filePath)) + .join("\n\n") + .trim() + return new grammarParser(asOneFile)._sortNodesByInScopeOrder()._sortWithParentParsersUpTop().asString + } } -const server = new Server() -server.start() +if (!module.parent) new Server().start() +module.exports = { Server } diff --git a/simoji.css b/simoji.css index 767bd02..7c30dd0 100644 --- a/simoji.css +++ b/simoji.css @@ -207,6 +207,14 @@ body { .Agent { position: absolute; + z-index: 1; + user-select: none; + cursor: pointer; +} + +.Agent:hover { + background-color: rgba(0, 0, 0, 0.2); + border-radius: 3px; } .ExamplesComponent { diff --git a/testAll.js b/testAll.js index 545669b..227451a 100644 --- a/testAll.js +++ b/testAll.js @@ -1,8 +1,17 @@ -const runTree = testTree => { - const tap = require("tap") - Object.keys(testTree).forEach(key => { - testTree[key](tap.equal) - }) -} +#! /usr/bin/env node + +const { TestRacer } = require("jtree/products/TestRacer.js") -runTree({ ...require("./yodash.test.node.js").testTree, ...require("./components/SimojiApp.test.node.js").testTree }) +const testAll = async () => { + const fileTree = {} + let folders = `./yodash.test.node.js +./components/SimojiApp.test.node.js +./components/Board.test.node.js +./components/CollisionDetector.test.node.js` + .split("\n") + .forEach(file => (fileTree[file] = require(file).testTree)) + const runner = new TestRacer(fileTree) + await runner.execute() + runner.finish() +} +testAll() diff --git a/yodash.js b/yodash.js index 131e8b0..7c7c477 100644 --- a/yodash.js +++ b/yodash.js @@ -1,26 +1,7 @@ const yodash = {} -const lodash = require("lodash") const math = require("mathjs") const { Utils } = require("jtree/products/Utils.js") -const { Directions, ParserTypes } = require("./components/Types.js") - -yodash.parseInts = (arr, start) => arr.map((item, index) => (index >= start ? parseInt(item) : item)) - -yodash.getRandomAngle = randomNumberGenerator => { - const r1 = randomNumberGenerator() - const r2 = randomNumberGenerator() - if (r1 > 0.5) return r2 > 0.5 ? Directions.North : Directions.South - return r2 > 0.5 ? Directions.West : Directions.East -} - -yodash.flipAngle = angle => { - let newAngle = "" - if (angle.includes(Directions.North)) newAngle += Directions.South - else if (angle.includes(Directions.South)) newAngle += Directions.North - if (angle.includes(Directions.East)) newAngle += Directions.West - else if (angle.includes(Directions.West)) newAngle += Directions.East - return newAngle -} +const { ParserTypes } = require("./components/Types.js") yodash.compare = (left, operator, right) => { if (operator === "=") return left == right @@ -32,14 +13,16 @@ yodash.compare = (left, operator, right) => { return false } +// Todo: why do we do this? Very confusing. Caught me by surprise. +// Is it because sometimes the class name is not valid JS? yodash.compileAgentClassDeclarationsAndMap = program => { - const clone = program.clone() - clone.filter(node => node.parserId !== ParserTypes.agentDefinitionParser).forEach(node => node.destroy()) - clone.agentKeywordMap = {} - clone.agentTypes.forEach((node, index) => (clone.agentKeywordMap[node.firstWord] = `simAgent${index}`)) - const compiled = clone.compile() - const agentMap = Object.keys(clone.agentKeywordMap) - .map(key => `"${key}":${clone.agentKeywordMap[key]}`) + const agentKeywordMap = {} + program.agentKeywordMap = agentKeywordMap // confusing + const agentDefs = program.filter(node => node.parserId === ParserTypes.agentDefinitionParser) + agentDefs.forEach((node, index) => (agentKeywordMap[node.firstWord] = `simAgent${index}`)) + const compiled = agentDefs.map(node => node.compile()).join("\n") + const agentMap = Object.keys(agentKeywordMap) + .map(key => `"${key}":${agentKeywordMap[key]}`) .join(",") return `${compiled} const map = {${agentMap}}; @@ -68,189 +51,39 @@ yodash.patchExperimentAndReplaceSymbols = (program, experiment) => { return withVarsReplaced } -yodash.getBestAngle = (targets, position) => { +yodash.getClosest = (targets, subject) => { let closest = Infinity let target - targets.forEach(candidate => { - const pos = candidate.position - const distance = math.distance([pos.down, pos.right], [position.down, position.right]) + targets.forEach(agent => { + if (agent === subject) return + const distance = math.distance([agent.y, agent.x], [subject.y, subject.x]) if (distance < closest) { closest = distance - target = candidate + target = agent } }) - const heading = target.position - return yodash.angle(position.down, position.right, heading.down, heading.right) + return target } -yodash.angle = (cx, cy, ex, ey) => { - const dy = ey - cy - const dx = ex - cx - let theta = Math.atan2(dy, dx) // range (-PI, PI] - theta *= 180 / Math.PI // rads to degs, range (-180, 180] - //if (theta < 0) theta = 360 + theta; // range [0, 360) - let angle = "" - - if (Math.abs(theta) > 90) angle += Directions.North - else angle += Directions.South - if (theta < 0) angle += Directions.West - else angle += Directions.East - return angle -} - -yodash.getRandomLocation = (rows, cols, randomNumberGenerator) => { - const maxRight = cols - const maxBottom = rows - const right = Math.round(randomNumberGenerator() * maxRight) - const down = Math.round(randomNumberGenerator() * maxBottom) - return { right, down } -} - -yodash.getRandomLocationHash = (rows, cols, occupiedSpots, randomNumberGenerator) => { - const { right, down } = yodash.getRandomLocation(rows, cols, randomNumberGenerator) - const hash = yodash.makePositionHash({ right, down }) - if (occupiedSpots && occupiedSpots.has(hash)) - return yodash.getRandomLocationHash(rows, cols, occupiedSpots, randomNumberGenerator) - return hash -} - -yodash.fill = (rows, cols, occupiedSpots, emoji) => { - const board = [] - while (rows >= 0) { - let col = cols - while (col >= 0) { - const hash = yodash.makePositionHash({ right: col, down: rows }) - col-- - if (occupiedSpots.has(hash)) continue - board.push(`${emoji} ${hash}`) - } - rows-- +yodash.unitVector = (objA, objB) => { + // calculate direction vector (delta) + const delta = { + x: objB.x - objA.x, + y: objB.y - objA.y } - return board.join("\n") -} -yodash.positionsAdjacentTo = position => { - let { right, down } = position - const positions = [] - down-- - positions.push({ down, right }) - right-- - positions.push({ down, right }) - right++ - right++ - positions.push({ down, right }) - down++ - positions.push({ down, right }) - right-- - right-- - positions.push({ down, right }) - down++ - positions.push({ down, right }) - right++ - positions.push({ down, right }) - right++ - positions.push({ down, right }) - return positions -} - -yodash.makePositionHash = position => `${position.down + "⬇️ " + position.right + "➡️"}` + // calculate magnitude of delta (distance between two points) + const magDelta = Math.sqrt(delta.x * delta.x + delta.y * delta.y) -yodash.makeRectangle = (character = "🧱", width = 20, height = 20, startRight = 0, startDown = 0) => { - if (width < 1 || height < 1) { - return "" - } - const cells = [] - let row = 0 - while (row < height) { - let col = 0 - while (col < width) { - const isPerimeter = row === 0 || row === height - 1 || col === 0 || col === width - 1 - if (isPerimeter) - cells.push( - `${character} ${yodash.makePositionHash({ - down: startDown + row, - right: startRight + col - })}` - ) - col++ - } - row++ - } - return cells.join("\n") -} - -yodash.parsePosition = words => { + // calculate unit vector (normalize direction vector by dividing by magnitude) return { - down: parseInt(words.find(word => word.includes("⬇️")).slice(0, -1)), - right: parseInt(words.find(word => word.includes("➡️")).slice(0, -1)) - } -} - -yodash.draw = str => { - const lines = str.split("\n") - const output = [] - for (let index = 0; index < lines.length; index++) { - const words = lines[index].split(" ") - for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { - const word = words[wordIndex] - if (word !== "") output.push(`${word} ${yodash.makePositionHash({ down: index, right: wordIndex })}`) - } - } - return output.join("\n") -} - -yodash.updateOccupiedSpots = (board, occupiedSpots) => { - new TreeNode(board).forEach(line => { - occupiedSpots.add(yodash.makePositionHash(yodash.parsePosition(line.words))) - }) -} - -yodash.getAllAvailableSpots = (rows, cols, occupiedSpots, rowStart = 0, colStart = 0) => { - const availablePositions = [] - let down = rows - while (down >= rowStart) { - let right = cols - while (right >= colStart) { - const hash = yodash.makePositionHash({ right, down }) - if (!occupiedSpots.has(hash)) availablePositions.push({ right, down, hash }) - right-- - } - down-- + x: delta.x / magDelta, + y: delta.y / magDelta } - return availablePositions } yodash.parsePercent = str => parseFloat(str.replace("%", "")) / 100 -yodash.insertClusteredRandomAgents = ( - randomNumberGenerator, - amount, - char, - rows, - cols, - occupiedSpots, - originRow, - originColumn -) => { - const availableSpots = yodash.getAllAvailableSpots(rows, cols, occupiedSpots) - const spots = yodash.sampleFrom(availableSpots, amount * 10, randomNumberGenerator) - const origin = originColumn - ? { down: parseInt(originRow), right: parseInt(originColumn) } - : yodash.getRandomLocation(rows, cols, randomNumberGenerator) - const sortedByDistance = lodash.sortBy(spots, spot => - math.distance([origin.down, origin.right], [spot.down, spot.right]) - ) - - return sortedByDistance - .slice(0, amount) - .map(spot => { - const { hash } = spot - occupiedSpots.add(hash) - return `${char} ${hash}` - }) - .join("\n") -} - yodash.getRandomNumberGenerator = seed => () => { const semiRand = Math.sin(seed++) * 10000 return semiRand - Math.floor(semiRand) diff --git a/yodash.test.node.js b/yodash.test.node.js index 5be76b6..1b3f795 100755 --- a/yodash.test.node.js +++ b/yodash.test.node.js @@ -1,32 +1,9 @@ #!/usr/bin/env node const { yodash } = require("./yodash.js") +const { TestRacer } = require("jtree/products/TestRacer.js") const testTree = {} -testTree.getRandomAngle = areEqual => { - areEqual(yodash.getRandomAngle(Math.random).match(/(East|West|North|South)/).length, 2) -} - -testTree.makeRectangle = areEqual => { - const expected = `😀 0⬇️ 0➡️ -😀 0⬇️ 1➡️ -😀 1⬇️ 0➡️ -😀 1⬇️ 1➡️` - - areEqual(yodash.makeRectangle("😀", 2, 2), expected) - areEqual( - yodash.makeRectangle("🚪", 2, 1, 1, 1), - `🚪 1⬇️ 1➡️ -🚪 1⬇️ 2➡️` - ) -} - +if (!module.parent) TestRacer.testSingleFile(__filename, testTree) module.exports = { testTree } -const runTree = testTree => { - const tap = require("tap") - Object.keys(testTree).forEach(key => { - testTree[key](tap.equal) - }) -} -if (module && !module.parent) runTree(testTree) ------------------------------------------------------------
commit 6f41cdda06f3bab5f221bf8a60b9601b3c54ba56
Author: Breck Yunits <breck7@gmail.com> Date: Sat Apr 8 20:43:46 2023 -1000 Add readme and release notes diff --git a/readme.html b/readme.html new file mode 100644 index 0000000..7192464 --- /dev/null +++ b/readme.html @@ -0,0 +1,377 @@ +<meta charset="utf-8"></meta> +<title>Simoji - Write simulations with Emojis</title> +<script>/* This HTML was generated by 📜 Scroll v68.0.0. http://scroll.pub */</script> +<style>@media print {.doNotPrint {display: none !important;}}</style> +<link rel="canonical" href="https://simoji.pub/readme.html"></link> +<meta charset="iso-8859-1"></meta> +<meta name="viewport" content="width=device-width,initial-scale=1"></meta> +<meta name="description" content="Design quick simulations with Emojis"></meta> +<meta name="generator" content="Scroll v68.0.0"></meta> +<meta property="og:title" content="Simoji - Write simulations with Emojis"></meta> +<meta property="og:description" content="Design quick simulations with Emojis"></meta> +<meta property="og:image" content=""></meta> +<meta name="twitter:card" content="summary_large_image"></meta> + +<style>html,body,div,span,h1,h2,h3,h4,p,ol,ul,li,table,figure { + margin: 0; + padding: 0; + border: 0; + vertical-align: baseline; + border-spacing: 0; +} +li { + list-style-position: inside; + margin-top: .4em; + line-height: 1.2em; +} +a { + text-decoration-color: transparent; +} +a:hover { + text-decoration-color: initial; +} +sup,sub { + vertical-align: baseline; + position: relative; + top: -0.6em; +} +sub { + top: 0.6em; +} +html { + padding: 4px; + background-color: rgb(244,244,244); + font-family: Exchange,Georgia,serif; + color: #000; + font-size: 14px; + hyphens: auto; +} +p { + margin-top: 0.4em; + line-height: 1.4em; +} +.scrollQuote { + break-inside: avoid; + display: block; + margin: .5em 0; + padding: .5em; + background: rgba(204,204,204,.5); + white-space: pre-line; + border-left: .5em solid rgba(204,204,204,.8); +} +code { + font-size: 90%; + background-color: rgba(204,204,204,.5); + padding: 2px 4px; + border-radius: 4px; +} +.scrollColumns { + column-count: auto; + column-fill: balance; + column-width: 35ch; + column-gap: 20px; + padding-left: 20px; + padding-right: 20px; + margin: auto; +} +.scrollSnippetContainer { + padding: 1ch 0; + break-inside: avoid; + text-align: justify; +} +.scrollTitle { + text-align: center; + margin-bottom: .25em; +} +.scrollTitle a { + color: #000; +} +.scrollDateline { + font-style: italic; + font-size: 80%; +} +.scrollSection { + break-inside: avoid; +} +.scrollSection h3 { + margin-top: 1em; + text-align: center; +} +.scrollSection h4 { + margin-top: 1em; + text-align: center; +} +h4.scrollQuestion { + text-align: left; +} +.scrollNoteLink { + opacity: .4; +} +.scrollHoverNote { + text-decoration: underline dashed 1px rgba(0,0,0,.1); + cursor: default; +} +.scrollCodeBlock { + overflow: auto; + font-size: 80%; + hyphens: none; + white-space: pre; + border-left: .5em solid rgba(204,204,204,.8); + break-inside: avoid; + display: block; + margin: .5em 0; + padding: .5em; + border-radius: 0; + position: relative; +} +.scrollCodeBlock:hover .scrollCopyButton { + opacity: .5; +} +.scrollCodeBlock:hover .scrollCopyButton:hover { + opacity: .8; +} +.scrollCodeBlock:hover .scrollCopyButton:active { + opacity: 1; +} +.scrollCopyButton { + position: absolute; + top: 2px; + right: 2px; + font-size: 14px; + cursor: pointer; + opacity: 0; +} +.scrollCopyButton::after { + content: "[ ]"; +} +.scrollCopiedButton::after { + content: "[✓]"; +} +.scrollTable { + table-layout: fixed; + margin: .5em 0; + overflow: hidden; + font-size: 80%; + width: 100%; + hyphens: none; + border: 1px solid rgba(224,224,224,.8); +} +.scrollTable td,.scrollTable th { + padding: 3px; + overflow: hidden; +} +.scrollTable th { + border-bottom: 2px solid rgba(0,0,0,.6); + text-align: left; +} +.scrollTable tr:nth-child(even) { + background: rgba(224,224,224,.6); +} +.scrollByLine { + font-size: 12px; + font-style: italic; + margin: 4px 0; + text-align: center; +} +.scrollViewSource { + text-align: center; + font-size: 80%; + margin: 0; + margin-top: 0.4em; + line-height: 1.4em; + margin-bottom: 1em; +} +.scrollViewSource a { + color: #000; +} +.scrollContinueReadingLink { + display: block; + text-align: center; +} +.scrollCaptionedFigure { + display: block; + text-align: center; +} +.scrollCaptionedFigure img { + max-width: 98%; + height: auto; +} +.scrollCaptionedFigure figcaption { + font-style: italic; +} +.scrollDashboard { + width: 100%; + font-size: 30px; + text-align: center; + font-weight: bold; + break-inside: avoid; + margin-top: 8px; + margin-bottom: 8px; +} +.scrollDashboard td { + width: 33.3%; + border: 1px solid #e8e8e8; +} +.scrollDashboard span { + font-size: 20p;; + display: block; +} +.scrollChat span { + font-family: Verdana; + margin-top: 5px; + padding: 5px 20px; + border-radius: 15px; + display: inline-block; +} +.scrollChatLeft { + text-align: left; +} +.scrollChatLeft span { + background: rgba(204,204,204, .5); +} +.scrollChatRight { + text-align: right; +} +.scrollChatRight span { + color: white; + background: rgb(0,132,255); +} +.scrollYouTubeHolder { + position: relative; + width: 100%; + height: 0; + padding-bottom: 56.25%; +} +.scrollYouTubeEmbed { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.gazetteHeader svg { + width: 30px; + height: 30px; + fill: rgba(204,204,204,.8); +} +.gazetteHeader svg:hover { + fill: #333; +} +.gazetteHeader a { + color: rgba(204,204,204,.8); + position: absolute; + font-size: 30px; + line-height: 27px; + text-decoration: none; +} +.gazetteHeader a:hover { + color: #333; +} +.gazetteHeader .gazetteTopLeftBar { + text-align: left; + left: 25px; +} +.gazetteHeader .gazetteTopRightBar { + text-align: right; + right: 25px; +} +.gazetteHeader a.gazettePrevPageLink { + left: 3px; +} +.gazetteHeader a.gazetteNextPageLink { + right: 3px; +} +.gazetteFooter { + margin-top: 8px; + padding-top: 8px; + text-align: center; +} +.gazetteFooter svg { + width: 30px; + height: 30px; + fill: rgba(204,204,204, .5); + padding: 0 7px; +} +.gazetteFooter svg:hover { + fill: #333; +} +.gazetteScrollLink { + display: block; + font-family: Verdana; + font-weight: 100; + margin: .5em; + color: rgba(204,204,204,.5); +} +</style> +<div class="gazetteHeader doNotPrint"> + <a class="gazettePrevPageLink" href="releaseNotes.html"><</a> + <a class="gazetteTopLeftBar" href="index.html"><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12.7166 3.79541C12.2835 3.49716 11.7165 3.49716 11.2834 3.79541L4.14336 8.7121C3.81027 8.94146 3.60747 9.31108 3.59247 9.70797C3.54064 11.0799 3.4857 13.4824 3.63658 15.1877C3.7504 16.4742 4.05336 18.1747 4.29944 19.4256C4.41371 20.0066 4.91937 20.4284 5.52037 20.4284H8.84433C8.98594 20.4284 9.10074 20.3111 9.10074 20.1665V15.9754C9.10074 14.9627 9.90433 14.1417 10.8956 14.1417H13.4091C14.4004 14.1417 15.204 14.9627 15.204 15.9754V20.1665C15.204 20.3111 15.3188 20.4284 15.4604 20.4284H18.4796C19.0806 20.4284 19.5863 20.0066 19.7006 19.4256C19.9466 18.1747 20.2496 16.4742 20.3634 15.1877C20.5143 13.4824 20.4594 11.0799 20.4075 9.70797C20.3925 9.31108 20.1897 8.94146 19.8566 8.7121L12.7166 3.79541ZM10.4235 2.49217C11.3764 1.83602 12.6236 1.83602 13.5765 2.49217L20.7165 7.40886C21.4457 7.91098 21.9104 8.73651 21.9448 9.64736C21.9966 11.0178 22.0564 13.5119 21.8956 15.3292C21.7738 16.7067 21.4561 18.4786 21.2089 19.7353C20.9461 21.0711 19.7924 22.0001 18.4796 22.0001H15.4604C14.4691 22.0001 13.6655 21.1791 13.6655 20.1665V15.9754C13.6655 15.8307 13.5507 15.7134 13.4091 15.7134H10.8956C10.754 15.7134 10.6392 15.8307 10.6392 15.9754V20.1665C10.6392 21.1791 9.83561 22.0001 8.84433 22.0001H5.52037C4.20761 22.0001 3.05389 21.0711 2.79113 19.7353C2.54392 18.4786 2.22624 16.7067 2.10437 15.3292C1.94358 13.5119 2.00338 11.0178 2.05515 9.64736C2.08957 8.73652 2.55427 7.91098 3.28346 7.40886L10.4235 2.49217Z"/></svg></a> + <a class="gazetteTopRightBar" href="https://github.com/breck7/simoji"><svg xmlns="http://www.w3.org/2000/svg" width="92pt" height="92pt" viewBox="0 0 92 92"><path d="M90.156 41.965 50.036 1.848a5.913 5.913 0 0 0-8.368 0l-8.332 8.332 10.566 10.566a7.03 7.03 0 0 1 7.23 1.684 7.043 7.043 0 0 1 1.673 7.277l10.183 10.184a7.026 7.026 0 0 1 7.278 1.672 7.04 7.04 0 0 1 0 9.957 7.045 7.045 0 0 1-9.961 0 7.038 7.038 0 0 1-1.532-7.66l-9.5-9.497V59.36a7.04 7.04 0 0 1 1.86 11.29 7.04 7.04 0 0 1-9.957 0 7.04 7.04 0 0 1 0-9.958 7.034 7.034 0 0 1 2.308-1.539V33.926a7.001 7.001 0 0 1-2.308-1.535 7.049 7.049 0 0 1-1.516-7.7L29.242 14.273 1.734 41.777a5.918 5.918 0 0 0 0 8.371L41.855 90.27a5.92 5.92 0 0 0 8.368 0l39.933-39.934a5.925 5.925 0 0 0 0-8.371"/></g></svg></a> + <a class="gazetteNextPageLink" href="cheatSheet.html">></a> +</div> + +<div class="scrollSection"><h1 class="scrollTitle"><a href="readme.html">Simoji - Write simulations with Emojis</a></h1> +</div> +<div class="scrollSection"><h4 class="scrollParagraph">A work in progress.</h4> +</div> +<div class="scrollColumns" style="column-width:35ch;column-count:2;max-width:90ch;"> +<div class="scrollSection"><h3 class="scrollParagraph">Documentation</h3> +<p class="scrollParagraph"><a href="./cheatSheet.html">QuickStart</a></p> +</div> +<div class="scrollSection"><h3 class="scrollParagraph">Related Work</h3> +<ul style="text-indent:0px;"><li ><a href="https://www2.econ.iastate.edu/tesfatsi/ABMSoftwareReview.AbarEtAl2017.pdf">Agent Based Modelling and Simulation tools: A review of the state-of-art software</a></li> +<li ><a href="https://en.wikipedia.org/wiki/Comparison_of_agent-based_modeling_software">Comparison of agent-based modeling software</a></li> +<li ><a href="https://www.netlogoweb.org/launch#http://www.netlogoweb.org/assets/modelslib/Sample%20Models/Biology/Flocking%20Vee%20Formations.nlogo">NetLogo</a> - 2D/3D, Web/Desktop, Open</li> +<li ><a href="https://gama-platform.github.io">GAMA</a></li> +<li ><a href="https://gama-platform.github.io/wiki/Introduction">GAML Language</a> - Desktop, OpenSource</li> +<li ><a href="https://ncase.me/sim/">EmojiSimulator</a> - After shipping I learned Nicky had a similar idea 5+ years before me and executed very well!</li> +<li ><a href="https://insightmaker.com">InsightMaker</a></li> +<li ><a href="https://www.anylogic.com">AnyLogic</a> - 2D/3D</li> +<li ><a href="https://en.wikipedia.org/wiki/SARL_language">SARL</a></li> +<li ><a href="https://www.framsticks.com/">Framsticks</a> - 3D, Desktop</li> +<li ><a href="https://hash.ai">Hash.ai</a></li> +<li ><a href="https://en.wikipedia.org/wiki/Soar_(cognitive_architecture)">SOAR</a></li> +<li ><a href="https://en.wikipedia.org/wiki/Java_Agent_Development_Framework">JADE</a></li> +<li ><a href="https://cs.gmu.edu/~eclab/projects/mason/">MASON</a></li> +<li ><a href="https://repast.github.io">REPAST</a></li> +<li ><a href="https://demonstrations.wolfram.com/SugarscapeAgentBasedModeling/">Simulations in Mathematica</a></li> +<li ><a href="https://swarm.org/wiki/Swarm_main_page">Swarm</a></li> +<li ><a href="https://www.altreva.com">Altreva</a></li> +<li ><a href="https://sandspiel.club">sandspiel</a></li> +<li ><a href="https://github.com/The-Powder-Toy/The-Powder-Toy">The-Powder-Toy</a></li></ul> +</div> +<div class="scrollSection"><h4 class="scrollParagraph">Development</h4> +<div class="scrollSection"><h4 class="scrollQuestion">What is the dev loop like?</h4> +</div></div> +<ol style="text-indent:0px;"><li >Start the dev server with <code>node server.js</code>.</li> +<li >Open <code>localhost/dev.html</code></li> +<li >Edit files</li> +<li >Refresh</li> +<li >Run tests with <code>npm test</code></li> +<li >Build distribution with <code>./build.js</code></li></ol> +<div class="scrollSection"><h4 class="scrollParagraph">Roadmap</h4> +<ul style="text-indent:0px;"><li >Run sims with 8x as many agents</li> +<li >Run 8x more sims at once</li> +<li >Write 8x more kinds of sims with the same number of words</li> +<li >Write models that are 8x less wrong</li> +<li >Generate 8x better distributables</li></ul> +</div> +<div class="scrollSection"><h3 class="scrollParagraph">❤️ Public Domain ❤️ </h3> +</div> +</div> +<div class="scrollKeyboardNav" style="display:none;"><a href="releaseNotes.html">releaseNotes.html</a> · readme.html · <a href="cheatSheet.html">cheatSheet.html</a><script>document.addEventListener('keydown', function(event) { + if (document.activeElement !== document.body) return + const getLinks = () => document.getElementsByClassName("scrollKeyboardNav")[0].getElementsByTagName("a") + if (event.key === "ArrowLeft") + getLinks()[0].click() + else if (event.key === "ArrowRight") + getLinks()[1].click() + });</script></div> +</div><p class="scrollViewSource doNotPrint"> + <a href="readme.scroll">View source</a> +</p> +<div class="gazetteFooter doNotPrint"> + <a href="mailto:breck7+simoji@gmail.com"><svg viewBox="3 5 24 20" width="24" height="20" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(1, 0, 0, 1, 0, -289.0625)"><path style="opacity:1;stroke:none;stroke-width:0.49999997;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M 5 5 C 4.2955948 5 3.6803238 5.3628126 3.3242188 5.9101562 L 14.292969 16.878906 C 14.696939 17.282876 15.303061 17.282876 15.707031 16.878906 L 26.675781 5.9101562 C 26.319676 5.3628126 25.704405 5 25 5 L 5 5 z M 3 8.4140625 L 3 23 C 3 24.108 3.892 25 5 25 L 25 25 C 26.108 25 27 24.108 27 23 L 27 8.4140625 L 17.121094 18.292969 C 15.958108 19.455959 14.041892 19.455959 12.878906 18.292969 L 3 8.4140625 z " transform="translate(0,289.0625)" id="rect4592"/></g></svg></a> + <a href="https://github.com/breck7/simoji"><svg xmlns="http://www.w3.org/2000/svg" width="92pt" height="92pt" viewBox="0 0 92 92"><path d="M90.156 41.965 50.036 1.848a5.913 5.913 0 0 0-8.368 0l-8.332 8.332 10.566 10.566a7.03 7.03 0 0 1 7.23 1.684 7.043 7.043 0 0 1 1.673 7.277l10.183 10.184a7.026 7.026 0 0 1 7.278 1.672 7.04 7.04 0 0 1 0 9.957 7.045 7.045 0 0 1-9.961 0 7.038 7.038 0 0 1-1.532-7.66l-9.5-9.497V59.36a7.04 7.04 0 0 1 1.86 11.29 7.04 7.04 0 0 1-9.957 0 7.04 7.04 0 0 1 0-9.958 7.034 7.034 0 0 1 2.308-1.539V33.926a7.001 7.001 0 0 1-2.308-1.535 7.049 7.049 0 0 1-1.516-7.7L29.242 14.273 1.734 41.777a5.918 5.918 0 0 0 0 8.371L41.855 90.27a5.92 5.92 0 0 0 8.368 0l39.933-39.934a5.925 5.925 0 0 0 0-8.371"/></g></svg></a> + <a href="https://scroll.pub" class="gazetteScrollLink">Built with Scroll v68.0.0</a> +</div> \ No newline at end of file diff --git a/releaseNotes.html b/releaseNotes.html new file mode 100644 index 0000000..3c4164e --- /dev/null +++ b/releaseNotes.html @@ -0,0 +1,339 @@ +<meta charset="utf-8"></meta> +<title>Simoji Release Notes</title> +<script>/* This HTML was generated by 📜 Scroll v68.0.0. http://scroll.pub */</script> +<style>@media print {.doNotPrint {display: none !important;}}</style> +<link rel="canonical" href="https://simoji.pub/releaseNotes.html"></link> +<meta charset="iso-8859-1"></meta> +<meta name="viewport" content="width=device-width,initial-scale=1"></meta> +<meta name="description" content="Design quick simulations with Emojis"></meta> +<meta name="generator" content="Scroll v68.0.0"></meta> +<meta property="og:title" content="Simoji Release Notes"></meta> +<meta property="og:description" content="Design quick simulations with Emojis"></meta> +<meta property="og:image" content=""></meta> +<meta name="twitter:card" content="summary_large_image"></meta> + +<style>html,body,div,span,h1,h2,h3,h4,p,ol,ul,li,table,figure { + margin: 0; + padding: 0; + border: 0; + vertical-align: baseline; + border-spacing: 0; +} +li { + list-style-position: inside; + margin-top: .4em; + line-height: 1.2em; +} +a { + text-decoration-color: transparent; +} +a:hover { + text-decoration-color: initial; +} +sup,sub { + vertical-align: baseline; + position: relative; + top: -0.6em; +} +sub { + top: 0.6em; +} +html { + padding: 4px; + background-color: rgb(244,244,244); + font-family: Exchange,Georgia,serif; + color: #000; + font-size: 14px; + hyphens: auto; +} +p { + margin-top: 0.4em; + line-height: 1.4em; +} +.scrollQuote { + break-inside: avoid; + display: block; + margin: .5em 0; + padding: .5em; + background: rgba(204,204,204,.5); + white-space: pre-line; + border-left: .5em solid rgba(204,204,204,.8); +} +code { + font-size: 90%; + background-color: rgba(204,204,204,.5); + padding: 2px 4px; + border-radius: 4px; +} +.scrollColumns { + column-count: auto; + column-fill: balance; + column-width: 35ch; + column-gap: 20px; + padding-left: 20px; + padding-right: 20px; + margin: auto; +} +.scrollSnippetContainer { + padding: 1ch 0; + break-inside: avoid; + text-align: justify; +} +.scrollTitle { + text-align: center; + margin-bottom: .25em; +} +.scrollTitle a { + color: #000; +} +.scrollDateline { + font-style: italic; + font-size: 80%; +} +.scrollSection { + break-inside: avoid; +} +.scrollSection h3 { + margin-top: 1em; + text-align: center; +} +.scrollSection h4 { + margin-top: 1em; + text-align: center; +} +h4.scrollQuestion { + text-align: left; +} +.scrollNoteLink { + opacity: .4; +} +.scrollHoverNote { + text-decoration: underline dashed 1px rgba(0,0,0,.1); + cursor: default; +} +.scrollCodeBlock { + overflow: auto; + font-size: 80%; + hyphens: none; + white-space: pre; + border-left: .5em solid rgba(204,204,204,.8); + break-inside: avoid; + display: block; + margin: .5em 0; + padding: .5em; + border-radius: 0; + position: relative; +} +.scrollCodeBlock:hover .scrollCopyButton { + opacity: .5; +} +.scrollCodeBlock:hover .scrollCopyButton:hover { + opacity: .8; +} +.scrollCodeBlock:hover .scrollCopyButton:active { + opacity: 1; +} +.scrollCopyButton { + position: absolute; + top: 2px; + right: 2px; + font-size: 14px; + cursor: pointer; + opacity: 0; +} +.scrollCopyButton::after { + content: "[ ]"; +} +.scrollCopiedButton::after { + content: "[✓]"; +} +.scrollTable { + table-layout: fixed; + margin: .5em 0; + overflow: hidden; + font-size: 80%; + width: 100%; + hyphens: none; + border: 1px solid rgba(224,224,224,.8); +} +.scrollTable td,.scrollTable th { + padding: 3px; + overflow: hidden; +} +.scrollTable th { + border-bottom: 2px solid rgba(0,0,0,.6); + text-align: left; +} +.scrollTable tr:nth-child(even) { + background: rgba(224,224,224,.6); +} +.scrollByLine { + font-size: 12px; + font-style: italic; + margin: 4px 0; + text-align: center; +} +.scrollViewSource { + text-align: center; + font-size: 80%; + margin: 0; + margin-top: 0.4em; + line-height: 1.4em; + margin-bottom: 1em; +} +.scrollViewSource a { + color: #000; +} +.scrollContinueReadingLink { + display: block; + text-align: center; +} +.scrollCaptionedFigure { + display: block; + text-align: center; +} +.scrollCaptionedFigure img { + max-width: 98%; + height: auto; +} +.scrollCaptionedFigure figcaption { + font-style: italic; +} +.scrollDashboard { + width: 100%; + font-size: 30px; + text-align: center; + font-weight: bold; + break-inside: avoid; + margin-top: 8px; + margin-bottom: 8px; +} +.scrollDashboard td { + width: 33.3%; + border: 1px solid #e8e8e8; +} +.scrollDashboard span { + font-size: 20p;; + display: block; +} +.scrollChat span { + font-family: Verdana; + margin-top: 5px; + padding: 5px 20px; + border-radius: 15px; + display: inline-block; +} +.scrollChatLeft { + text-align: left; +} +.scrollChatLeft span { + background: rgba(204,204,204, .5); +} +.scrollChatRight { + text-align: right; +} +.scrollChatRight span { + color: white; + background: rgb(0,132,255); +} +.scrollYouTubeHolder { + position: relative; + width: 100%; + height: 0; + padding-bottom: 56.25%; +} +.scrollYouTubeEmbed { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.gazetteHeader svg { + width: 30px; + height: 30px; + fill: rgba(204,204,204,.8); +} +.gazetteHeader svg:hover { + fill: #333; +} +.gazetteHeader a { + color: rgba(204,204,204,.8); + position: absolute; + font-size: 30px; + line-height: 27px; + text-decoration: none; +} +.gazetteHeader a:hover { + color: #333; +} +.gazetteHeader .gazetteTopLeftBar { + text-align: left; + left: 25px; +} +.gazetteHeader .gazetteTopRightBar { + text-align: right; + right: 25px; +} +.gazetteHeader a.gazettePrevPageLink { + left: 3px; +} +.gazetteHeader a.gazetteNextPageLink { + right: 3px; +} +.gazetteFooter { + margin-top: 8px; + padding-top: 8px; + text-align: center; +} +.gazetteFooter svg { + width: 30px; + height: 30px; + fill: rgba(204,204,204, .5); + padding: 0 7px; +} +.gazetteFooter svg:hover { + fill: #333; +} +.gazetteScrollLink { + display: block; + font-family: Verdana; + font-weight: 100; + margin: .5em; + color: rgba(204,204,204,.5); +} +</style> +<div class="gazetteHeader doNotPrint"> + <a class="gazettePrevPageLink" href="cheatSheet.html"><</a> + <a class="gazetteTopLeftBar" href="index.html"><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12.7166 3.79541C12.2835 3.49716 11.7165 3.49716 11.2834 3.79541L4.14336 8.7121C3.81027 8.94146 3.60747 9.31108 3.59247 9.70797C3.54064 11.0799 3.4857 13.4824 3.63658 15.1877C3.7504 16.4742 4.05336 18.1747 4.29944 19.4256C4.41371 20.0066 4.91937 20.4284 5.52037 20.4284H8.84433C8.98594 20.4284 9.10074 20.3111 9.10074 20.1665V15.9754C9.10074 14.9627 9.90433 14.1417 10.8956 14.1417H13.4091C14.4004 14.1417 15.204 14.9627 15.204 15.9754V20.1665C15.204 20.3111 15.3188 20.4284 15.4604 20.4284H18.4796C19.0806 20.4284 19.5863 20.0066 19.7006 19.4256C19.9466 18.1747 20.2496 16.4742 20.3634 15.1877C20.5143 13.4824 20.4594 11.0799 20.4075 9.70797C20.3925 9.31108 20.1897 8.94146 19.8566 8.7121L12.7166 3.79541ZM10.4235 2.49217C11.3764 1.83602 12.6236 1.83602 13.5765 2.49217L20.7165 7.40886C21.4457 7.91098 21.9104 8.73651 21.9448 9.64736C21.9966 11.0178 22.0564 13.5119 21.8956 15.3292C21.7738 16.7067 21.4561 18.4786 21.2089 19.7353C20.9461 21.0711 19.7924 22.0001 18.4796 22.0001H15.4604C14.4691 22.0001 13.6655 21.1791 13.6655 20.1665V15.9754C13.6655 15.8307 13.5507 15.7134 13.4091 15.7134H10.8956C10.754 15.7134 10.6392 15.8307 10.6392 15.9754V20.1665C10.6392 21.1791 9.83561 22.0001 8.84433 22.0001H5.52037C4.20761 22.0001 3.05389 21.0711 2.79113 19.7353C2.54392 18.4786 2.22624 16.7067 2.10437 15.3292C1.94358 13.5119 2.00338 11.0178 2.05515 9.64736C2.08957 8.73652 2.55427 7.91098 3.28346 7.40886L10.4235 2.49217Z"/></svg></a> + <a class="gazetteTopRightBar" href="https://github.com/breck7/simoji"><svg xmlns="http://www.w3.org/2000/svg" width="92pt" height="92pt" viewBox="0 0 92 92"><path d="M90.156 41.965 50.036 1.848a5.913 5.913 0 0 0-8.368 0l-8.332 8.332 10.566 10.566a7.03 7.03 0 0 1 7.23 1.684 7.043 7.043 0 0 1 1.673 7.277l10.183 10.184a7.026 7.026 0 0 1 7.278 1.672 7.04 7.04 0 0 1 0 9.957 7.045 7.045 0 0 1-9.961 0 7.038 7.038 0 0 1-1.532-7.66l-9.5-9.497V59.36a7.04 7.04 0 0 1 1.86 11.29 7.04 7.04 0 0 1-9.957 0 7.04 7.04 0 0 1 0-9.958 7.034 7.034 0 0 1 2.308-1.539V33.926a7.001 7.001 0 0 1-2.308-1.535 7.049 7.049 0 0 1-1.516-7.7L29.242 14.273 1.734 41.777a5.918 5.918 0 0 0 0 8.371L41.855 90.27a5.92 5.92 0 0 0 8.368 0l39.933-39.934a5.925 5.925 0 0 0 0-8.371"/></g></svg></a> + <a class="gazetteNextPageLink" href="readme.html">></a> +</div> + +<div class="scrollSection"><h1 class="scrollTitle"><a href="releaseNotes.html">Simoji Release Notes</a></h1> +</div><div class="scrollColumns" style="column-width:35ch;column-count:10;max-width:530ch;"> +<p class="scrollParagraph">Here's a list of the notable changes in Simoji.</p> +<div class="scrollSection"><h3 class="scrollParagraph">2.0.0 07-30-2021</h3> +<ul style="text-indent:0px;"><li >🎉 fill command</li> +<li >🎉 kickIt behaves a little smarter</li> +<li >🧹 kickIt now uses a tick command stack internally</li> +<li >⚠️ speed is gone. you now need to explcitly move agents in their onTick.</li> +<li >⚠️ mass, diameter, force removed</li></ul> +</div> +</div> +<div class="scrollKeyboardNav" style="display:none;"><a href="cheatSheet.html">cheatSheet.html</a> · releaseNotes.html · <a href="readme.html">readme.html</a><script>document.addEventListener('keydown', function(event) { + if (document.activeElement !== document.body) return + const getLinks = () => document.getElementsByClassName("scrollKeyboardNav")[0].getElementsByTagName("a") + if (event.key === "ArrowLeft") + getLinks()[0].click() + else if (event.key === "ArrowRight") + getLinks()[1].click() + });</script></div> +</div><p class="scrollViewSource doNotPrint"> + <a href="releaseNotes.scroll">View source</a> +</p> +<div class="gazetteFooter doNotPrint"> + <a href="mailto:breck7+simoji@gmail.com"><svg viewBox="3 5 24 20" width="24" height="20" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(1, 0, 0, 1, 0, -289.0625)"><path style="opacity:1;stroke:none;stroke-width:0.49999997;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M 5 5 C 4.2955948 5 3.6803238 5.3628126 3.3242188 5.9101562 L 14.292969 16.878906 C 14.696939 17.282876 15.303061 17.282876 15.707031 16.878906 L 26.675781 5.9101562 C 26.319676 5.3628126 25.704405 5 25 5 L 5 5 z M 3 8.4140625 L 3 23 C 3 24.108 3.892 25 5 25 L 25 25 C 26.108 25 27 24.108 27 23 L 27 8.4140625 L 17.121094 18.292969 C 15.958108 19.455959 14.041892 19.455959 12.878906 18.292969 L 3 8.4140625 z " transform="translate(0,289.0625)" id="rect4592"/></g></svg></a> + <a href="https://github.com/breck7/simoji"><svg xmlns="http://www.w3.org/2000/svg" width="92pt" height="92pt" viewBox="0 0 92 92"><path d="M90.156 41.965 50.036 1.848a5.913 5.913 0 0 0-8.368 0l-8.332 8.332 10.566 10.566a7.03 7.03 0 0 1 7.23 1.684 7.043 7.043 0 0 1 1.673 7.277l10.183 10.184a7.026 7.026 0 0 1 7.278 1.672 7.04 7.04 0 0 1 0 9.957 7.045 7.045 0 0 1-9.961 0 7.038 7.038 0 0 1-1.532-7.66l-9.5-9.497V59.36a7.04 7.04 0 0 1 1.86 11.29 7.04 7.04 0 0 1-9.957 0 7.04 7.04 0 0 1 0-9.958 7.034 7.034 0 0 1 2.308-1.539V33.926a7.001 7.001 0 0 1-2.308-1.535 7.049 7.049 0 0 1-1.516-7.7L29.242 14.273 1.734 41.777a5.918 5.918 0 0 0 0 8.371L41.855 90.27a5.92 5.92 0 0 0 8.368 0l39.933-39.934a5.925 5.925 0 0 0 0-8.371"/></g></svg></a> + <a href="https://scroll.pub" class="gazetteScrollLink">Built with Scroll v68.0.0</a> +</div> \ No newline at end of file ------------------------------------------------------------
commit fa76b0dacc9bd7312a50fc6663bcbd491a1e3337
Author: Breck Yunits <breck7@gmail.com> Date: Sat Apr 8 20:20:07 2023 -1000 Update to Jtree 74 diff --git a/BrowserGlue.js b/BrowserGlue.js index ac32540..2f632de 100644 --- a/BrowserGlue.js +++ b/BrowserGlue.js @@ -1,10 +1,11 @@ const DEFAULT_SIM = "fire" -const { jtree } = require("jtree") +const { TreeNode } = require("jtree/products/TreeNode.js") +const { HandGrammarProgram } = require("jtree/products/GrammarLanguage.js") const { ExampleSims } = require("./components/ExampleSims.js") -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") const { Keywords, LocalStorageKeys, UrlKeys } = require("./components/Types.js") -class BrowserGlue extends AbstractTreeComponent { +class BrowserGlue extends AbstractTreeComponentParser { async fetchAndLoadSimCodeFromUrlCommand(url) { const simCode = await this.fetchText(url) return simCode @@ -22,7 +23,7 @@ class BrowserGlue extends AbstractTreeComponent { async fetchSimCode() { const hash = this.willowBrowser.getHash().substr(1) - const deepLink = new jtree.TreeNode(decodeURIComponent(hash)) + const deepLink = new TreeNode(decodeURIComponent(hash)) const example = deepLink.get(UrlKeys.example) const fromUrl = deepLink.get(UrlKeys.url) const simojiCode = deepLink.getNode(UrlKeys.simoji) @@ -50,7 +51,7 @@ class BrowserGlue extends AbstractTreeComponent { } async init(grammarCode, theExamples) { - window.simojiCompiler = new jtree.HandGrammarProgram(grammarCode).compileAndReturnRootConstructor() + window.simojiParser = new HandGrammarProgram(grammarCode).compileAndReturnRootParser() ExampleSims.setChildren(theExamples) const simCode = await this.fetchSimCode() diff --git a/build.js b/build.js index 48af45b..db8350d 100755 --- a/build.js +++ b/build.js @@ -13,7 +13,10 @@ lib/jquery-ui.min.js lib/jquery.ui.touch-punch.min.js node_modules/jtree/sandbox/lib/codemirror.js node_modules/jtree/sandbox/lib/show-hint.js -node_modules/jtree/products/jtree.browser.js +node_modules/jtree/products/Utils.browser.js +node_modules/jtree/products/TreeNode.browser.js +node_modules/jtree/products/GrammarLanguage.browser.js +node_modules/jtree/products/GrammarCodeMirrorMode.browser.js node_modules/jtree/products/stump.browser.js node_modules/jtree/products/hakon.browser.js node_modules/jtree/products/TreeComponentFramework.browser.js`.split("\n") diff --git a/components/AbstractContextMenu.js b/components/AbstractContextMenu.js index dc6e619..26baf59 100644 --- a/components/AbstractContextMenu.js +++ b/components/AbstractContextMenu.js @@ -1,9 +1,9 @@ -const { jtree } = require("jtree") -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { TreeNode } = require("jtree/products/TreeNode.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") var jQuery -class AbstractContextMenuComponent extends AbstractTreeComponent { +class AbstractContextMenuComponent extends AbstractTreeComponentParser { toHakonCode() { const theme = this.getTheme() return `.AbstractContextMenuComponent @@ -26,14 +26,14 @@ class AbstractContextMenuComponent extends AbstractTreeComponent { } toStumpCode() { - return new jtree.TreeNode(`div + return new TreeNode(`div class AbstractContextMenuComponent {constructorName} {body}`).templateToString({ constructorName: this.constructor.name, body: this.getContextMenuBodyStumpCode() }) } treeComponentDidMount() { const container = this.getStumpNode() - const app = this.getRootNode() + const app = this.root const { willowBrowser } = app const bodyShadow = willowBrowser.getBodyStumpNode().getShadow() const unmountOnClick = function() { @@ -55,7 +55,7 @@ class AbstractContextMenuComponent extends AbstractTreeComponent { top = undefined get left() { - return this.getRootNode().getMouseEvent().clientX + return this.root.getMouseEvent().clientX } _getContextMenuPosition(windowWidth, windowHeight, x, y, shadow) { diff --git a/components/Agent.js b/components/Agent.js index b065f04..ee0e7bf 100644 --- a/components/Agent.js +++ b/components/Agent.js @@ -1,11 +1,11 @@ -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") const { yodash } = require("../yodash.js") -const { jtree } = require("jtree") +const { TreeNode } = require("jtree/products/TreeNode.js") const { Keywords, Directions } = require("./Types.js") const SelectedClass = "selected" -class Agent extends jtree.TreeNode { +class Agent extends TreeNode { get name() { return this._name ?? this.icon } @@ -17,8 +17,8 @@ class Agent extends jtree.TreeNode { } get definitionWithBehaviors() { - if (!this.behaviors.length) return this.board.simojiProgram.getNode(this.getWord(0)) - const behaviors = yodash.flatten(yodash.pick(this.board.simojiProgram, [this.getWord(0), ...this.behaviors])) + if (!this.behaviors.length) return this.board.simojiProgram.getNode(this.firstWord) + const behaviors = yodash.flatten(yodash.pick(this.board.simojiProgram, [this.firstWord, ...this.behaviors])) return behaviors } @@ -39,7 +39,7 @@ class Agent extends jtree.TreeNode { const { neighorCount } = this neighborConditions.forEach(conditionAndCommandsBlock => { - const [emoji, operator, count] = conditionAndCommandsBlock.getWords() + const [emoji, operator, count] = conditionAndCommandsBlock.words const actual = neighorCount[emoji] if (!yodash.compare(actual ?? 0, operator, count)) return conditionAndCommandsBlock.forEach(command => this._executeCommand(this, command)) @@ -57,7 +57,7 @@ class Agent extends jtree.TreeNode { for (let pos of yodash.positionsAdjacentTo(this.position)) { const hits = agentPositionMap.get(yodash.makePositionHash(pos)) ?? [] for (let target of hits) { - const targetId = target.getWord(0) + const targetId = target.firstWord const commandBlock = touchMap.getNode(targetId) if (commandBlock) { commandBlock.forEach(command => this._executeCommand(target, command)) @@ -73,7 +73,7 @@ class Agent extends jtree.TreeNode { this.getCommandBlocks(Keywords.onHit).forEach(hitMap => { if (this.skip(hitMap.getWord(1))) return targets.forEach(target => { - const targetId = target.getWord(0) + const targetId = target.firstWord const commandBlock = hitMap.getNode(targetId) if (commandBlock) commandBlock.forEach(command => this._executeCommand(target, command)) }) @@ -85,7 +85,7 @@ class Agent extends jtree.TreeNode { } _executeCommand(target, instruction) { - const commandName = instruction.getWord(0) + const commandName = instruction.firstWord if (this[commandName]) this[commandName](target, instruction) // board commands else this.board[commandName](instruction) @@ -133,7 +133,7 @@ class Agent extends jtree.TreeNode { } _replaceWith(newObject) { - this.getParent().appendLine(`${newObject} ${this.positionHash}`) + this.parent.appendLine(`${newObject} ${this.positionHash}`) this.remove() } @@ -183,10 +183,6 @@ class Agent extends jtree.TreeNode { } } - get root() { - return this.getRootNode() - } - set position(value) { if (this.board.isSolidAgent(value)) return this.bouncy ? this.bounce() : this const newLine = this.getLine() @@ -197,7 +193,7 @@ class Agent extends jtree.TreeNode { } get board() { - return this.getParent() + return this.parent } get maxRight() { @@ -223,7 +219,7 @@ class Agent extends jtree.TreeNode { } get position() { - return yodash.parsePosition(this.getWords()) + return yodash.parsePosition(this.words) } get positionHash() { @@ -231,7 +227,7 @@ class Agent extends jtree.TreeNode { } get gridSize() { - return this.getParent().gridSize + return this.parent.gridSize } get selected() { @@ -272,8 +268,9 @@ class Agent extends jtree.TreeNode { get inlineStyle() { const { gridSize, health } = this const opacity = health === undefined ? "" : `opacity:${this.health / this.startHealth};` - return `top:${this.top * gridSize}px;left:${this.left * - gridSize}px;font-size:${gridSize}px;line-height: ${gridSize}px;${opacity};${this.style ?? ""}` + return `top:${this.top * gridSize}px;left:${ + this.left * gridSize + }px;font-size:${gridSize}px;line-height: ${gridSize}px;${opacity};${this.style ?? ""}` } toElement() { @@ -309,7 +306,7 @@ class Agent extends jtree.TreeNode { kickIt(target) { target.angle = this.angle - target.tickStack = new jtree.TreeNode(`1 + target.tickStack = new TreeNode(`1 move move move @@ -342,7 +339,7 @@ class Agent extends jtree.TreeNode { } narrate(subject, command) { - this.root.log(`${this.getWord(0)} ${command.getContent()}`) + this.root.log(`${this.firstWord} ${command.content}`) } shoot() { diff --git a/components/AgentPalette.js b/components/AgentPalette.js index 6a48caf..072d9d3 100644 --- a/components/AgentPalette.js +++ b/components/AgentPalette.js @@ -1,11 +1,11 @@ -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") -class AgentPaletteComponent extends AbstractTreeComponent { +class AgentPaletteComponent extends AbstractTreeComponentParser { toStumpCode() { - const root = this.getRootNode() + const root = this.root const { agentToInsert } = root const items = this.paletteItems - .map(item => item.getWord(0)) + .map(item => item.firstWord) .map( word => ` div ${word} class ${agentToInsert === word ? "ActiveAgent" : ""} @@ -18,16 +18,16 @@ ${items}` } get paletteItems() { - return this.getRootNode().allAgentTypes.filter(item => !item.has("noPalette")) + return this.root.allAgentTypes.filter(item => !item.has("noPalette")) } changeAgentBrushCommand(x) { - this.getRootNode().changeAgentBrushCommand(x) + this.root.changeAgentBrushCommand(x) this.setContent(Date.now()).renderAndGetRenderReport() } getDependencies() { - return [this.getRootNode().board] + return [this.root.board] } } diff --git a/components/Board.js b/components/Board.js index b8d6859..8b6bf93 100644 --- a/components/Board.js +++ b/components/Board.js @@ -1,17 +1,17 @@ -const { jtree } = require("jtree") +const { TreeNode } = require("jtree/products/TreeNode.js") const { yodash } = require("../yodash.js") -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") const { GridComponent } = require("./Grid.js") const { Agent } = require("./Agent.js") -const { Keywords, NodeTypes } = require("./Types.js") +const { Keywords, ParserTypes } = require("./Types.js") let nodeJsPrefix = "" // prettier-ignore /*NODE_JS_ONLY*/ nodeJsPrefix = `const { Agent } = require("${__dirname}/Agent.js");` -class BoardErrorNode extends AbstractTreeComponent { - _isErrorNodeType() { +class BoardErrorParser extends AbstractTreeComponentParser { + _isErrorParser() { return true } toStumpCode() { @@ -21,17 +21,17 @@ class BoardErrorNode extends AbstractTreeComponent { } } -class leftStartPosition extends jtree.TreeNode { +class leftStartPosition extends TreeNode { get width() { return parseInt(this.getWord(1)) } } -class BoardComponent extends AbstractTreeComponent { +class BoardComponent extends AbstractTreeComponentParser { // override default parser creation. _getParser() { if (!this._parser) - this._parser = new jtree.TreeNode.Parser(BoardErrorNode, { + this._parser = new TreeNode.ParserCombinator(BoardErrorParser, { ...this.agentMap, GridComponent, BoardStyleComponent, @@ -72,7 +72,7 @@ class BoardComponent extends AbstractTreeComponent { } get populationCsv() { - const csv = new TreeNode(this._populationCounts).toCsv() + const csv = new TreeNode(this._populationCounts).asCsv // add 0's for missing values return csv .split("\n") @@ -102,7 +102,7 @@ class BoardComponent extends AbstractTreeComponent { if (blocks) blocks.forEach(block => block - .filter(node => node.doesExtend(NodeTypes.abstractInjectCommandNode)) + .filter(node => node.doesExtend(ParserTypes.abstractInjectCommandParser)) .forEach(command => this.runInjectCommand(command)) ) } @@ -168,7 +168,7 @@ class BoardComponent extends AbstractTreeComponent { if (this.resetAfterLoop) { this.resetAfterLoop = false - this.getRootNode().resetAllCommand() + this.root.resetAllCommand() } } @@ -200,7 +200,7 @@ class BoardComponent extends AbstractTreeComponent { } get root() { - return this.getParent() + return this.parent } get ticksPerSecond() { @@ -211,10 +211,10 @@ class BoardComponent extends AbstractTreeComponent { occupiedSpots = new Set() runInjectCommand(command) { - this[command.getNodeTypeId()](command) + this[command.parserId](command) } - insertClusterNode(commandNode) { + insertClusterParser(commandNode) { this.concat( yodash.insertClusteredRandomAgents( this.randomNumberGenerator, @@ -229,28 +229,28 @@ class BoardComponent extends AbstractTreeComponent { ) } - insertAtNode(commandNode) { + insertAtParser(commandNode) { this.appendLine(`${commandNode.getWord(1)} ${commandNode.getWord(3)} ${commandNode.getWord(2)}`) // TODO: update occupied spots cache? } - rectangleDrawNode(commandNode) { - const newLines = yodash.makeRectangle(...yodash.parseInts(commandNode.getWords().slice(1), 1)) + rectangleDrawParser(commandNode) { + const newLines = yodash.makeRectangle(...yodash.parseInts(commandNode.words.slice(1), 1)) this.concat(newLines) // TODO: update occupied spots cache? } - pasteDrawNode(commandNode) { + pasteDrawParser(commandNode) { const newSpots = new TreeNode(commandNode.childrenToString()) yodash.updateOccupiedSpots(newSpots, this.occupiedSpots) this.concat(newSpots) } - fillNode(commandNode) { + fillParser(commandNode) { this.concat(yodash.fill(this.rows, this.cols, this.occupiedSpots, commandNode.getWord(1))) } - drawNode(commandNode) { + drawParser(commandNode) { const { occupiedSpots } = this const spots = yodash.draw(commandNode.childrenToString()) yodash.updateOccupiedSpots(spots, occupiedSpots) @@ -268,7 +268,7 @@ class BoardComponent extends AbstractTreeComponent { return this._rng } - insertNode(commandNode) { + insertParser(commandNode) { const { rows, cols, occupiedSpots } = this const emoji = commandNode.getWord(2) let amount = commandNode.getWord(1) @@ -291,7 +291,7 @@ class BoardComponent extends AbstractTreeComponent { const emoji = commands.getWord(1) if (emoji && this.has(emoji)) return commands.forEach(instruction => { - this[instruction.getWord(0)](instruction) + this[instruction.firstWord](instruction) }) }) } @@ -301,7 +301,7 @@ class BoardComponent extends AbstractTreeComponent { const probability = commands.getWord(1) if (probability && this.randomNumberGenerator() > parseFloat(probability)) return commands.forEach(instruction => { - this[instruction.getWord(0)](instruction) + this[instruction.firstWord](instruction) }) }) } @@ -315,7 +315,7 @@ class BoardComponent extends AbstractTreeComponent { } get agents() { - return this.getTopDownArray().filter(node => node instanceof Agent) + return this.topDownArray.filter(node => node instanceof Agent) } get agentPositionMap() { @@ -407,7 +407,7 @@ class BoardComponent extends AbstractTreeComponent { get experimentTitle() { if (!this.hasMultipleBoards) return "" - return this.root.mainExperiment.findNodes(Keywords.experiment)[this.boardIndex].getContent() ?? "" + return this.root.mainExperiment.findNodes(Keywords.experiment)[this.boardIndex].content ?? "" } startInterval() { @@ -454,7 +454,7 @@ class BoardComponent extends AbstractTreeComponent { } alert(command) { - const message = command.getContent() + const message = command.content if (!this.isNodeJs()) // todo: willow should shim this alert(message) @@ -472,13 +472,13 @@ class BoardComponent extends AbstractTreeComponent { } log(command) { - this.root.log(command.getContent()) + this.root.log(command.content) } } -class BoardStyleComponent extends AbstractTreeComponent { - createParser() { - return new jtree.TreeNode.Parser(TreeNode) +class BoardStyleComponent extends AbstractTreeComponentParser { + createParserCombinator() { + return new TreeNode.ParserCombinator(TreeNode) } toStumpCode() { diff --git a/components/BottomBar.js b/components/BottomBar.js index d97dc8f..a1a8a6d 100644 --- a/components/BottomBar.js +++ b/components/BottomBar.js @@ -1,12 +1,12 @@ -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") const { PlayButtonComponent } = require("./PlayButton.js") const { ReportButtonComponent } = require("./ReportButton.js") const { ResetButtonComponent } = require("./ResetButton.js") -const { jtree } = require("jtree") +const { TreeNode } = require("jtree/products/TreeNode.js") -class BottomBarComponent extends AbstractTreeComponent { - createParser() { - return new jtree.TreeNode.Parser(undefined, { +class BottomBarComponent extends AbstractTreeComponentParser { + createParserCombinator() { + return new TreeNode.ParserCombinator(undefined, { PlayButtonComponent, ReportButtonComponent, ResetButtonComponent diff --git a/components/EditorHandle.js b/components/EditorHandle.js index 8441aa9..7ff2812 100644 --- a/components/EditorHandle.js +++ b/components/EditorHandle.js @@ -1,14 +1,14 @@ -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") -class EditorHandleComponent extends AbstractTreeComponent { +class EditorHandleComponent extends AbstractTreeComponentParser { get left() { - return this.getRootNode().editor.width + return this.root.editor.width } makeDraggable() { if (this.isNodeJs()) return - const root = this.getRootNode() + const root = this.root jQuery(this.getStumpNode().getShadow().element).draggable({ axis: "x", drag: function(event, ui) { @@ -37,7 +37,7 @@ class EditorHandleComponent extends AbstractTreeComponent { } getDependencies() { - return [this.getRootNode().editor] + return [this.root.editor] } } diff --git a/components/ExampleSims.js b/components/ExampleSims.js index 0bfd1d2..443133f 100644 --- a/components/ExampleSims.js +++ b/components/ExampleSims.js @@ -1,6 +1,6 @@ -const { jtree } = require("jtree") +const { TreeNode } = require("jtree/products/TreeNode.js") -const ExampleSims = new jtree.TreeNode() +const ExampleSims = new TreeNode() // prettier-ignore /*NODE_JS_ONLY*/ ExampleSims.setChildren(require("../examples.js").getExamples().toString()) diff --git a/components/Examples.js b/components/Examples.js index 64d8607..1796308 100644 --- a/components/Examples.js +++ b/components/Examples.js @@ -1,10 +1,11 @@ -const { jtree } = require("jtree") +const { TreeNode } = require("jtree/products/TreeNode.js") +const { Utils } = require("jtree/products/Utils.js") const { ExampleSims } = require("./ExampleSims.js") const { AbstractContextMenuComponent } = require("./AbstractContextMenu.js") -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") -const Categories = new jtree.TreeNode(`🦠 Epidemiology +const Categories = new TreeNode(`🦠 Epidemiology virus covid19 🌲 Forests @@ -32,10 +33,10 @@ class ExampleMenuComponent extends AbstractContextMenuComponent { return category .map(node => { - const name = node.getFirstWord() + const name = node.firstWord const program = ExampleSims.getNode(name) const icon = program.childrenToString().match(/(\p{Extended_Pictographic}+)/u)[1] - const properName = jtree.Utils.ucfirst(name) + const properName = Utils.ucfirst(name) return `a ${icon} &nbsp; ${properName} clickCommand loadExampleCommand ${name} class ExampleButton` @@ -46,17 +47,17 @@ class ExampleMenuComponent extends AbstractContextMenuComponent { // Align these to below and to the left of the clicked button top = 28 get left() { - const evt = this.getRootNode().getMouseEvent() + const evt = this.root.getMouseEvent() return evt.clientX - evt.offsetX } } -class ExamplesComponent extends AbstractTreeComponent { +class ExamplesComponent extends AbstractTreeComponentParser { toStumpCode() { const categories = Categories.map(category => { - const icon = category.getFirstWord() - const name = category.getContent() - const firstFile = category.nodeAt(0).getFirstWord() + const icon = category.firstWord + const name = category.content + const firstFile = category.nodeAt(0).firstWord return ` a ${icon} href index.html#example%20${firstFile} title ${name} @@ -68,10 +69,10 @@ ${categories}` } async openCategoryCommand(icon) { - const root = this.getRootNode() + const root = this.root const category = Categories.getNode(icon) - const firstFile = category.nodeAt(0).getFirstWord() - this.getRootNode().toggleAndRender(`${ExampleMenuComponent.name} ${icon}`) + const firstFile = category.nodeAt(0).firstWord + this.root.toggleAndRender(`${ExampleMenuComponent.name} ${icon}`) } } diff --git a/components/Grid.js b/components/Grid.js index 861e36b..f80db79 100644 --- a/components/Grid.js +++ b/components/Grid.js @@ -1,9 +1,9 @@ const { yodash } = require("../yodash.js") -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") -class GridComponent extends AbstractTreeComponent { +class GridComponent extends AbstractTreeComponentParser { gridClickCommand(down, right) { - return this.getParent().insertAgentAtCommand(right, down) + return this.parent.insertAgentAtCommand(right, down) } makeBlock(down, right, gridSize) { @@ -14,7 +14,7 @@ class GridComponent extends AbstractTreeComponent { } toStumpCode() { - const { cols, rows, gridSize } = this.getParent() + const { cols, rows, gridSize } = this.parent let blocks = "" let rs = rows while (rs >= 0) { diff --git a/components/HelpModal.js b/components/HelpModal.js index f444fb0..bdffeb7 100644 --- a/components/HelpModal.js +++ b/components/HelpModal.js @@ -1,7 +1,7 @@ -const { jtree } = require("jtree") -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { TreeNode } = require("jtree/products/TreeNode.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") -class AbstractModalTreeComponent extends AbstractTreeComponent { +class AbstractModalTreeComponent extends AbstractTreeComponentParser { toHakonCode() { return `.modalBackground position fixed @@ -39,7 +39,7 @@ class AbstractModalTreeComponent extends AbstractTreeComponent { } toStumpCode() { - return new jtree.TreeNode(`section + return new TreeNode(`section clickCommand unmountAndDestroyCommand class modalBackground section diff --git a/components/PlayButton.js b/components/PlayButton.js index de1dbb5..7c60e50 100644 --- a/components/PlayButton.js +++ b/components/PlayButton.js @@ -1,8 +1,8 @@ -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") -class PlayButtonComponent extends AbstractTreeComponent { +class PlayButtonComponent extends AbstractTreeComponentParser { get isStarted() { - return this.getRootNode().isRunning + return this.root.isRunning } toStumpCode() { diff --git a/components/ReportButton.js b/components/ReportButton.js index ee6d9c2..0513cd1 100644 --- a/components/ReportButton.js +++ b/components/ReportButton.js @@ -1,6 +1,6 @@ -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") -class ReportButtonComponent extends AbstractTreeComponent { +class ReportButtonComponent extends AbstractTreeComponentParser { toStumpCode() { return `span Δ title Generate Report diff --git a/components/ResetButton.js b/components/ResetButton.js index 60cbcec..bedc596 100644 --- a/components/ResetButton.js +++ b/components/ResetButton.js @@ -1,6 +1,6 @@ -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") -class ResetButtonComponent extends AbstractTreeComponent { +class ResetButtonComponent extends AbstractTreeComponentParser { toStumpCode() { return `span ≪ title Clear and reset @@ -9,8 +9,8 @@ class ResetButtonComponent extends AbstractTreeComponent { } resetAllCommand() { - this.getRootNode().pauseAllCommand() - this.getRootNode().resetAllCommand() + this.root.pauseAllCommand() + this.root.resetAllCommand() } } diff --git a/components/RightBar.js b/components/RightBar.js index fe75b99..6e745e5 100644 --- a/components/RightBar.js +++ b/components/RightBar.js @@ -1,10 +1,10 @@ -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") -const { jtree } = require("jtree") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") +const { TreeNode } = require("jtree/products/TreeNode.js") const { AgentPaletteComponent } = require("./AgentPalette.js") -class RightBarComponent extends AbstractTreeComponent { - createParser() { - return new jtree.TreeNode.Parser(undefined, { +class RightBarComponent extends AbstractTreeComponentParser { + createParserCombinator() { + return new TreeNode.ParserCombinator(undefined, { AgentPaletteComponent }) } diff --git a/components/Share.js b/components/Share.js index 3145b6a..ebd5363 100644 --- a/components/Share.js +++ b/components/Share.js @@ -1,6 +1,6 @@ -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") -class ShareComponent extends AbstractTreeComponent { +class ShareComponent extends AbstractTreeComponentParser { toStumpCode() { return `div class ShareComponent @@ -11,13 +11,13 @@ class ShareComponent extends AbstractTreeComponent { } getDependencies() { - return [this.getRootNode().firstProgram] + return [this.root.firstProgram] } get link() { const url = new URL(typeof location === "undefined" ? "http://localhost/" : location.href) // todo: TCF should provide shim for this url.hash = "" - return url.toString() + this.getRootNode().urlHash + return url.toString() + this.root.urlHash } } diff --git a/components/SimEditor.js b/components/SimEditor.js index 7554220..7c718f0 100644 --- a/components/SimEditor.js +++ b/components/SimEditor.js @@ -1,8 +1,8 @@ -const { jtree } = require("jtree") -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { TreeNode } = require("jtree/products/TreeNode.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") // prettier-ignore -/*NODE_JS_ONLY*/ const simojiCompiler = jtree.compileGrammarFileAtPathAndReturnRootConstructor( __dirname + "/../simoji.grammar") +/*NODE_JS_ONLY*/ const simojiParser = require("jtree/products/GrammarCompiler.js").GrammarCompiler.compileGrammarFileAtPathAndReturnRootParser( __dirname + "/../simoji.grammar") class CodeMirrorShim { setSize() {} @@ -14,7 +14,7 @@ class CodeMirrorShim { } } -class SimEditorComponent extends AbstractTreeComponent { +class SimEditorComponent extends AbstractTreeComponentParser { toStumpCode() { return `div class ${SimEditorComponent.name} @@ -26,9 +26,9 @@ class SimEditorComponent extends AbstractTreeComponent { id codeErrorsConsole` } - createParser() { - return new jtree.TreeNode.Parser(undefined, { - value: jtree.TreeNode + createParserCombinator() { + return new TreeNode.ParserCombinator(undefined, { + value: TreeNode }) } @@ -43,11 +43,11 @@ class SimEditorComponent extends AbstractTreeComponent { const code = this.codeMirrorValue if (this._code === code) return this._code = code - const root = this.getRootNode() + const root = this.root root.pauseAllCommand() // this._updateLocalStorage() - this.program = new simojiCompiler(code) + this.program = new simojiParser(code) const errs = this.program.getAllErrors() const errMessage = errs.length ? `${errs.length} errors` : "&nbsp;" @@ -70,7 +70,7 @@ class SimEditorComponent extends AbstractTreeComponent { this._onCodeKeyUp() }) this.codeWidgets.push( - this.codeMirrorInstance.addLineWidget(err.getLineNumber() - 1, el, { coverGutter: false, noHScroll: false }) + this.codeMirrorInstance.addLineWidget(err.lineNumber - 1, el, { coverGutter: false, noHScroll: false }) ) }) const info = this.codeMirrorInstance.getScrollInfo() @@ -85,7 +85,7 @@ class SimEditorComponent extends AbstractTreeComponent { } loadFromEditor() { - this.getRootNode().loadNewSim(this._code) + this.root.loadNewSim(this._code) } get simCode() { @@ -116,12 +116,7 @@ class SimEditorComponent extends AbstractTreeComponent { _initCodeMirror() { if (this.isNodeJs()) return (this.codeMirrorInstance = new CodeMirrorShim()) - this.codeMirrorInstance = new jtree.TreeNotationCodeMirrorMode( - "custom", - () => simojiCompiler, - undefined, - CodeMirror - ) + this.codeMirrorInstance = new GrammarCodeMirrorMode("custom", () => simojiParser, undefined, CodeMirror) .register() .fromTextAreaWithAutocomplete(document.getElementById("EditorTextarea"), { lineWrapping: false, diff --git a/components/SimojiApp.js b/components/SimojiApp.js index ee8b2c5..f5c0b04 100644 --- a/components/SimojiApp.js +++ b/components/SimojiApp.js @@ -1,7 +1,7 @@ // prettier-ignore -/*NODE_JS_ONLY*/ const { AbstractTreeComponent, TreeComponentFrameworkDebuggerComponent } = require("jtree/products/TreeComponentFramework.node.js") +/*NODE_JS_ONLY*/ const { AbstractTreeComponentParser, TreeComponentFrameworkDebuggerComponent } = require("jtree/products/TreeComponentFramework.node.js") -const { jtree } = require("jtree") +const { TreeNode } = require("jtree/products/TreeNode.js") const { yodash } = require("../yodash") const { ExampleSims } = require("./ExampleSims.js") @@ -20,7 +20,7 @@ const { BottomBarComponent } = require("./BottomBar.js") const { RightBarComponent } = require("./RightBar.js") const { EditorHandleComponent } = require("./EditorHandle.js") const { TitleComponent } = require("./Title.js") -const { Keywords, LocalStorageKeys, UrlKeys, Directions, NodeTypes } = require("./Types.js") +const { Keywords, LocalStorageKeys, UrlKeys, Directions, ParserTypes } = require("./Types.js") const MIN_GRID_SIZE = 10 const MAX_GRID_SIZE = 200 @@ -29,9 +29,9 @@ const MIN_GRID_COLUMNS = 10 const MIN_GRID_ROWS = 10 // prettier-ignore -/*NODE_JS_ONLY*/ const simojiCompiler = jtree.compileGrammarFileAtPathAndReturnRootConstructor( __dirname + "/../simoji.grammar") +/*NODE_JS_ONLY*/ const simojiParser = require("jtree/products/GrammarCompiler.js").GrammarCompiler.compileGrammarFileAtPathAndReturnRootParser( __dirname + "/../simoji.grammar") -class githubTriangleComponent extends AbstractTreeComponent { +class githubTriangleComponent extends AbstractTreeComponentParser { githubLink = `https://github.com/breck7/simoji` toHakonCode() { return `.AbstractGithubTriangleComponent @@ -52,8 +52,8 @@ class githubTriangleComponent extends AbstractTreeComponent { } } -class ErrorNode extends AbstractTreeComponent { - _isErrorNodeType() { +class ErrorParser extends AbstractTreeComponentParser { + _isErrorParser() { return true } toStumpCode() { @@ -63,9 +63,9 @@ class ErrorNode extends AbstractTreeComponent { } } -class SimojiApp extends AbstractTreeComponent { - createParser() { - return new jtree.TreeNode.Parser(ErrorNode, { +class SimojiApp extends AbstractTreeComponentParser { + createParserCombinator() { + return new TreeNode.ParserCombinator(ErrorParser, { TopBarComponent, githubTriangleComponent, SimEditorComponent, @@ -122,7 +122,7 @@ class SimojiApp extends AbstractTreeComponent { try { program - .filter(node => node.doesExtend(NodeTypes.abstractInjectCommandNode)) + .filter(node => node.doesExtend(ParserTypes.abstractInjectCommandParser)) .forEach(command => board.runInjectCommand(command)) } catch (err) { if (this.verbose) console.error(err) @@ -203,8 +203,8 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : } dumpErrorsCommand() { - const errs = new simojiCompiler(this.simCode).getAllErrors() - console.log(new jtree.TreeNode(errs.map(err => err.toObject())).toFormattedTable(200)) + const errs = new simojiParser(this.simCode).getAllErrors() + console.log(new TreeNode(errs.map(err => err.toObject())).toFormattedTable(200)) } get boards() { @@ -216,7 +216,7 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : } get mainExperiment() { - return new simojiCompiler(this.simCode) + return new simojiParser(this.simCode) } get simojiPrograms() { @@ -229,7 +229,7 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : this._simojiPrograms.push(yodash.patchExperimentAndReplaceSymbols(mainExperiment, experiment)) }) // Evaluate the variables - this._simojiPrograms = this._simojiPrograms.map(program => new simojiCompiler(program.toString())) + this._simojiPrograms = this._simojiPrograms.map(program => new simojiParser(program.toString())) } return this._simojiPrograms } @@ -273,7 +273,7 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : const keyboardShortcuts = this._getKeyboardShortcuts() Object.keys(keyboardShortcuts).forEach(key => { - willowBrowser.getMousetrap().bind(key, function(evt) { + willowBrowser.getMousetrap().bind(key, function (evt) { keyboardShortcuts[key]() // todo: handle the below when we need to if (evt.preventDefault) evt.preventDefault() @@ -310,14 +310,7 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : if (!this.selection.length) return "" - const str = this.selection - .map(agent => - agent - .getWords() - .slice(0, 3) - .join(" ") - ) - .join("\n") + const str = this.selection.map(agent => agent.words.slice(0, 3).join(" ")).join("\n") evt.preventDefault() evt.clipboardData.setData("text/plain", str) @@ -376,7 +369,7 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : } get urlHash() { - const tree = new jtree.TreeNode() + const tree = new TreeNode() tree.appendLineAndChildren(UrlKeys.simoji, this.simCode ?? "") return "#" + encodeURIComponent(tree.toString()) } @@ -455,12 +448,12 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : get isSnapshotOn() { // technically also needs rows and column settings - return new jtree.TreeNode(this.simCode).has(Keywords.seed) + return new TreeNode(this.simCode).has(Keywords.seed) } // Save the current random play for reproducibility and shareability snapShotCommand() { - const newCode = new jtree.TreeNode(this.simCode) + const newCode = new TreeNode(this.simCode) const boards = this.boards // todo: buggy. we should rename the board class to experiment, or rename experiment keyword to board. @@ -546,7 +539,7 @@ SimojiApp.setupApp = (simojiCode, windowWidth = 1000, windowHeight = 1000) => { typeof localStorage !== "undefined" ? localStorage.getItem(LocalStorageKeys.editorStartWidth) ?? SIZES.EDITOR_WIDTH : SIZES.EDITOR_WIDTH - const startState = new jtree.TreeNode(`${githubTriangleComponent.name} + const startState = new TreeNode(`${githubTriangleComponent.name} ${TopBarComponent.name} ${LogoComponent.name} ${ShareComponent.name} diff --git a/components/SimojiApp.test.node.js b/components/SimojiApp.test.node.js index 8152577..c6ad791 100755 --- a/components/SimojiApp.test.node.js +++ b/components/SimojiApp.test.node.js @@ -1,18 +1,19 @@ #!/usr/bin/env node -const { jtree } = require("jtree") +const { TreeNode } = require("jtree/products/TreeNode.js") +const { GrammarCompiler } = require("jtree/products/GrammarCompiler.js") const { Disk } = require("jtree/products/Disk.node.js") const grammarNode = require("jtree/products/grammar.nodejs.js") const { SimojiApp } = require("./SimojiApp.js") const grammarPath = __dirname + "/../simoji.grammar" const examplesPath = __dirname + "/../examples/" -const simojiCompiler = jtree.compileGrammarFileAtPathAndReturnRootConstructor(grammarPath) +const simojiParser = GrammarCompiler.compileGrammarFileAtPathAndReturnRootParser(grammarPath) const testTree = {} testTree.simojiGrammar = areEqual => { const errs = new grammarNode(Disk.read(grammarPath)).getAllErrors().map(err => err.toObject()) - if (errs.length) console.log(new jtree.TreeNode(errs).toFormattedTable(60)) + if (errs.length) console.log(new TreeNode(errs).toFormattedTable(60)) areEqual(errs.length, 0, "no grammar errors") } @@ -20,7 +21,7 @@ testTree.simGrammarErrors = areEqual => { const errs = Disk.getFiles(examplesPath) .map(path => { const code = Disk.read(path) - const program = new simojiCompiler(code) + const program = new simojiParser(code) const errors = program.getAllErrors() areEqual(errors.length, 0) return errors.map(err => { @@ -28,7 +29,7 @@ testTree.simGrammarErrors = areEqual => { }) }) .flat() - if (errs.length) console.log(new jtree.TreeNode(errs).toFormattedTable(60)) + if (errs.length) console.log(new TreeNode(errs).toFormattedTable(60)) } testTree.SimojiApp = areEqual => { diff --git a/components/Title.js b/components/Title.js index 161bfff..7183f1a 100644 --- a/components/Title.js +++ b/components/Title.js @@ -1,13 +1,13 @@ -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") const { Keywords } = require("./Types.js") -class TitleComponent extends AbstractTreeComponent { +class TitleComponent extends AbstractTreeComponentParser { get question() { return this.app.mainExperiment.get(Keywords.question) ?? "" } get app() { - return this.getRootNode() + return this.root } getDependencies() { diff --git a/components/TopBar.js b/components/TopBar.js index 4a15770..761443e 100644 --- a/components/TopBar.js +++ b/components/TopBar.js @@ -1,11 +1,11 @@ -const { AbstractTreeComponent } = require("jtree/products/TreeComponentFramework.node.js") +const { AbstractTreeComponentParser } = require("jtree/products/TreeComponentFramework.node.js") const { ShareComponent } = require("./Share.js") const { ExamplesComponent } = require("./Examples.js") -const { jtree } = require("jtree") +const { TreeNode } = require("jtree/products/TreeNode.js") -class TopBarComponent extends AbstractTreeComponent { - createParser() { - return new jtree.TreeNode.Parser(undefined, { +class TopBarComponent extends AbstractTreeComponentParser { + createParserCombinator() { + return new TreeNode.ParserCombinator(undefined, { LogoComponent, ShareComponent, ExamplesComponent @@ -13,7 +13,7 @@ class TopBarComponent extends AbstractTreeComponent { } } -class LogoComponent extends AbstractTreeComponent { +class LogoComponent extends AbstractTreeComponentParser { toStumpCode() { return `a ❔ href cheatSheet.html @@ -22,7 +22,7 @@ class LogoComponent extends AbstractTreeComponent { } toggleHelpCommand() { - this.getRootNode().toggleHelpCommand() + this.root.toggleHelpCommand() } } diff --git a/components/Types.js b/components/Types.js index 4137f4b..631356f 100644 --- a/components/Types.js +++ b/components/Types.js @@ -36,11 +36,11 @@ Directions.East = "East" Directions.South = "South" Directions.West = "West" -const NodeTypes = {} +const ParserTypes = {} -NodeTypes.agentDefinitionNode = "agentDefinitionNode" -NodeTypes.experimentNode = "experimentNode" -NodeTypes.settingDefinitionNode = "settingDefinitionNode" -NodeTypes.abstractInjectCommandNode = "abstractInjectCommandNode" +ParserTypes.agentDefinitionParser = "agentDefinitionParser" +ParserTypes.experimentParser = "experimentParser" +ParserTypes.settingDefinitionParser = "settingDefinitionParser" +ParserTypes.abstractInjectCommandParser = "abstractInjectCommandParser" -module.exports = { Keywords, LocalStorageKeys, UrlKeys, Directions, NodeTypes } +module.exports = { Keywords, LocalStorageKeys, UrlKeys, Directions, ParserTypes } diff --git a/dev.html b/dev.html index c9499d2..0d4b5c2 100644 --- a/dev.html +++ b/dev.html @@ -12,7 +12,10 @@ <script type="text/javascript" src="node_modules/jquery/dist/jquery.min.js"></script> <script type="text/javascript" src="lib/jquery-ui.min.js"></script> <script type="text/javascript" src="lib/jquery.ui.touch-punch.min.js"></script> - <script src="node_modules/jtree/products/jtree.browser.js"></script> + <script src="node_modules/jtree/products/Utils.browser.js"></script> + <script src="node_modules/jtree/products/TreeNode.browser.js"></script> + <script src="node_modules/jtree/products/GrammarLanguage.browser.js"></script> + <script src="node_modules/jtree/products/GrammarCodeMirrorMode.browser.js"></script> <script src="node_modules/jtree/products/stump.browser.js"></script> <script src="node_modules/jtree/products/hakon.browser.js"></script> <script src="node_modules/jtree/products/TreeComponentFramework.browser.js"></script> diff --git a/dist/constants.js b/dist/constants.js index 479965a..f86e503 100644 --- a/dist/constants.js +++ b/dist/constants.js @@ -1 +1 @@ -const SimConstants = {"grammar":"anyCell\nbooleanCell\nstringCell\n highlightScope string\nsettingValueCell\n highlightScope constant.numeric\ncssCell\n highlightScope string\njavascriptCell\n highlightScope string\nhtmlCell\n highlightScope string\nemojiCell\n highlightScope string\nohayoCell\n highlightScope string\nblankCell\ncodeCell\n highlightScope comment\ncommentCell\n highlightScope comment\nkeywordCell\n highlightScope keyword\ntextCell\n highlightScope string\nintegerCell\n highlightScope constant.numeric\nbehaviorNameCell\n highlightScope keyword\nconditionalOperatorCell\n highlightScope keyword\n enum < > = <= >=\npositionCell\n highlightScope constant.numeric\nneighborCountCell\n extends integerCell\n min 0\n max 8\nintegerOrPercentCell\n highlightScope constant.numeric\nprobabilityCell\n description A number between 0 and 1\n highlightScope constant.numeric\npropertyNameCell\n highlightScope keyword\nangleCell\n enum North South East West NorthWest NorthEast SouthWest SouthEast\n highlightScope constant.numeric\nerrorNode\n baseNodeType errorNode\nsimojiNode\n extensions simoji\n description A Tree Language that compiles to a TreeComponentFramework app.\n root\n inScope abstractIgnoreNode abstractSetupNode abstractInjectCommandNode onTickNode onExtinctNode behaviorDefinitionNode experimentNode settingDefinitionNode\n catchAllNodeType agentDefinitionNode\n compilesTo javascript\n example\n 🦋\n onTick .1\n turnRandomly\n move\n onTick .2\n turnToward 💡\n move\n 💡\n \n insert 10 🦋\n insert 2 💡\n javascript\n get agentTypes() {\n return this.filter(node => node.getNodeTypeId() === \"agentDefinitionNode\")\n }\nexperimentNode\n crux experiment\n cells keywordCell\n inScope abstractIgnoreNode abstractSetupNode abstractInjectCommandNode onTickNode onExtinctNode settingDefinitionNode\n catchAllCellType stringCell\nabstractSetupNode\natTimeNode\n crux atTime\n description Run commands at a certain tick.\n cells keywordCell integerCell\n extends abstractSetupNode\n inScope abstractInjectCommandNode\nabstractSetupNumberNode\n cells keywordCell integerCell\n extends abstractSetupNode\n javascript\n compile() {\n return \"\"\n }\nsizeNode\n description Size of a grid cell in pixels. Min is 10. Max is 200.\n extends abstractSetupNumberNode\n crux size\nrowsNode\n description Number of rows in the grid. Default is based on screen size.\n extends abstractSetupNumberNode\n crux rows\ncolumnsNode\n description Number of columns in the grid. Default is based on screen size.\n extends abstractSetupNumberNode\n crux columns\nseedNode\n description If you'd like reproducible runs set a seed for the random number generator.\n extends abstractSetupNumberNode\n crux seed\nticksPerSecondNode\n description Time in milliseconds of one step.\n extends abstractSetupNumberNode\n crux ticksPerSecond\nreportNode\n crux report\n description Define a custom report template.\n catchAllNodeType ohayoLineNode\n extends abstractSetupNode\n cells keywordCell\n javascript\n compile() {\n return \"\"\n }\nstyleNode\n description Optional CSS to load in BoardStyleComponent\n extends abstractSetupNode\n cells keywordCell\n crux style\n catchAllNodeType styleLineNode\n javascript\n compile() {\n return \"\"\n }\nquestionNode\n crux question\n description What are you trying to figure out?\n cells keywordCell\n catchAllCellType stringCell\n extends abstractSetupNode\nabstractInjectCommandNode\nfillNode\n description Fill all blank cells with this agent.\n extends abstractInjectCommandNode\n cells keywordCell emojiCell\n crux fill\ndrawNode\n extends abstractInjectCommandNode\n cells keywordCell\n crux draw\n catchAllNodeType drawLineNode\ninsertNode\n extends abstractInjectCommandNode\n cells keywordCell integerOrPercentCell emojiCell\n crux insert\ninsertAtNode\n extends insertNode\n description Insert at X Y\n cells keywordCell emojiCell positionCell positionCell\n crux insertAt\ninsertClusterNode\n extends insertNode\n crux insertCluster\n catchAllCellType integerCell\nrectangleDrawNode\n extends abstractInjectCommandNode\n cells keywordCell emojiCell integerCell integerCell\n catchAllCellType integerCell\n crux rectangle\npasteDrawNode\n extends abstractInjectCommandNode\n cells keywordCell\n crux paste\n catchAllNodeType pasteLineNode\ndrawLineNode\n catchAllCellType emojiCell\npasteLineNode\n catchAllCellType anyCell\n catchAllNodeType pasteLineNode\nagentDefinitionNode\n inScope abstractIgnoreNode abstractEventNode abstractAgentAttributeNode abstractBehaviorAttributeNode\n cells keywordCell\n catchAllNodeType errorNode\n compiler\n stringTemplate \n javascript\n compile() {\n const root = this.getRootNode()\n const name = root.agentKeywordMap[this.getWord(0)]\n const normal = super.compile()\n const behaviors = this.filter(node => node.getNodeTypeId() === \"abstractBehaviorAttributeNode\")\n .map(behavior => `\"${behavior.getLine()}\"`)\n .join(\",\")\n return `class ${name} extends Agent {\n icon = \"${this.getWord(0)}\"\n behaviors = [${behaviors}]\n ${normal}\n }`\n }\nabstractCommandNode\n cells keywordCell\nabstractSubjectObjectCommandNode\n extends abstractCommandNode\nreplaceWithCommandNode\n extends abstractSubjectObjectCommandNode\n crux replaceWith\n cells keywordCell emojiCell\nkickItCommandNode\n extends abstractSubjectObjectCommandNode\n crux kickIt\nshootCommandNode\n extends abstractSubjectObjectCommandNode\n crux shoot\npickItUpCommandNode\n extends abstractSubjectObjectCommandNode\n crux pickItUp\nspawnCommandNode\n crux spawn\n extends abstractCommandNode\n cells keywordCell emojiCell\n catchAllCellType positionCell\nmoveToEmptySpotCommandNode\n crux moveToEmptySpot\n extends abstractCommandNode\n cells keywordCell\nremoveCommandNode\n description Remove this agent from the board.\n crux remove\n extends abstractCommandNode\n cells keywordCell\njavascriptCommandNode\n description An escape hatch so you can write custom javascript in a pinch.\n extends abstractCommandNode\n crux javascript\n catchAllNodeType javascriptLineNode\n cells keywordCell\nalertCommandNode\n extends abstractCommandNode\n crux alert\n catchAllCellType stringCell\nlogCommandNode\n extends abstractCommandNode\n crux log\n catchAllCellType stringCell\nnarrateCommandNode\n extends abstractCommandNode\n crux narrate\n catchAllCellType stringCell\npauseCommandNode\n extends abstractCommandNode\n crux pause\ndecreaseCommandNode\n extends abstractCommandNode\n description Decrease a property by 1.\n crux decrease\n cells keywordCell propertyNameCell\nincreaseCommandNode\n extends abstractCommandNode\n description Increase a property by 1.\n crux increase\n cells keywordCell propertyNameCell\nmoveCommandNode\n extends abstractCommandNode\n crux move\nturnRandomlyCommandNode\n extends abstractCommandNode\n crux turnRandomly\njitterCommandNode\n extends abstractCommandNode\n crux jitter\nturnTowardCommandNode\n description Turn to the closest agent of a certain type.\n extends abstractCommandNode\n crux turnToward\n cells keywordCell emojiCell\nturnFromCommandNode\n description Turn away from the closest agent of a certain type.\n extends abstractCommandNode\n crux turnFrom\n cells keywordCell emojiCell\nlearnCommandNode\n crux learn\n extends abstractCommandNode\n cells keywordCell behaviorNameCell\nunlearnCommandNode\n crux unlearn\n extends abstractCommandNode\n cells keywordCell behaviorNameCell\nabstractAgentAttributeNode\n cells keywordCell\nabstractStringAttributeNode\n extends abstractAgentAttributeNode\n pattern ^\\w+ .+$\n catchAllCellType stringCell\n javascript\n compile() {\n return `${this.getWord(0)} = \"${this.getWord(1)}\"`\n }\nangleNode\n extends abstractStringAttributeNode\n cells keywordCell angleCell\n crux angle\nagentStyleNode\n description Provide custom CSS for an agent type.\n extends abstractStringAttributeNode\n cells keywordCell cssCell\n crux style\nagentHtmlNode\n description Provide custom HTML for each rendered agent.\n extends abstractStringAttributeNode\n cells keywordCell htmlCell\n crux html\nabstractBooleanAttributeNode\n description A boolean attribute.\n extends abstractAgentAttributeNode\n javascript\n compile() {\n return `${this.getWord(0)} = true`\n }\nnoPaletteNode\n extends abstractBooleanAttributeNode\n cruxFromId\n description Don't show this agent in the palette.\nsolidTraitNode\n description If set other agents won't pass through these.\n extends abstractBooleanAttributeNode\n crux solid\nbouncyTraitNode\n description If set other agents will bounce off this after a collision.\n extends abstractBooleanAttributeNode\n crux bouncy\nabstractIntegerAttributeNode\n extends abstractAgentAttributeNode\n description An integer attribute.\n cells keywordCell integerCell\n javascript\n compile() {\n return `${this.getWord(0)} = ${this.getWord(1)}`\n }\ncustomIntegerAttributeNode\n pattern ^\\w+ \\d+$\n extends abstractIntegerAttributeNode\nhealthNode\n extends abstractIntegerAttributeNode\n crux health\nsettingDefinitionNode\n description Define a configurable input.\n cells keywordCell settingValueCell\n pattern ^\\w+Setting .+$\nohayoLineNode\n description Data visualization code written for Ohayo.\n catchAllCellType ohayoCell\nstyleLineNode\n catchAllCellType cssCell\n catchAllNodeType styleLineNode\ntargetEmojiNode\n inScope abstractCommandNode\n cells emojiCell\nabstractEventNode\n cells keywordCell\n catchAllCellType probabilityCell\n javascript\n compile() {\n return ``\n }\nabstractInteractionEventNode\n extends abstractEventNode\n catchAllNodeType targetEmojiNode\nonHitNode\n extends abstractInteractionEventNode\n crux onHit\n description Define what happens when this agent collides with other agents.\nonTouchNode\n extends abstractInteractionEventNode\n crux onTouch\n description Define what happens when this agent is adjacent to other agents.\nonNeighborsNode\n description Define what happens when a certain amount of neighbors are nearby.\n extends abstractInteractionEventNode\n inScope emojiAndNeighborConditionNode\n crux onNeighbors\nonDeathNode\n extends abstractEventNode\n crux onDeath\n inScope abstractCommandNode\n description Define what happens when this agent runs out of health.\nonTickNode\n extends abstractEventNode\n crux onTick\n inScope abstractCommandNode\n description Define what happens each tick.\nemojiAndNeighborConditionNode\n inScope abstractCommandNode\n pattern ^.+ (<|>|=|<=|>=)+ .+$\n cells emojiCell conditionalOperatorCell neighborCountCell\nonExtinctNode\n crux onExtinct\n inScope abstractCommandNode\n cells keywordCell emojiCell\n description Define what happens when a type of agent goes extinct from the board.\n javascript\n compile() {\n return \"\"\n }\nabstractIgnoreNode\n tags doNotSynthesize\n javascript\n compile () {\n return \"\"\n }\ncommentNode\n extends abstractIgnoreNode\n catchAllCellType commentCell\n crux comment\n catchAllNodeType commentLineNode\ncommentAliasNode\n description Alternate alias for a comment.\n crux #\n extends commentNode\nblankLineNode\n extends abstractIgnoreNode\n description Blank lines compile do nothing.\n cells blankCell\n pattern ^$\ncommentLineNode\n catchAllCellType commentCell\njavascriptLineNode\n catchAllCellType javascriptCell\nabstractBehaviorAttributeNode\n cells behaviorNameCell\n pattern ^.*Behavior$\n javascript\n compile() {\n return \"\"\n }\nbehaviorDefinitionNode\n inScope abstractIgnoreNode abstractEventNode\n cells behaviorNameCell\n pattern ^.*Behavior$\n catchAllNodeType errorNode\n javascript\n compile() {\n return \"\"\n }","examples":"ants\n comment\n https://ccl.northwestern.edu/netlogo/models/Ants\n \n ⛰\n onTick 0.05\n spawn 🐜\n 🐜\n onTick\n jitter\n onHit\n 🥖\n pickItUp\n 🥖\n \n insert 3 🥖\n insert 1 ⛰\nbasketball\n question Is it better to shoot wildly or to bring it close to the basket?\n \n experiment Shoot rarely\n shotProbabilitySetting .02\n \n experiment\n shotProbabilitySetting .2\n \n experiment\n shotProbabilitySetting .4\n \n experiment Shoot right away\n shotProbabilitySetting .8\n \n 🏀\n onHit\n 🥅⛹️‍♂️\n narrate Blue scores!\n spawn 🏀 9⬇️ 15➡️\n spawn 🔵 18⬇️ 1➡️\n remove\n 🥅⛹️‍♀️\n narrate Red scores!\n spawn 🏀 9⬇️ 15➡️\n spawn 🔴 17⬇️ 1➡️\n remove\n \n \n moveEastToBlankSpotBehavior\n onTick\n moveToEmptySpot\n unlearn moveEastToBlankSpotBehavior\n \n \n 🔵\n angle East\n moveEastToBlankSpotBehavior\n 🔴\n angle East\n moveEastToBlankSpotBehavior\n \n \n ticksPerSecond 30\n \n hasBallBehavior\n comment Sprint toward net\n onTick .5\n turnToward net\n move\n narrate breaks toward the net.\n comment Shoot\n onTick shotProbabilitySetting\n turnToward net\n shoot\n narrate shoots!\n learn noBallBehavior\n unlearn hasBallBehavior\n comment Pass\n onTick .02\n turnToward team\n shoot\n narrate passes the ball!\n learn noBallBehavior\n unlearn hasBallBehavior\n \n noBallBehavior\n onTick .3\n turnToward 🏀\n move\n onHit\n 🏀\n pickItUp\n narrate has the ball\n learn hasBallBehavior\n unlearn noBallBehavior\n onTick .05\n turnFrom opponent\n move\n onTick .1\n turnFrom opponent\n jitter\n \n # Blue Team\n ⛹️‍♂️\n net 🥅⛹️‍♂️\n team ⛹️‍♂️\n opponent ⛹️‍♀️\n noBallBehavior\n \n # Red Team\n ⛹️‍♀️\n net 🥅⛹️‍♀️\n team ⛹️‍♀️\n opponent ⛹️‍♂️\n noBallBehavior\n \n # Baskets\n 🥅⛹️‍♂️\n html 🥅\n 🥅⛹️‍♀️\n html 🥅\n paste\n 🥅⛹️‍♂️ 8⬇️ 2➡️\n 🥅⛹️‍♀️ 8⬇️ 29➡️\n 🏀 9⬇️ 15➡️\n \n # Court\n 🪵\n solid\n rectangle 🪵 30 15 1 1\n \n size 30\n \n # Red Team\n paste\n ⛹️‍♀️ 9⬇️ 6➡️\n ⛹️‍♀️ 5⬇️ 6➡️\n ⛹️‍♀️ 11⬇️ 11➡️\n ⛹️‍♀️ 8⬇️ 11➡️\n ⛹️‍♀️ 5⬇️ 11➡️\n \n # Blue Team\n paste\n ⛹️‍♂️ 8⬇️ 25➡️\n ⛹️‍♂️ 6⬇️ 25➡️\n ⛹️‍♂️ 11⬇️ 20➡️\n ⛹️‍♂️ 7⬇️ 20➡️\n ⛹️‍♂️ 4⬇️ 20➡️\n \ncity\n ⛪️\n comment church\n 🏟\n comment stadium\n 🏥\n comment hospital\n 🏭\n comment factory\n 🏦\n comment bank\n 🏛\n comment courthouse\n 🏫\n comment school\n 🏡\n comment house\n 🏘\n comment houses\n 🎡\n comment park\n 🏪\n comment store\n 🚗\n onTick\n move\n move\n angle West\n 🚓\n onTick\n move\n move\n angle West\n 🚋\n comment subway\n onTick\n move\n move\n move\n angle West\n ⬜️\n comment road\n 🟩\n ⛳️\n size 20\n \n rectangle ⬜️ 10 20 0 0\n rectangle ⬜️ 10 1 0 10\n paste\n 🏛 9⬇️ 8➡️\n 🏭 1⬇️ 4➡️\n 🏭 1⬇️ 3➡️\n 🏭 1⬇️ 2➡️\n 🏭 1⬇️ 1➡️\n 🏡 18⬇️ 5➡️\n 🏡 18⬇️ 6➡️\n 🏡 18⬇️ 7➡️\n 🏡 18⬇️ 8➡️\ncops\n 🚗\n onTick .5\n jitter\n move\n 🚓\n onHit\n 🚗\n alert Got em!\n pause\n onTick .1\n turnToward 🚗\n move\n onTick .1\n move\n \n size 20\n ticksPerSecond 30\n \n paste\n 🚓 1⬇️ 1➡️\n 🚗 15⬇️ 5➡️\ncovid19\n 🦠\n \n question How long will the pandemic last?\n \n # Given someone has never been infected, what are the odds they get infected?\n succeptibilitySetting .95\n # What are odds of reinfection?\n reinfectionRateSetting .002\n \n # 1 is no lockdowns. 0 is total lockdown.\n freedomOfMovementSetting 1\n \n # What is starting population size?\n urbanPopulationSetting 150\n # What is starting rural population?\n ruralPopulationSetting 30\n # What is starting infected size?\n startingInfectedSetting 3\n \n # How many places can one get the vaccine?\n vaccineCentersSetting 5\n # How likely are people to seek the vaccine?\n vaccinationDesirabilitySetting .3\n # Given someone was vaxed, what are the odds they get infected?\n vaxSucceptibilitySetting .5\n \n experiment High Vaccination Rate, High Vaccine Efficacy\n vaccinationDesirabilitySetting .8\n vaxSucceptibilitySetting .05\n \n \n experiment High Vaccination Rate, Low Vaccine Efficacy\n vaccinationDesirabilitySetting .8\n vaxSucceptibilitySetting .75\n \n experiment Lockdown\n freedomOfMovementSetting .3\n \n experiment High Reinfection Rate\n reinfectionRateSetting .2\n \n \n insert startingInfectedSetting 🧟\n insert vaccineCentersSetting 💉\n \n insertCluster urbanPopulationSetting 🙍\n insert ruralPopulationSetting 🙍\n \n \n 🧟\n health 100\n onTick .03\n log recovered\n replaceWith 🦸‍♂️\n onTick\n decrease health\n jitter\n onDeath\n replaceWith 🪦\n \n 🦸‍♂️\n comment Recovered\n onTick\n jitter\n onTouch reinfectionRateSetting\n 🧟\n replaceWith 🧟\n \n lifeBehavior\n onTick freedomOfMovementSetting\n jitter\n \n seekVaccineBehavior\n onTick vaccinationDesirabilitySetting\n turnToward 💉\n move\n \n \n 🙍\n lifeBehavior\n seekVaccineBehavior\n onTouch innateImmunitySetting\n 🧟\n replaceWith 🧟\n 💉\n replaceWith 🧑🏽‍🚒\n \n \n 💉\n \n \n 🧑🏽‍🚒\n lifeBehavior\n onTouch vaxSucceptibilitySetting\n 🧟\n replaceWith 🧟\n \n \n \n \n onExtinct 🧟\n log No more cases.\n pause\n \n \n 🪦\n \n size 15\n ticksPerSecond 10\n \n report\n roughjs.line\n columns.keep 🧟\n roughjs.line Active Cases\n columns.keep 🪦\n roughjs.line Cumulative Deaths\n \n \n comment\n See Also\n - http://covidsim.eu/\n - http://modelingcommons.org/browse/one_model/6282#model_tabs_browse_info\n - https://github.com/maplerainresearch/covid19-sim-mesa/blob/master/model.py\n - https://www.frontiersin.org/articles/10.3389/fpubh.2020.563247/full\n - https://ncase.me/covid-19/\n - https://en.wikipedia.org/wiki/List_of_COVID-19_simulation_models\n \n \ncovid19simple\n question What is the effect of population density on pandemic duration?\n \n experiment\n insertCluster 100 🙍\n insertCluster 100 🙍\n insertCluster 100 🙍\n insertCluster 30 🙍\n insertCluster 30 🙍\n insertCluster 10 🙍\n \n experiment\n insert 200 🙍\n \n experiment\n insertCluster 200 🙍\n \n experiment\n insertCluster 200 🙍\n insert 200 🙍\n \n \n 🦠\n health 10\n onTick\n decrease health\n onDeath\n remove\n \n 🧟\n health 100\n onTick .03\n log recovered\n replaceWith 🦸‍♂️\n onTick\n decrease health\n jitter\n onDeath\n replaceWith 🪦\n \n 🦸‍♂️\n comment immune\n onTick\n jitter\n \n 🙍\n onTick\n jitter\n onTouch\n 🦠\n replaceWith 🧟\n 🧟\n replaceWith 🧟\n \n insert 1 🦠\n \n onExtinct 🧟\n log No more cases.\n pause\n \n \n 🪦\n \n size 15\n ticksPerSecond 10\n \n report\n roughjs.line\n columns.keep 🧟\n roughjs.line Active Cases\n columns.keep 🪦\n roughjs.line Cumulative Deaths\n \n \n comment\n See Also\n - http://covidsim.eu/\n - http://modelingcommons.org/browse/one_model/6282#model_tabs_browse_info\n - https://github.com/maplerainresearch/covid19-sim-mesa/blob/master/model.py\n - https://www.frontiersin.org/articles/10.3389/fpubh.2020.563247/full\n - https://ncase.me/covid-19/\n - https://en.wikipedia.org/wiki/List_of_COVID-19_simulation_models\n \neatTheBacon\n 🐕\n onTick .2\n turnToward 🥓\n move\n onTick .2\n jitter\n 🥓\n onTouch\n 🐕\n remove\n 🥦\n \n \n insert 1 🐕\n insert 3 🥓\n insert 10 🥦\n \nelevators\n 🛗\n onTick\n move\n move\n angle South\n bouncy\n onHit\n 🚶🏻\n pickItUp\n 🚶🏻\n angle West\n onTick\n move\n bouncy\n 🌾\n 🚪\n onTick .001\n spawn 🚶🏻\n 🪵\n solid\n 🚗\n \n \n size 15\n \n rectangle 🪵 20➡️ 47⬇️ 5 1\n rectangle 🌾 40➡️ 1⬇️ 0 48\n rectangle 🚪 1➡️ 45⬇️ 15 2\n paste\n 🛗 6⬇️ 19➡️\n 🛗 4⬇️ 22➡️\n 🛗 3⬇️ 20➡️\n 🛗 10⬇️ 13➡️\n 🛗 4⬇️ 9➡️\n 🛗 3⬇️ 11➡️\n 🚗 47⬇️ 30➡️\n 🚗 47⬇️ 28➡️\nfire\n question How fast do fires spread?\n \n 🌲\n onHit\n ⚡️\n replaceWith 🔥\n onTouch\n 🔥\n replaceWith 🔥\n \n ⚡️\n health 10\n onTick\n decrease health\n onDeath\n remove\n \n 🔥\n health 50\n onTick\n decrease health\n onDeath\n replaceWith ⬛️\n \n \n ⬛️\n comment Burnt forest\n html 🌲\n style filter:grayscale(100%);\n \n \n insert 50% 🌲\n onTick .3\n spawn ⚡️\nfireAdvanced\n question What is the effect of forest density on fire risk?\n \n experiment\n treeDensitySetting 10%\n \n experiment\n treeDensitySetting 20%\n \n experiment\n treeDensitySetting 40%\n \n experiment\n treeDensitySetting 80%\n \n catchFireSetting .3\n fireSpreadSetting .7\n fireLifetimeSetting 10\n lightningFrequencySetting .1\n \n 🌲\n onHit catchFireSetting\n ⚡️\n replaceWith 🔥\n onTouch fireSpreadSetting\n 🔥\n replaceWith 🔥\n \n ⚡️\n health 10\n onTick\n decrease health\n onDeath\n remove\n \n 🔥\n health fireLifetimeSetting\n onTick\n decrease health\n onDeath\n replaceWith ⬛️\n \n \n ⬛️\n comment Burnt forest\n html 🌲\n style filter:grayscale(100%);\n \n \n insert treeDensitySetting 🌲\n onTick lightningFrequencySetting\n spawn ⚡️\n \ngameOfLife\n question Can simple rules produce complex effects?\n \n ⬛️\n onNeighbors\n ⬛️ < 2\n replaceWith ◻️\n ⬛️ > 3\n replaceWith ◻️\n \n ◻️\n onNeighbors\n ⬛️ = 3\n replaceWith ⬛️\n \n insert 10% ⬛️\n fill ◻️\n size 15\ngameOfLifeAdvanced\n # Conway's Game of Life\n \n experiment\n neighborSetting 2\n \n experiment\n neighborSetting 3\n \n experiment\n neighborSetting 4\n \n experiment\n neighborSetting 5\n \n ⬛️\n onNeighbors\n ⬛️ < 2\n replaceWith ◻️\n ⬛️ > neighborSetting\n replaceWith ◻️\n \n ◻️\n onNeighbors\n ⬛️ = 3\n replaceWith ⬛️\n \n insert 10% ⬛️\n fill ◻️\n size 15\ngospersGliderGun\n ⬛️\n onNeighbors\n ⬛️ < 2\n replaceWith ◻️\n ⬛️ > 3\n replaceWith ◻️\n \n ◻️\n onNeighbors\n ⬛️ = 3\n replaceWith ⬛️\n \n # Gosper's Glider Gun\n \n draw\n ⬛️ \n ⬛️ ⬛️ \n ⬛️ ⬛️ ⬛️ ⬛️ \n ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️\n ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️\n ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ \n ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ \n ⬛️ ⬛️ \n ⬛️ ⬛️ \n \n \n fill ◻️\n \nmoths\n question Can you move the moths from one light to the other?\n \n 🦋\n onTick .1\n jitter\n move\n onTick .2\n turnToward 💡\n move\n move\n 💡\n \n ticksPerSecond 10\n size 20\n style\n .BoardComponent {background:black;}\n \n insert 10 🦋\n insert 2 💡\n \n comment\n http://www.netlogoweb.org/launch#http://www.netlogoweb.org/assets/modelslib/Sample%20Models/Biology/Moths.nlogo\nninjas\n 🤺\n health 100\n onTick\n decrease health\n jitter\n \n 🥷\n health 100\n onTick\n decrease health\n jitter\n \n insert 50 🤺\n insert 50 🥷\npong\n \n \n 🏐\n bouncy\n onTick\n move\n angle West\n 🏓\n angle East\n onHit\n 🏐\n kickIt\n 🏸\n angle West\n onHit\n 🏐\n kickIt\n 🪵\n solid\n \n size 20\n ticksPerSecond 10\n \n rectangle 🪵 30 15 5 5\n paste\n 🏓 13⬇️ 6➡️\n 🏸 13⬇️ 33➡️\n 🏐 13⬇️ 19➡️\n \n \npoolTable\n comment\n Needs balls to collide. Acceleration.\n \n 🎱\n bouncy\n onHit\n 🎱\n kickIt\n 🏐\n kickIt\n \n 🪵\n solid\n \n 🏐\n bouncy\n onTick .1\n turnRandomly\n kickIt\n onTick .5\n kickIt\n onHit\n 🎱\n kickIt\n angle West\n \n rectangle 🪵 40 20 0 7\n paste\n 🏐 17⬇️ 32➡️\n 🎱 12⬇️ 7➡️\n 🎱 14⬇️ 7➡️\n 🎱 16⬇️ 7➡️\n 🎱 18⬇️ 7➡️\n 🎱 20⬇️ 7➡️\n 🎱 22⬇️ 7➡️\n 🎱 21⬇️ 8➡️\n 🎱 19⬇️ 8➡️\n 🎱 17⬇️ 8➡️\n 🎱 15⬇️ 8➡️\n 🎱 13⬇️ 8➡️\n 🎱 20⬇️ 9➡️\n 🎱 18⬇️ 9➡️\n 🎱 16⬇️ 9➡️\n 🎱 14⬇️ 9➡️\n 🎱 15⬇️ 10➡️\n 🎱 17⬇️ 10➡️\n 🎱 19⬇️ 10➡️\n 🎱 18⬇️ 11➡️\n 🎱 16⬇️ 11➡️\n 🎱 17⬇️ 12➡️\nsoccer\n \n \n ⚽️\n onHit\n 🥅\n pause\n alert GOAAAAAAAAALLLL!\n bouncy\n \n ⛹️‍♂️\n onTick\n jitter\n onHit\n ⚽️\n kickIt\n \n ⛹️‍♀️\n onTick\n jitter\n onHit\n ⚽️\n kickIt\n 🥅\n 🪵\n solid\n \n size 20\n ticksPerSecond 10\n \n rectangle 🪵 30 15 5 5\n \n paste\n 🥅 13⬇️ 6➡️\n 🥅 13⬇️ 33➡️\n ⚽️ 13⬇️ 19➡️\n \n paste\n ⛹️‍♀️ 17⬇️ 14➡️\n ⛹️‍♀️ 17⬇️ 17➡️\n ⛹️‍♀️ 13⬇️ 17➡️\n ⛹️‍♀️ 13⬇️ 14➡️\n ⛹️‍♀️ 8⬇️ 14➡️\n ⛹️‍♀️ 8⬇️ 17➡️\n ⛹️‍♀️ 10⬇️ 14➡️\n ⛹️‍♀️ 9⬇️ 10➡️\n ⛹️‍♀️ 13⬇️ 8➡️\n ⛹️‍♀️ 13⬇️ 10➡️\n ⛹️‍♀️ 17⬇️ 10➡️\n \n paste\n ⛹️‍♂️ 13⬇️ 31➡️\n ⛹️‍♂️ 17⬇️ 28➡️\n ⛹️‍♂️ 13⬇️ 28➡️\n ⛹️‍♂️ 8⬇️ 29➡️\n ⛹️‍♂️ 8⬇️ 25➡️\n ⛹️‍♂️ 10⬇️ 25➡️\n ⛹️‍♂️ 13⬇️ 25➡️\n ⛹️‍♂️ 17⬇️ 25➡️\n ⛹️‍♂️ 17⬇️ 21➡️\n ⛹️‍♂️ 8⬇️ 21➡️\n ⛹️‍♂️ 13⬇️ 21➡️\n \nstartupIdeas\n question What is the effect of ideas vs ideas with revenue?\n \n 👨‍💼🔖\n comment person with an idea\n onTick\n jitter\n \n 👨‍💼💰\n comment peron with an idea\n that is making money\n onTick\n jitter\n \n 👨‍\n onTick .1\n jitter\n onTick .1\n turnToward 👨‍💼💰\n move\n \n \n size 10\n insert 200 👨‍\n insert 30 👨‍💼🔖\n insert 3 👨‍💼💰\n \n \nstore\n 🚶🏻\n onTick\n move\n angle North\n 🛒\n 🚪\n onTick .1\n spawn 🚶🏻\n 🪵\n solid\n \n size 25\n \n rectangle 🪵 30 15 3 3\n paste\n 🚪 16⬇️ 17➡️\n \nvirus\n question What might the spread of a simple virus look like?\n \n 🧟\n health 100\n onTick .9\n decrease health\n jitter\n onTick .01\n log recovered\n replaceWith 🦸‍♂️\n onDeath\n replaceWith 🪦\n \n 🙍\n onTick\n jitter\n onTouch\n 🧟\n replaceWith 🧟\n \n 🦸‍♂️\n onTick\n jitter\n \n insert 10% 🙍\n insert 1 🧟\n \n 🪦\n \n onExtinct 🧟\n log No more cases.\n pause\nwaves\n 🌊\n onTick\n move\n angle South\n \n size 25\n ticksPerSecond 5\n \n rectangle 🌊 100 1 0\n rectangle 🌊 100 1 0 6\n rectangle 🌊 100 1 0 11\nzombies\n question Can you protect the family from the zombies?\n \n 🧟‍♂️\n noPalette\n onTick\n jitter\n onHit\n 🪃\n replaceWith 🪦\n 💣\n replaceWith 🪦\n 👨‍👩‍👧‍👦\n pause\n alert TheyGotYou!\n \n 🧱\n solid\n \n 🔫\n onTick .1\n spawn 🪃\n \n 🪃\n noPalette\n angle West\n onTick\n move\n \n 💣\n \n 🪦\n noPalette\n comment Dead zombie\n \n 👨‍👩‍👧‍👦\n noPalette\n \n size 30\n ticksPerSecond 10\n \n insertCluster 30 🧟‍♂️ 1 1\n paste\n 👨‍👩‍👧‍👦 12⬇️ 11➡️"} \ No newline at end of file +const SimConstants = {"grammar":"anyCell\nbooleanCell\nstringCell\n highlightScope string\nsettingValueCell\n highlightScope constant.numeric\ncssCell\n highlightScope string\njavascriptCell\n highlightScope string\nhtmlCell\n highlightScope string\nemojiCell\n highlightScope string\nohayoCell\n highlightScope string\nblankCell\ncodeCell\n highlightScope comment\ncommentCell\n highlightScope comment\nkeywordCell\n highlightScope keyword\ntextCell\n highlightScope string\nintegerCell\n highlightScope constant.numeric\nbehaviorNameCell\n highlightScope keyword\nconditionalOperatorCell\n highlightScope keyword\n enum < > = <= >=\npositionCell\n highlightScope constant.numeric\nneighborCountCell\n extends integerCell\n min 0\n max 8\nintegerOrPercentCell\n highlightScope constant.numeric\nprobabilityCell\n description A number between 0 and 1\n highlightScope constant.numeric\npropertyNameCell\n highlightScope keyword\nangleCell\n enum North South East West NorthWest NorthEast SouthWest SouthEast\n highlightScope constant.numeric\nerrorParser\n baseParser errorParser\nsimojiParser\n extensions simoji\n description A Tree Language that compiles to a TreeComponentFramework app.\n root\n inScope abstractIgnoreParser abstractSetupParser abstractInjectCommandParser onTickParser onExtinctParser behaviorDefinitionParser experimentParser settingDefinitionParser\n catchAllParser agentDefinitionParser\n compilesTo javascript\n example\n 🦋\n onTick .1\n turnRandomly\n move\n onTick .2\n turnToward 💡\n move\n 💡\n \n insert 10 🦋\n insert 2 💡\n javascript\n get agentTypes() {\n return this.filter(node => node.parserId === \"agentDefinitionParser\")\n }\nexperimentParser\n cruxFromId\n cells keywordCell\n inScope abstractIgnoreParser abstractSetupParser abstractInjectCommandParser onTickParser onExtinctParser settingDefinitionParser\n catchAllCellType stringCell\nabstractSetupParser\natTimeParser\n cruxFromId\n description Run commands at a certain tick.\n cells keywordCell integerCell\n extends abstractSetupParser\n inScope abstractInjectCommandParser\nabstractSetupNumberParser\n cells keywordCell integerCell\n extends abstractSetupParser\n javascript\n compile() {\n return \"\"\n }\nsizeParser\n description Size of a grid cell in pixels. Min is 10. Max is 200.\n extends abstractSetupNumberParser\n cruxFromId\nrowsParser\n description Number of rows in the grid. Default is based on screen size.\n extends abstractSetupNumberParser\n cruxFromId\ncolumnsParser\n description Number of columns in the grid. Default is based on screen size.\n extends abstractSetupNumberParser\n cruxFromId\nseedParser\n description If you'd like reproducible runs set a seed for the random number generator.\n extends abstractSetupNumberParser\n cruxFromId\nticksPerSecondParser\n description Time in milliseconds of one step.\n extends abstractSetupNumberParser\n cruxFromId\nreportParser\n cruxFromId\n description Define a custom report template.\n catchAllParser ohayoLineParser\n extends abstractSetupParser\n cells keywordCell\n javascript\n compile() {\n return \"\"\n }\nstyleParser\n description Optional CSS to load in BoardStyleComponent\n extends abstractSetupParser\n cells keywordCell\n cruxFromId\n catchAllParser styleLineParser\n javascript\n compile() {\n return \"\"\n }\nquestionParser\n cruxFromId\n description What are you trying to figure out?\n cells keywordCell\n catchAllCellType stringCell\n extends abstractSetupParser\nabstractInjectCommandParser\nfillParser\n description Fill all blank cells with this agent.\n extends abstractInjectCommandParser\n cells keywordCell emojiCell\n cruxFromId\ndrawParser\n extends abstractInjectCommandParser\n cells keywordCell\n cruxFromId\n catchAllParser drawLineParser\ninsertParser\n extends abstractInjectCommandParser\n cells keywordCell integerOrPercentCell emojiCell\n cruxFromId\ninsertAtParser\n extends insertParser\n description Insert at X Y\n cells keywordCell emojiCell positionCell positionCell\n cruxFromId\ninsertClusterParser\n extends insertParser\n cruxFromId\n catchAllCellType integerCell\nrectangleDrawParser\n extends abstractInjectCommandParser\n cells keywordCell emojiCell integerCell integerCell\n catchAllCellType integerCell\n crux rectangle\npasteDrawParser\n extends abstractInjectCommandParser\n cells keywordCell\n crux paste\n catchAllParser pasteLineParser\ndrawLineParser\n catchAllCellType emojiCell\npasteLineParser\n catchAllCellType anyCell\n catchAllParser pasteLineParser\nagentDefinitionParser\n inScope abstractIgnoreParser abstractEventParser abstractAgentAttributeParser behaviorAttributeParser\n cells keywordCell\n catchAllParser errorParser\n compiler\n stringTemplate \n javascript\n compile() {\n const root = this.root\n const name = root.agentKeywordMap[this.firstWord]\n const normal = super.compile()\n const behaviors = this.filter(node => node.parserId === \"behaviorAttributeParser\")\n .map(behavior => `\"${behavior.getLine()}\"`)\n .join(\",\")\n return `class ${name} extends Agent {\n icon = \"${this.firstWord}\"\n behaviors = [${behaviors}]\n ${normal}\n }`\n }\nabstractCommandParser\n cells keywordCell\nabstractSubjectObjectCommandParser\n extends abstractCommandParser\nreplaceWithCommandParser\n extends abstractSubjectObjectCommandParser\n crux replaceWith\n cells keywordCell emojiCell\nkickItCommandParser\n extends abstractSubjectObjectCommandParser\n crux kickIt\nshootCommandParser\n extends abstractSubjectObjectCommandParser\n crux shoot\npickItUpCommandParser\n extends abstractSubjectObjectCommandParser\n crux pickItUp\nspawnCommandParser\n crux spawn\n extends abstractCommandParser\n cells keywordCell emojiCell\n catchAllCellType positionCell\nmoveToEmptySpotCommandParser\n crux moveToEmptySpot\n extends abstractCommandParser\n cells keywordCell\nremoveCommandParser\n description Remove this agent from the board.\n crux remove\n extends abstractCommandParser\n cells keywordCell\njavascriptCommandParser\n description An escape hatch so you can write custom javascript in a pinch.\n extends abstractCommandParser\n crux javascript\n catchAllParser javascriptLineParser\n cells keywordCell\nalertCommandParser\n extends abstractCommandParser\n crux alert\n catchAllCellType stringCell\nlogCommandParser\n extends abstractCommandParser\n crux log\n catchAllCellType stringCell\nnarrateCommandParser\n extends abstractCommandParser\n crux narrate\n catchAllCellType stringCell\npauseCommandParser\n extends abstractCommandParser\n crux pause\ndecreaseCommandParser\n extends abstractCommandParser\n description Decrease a property by 1.\n crux decrease\n cells keywordCell propertyNameCell\nincreaseCommandParser\n extends abstractCommandParser\n description Increase a property by 1.\n crux increase\n cells keywordCell propertyNameCell\nmoveCommandParser\n extends abstractCommandParser\n crux move\nturnRandomlyCommandParser\n extends abstractCommandParser\n crux turnRandomly\njitterCommandParser\n extends abstractCommandParser\n crux jitter\nturnTowardCommandParser\n description Turn to the closest agent of a certain type.\n extends abstractCommandParser\n crux turnToward\n cells keywordCell emojiCell\nturnFromCommandParser\n description Turn away from the closest agent of a certain type.\n extends abstractCommandParser\n crux turnFrom\n cells keywordCell emojiCell\nlearnCommandParser\n crux learn\n extends abstractCommandParser\n cells keywordCell behaviorNameCell\nunlearnCommandParser\n crux unlearn\n extends abstractCommandParser\n cells keywordCell behaviorNameCell\nabstractAgentAttributeParser\n cells keywordCell\nstringAttributeParser\n extends abstractAgentAttributeParser\n pattern ^\\w+ .+$\n catchAllCellType stringCell\n javascript\n compile() {\n return `${this.firstWord} = \"${this.getWord(1)}\"`\n }\nangleParser\n extends stringAttributeParser\n cells keywordCell angleCell\n cruxFromId\nagentStyleParser\n description Provide custom CSS for an agent type.\n extends stringAttributeParser\n cells keywordCell cssCell\n crux style\nagentHtmlParser\n description Provide custom HTML for each rendered agent.\n extends stringAttributeParser\n cells keywordCell htmlCell\n crux html\nabstractBooleanAttributeParser\n description A boolean attribute.\n extends abstractAgentAttributeParser\n javascript\n compile() {\n return `${this.firstWord} = true`\n }\nnoPaletteParser\n extends abstractBooleanAttributeParser\n cruxFromId\n description Don't show this agent in the palette.\nsolidTraitParser\n description If set other agents won't pass through these.\n extends abstractBooleanAttributeParser\n crux solid\nbouncyTraitParser\n description If set other agents will bounce off this after a collision.\n extends abstractBooleanAttributeParser\n crux bouncy\nabstractIntegerAttributeParser\n extends abstractAgentAttributeParser\n description An integer attribute.\n cells keywordCell integerCell\n javascript\n compile() {\n return `${this.firstWord} = ${this.getWord(1)}`\n }\ncustomIntegerAttributeParser\n pattern ^\\w+ \\d+$\n extends abstractIntegerAttributeParser\nhealthParser\n extends abstractIntegerAttributeParser\n cruxFromId\nsettingDefinitionParser\n description Define a configurable input.\n cells keywordCell settingValueCell\n pattern ^\\w+Setting .+$\nohayoLineParser\n description Data visualization code written for Ohayo.\n catchAllCellType ohayoCell\nstyleLineParser\n catchAllCellType cssCell\n catchAllParser styleLineParser\ntargetEmojiParser\n inScope abstractCommandParser\n cells emojiCell\nabstractEventParser\n cells keywordCell\n catchAllCellType probabilityCell\n javascript\n compile() {\n return ``\n }\nabstractInteractionEventParser\n extends abstractEventParser\n catchAllParser targetEmojiParser\nonHitParser\n extends abstractInteractionEventParser\n cruxFromId\n description Define what happens when this agent collides with other agents.\nonTouchParser\n extends abstractInteractionEventParser\n cruxFromId\n description Define what happens when this agent is adjacent to other agents.\nonNeighborsParser\n description Define what happens when a certain amount of neighbors are nearby.\n extends abstractInteractionEventParser\n inScope emojiAndNeighborConditionParser\n cruxFromId\nonDeathParser\n extends abstractEventParser\n cruxFromId\n inScope abstractCommandParser\n description Define what happens when this agent runs out of health.\nonTickParser\n extends abstractEventParser\n cruxFromId\n inScope abstractCommandParser\n description Define what happens each tick.\nemojiAndNeighborConditionParser\n inScope abstractCommandParser\n pattern ^.+ (<|>|=|<=|>=)+ .+$\n cells emojiCell conditionalOperatorCell neighborCountCell\nonExtinctParser\n cruxFromId\n inScope abstractCommandParser\n cells keywordCell emojiCell\n description Define what happens when a type of agent goes extinct from the board.\n javascript\n compile() {\n return \"\"\n }\nabstractIgnoreParser\n tags doNotSynthesize\n javascript\n compile () {\n return \"\"\n }\ncommentParser\n extends abstractIgnoreParser\n catchAllCellType commentCell\n cruxFromId\n catchAllParser commentLineParser\ncommentAliasParser\n description Alternate alias for a comment.\n crux #\n extends commentParser\nblankLineParser\n extends abstractIgnoreParser\n description Blank lines compile do nothing.\n cells blankCell\n pattern ^$\ncommentLineParser\n catchAllCellType commentCell\njavascriptLineParser\n catchAllCellType javascriptCell\nbehaviorAttributeParser\n cells behaviorNameCell\n pattern ^.*Behavior$\n javascript\n compile() {\n return \"\"\n }\nbehaviorDefinitionParser\n inScope abstractIgnoreParser abstractEventParser\n cells behaviorNameCell\n pattern ^.*Behavior$\n catchAllParser errorParser\n javascript\n compile() {\n return \"\"\n }","examples":"ants\n comment\n https://ccl.northwestern.edu/netlogo/models/Ants\n \n ⛰\n onTick 0.05\n spawn 🐜\n 🐜\n onTick\n jitter\n onHit\n 🥖\n pickItUp\n 🥖\n \n insert 3 🥖\n insert 1 ⛰\nbasketball\n question Is it better to shoot wildly or to bring it close to the basket?\n \n experiment Shoot rarely\n shotProbabilitySetting .02\n \n experiment\n shotProbabilitySetting .2\n \n experiment\n shotProbabilitySetting .4\n \n experiment Shoot right away\n shotProbabilitySetting .8\n \n 🏀\n onHit\n 🥅⛹️‍♂️\n narrate Blue scores!\n spawn 🏀 9⬇️ 15➡️\n spawn 🔵 18⬇️ 1➡️\n remove\n 🥅⛹️‍♀️\n narrate Red scores!\n spawn 🏀 9⬇️ 15➡️\n spawn 🔴 17⬇️ 1➡️\n remove\n \n \n moveEastToBlankSpotBehavior\n onTick\n moveToEmptySpot\n unlearn moveEastToBlankSpotBehavior\n \n \n 🔵\n angle East\n moveEastToBlankSpotBehavior\n 🔴\n angle East\n moveEastToBlankSpotBehavior\n \n \n ticksPerSecond 30\n \n hasBallBehavior\n comment Sprint toward net\n onTick .5\n turnToward net\n move\n narrate breaks toward the net.\n comment Shoot\n onTick shotProbabilitySetting\n turnToward net\n shoot\n narrate shoots!\n learn noBallBehavior\n unlearn hasBallBehavior\n comment Pass\n onTick .02\n turnToward team\n shoot\n narrate passes the ball!\n learn noBallBehavior\n unlearn hasBallBehavior\n \n noBallBehavior\n onTick .3\n turnToward 🏀\n move\n onHit\n 🏀\n pickItUp\n narrate has the ball\n learn hasBallBehavior\n unlearn noBallBehavior\n onTick .05\n turnFrom opponent\n move\n onTick .1\n turnFrom opponent\n jitter\n \n # Blue Team\n ⛹️‍♂️\n net 🥅⛹️‍♂️\n team ⛹️‍♂️\n opponent ⛹️‍♀️\n noBallBehavior\n \n # Red Team\n ⛹️‍♀️\n net 🥅⛹️‍♀️\n team ⛹️‍♀️\n opponent ⛹️‍♂️\n noBallBehavior\n \n # Baskets\n 🥅⛹️‍♂️\n html 🥅\n 🥅⛹️‍♀️\n html 🥅\n paste\n 🥅⛹️‍♂️ 8⬇️ 2➡️\n 🥅⛹️‍♀️ 8⬇️ 29➡️\n 🏀 9⬇️ 15➡️\n \n # Court\n 🪵\n solid\n rectangle 🪵 30 15 1 1\n \n size 30\n \n # Red Team\n paste\n ⛹️‍♀️ 9⬇️ 6➡️\n ⛹️‍♀️ 5⬇️ 6➡️\n ⛹️‍♀️ 11⬇️ 11➡️\n ⛹️‍♀️ 8⬇️ 11➡️\n ⛹️‍♀️ 5⬇️ 11➡️\n \n # Blue Team\n paste\n ⛹️‍♂️ 8⬇️ 25➡️\n ⛹️‍♂️ 6⬇️ 25➡️\n ⛹️‍♂️ 11⬇️ 20➡️\n ⛹️‍♂️ 7⬇️ 20➡️\n ⛹️‍♂️ 4⬇️ 20➡️\n \ncity\n ⛪️\n comment church\n 🏟\n comment stadium\n 🏥\n comment hospital\n 🏭\n comment factory\n 🏦\n comment bank\n 🏛\n comment courthouse\n 🏫\n comment school\n 🏡\n comment house\n 🏘\n comment houses\n 🎡\n comment park\n 🏪\n comment store\n 🚗\n onTick\n move\n move\n angle West\n 🚓\n onTick\n move\n move\n angle West\n 🚋\n comment subway\n onTick\n move\n move\n move\n angle West\n ⬜️\n comment road\n 🟩\n ⛳️\n size 20\n \n rectangle ⬜️ 10 20 0 0\n rectangle ⬜️ 10 1 0 10\n paste\n 🏛 9⬇️ 8➡️\n 🏭 1⬇️ 4➡️\n 🏭 1⬇️ 3➡️\n 🏭 1⬇️ 2➡️\n 🏭 1⬇️ 1➡️\n 🏡 18⬇️ 5➡️\n 🏡 18⬇️ 6➡️\n 🏡 18⬇️ 7➡️\n 🏡 18⬇️ 8➡️\ncops\n 🚗\n onTick .5\n jitter\n move\n 🚓\n onHit\n 🚗\n alert Got em!\n pause\n onTick .1\n turnToward 🚗\n move\n onTick .1\n move\n \n size 20\n ticksPerSecond 30\n \n paste\n 🚓 1⬇️ 1➡️\n 🚗 15⬇️ 5➡️\ncovid19\n 🦠\n \n question How long will the pandemic last?\n \n # Given someone has never been infected, what are the odds they get infected?\n succeptibilitySetting .95\n # What are odds of reinfection?\n reinfectionRateSetting .002\n \n # 1 is no lockdowns. 0 is total lockdown.\n freedomOfMovementSetting 1\n \n # What is starting population size?\n urbanPopulationSetting 150\n # What is starting rural population?\n ruralPopulationSetting 30\n # What is starting infected size?\n startingInfectedSetting 3\n \n # How many places can one get the vaccine?\n vaccineCentersSetting 5\n # How likely are people to seek the vaccine?\n vaccinationDesirabilitySetting .3\n # Given someone was vaxed, what are the odds they get infected?\n vaxSucceptibilitySetting .5\n \n experiment High Vaccination Rate, High Vaccine Efficacy\n vaccinationDesirabilitySetting .8\n vaxSucceptibilitySetting .05\n \n \n experiment High Vaccination Rate, Low Vaccine Efficacy\n vaccinationDesirabilitySetting .8\n vaxSucceptibilitySetting .75\n \n experiment Lockdown\n freedomOfMovementSetting .3\n \n experiment High Reinfection Rate\n reinfectionRateSetting .2\n \n \n insert startingInfectedSetting 🧟\n insert vaccineCentersSetting 💉\n \n insertCluster urbanPopulationSetting 🙍\n insert ruralPopulationSetting 🙍\n \n \n 🧟\n health 100\n onTick .03\n log recovered\n replaceWith 🦸‍♂️\n onTick\n decrease health\n jitter\n onDeath\n replaceWith 🪦\n \n 🦸‍♂️\n comment Recovered\n onTick\n jitter\n onTouch reinfectionRateSetting\n 🧟\n replaceWith 🧟\n \n lifeBehavior\n onTick freedomOfMovementSetting\n jitter\n \n seekVaccineBehavior\n onTick vaccinationDesirabilitySetting\n turnToward 💉\n move\n \n \n 🙍\n lifeBehavior\n seekVaccineBehavior\n onTouch innateImmunitySetting\n 🧟\n replaceWith 🧟\n 💉\n replaceWith 🧑🏽‍🚒\n \n \n 💉\n \n \n 🧑🏽‍🚒\n lifeBehavior\n onTouch vaxSucceptibilitySetting\n 🧟\n replaceWith 🧟\n \n \n \n \n onExtinct 🧟\n log No more cases.\n pause\n \n \n 🪦\n \n size 15\n ticksPerSecond 10\n \n report\n roughjs.line\n columns.keep 🧟\n roughjs.line Active Cases\n columns.keep 🪦\n roughjs.line Cumulative Deaths\n \n \n comment\n See Also\n - http://covidsim.eu/\n - http://modelingcommons.org/browse/one_model/6282#model_tabs_browse_info\n - https://github.com/maplerainresearch/covid19-sim-mesa/blob/master/model.py\n - https://www.frontiersin.org/articles/10.3389/fpubh.2020.563247/full\n - https://ncase.me/covid-19/\n - https://en.wikipedia.org/wiki/List_of_COVID-19_simulation_models\n \n \ncovid19simple\n question What is the effect of population density on pandemic duration?\n \n experiment\n insertCluster 100 🙍\n insertCluster 100 🙍\n insertCluster 100 🙍\n insertCluster 30 🙍\n insertCluster 30 🙍\n insertCluster 10 🙍\n \n experiment\n insert 200 🙍\n \n experiment\n insertCluster 200 🙍\n \n experiment\n insertCluster 200 🙍\n insert 200 🙍\n \n \n 🦠\n health 10\n onTick\n decrease health\n onDeath\n remove\n \n 🧟\n health 100\n onTick .03\n log recovered\n replaceWith 🦸‍♂️\n onTick\n decrease health\n jitter\n onDeath\n replaceWith 🪦\n \n 🦸‍♂️\n comment immune\n onTick\n jitter\n \n 🙍\n onTick\n jitter\n onTouch\n 🦠\n replaceWith 🧟\n 🧟\n replaceWith 🧟\n \n insert 1 🦠\n \n onExtinct 🧟\n log No more cases.\n pause\n \n \n 🪦\n \n size 15\n ticksPerSecond 10\n \n report\n roughjs.line\n columns.keep 🧟\n roughjs.line Active Cases\n columns.keep 🪦\n roughjs.line Cumulative Deaths\n \n \n comment\n See Also\n - http://covidsim.eu/\n - http://modelingcommons.org/browse/one_model/6282#model_tabs_browse_info\n - https://github.com/maplerainresearch/covid19-sim-mesa/blob/master/model.py\n - https://www.frontiersin.org/articles/10.3389/fpubh.2020.563247/full\n - https://ncase.me/covid-19/\n - https://en.wikipedia.org/wiki/List_of_COVID-19_simulation_models\n \neatTheBacon\n 🐕\n onTick .2\n turnToward 🥓\n move\n onTick .2\n jitter\n 🥓\n onTouch\n 🐕\n remove\n 🥦\n \n \n insert 1 🐕\n insert 3 🥓\n insert 10 🥦\n \nelevators\n 🛗\n onTick\n move\n move\n angle South\n bouncy\n onHit\n 🚶🏻\n pickItUp\n 🚶🏻\n angle West\n onTick\n move\n bouncy\n 🌾\n 🚪\n onTick .001\n spawn 🚶🏻\n 🪵\n solid\n 🚗\n \n \n size 15\n \n rectangle 🪵 20➡️ 47⬇️ 5 1\n rectangle 🌾 40➡️ 1⬇️ 0 48\n rectangle 🚪 1➡️ 45⬇️ 15 2\n paste\n 🛗 6⬇️ 19➡️\n 🛗 4⬇️ 22➡️\n 🛗 3⬇️ 20➡️\n 🛗 10⬇️ 13➡️\n 🛗 4⬇️ 9➡️\n 🛗 3⬇️ 11➡️\n 🚗 47⬇️ 30➡️\n 🚗 47⬇️ 28➡️\nfire\n question How fast do fires spread?\n \n 🌲\n onHit\n ⚡️\n replaceWith 🔥\n onTouch\n 🔥\n replaceWith 🔥\n \n ⚡️\n health 10\n onTick\n decrease health\n onDeath\n remove\n \n 🔥\n health 50\n onTick\n decrease health\n onDeath\n replaceWith ⬛️\n \n \n ⬛️\n comment Burnt forest\n html 🌲\n style filter:grayscale(100%);\n \n \n insert 50% 🌲\n onTick .3\n spawn ⚡️\nfireAdvanced\n question What is the effect of forest density on fire risk?\n \n experiment\n treeDensitySetting 10%\n \n experiment\n treeDensitySetting 20%\n \n experiment\n treeDensitySetting 40%\n \n experiment\n treeDensitySetting 80%\n \n catchFireSetting .3\n fireSpreadSetting .7\n fireLifetimeSetting 10\n lightningFrequencySetting .1\n \n 🌲\n onHit catchFireSetting\n ⚡️\n replaceWith 🔥\n onTouch fireSpreadSetting\n 🔥\n replaceWith 🔥\n \n ⚡️\n health 10\n onTick\n decrease health\n onDeath\n remove\n \n 🔥\n health fireLifetimeSetting\n onTick\n decrease health\n onDeath\n replaceWith ⬛️\n \n \n ⬛️\n comment Burnt forest\n html 🌲\n style filter:grayscale(100%);\n \n \n insert treeDensitySetting 🌲\n onTick lightningFrequencySetting\n spawn ⚡️\n \ngameOfLife\n question Can simple rules produce complex effects?\n \n ⬛️\n onNeighbors\n ⬛️ < 2\n replaceWith ◻️\n ⬛️ > 3\n replaceWith ◻️\n \n ◻️\n onNeighbors\n ⬛️ = 3\n replaceWith ⬛️\n \n insert 10% ⬛️\n fill ◻️\n size 15\ngameOfLifeAdvanced\n # Conway's Game of Life\n \n experiment\n neighborSetting 2\n \n experiment\n neighborSetting 3\n \n experiment\n neighborSetting 4\n \n experiment\n neighborSetting 5\n \n ⬛️\n onNeighbors\n ⬛️ < 2\n replaceWith ◻️\n ⬛️ > neighborSetting\n replaceWith ◻️\n \n ◻️\n onNeighbors\n ⬛️ = 3\n replaceWith ⬛️\n \n insert 10% ⬛️\n fill ◻️\n size 15\ngospersGliderGun\n ⬛️\n onNeighbors\n ⬛️ < 2\n replaceWith ◻️\n ⬛️ > 3\n replaceWith ◻️\n \n ◻️\n onNeighbors\n ⬛️ = 3\n replaceWith ⬛️\n \n # Gosper's Glider Gun\n \n draw\n ⬛️ \n ⬛️ ⬛️ \n ⬛️ ⬛️ ⬛️ ⬛️ \n ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️\n ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️\n ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ \n ⬛️ ⬛️ ⬛️ ⬛️ ⬛️ \n ⬛️ ⬛️ \n ⬛️ ⬛️ \n \n \n fill ◻️\n \nmoths\n question Can you move the moths from one light to the other?\n \n 🦋\n onTick .1\n jitter\n move\n onTick .2\n turnToward 💡\n move\n move\n 💡\n \n ticksPerSecond 10\n size 20\n style\n .BoardComponent {background:black;}\n \n insert 10 🦋\n insert 2 💡\n \n comment\n http://www.netlogoweb.org/launch#http://www.netlogoweb.org/assets/modelslib/Sample%20Models/Biology/Moths.nlogo\nninjas\n 🤺\n health 100\n onTick\n decrease health\n jitter\n \n 🥷\n health 100\n onTick\n decrease health\n jitter\n \n insert 50 🤺\n insert 50 🥷\npong\n \n \n 🏐\n bouncy\n onTick\n move\n angle West\n 🏓\n angle East\n onHit\n 🏐\n kickIt\n 🏸\n angle West\n onHit\n 🏐\n kickIt\n 🪵\n solid\n \n size 20\n ticksPerSecond 10\n \n rectangle 🪵 30 15 5 5\n paste\n 🏓 13⬇️ 6➡️\n 🏸 13⬇️ 33➡️\n 🏐 13⬇️ 19➡️\n \n \npoolTable\n comment\n Needs balls to collide. Acceleration.\n \n 🎱\n bouncy\n onHit\n 🎱\n kickIt\n 🏐\n kickIt\n \n 🪵\n solid\n \n 🏐\n bouncy\n onTick .1\n turnRandomly\n kickIt\n onTick .5\n kickIt\n onHit\n 🎱\n kickIt\n angle West\n \n rectangle 🪵 40 20 0 7\n paste\n 🏐 17⬇️ 32➡️\n 🎱 12⬇️ 7➡️\n 🎱 14⬇️ 7➡️\n 🎱 16⬇️ 7➡️\n 🎱 18⬇️ 7➡️\n 🎱 20⬇️ 7➡️\n 🎱 22⬇️ 7➡️\n 🎱 21⬇️ 8➡️\n 🎱 19⬇️ 8➡️\n 🎱 17⬇️ 8➡️\n 🎱 15⬇️ 8➡️\n 🎱 13⬇️ 8➡️\n 🎱 20⬇️ 9➡️\n 🎱 18⬇️ 9➡️\n 🎱 16⬇️ 9➡️\n 🎱 14⬇️ 9➡️\n 🎱 15⬇️ 10➡️\n 🎱 17⬇️ 10➡️\n 🎱 19⬇️ 10➡️\n 🎱 18⬇️ 11➡️\n 🎱 16⬇️ 11➡️\n 🎱 17⬇️ 12➡️\nsoccer\n \n \n ⚽️\n onHit\n 🥅\n pause\n alert GOAAAAAAAAALLLL!\n bouncy\n \n ⛹️‍♂️\n onTick\n jitter\n onHit\n ⚽️\n kickIt\n \n ⛹️‍♀️\n onTick\n jitter\n onHit\n ⚽️\n kickIt\n 🥅\n 🪵\n solid\n \n size 20\n ticksPerSecond 10\n \n rectangle 🪵 30 15 5 5\n \n paste\n 🥅 13⬇️ 6➡️\n 🥅 13⬇️ 33➡️\n ⚽️ 13⬇️ 19➡️\n \n paste\n ⛹️‍♀️ 17⬇️ 14➡️\n ⛹️‍♀️ 17⬇️ 17➡️\n ⛹️‍♀️ 13⬇️ 17➡️\n ⛹️‍♀️ 13⬇️ 14➡️\n ⛹️‍♀️ 8⬇️ 14➡️\n ⛹️‍♀️ 8⬇️ 17➡️\n ⛹️‍♀️ 10⬇️ 14➡️\n ⛹️‍♀️ 9⬇️ 10➡️\n ⛹️‍♀️ 13⬇️ 8➡️\n ⛹️‍♀️ 13⬇️ 10➡️\n ⛹️‍♀️ 17⬇️ 10➡️\n \n paste\n ⛹️‍♂️ 13⬇️ 31➡️\n ⛹️‍♂️ 17⬇️ 28➡️\n ⛹️‍♂️ 13⬇️ 28➡️\n ⛹️‍♂️ 8⬇️ 29➡️\n ⛹️‍♂️ 8⬇️ 25➡️\n ⛹️‍♂️ 10⬇️ 25➡️\n ⛹️‍♂️ 13⬇️ 25➡️\n ⛹️‍♂️ 17⬇️ 25➡️\n ⛹️‍♂️ 17⬇️ 21➡️\n ⛹️‍♂️ 8⬇️ 21➡️\n ⛹️‍♂️ 13⬇️ 21➡️\n \nstartupIdeas\n question What is the effect of ideas vs ideas with revenue?\n \n 👨‍💼🔖\n comment person with an idea\n onTick\n jitter\n \n 👨‍💼💰\n comment peron with an idea\n that is making money\n onTick\n jitter\n \n 👨‍\n onTick .1\n jitter\n onTick .1\n turnToward 👨‍💼💰\n move\n \n \n size 10\n insert 200 👨‍\n insert 30 👨‍💼🔖\n insert 3 👨‍💼💰\n \n \nstore\n 🚶🏻\n onTick\n move\n angle North\n 🛒\n 🚪\n onTick .1\n spawn 🚶🏻\n 🪵\n solid\n \n size 25\n \n rectangle 🪵 30 15 3 3\n paste\n 🚪 16⬇️ 17➡️\n \nvirus\n question What might the spread of a simple virus look like?\n \n 🧟\n health 100\n onTick .9\n decrease health\n jitter\n onTick .01\n log recovered\n replaceWith 🦸‍♂️\n onDeath\n replaceWith 🪦\n \n 🙍\n onTick\n jitter\n onTouch\n 🧟\n replaceWith 🧟\n \n 🦸‍♂️\n onTick\n jitter\n \n insert 10% 🙍\n insert 1 🧟\n \n 🪦\n \n onExtinct 🧟\n log No more cases.\n pause\nwaves\n 🌊\n onTick\n move\n angle South\n \n size 25\n ticksPerSecond 5\n \n rectangle 🌊 100 1 0\n rectangle 🌊 100 1 0 6\n rectangle 🌊 100 1 0 11\nzombies\n question Can you protect the family from the zombies?\n \n 🧟‍♂️\n noPalette\n onTick\n jitter\n onHit\n 🪃\n replaceWith 🪦\n 💣\n replaceWith 🪦\n 👨‍👩‍👧‍👦\n pause\n alert TheyGotYou!\n \n 🧱\n solid\n \n 🔫\n onTick .1\n spawn 🪃\n \n 🪃\n noPalette\n angle West\n onTick\n move\n \n 💣\n \n 🪦\n noPalette\n comment Dead zombie\n \n 👨‍👩‍👧‍👦\n noPalette\n \n size 30\n ticksPerSecond 10\n \n insertCluster 30 🧟‍♂️ 1 1\n paste\n 👨‍👩‍👧‍👦 12⬇️ 11➡️"} \ No newline at end of file diff --git a/dist/libs.js b/dist/libs.js index 8ab7d22..f41ab9f 100644 --- a/dist/libs.js +++ b/dist/libs.js @@ -10484,10 +10484,9 @@ Z.prototype.chain=tf,Z.prototype.commit=rf,Z.prototype.next=ef,Z.prototype.plant }) -"use strict" class Timer { constructor() { - this._tickTime = Date.now() - (TreeUtils.isNodeJs() ? 1000 * process.uptime() : 0) + this._tickTime = Date.now() - (Utils.isNodeJs() ? 1000 * process.uptime() : 0) this._firstTickTime = this._tickTime } tick(msg) { @@ -10500,11 +10499,108 @@ class Timer { return Date.now() - this._firstTickTime } } -class TreeUtils { +class Utils { static getFileExtension(filepath = "") { const match = filepath.match(/\.([^\.]+)$/) return (match && match[1]) || "" } + static runCommand(instance, command = "", param = undefined) { + const run = name => { + console.log(`Running ${name}:`) + instance[name](param) + } + if (instance[command + "Command"]) return run(command + "Command") + // Get commands from both the child and parent classes + const classes = [Object.getPrototypeOf(instance), Object.getPrototypeOf(Object.getPrototypeOf(instance))] + const allCommands = classes.map(classInstance => Object.getOwnPropertyNames(classInstance).filter(word => word.endsWith("Command"))).flat() + allCommands.sort() + const commandAsNumber = parseInt(command) - 1 + if (command.match(/^\d+$/) && allCommands[commandAsNumber]) return run(allCommands[commandAsNumber]) + console.log(`\n❌ No command provided. Available commands:\n\n` + allCommands.map((name, index) => `${index + 1}. ${name.replace("Command", "")}`).join("\n") + "\n") + } + static removeReturnChars(str = "") { + return str.replace(/\r/g, "") + } + static isAbsoluteUrl(url) { + return url.startsWith("https://") || url.startsWith("http://") + } + static removeEmptyLines(str = "") { + return str.replace(/\n\n+/g, "\n") + } + static shiftRight(str = "", numSpaces = 1) { + let spaces = " ".repeat(numSpaces) + return str.replace(/\n/g, `\n${spaces}`) + } + static getLinks(str = "") { + const _re = new RegExp("(^|[ \t\r\n])((ftp|http|https):(([A-Za-z0-9$_.+!*(),;/?:@&~=-])|%[A-Fa-f0-9]{2}){2,}(#([a-zA-Z0-9][a-zA-Z0-9$_.+!*(),;/?:@&~=%-]*))?([A-Za-z0-9$_+!*();/?:~-]))", "g") + return str.match(_re) || [] + } + // Only allow text content and inline styling. Don't allow HTML tags or any nested scroll tags or escape characters. + static escapeScrollAndHtml(content = "") { + return content.replace(/</g, "&lt;").replace(/\n/g, "").replace(/\r/g, "").replace(/\\/g, "") + } + static ensureDelimiterNotFound(strings, delimiter) { + const hit = strings.find(word => word.includes(delimiter)) + if (hit) throw `Delimiter "${delimiter}" found in hit` + } + // https://github.com/rigoneri/indefinite-article.js/blob/master/indefinite-article.js + static getIndefiniteArticle(phrase) { + // Getting the first word + const match = /\w+/.exec(phrase) + let word + if (match) word = match[0] + else return "an" + var l_word = word.toLowerCase() + // Specific start of words that should be preceded by 'an' + var alt_cases = ["honest", "hour", "hono"] + for (var i in alt_cases) { + if (l_word.indexOf(alt_cases[i]) == 0) return "an" + } + // Single letter word which should be preceded by 'an' + if (l_word.length == 1) { + if ("aedhilmnorsx".indexOf(l_word) >= 0) return "an" + else return "a" + } + // Capital words which should likely be preceded by 'an' + if (word.match(/(?!FJO|[HLMNS]Y.|RY[EO]|SQU|(F[LR]?|[HL]|MN?|N|RH?|S[CHKLMNPTVW]?|X(YL)?)[AEIOU])[FHLMNRSX][A-Z]/)) { + return "an" + } + // Special cases where a word that begins with a vowel should be preceded by 'a' + const regexes = [/^e[uw]/, /^onc?e\b/, /^uni([^nmd]|mo)/, /^u[bcfhjkqrst][aeiou]/] + for (var i in regexes) { + if (l_word.match(regexes[i])) return "a" + } + // Special capital words (UK, UN) + if (word.match(/^U[NK][AIEO]/)) { + return "a" + } else if (word == word.toUpperCase()) { + if ("aedhilmnorsx".indexOf(l_word[0]) >= 0) return "an" + else return "a" + } + // Basic method of words that begin with a vowel being preceded by 'an' + if ("aeiou".indexOf(l_word[0]) >= 0) return "an" + // Instances where y follwed by specific letters is preceded by 'an' + if (l_word.match(/^y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt)/)) return "an" + return "a" + } + static htmlEscaped(content = "") { + return content.replace(/</g, "&lt;") + } + static isValidEmail(email = "") { + return email.toLowerCase().match(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/) + } + static capitalizeFirstLetter(str) { + return str.charAt(0).toUpperCase() + str.slice(1) + } + // generate a random alpha numeric hash: + static getRandomCharacters(length) { + const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + let result = "" + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * characters.length)) + } + return result + } static isNodeJs() { return typeof exports !== "undefined" } @@ -10525,6 +10621,34 @@ class TreeUtils { if (result.length === 0) throw new Error(`Project root "${projectName}" in folder ${startingDirName} not found.`) return result } + static titleToPermalink(str) { + return str + .replace(/[\/\_\:\\\[\]]/g, "-") + .replace(/π/g, "pi") + .replace(/`/g, "tick") + .replace(/\$/g, "dollar-sign") + .replace(/\*$/g, "-star") + .replace(/^\*/g, "star-") + .replace(/\*/g, "-star-") + .replace(/\'+$/g, "q") + .replace(/^@/g, "at-") + .replace(/@$/g, "-at") + .replace(/@/g, "-at-") + .replace(/[\'\"\,\ū]/g, "") + .replace(/^\#/g, "sharp-") + .replace(/\#$/g, "-sharp") + .replace(/\#/g, "-sharp-") + .replace(/[\(\)]/g, "") + .replace(/\+\+$/g, "pp") + .replace(/\+$/g, "p") + .replace(/^\!/g, "bang-") + .replace(/\!$/g, "-bang") + .replace(/\!/g, "-bang-") + .replace(/\&/g, "-n-") + .replace(/[\+ ]/g, "-") + .replace(/[^a-zA-Z0-9\-\.]/g, "") + .toLowerCase() + } static escapeRegExp(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") } @@ -10537,7 +10661,7 @@ class TreeUtils { static makeMatrix(cols, rows, fill = 0) { const matrix = [] while (rows) { - matrix.push(TreeUtils.makeVector(cols, fill)) + matrix.push(Utils.makeVector(cols, fill)) rows-- } return matrix @@ -10578,7 +10702,7 @@ class TreeUtils { } static shuffleInPlace(arr, seed = Date.now()) { // https://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array - const randFn = TreeUtils._getPseudoRandom0to1FloatGenerator(seed) + const randFn = Utils._getPseudoRandom0to1FloatGenerator(seed) for (let index = arr.length - 1; index > 0; index--) { const tempIndex = Math.floor(randFn() * (index + 1)) ;[arr[index], arr[tempIndex]] = [arr[tempIndex], arr[index]] @@ -10587,12 +10711,7 @@ class TreeUtils { } // Only allows a-zA-Z0-9-_ (And optionally .) static _permalink(str, reg) { - return str.length - ? str - .toLowerCase() - .replace(reg, "") - .replace(/ /g, "-") - : "" + return str.length ? str.toLowerCase().replace(reg, "").replace(/ /g, "-") : "" } static isValueEmpty(value) { return value === undefined || value === "" || (typeof value === "number" && isNaN(value)) || (value instanceof Date && isNaN(value)) @@ -10690,7 +10809,7 @@ class TreeUtils { for (let optionIndex = 0; optionIndex < len; optionIndex++) { const candidate = options[optionIndex] if (!candidate) continue - const editDistance = TreeUtils._getEditDistance(str, caseSensitive ? candidate : candidate.toLowerCase(), maximumEditDistanceToBeBestMatch) + const editDistance = Utils._getEditDistance(str, caseSensitive ? candidate : candidate.toLowerCase(), maximumEditDistanceToBeBestMatch) if (editDistance < maximumEditDistanceToBeBestMatch) { maximumEditDistanceToBeBestMatch = editDistance closestMatch = candidate @@ -10701,7 +10820,7 @@ class TreeUtils { // Adapted from: https://github.com/dcporter/didyoumean.js/blob/master/didYouMean-1.2.1.js static _getEditDistance(stringA, stringB, maxInt) { // Handle null or undefined max. - maxInt = maxInt || maxInt === 0 ? maxInt : TreeUtils.MAX_INT + maxInt = maxInt || maxInt === 0 ? maxInt : Utils.MAX_INT const aLength = stringA.length const bLength = stringB.length // Fast path - no A or B. @@ -10724,7 +10843,7 @@ class TreeUtils { let maxJ // Loop over the rest of the columns. for (let bIndex = 1; bIndex <= bLength; bIndex++) { - colMin = TreeUtils.MAX_INT + colMin = Utils.MAX_INT minJ = 1 if (bIndex > maxInt) minJ = bIndex - maxInt maxJ = bLength + 1 @@ -10809,7 +10928,7 @@ class TreeUtils { } static getRandomString(length = 30, letters = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""), seed = Date.now()) { let str = "" - const randFn = TreeUtils._getPseudoRandom0to1FloatGenerator(seed) + const randFn = Utils._getPseudoRandom0to1FloatGenerator(seed) while (length) { str += letters[Math.round(Math.min(randFn() * letters.length, letters.length - 1))] length-- @@ -10820,7 +10939,7 @@ class TreeUtils { static makeRandomTree(lines = 1000, seed = Date.now()) { let str = "" let letters = " 123abc".split("") - const randFn = TreeUtils._getPseudoRandom0to1FloatGenerator(seed) + const randFn = Utils._getPseudoRandom0to1FloatGenerator(seed) while (lines) { let indent = " ".repeat(Math.round(randFn() * 6)) let bit = indent @@ -10838,7 +10957,7 @@ class TreeUtils { // adapted from https://gist.github.com/blixt/f17b47c62508be59987b // 1993 Park-Miller LCG static _getPseudoRandom0to1FloatGenerator(seed) { - return function() { + return function () { seed = Math.imul(48271, seed) | 0 % 2147483647 return (seed & 2147483647) / 2147483648 } @@ -10966,277 +11085,49 @@ class TreeUtils { } } } -TreeUtils.Timer = Timer +Utils.Timer = Timer //http://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links#21925491 -TreeUtils.linkify = text => { +Utils.linkify = (text, target = "_blank") => { let replacedText let replacePattern1 let replacePattern2 let replacePattern3 //URLs starting with http://, https://, or ftp:// - replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim - replacedText = text.replace(replacePattern1, '<a href="$1" target="_blank">$1</a>') + replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z\(\)0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+\(\)&@#\/%=~_|])/gim + replacedText = text.replace(replacePattern1, `<a href="$1" target="${target}">$1</a>`) //URLs starting with "www." (without // before it, or it'd re-link the ones done above). replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim - replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank">$2</a>') + replacedText = replacedText.replace(replacePattern2, `$1<a href="http://$2" target="${target}">$2</a>`) //Change email addresses to mailto:: links. replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>') return replacedText } // todo: switch algo to: http://indiegamr.com/generate-repeatable-random-numbers-in-js/? -TreeUtils.makeSemiRandomFn = (seed = Date.now()) => { +Utils.makeSemiRandomFn = (seed = Date.now()) => { return () => { const semiRand = Math.sin(seed++) * 10000 return semiRand - Math.floor(semiRand) } } -TreeUtils.randomUniformInt = (min, max, seed = Date.now()) => { - return Math.floor(TreeUtils.randomUniformFloat(min, max, seed)) +Utils.randomUniformInt = (min, max, seed = Date.now()) => { + return Math.floor(Utils.randomUniformFloat(min, max, seed)) } -TreeUtils.randomUniformFloat = (min, max, seed = Date.now()) => { - const randFn = TreeUtils.makeSemiRandomFn(seed) +Utils.randomUniformFloat = (min, max, seed = Date.now()) => { + const randFn = Utils.makeSemiRandomFn(seed) return min + (max - min) * randFn() } -TreeUtils.getRange = (startIndex, endIndexExclusive, increment = 1) => { +Utils.getRange = (startIndex, endIndexExclusive, increment = 1) => { const range = [] for (let index = startIndex; index < endIndexExclusive; index = index + increment) { range.push(index) } return range } -TreeUtils.MAX_INT = Math.pow(2, 32) - 1 -window.TreeUtils = TreeUtils -class TestRacerTestBlock { - constructor(testFile, testName, fn) { - this._parentFile = testFile - this._testName = testName - this._testFn = fn - } - _emitMessage(message) { - this._parentFile.getRunner()._emitMessage(message) - return message - } - async execute() { - let passes = [] - let failures = [] - const assertEqual = (actual, expected, message = "") => { - if (expected === actual) { - passes.push(message) - } else { - failures.push([actual, expected, message]) - } - } - try { - await this._testFn(assertEqual) - } catch (err) { - failures.push([ - "1", - "0", - `Should not have uncaught errors but in ${this._testName} got error: - toString: - ${new TreeNode(err.toString()).toString(2)} - stack: - ${new TreeNode(err.stack).toString(2)}` - ]) - } - failures.length ? this._emitBlockFailedMessage(failures) : this._emitBlockPassedMessage(passes) - return { - passes, - failures - } - } - _emitBlockPassedMessage(passes) { - this._emitMessage(`ok block ${this._testName} - ${passes.length} passed`) - } - _emitBlockFailedMessage(failures) { - // todo: should replace not replace last newline? - // todo: do side by side. - // todo: add diff. - this._emitMessage(`failed block ${this._testName}`) - this._emitMessage( - failures - .map(failure => { - const actualVal = failure[0] === undefined ? "undefined" : failure[0].toString() - const expectedVal = failure[1] === undefined ? "undefined" : failure[1].toString() - const actual = new jtree.TreeNode(`actual\n${new jtree.TreeNode(actualVal).toString(1)}`) - const expected = new jtree.TreeNode(`expected\n${new jtree.TreeNode(expectedVal.toString()).toString(1)}`) - const comparison = actual.toComparison(expected) - return new jtree.TreeNode(` assertion ${failure[2]}\n${comparison.toSideBySide([actual, expected]).toString(2)}`) - }) - .join("\n") - ) - } -} -class TestRacerFile { - constructor(runner, testTree, fileName) { - this._runner = runner - this._testTree = {} - this._fileName = fileName - Object.keys(testTree).forEach(key => { - this._testTree[key] = new TestRacerTestBlock(this, key, testTree[key]) - }) - } - getRunner() { - return this._runner - } - getFileName() { - return this._fileName - } - get length() { - return Object.values(this._testTree).length - } - get skippedTestBlockNames() { - const testsToRun = this._filterSkippedTestBlocks() - return Object.keys(this._testTree).filter(blockName => !testsToRun.includes(blockName)) - } - _emitMessage(message) { - this.getRunner()._emitMessage(message) - } - _filterSkippedTestBlocks() { - // _ prefix = run on these tests block - // $ prefix = skip this test - const runOnlyTheseTestBlocks = Object.keys(this._testTree).filter(key => key.startsWith("_")) - if (runOnlyTheseTestBlocks.length) return runOnlyTheseTestBlocks - return Object.keys(this._testTree).filter(key => !key.startsWith("$")) - } - async execute() { - const testBlockNames = this._filterSkippedTestBlocks() - this._emitStartFileMessage(testBlockNames.length) - const fileTimer = new TreeUtils.Timer() - const blockResults = {} - const blockPromises = testBlockNames.map(async testName => { - const results = await this._testTree[testName].execute() - blockResults[testName] = results - }) - await Promise.all(blockPromises) - const fileStats = this._aggregateBlockResultsIntoFileResults(blockResults) - const fileTimeElapsed = fileTimer.tick() - fileStats.blocksFailed ? this._emitFileFailedMessage(fileStats, fileTimeElapsed, testBlockNames.length) : this._emitFilePassedMessage(fileStats, fileTimeElapsed, testBlockNames.length) - return fileStats - } - _aggregateBlockResultsIntoFileResults(fileBlockResults) { - const fileStats = { - assertionsPassed: 0, - assertionsFailed: 0, - blocksPassed: 0, - blocksFailed: 0, - failedBlocks: [] - } - Object.keys(fileBlockResults).forEach(blockName => { - const results = fileBlockResults[blockName] - fileStats.assertionsPassed += results.passes.length - fileStats.assertionsFailed += results.failures.length - if (results.failures.length) { - fileStats.blocksFailed++ - fileStats.failedBlocks.push(blockName) - } else fileStats.blocksPassed++ - }) - return fileStats - } - _emitStartFileMessage(blockCount) { - this._emitMessage(`start file ${blockCount} test blocks in file ${this._fileName}`) - } - _emitFilePassedMessage(fileStats, fileTimeElapsed, blockCount) { - this._emitMessage(`ok file ${this._fileName} in ${fileTimeElapsed}ms. ${blockCount} blocks and ${fileStats.assertionsPassed} assertions passed.`) - } - _emitFileFailedMessage(fileStats, fileTimeElapsed, blockCount) { - this._emitMessage( - `failed file ${this._fileName} over ${fileTimeElapsed}ms. ${fileStats.blocksFailed} blocks and ${fileStats.assertionsFailed} failed. ${blockCount - fileStats.blocksFailed} blocks and ${fileStats.assertionsPassed} assertions passed` - ) - } -} -class TestRacer { - constructor(fileTestTree) { - this._logFunction = console.log - this._timer = new TreeUtils.Timer() - this._sessionFilesPassed = 0 - this._sessionFilesFailed = {} - this._sessionBlocksFailed = 0 - this._sessionBlocksPassed = 0 - this._sessionAssertionsFailed = 0 - this._sessionAssertionsPassed = 0 - this._fileTestTree = {} - Object.keys(fileTestTree).forEach(fileName => { - this._fileTestTree[fileName] = new TestRacerFile(this, fileTestTree[fileName], fileName) - }) - } - setLogFunction(logFunction) { - this._logFunction = logFunction - return this - } - _addFileResultsToSessionResults(fileStats, fileName) { - this._sessionAssertionsPassed += fileStats.assertionsPassed - this._sessionAssertionsFailed += fileStats.assertionsFailed - this._sessionBlocksPassed += fileStats.blocksPassed - this._sessionBlocksFailed += fileStats.blocksFailed - if (!fileStats.blocksFailed) this._sessionFilesPassed++ - else { - this._sessionFilesFailed[fileName] = fileStats.failedBlocks - } - } - async execute() { - this._emitSessionPlanMessage() - const proms = Object.values(this._fileTestTree).map(async testFile => { - const results = await testFile.execute() - this._addFileResultsToSessionResults(results, testFile.getFileName()) - }) - await Promise.all(proms) - return this - } - finish() { - return this._emitSessionFinishMessage() - } - _emitMessage(message) { - this._logFunction(message) - return message - } - get length() { - return Object.values(this._fileTestTree).length - } - _emitSessionPlanMessage() { - let blocks = 0 - Object.values(this._fileTestTree).forEach(value => (blocks += value.length)) - this._emitMessage(`${this.length} files and ${blocks} blocks to run. ${this._getSkippedBlockNames().length} skipped blocks.`) - } - _getSkippedBlockNames() { - const skippedBlocks = [] - Object.values(this._fileTestTree).forEach(file => { - file.skippedTestBlockNames.forEach(blockName => { - skippedBlocks.push(blockName) - }) - }) - return skippedBlocks - } - _getFailures() { - if (!Object.keys(this._sessionFilesFailed).length) return "" - return ` - failures -${new TreeNode(this._sessionFilesFailed).forEach(row => row.forEach(line => line.deleteWordAt(0))).toString(2)}` - } - _emitSessionFinishMessage() { - const skipped = this._getSkippedBlockNames() - return this._emitMessage(`finished in ${this._timer.getTotalElapsedTime()}ms - skipped - ${skipped.length} blocks${skipped ? " " + skipped.join(" ") : ""} - passed - ${this._sessionFilesPassed} files - ${this._sessionBlocksPassed} blocks - ${this._sessionAssertionsPassed} assertions - failed - ${Object.keys(this._sessionFilesFailed).length} files - ${this._sessionBlocksFailed} blocks - ${this._sessionAssertionsFailed} assertions${this._getFailures()}`) - } - static async testSingleFile(fileName, testTree) { - const obj = {} - obj[fileName] = testTree - const session = new TestRacer(obj) - await session.execute() - session.finish() - } -} -window.TestRacer = TestRacer +Utils.MAX_INT = Math.pow(2, 32) - 1 +window.Utils = Utils + + let _jtreeLatestTime = 0 let _jtreeMinTimeIncrement = 0.000000000001 class AbstractNode { @@ -11255,7 +11146,7 @@ class AbstractNode { } } var FileFormat -;(function(FileFormat) { +;(function (FileFormat) { FileFormat["csv"] = "csv" FileFormat["tsv"] = "tsv" FileFormat["tree"] = "tree" @@ -11283,7 +11174,7 @@ class TreeWord { } const TreeEvents = { ChildAddedTreeEvent, ChildRemovedTreeEvent, DescendantChangedTreeEvent, LineChangedTreeEvent } var WhereOperators -;(function(WhereOperators) { +;(function (WhereOperators) { WhereOperators["equal"] = "=" WhereOperators["notEqual"] = "!=" WhereOperators["lessThan"] = "<" @@ -11298,12 +11189,12 @@ var WhereOperators WhereOperators["notEmpty"] = "notEmpty" })(WhereOperators || (WhereOperators = {})) var TreeNotationConstants -;(function(TreeNotationConstants) { +;(function (TreeNotationConstants) { TreeNotationConstants["extends"] = "extends" })(TreeNotationConstants || (TreeNotationConstants = {})) -class Parser { - constructor(catchAllNodeConstructor, firstWordMap = {}, regexTests = undefined) { - this._catchAllNodeConstructor = catchAllNodeConstructor +class ParserCombinator { + constructor(catchAllParser, firstWordMap = {}, regexTests = undefined) { + this._catchAllParser = catchAllParser this._firstWordMap = new Map(Object.entries(firstWordMap)) this._regexTests = regexTests } @@ -11323,19 +11214,19 @@ class Parser { } return obj } - _getNodeConstructor(line, contextNode, wordBreakSymbol = " ") { - return this._getFirstWordMap().get(this._getFirstWord(line, wordBreakSymbol)) || this._getConstructorFromRegexTests(line) || this._getCatchAllNodeConstructor(contextNode) + _getParser(line, contextNode, wordBreakSymbol = " ") { + return this._getFirstWordMap().get(this._getFirstWord(line, wordBreakSymbol)) || this._getParserFromRegexTests(line) || this._getCatchAllParser(contextNode) } - _getCatchAllNodeConstructor(contextNode) { - if (this._catchAllNodeConstructor) return this._catchAllNodeConstructor - const parent = contextNode.getParent() - if (parent) return parent._getParser()._getCatchAllNodeConstructor(parent) + _getCatchAllParser(contextNode) { + if (this._catchAllParser) return this._catchAllParser + const parent = contextNode.parent + if (parent) return parent._getParser()._getCatchAllParser(parent) return contextNode.constructor } - _getConstructorFromRegexTests(line) { + _getParserFromRegexTests(line) { if (!this._regexTests) return undefined const hit = this._regexTests.find(test => test.regex.test(line)) - if (hit) return hit.nodeConstructor + if (hit) return hit.parser return undefined } _getFirstWord(line, wordBreakSymbol) { @@ -11360,9 +11251,9 @@ class TreeNode extends AbstractNode { getErrors() { return [] } - getLineCellTypes() { + get lineCellTypes() { // todo: make this any a constant - return "undefinedCellType ".repeat(this.getWords().length).trim() + return "undefinedCellType ".repeat(this.words.length).trim() } isNodeJs() { return typeof exports !== "undefined" @@ -11372,7 +11263,7 @@ class TreeNode extends AbstractNode { } getOlderSiblings() { if (this.isRoot()) return [] - return this.getParent().slice(0, this.getIndex()) + return this.parent.slice(0, this.getIndex()) } _getClosestOlderSibling() { const olderSiblings = this.getOlderSiblings() @@ -11380,27 +11271,27 @@ class TreeNode extends AbstractNode { } getYoungerSiblings() { if (this.isRoot()) return [] - return this.getParent().slice(this.getIndex() + 1) + return this.parent.slice(this.getIndex() + 1) } getSiblings() { if (this.isRoot()) return [] - return this.getParent().filter(node => node !== this) + return this.parent.filter(node => node !== this) } _getUid() { if (!this._uid) this._uid = TreeNode._makeUniqueId() return this._uid } // todo: rename getMother? grandMother et cetera? - getParent() { + get parent() { return this._parent } getIndentLevel(relativeTo) { return this._getIndentLevel(relativeTo) } - getIndentation(relativeTo) { - const indentLevel = this._getIndentLevel(relativeTo) - 1 + get indentation() { + const indentLevel = this._getIndentLevel() - 1 if (indentLevel < 0) return "" - return this.getEdgeSymbol().repeat(indentLevel) + return this.edgeSymbol.repeat(indentLevel) } _getTopDownArray(arr) { this.forEach(child => { @@ -11408,7 +11299,7 @@ class TreeNode extends AbstractNode { child._getTopDownArray(arr) }) } - getTopDownArray() { + get topDownArray() { const arr = [] this._getTopDownArray(arr) return arr @@ -11426,7 +11317,7 @@ class TreeNode extends AbstractNode { index++ } } - getNumberOfLines() { + get numberOfLines() { let lineCount = 0 for (let node of this.getTopDownArrayIterator()) { lineCount++ @@ -11436,25 +11327,25 @@ class TreeNode extends AbstractNode { _getMaxUnitsOnALine() { let max = 0 for (let node of this.getTopDownArrayIterator()) { - const count = node.getWords().length + node.getIndentLevel() + const count = node.words.length + node.getIndentLevel() if (count > max) max = count } return max } - getNumberOfWords() { + get numberOfWords() { let wordCount = 0 for (let node of this.getTopDownArrayIterator()) { - wordCount += node.getWords().length + wordCount += node.words.length } return wordCount } - getLineNumber() { + get lineNumber() { return this._getLineNumberRelativeTo() } _getLineNumber(target = this) { if (this._cachedLineNumber) return this._cachedLineNumber let lineNumber = 1 - for (let node of this.getRootNode().getTopDownArrayIterator()) { + for (let node of this.root.getTopDownArrayIterator()) { if (node === target) return lineNumber lineNumber++ } @@ -11467,26 +11358,29 @@ class TreeNode extends AbstractNode { return this.length ? new Set(this.getFirstWords()).size !== this.length : false } isEmpty() { - return !this.length && !this.getContent() + return !this.length && !this.content } _getLineNumberRelativeTo(relativeTo) { if (this.isRoot(relativeTo)) return 0 - const start = relativeTo || this.getRootNode() + const start = relativeTo || this.root return start._getLineNumber(this) } isRoot(relativeTo) { - return relativeTo === this || !this.getParent() + return relativeTo === this || !this.parent } - getRootNode() { + get root() { return this._getRootNode() } _getRootNode(relativeTo) { if (this.isRoot(relativeTo)) return this - return this.getParent()._getRootNode(relativeTo) + return this.parent._getRootNode(relativeTo) } toString(indentCount = 0, language = this) { if (this.isRoot()) return this._childrenToString(indentCount, language) - return language.getEdgeSymbol().repeat(indentCount) + this.getLine(language) + (this.length ? language.getNodeBreakSymbol() + this._childrenToString(indentCount + 1, language) : "") + return language.edgeSymbol.repeat(indentCount) + this.getLine(language) + (this.length ? language.nodeBreakSymbol + this._childrenToString(indentCount + 1, language) : "") + } + get asString() { + return this.toString() } printLinesFrom(start, quantity) { return this._printLinesFrom(start, quantity, false) @@ -11511,6 +11405,9 @@ class TreeNode extends AbstractNode { if (index < 0) index = words.length + index return words[index] } + get list() { + return this.getWordsFrom(1) + } _toHtml(indentCount) { const path = this.getPathVector().join(" ") const classes = { @@ -11519,21 +11416,21 @@ class TreeNode extends AbstractNode { nodeBreakSymbol: "nodeBreakSymbol", nodeChildren: "nodeChildren" } - const edge = this.getEdgeSymbol().repeat(indentCount) + const edge = this.edgeSymbol.repeat(indentCount) // Set up the firstWord part of the node const edgeHtml = `<span class="${classes.nodeLine}" data-pathVector="${path}"><span class="${classes.edgeSymbol}">${edge}</span>` const lineHtml = this._getLineHtml() - const childrenHtml = this.length ? `<span class="${classes.nodeBreakSymbol}">${this.getNodeBreakSymbol()}</span>` + `<span class="${classes.nodeChildren}">${this._childrenToHtml(indentCount + 1)}</span>` : "" + const childrenHtml = this.length ? `<span class="${classes.nodeBreakSymbol}">${this.nodeBreakSymbol}</span>` + `<span class="${classes.nodeChildren}">${this._childrenToHtml(indentCount + 1)}</span>` : "" return `${edgeHtml}${lineHtml}${childrenHtml}</span>` } _getWords(startFrom) { - if (!this._words) this._words = this._getLine().split(this.getWordBreakSymbol()) + if (!this._words) this._words = this._getLine().split(this.wordBreakSymbol) return startFrom ? this._words.slice(startFrom) : this._words } - getWords() { + get words() { return this._getWords(0) } - doesExtend(nodeTypeId) { + doesExtend(parserId) { return false } require(moduleName, filePath) { @@ -11544,7 +11441,7 @@ class TreeNode extends AbstractNode { return this._getWords(startFrom) } getFirstAncestor() { - const parent = this.getParent() + const parent = this.parent return parent.isRoot() ? this : parent.getFirstAncestor() } isLoaded() { @@ -11571,7 +11468,19 @@ class TreeNode extends AbstractNode { return chain } _getProjectRootDir() { - return this.isRoot() ? "" : this.getRootNode()._getProjectRootDir() + return this.isRoot() ? "" : this.root._getProjectRootDir() + } + // Concat 2 trees amd return a new true, but replace any nodes + // in this tree that start with the same node from the first tree with + // that patched version. Does not recurse. + patch(two) { + const copy = this.clone() + two.forEach(node => { + const hit = copy.getNode(node.getWord(0)) + if (hit) hit.destroy() + }) + copy.concat(two) + return copy } getSparsity() { const nodes = this.getChildren() @@ -11606,17 +11515,11 @@ class TreeNode extends AbstractNode { return [oneToTwo, twoToOne] } _getWordIndexCharacterStartPosition(wordIndex) { - const xiLength = this.getEdgeSymbol().length - const numIndents = this._getIndentLevel(undefined) - 1 + const xiLength = this.edgeSymbol.length + const numIndents = this._getIndentLevel() - 1 const indentPosition = xiLength * numIndents if (wordIndex < 1) return xiLength * (numIndents + wordIndex) - return ( - indentPosition + - this.getWords() - .slice(0, wordIndex) - .join(this.getWordBreakSymbol()).length + - this.getWordBreakSymbol().length - ) + return indentPosition + this.words.slice(0, wordIndex).join(this.wordBreakSymbol).length + this.wordBreakSymbol.length } getNodeInScopeAtCharIndex(charIndex) { if (this.isRoot()) return this @@ -11624,7 +11527,7 @@ class TreeNode extends AbstractNode { if (wordIndex > 0) return this let node = this while (wordIndex < 1) { - node = node.getParent() + node = node.parent wordIndex++ } return node @@ -11639,10 +11542,8 @@ class TreeNode extends AbstractNode { } } fill(fill = "") { - this.getTopDownArray().forEach(line => { - line.getWords().forEach((word, index) => { - line.setWord(index, fill) - }) + this.topDownArray.forEach(line => { + line.words.forEach((word, index) => line.setWord(index, fill)) }) return this } @@ -11663,9 +11564,9 @@ class TreeNode extends AbstractNode { } getWordBoundaryCharIndices() { let indentLevel = this._getIndentLevel() - const wordBreakSymbolLength = this.getWordBreakSymbol().length + const wordBreakSymbolLength = this.wordBreakSymbol.length let elapsed = indentLevel - return this.getWords().map((word, wordIndex) => { + return this.words.map((word, wordIndex) => { const boundary = elapsed elapsed += word.length + wordBreakSymbolLength return boundary @@ -11680,7 +11581,7 @@ class TreeNode extends AbstractNode { while (spots.length < numberOfIndents) { spots.push(-(numberOfIndents - spots.length)) } - this.getWords().forEach((word, wordIndex) => { + this.words.forEach((word, wordIndex) => { word.split("").forEach(letter => { spots.push(wordIndex) }) @@ -11691,7 +11592,7 @@ class TreeNode extends AbstractNode { // Note: This currently does not return any errors resulting from "required" or "single" getAllErrors(lineStartsAt = 1) { const errors = [] - for (let node of this.getTopDownArray()) { + for (let node of this.topDownArray) { node._cachedLineNumber = lineStartsAt // todo: cleanup const errs = node.getErrors() errs.forEach(err => errors.push(err)) @@ -11710,17 +11611,17 @@ class TreeNode extends AbstractNode { line++ } } - getFirstWord() { - return this.getWords()[0] + get firstWord() { + return this.words[0] } - getContent() { + get content() { const words = this.getWordsFrom(1) - return words.length ? words.join(this.getWordBreakSymbol()) : undefined + return words.length ? words.join(this.wordBreakSymbol) : undefined } - getContentWithChildren() { + get contentWithChildren() { // todo: deprecate - const content = this.getContent() - return (content ? content : "") + (this.length ? this.getNodeBreakSymbol() + this._childrenToString() : "") + const content = this.content + return (content ? content : "") + (this.length ? this.nodeBreakSymbol + this._childrenToString() : "") } getFirstNode() { return this.nodeAt(0) @@ -11730,18 +11631,18 @@ class TreeNode extends AbstractNode { } _getStack(relativeTo) { if (this.isRoot(relativeTo)) return [] - const parent = this.getParent() + const parent = this.parent if (parent.isRoot(relativeTo)) return [this] else return parent._getStack(relativeTo).concat([this]) } getStackString() { return this._getStack() - .map((node, index) => this.getEdgeSymbol().repeat(index) + node.getLine()) - .join(this.getNodeBreakSymbol()) + .map((node, index) => this.edgeSymbol.repeat(index) + node.getLine()) + .join(this.nodeBreakSymbol) } getLine(language) { if (!this._words && !language) return this._getLine() // todo: how does this interact with "language" param? - return this.getWords().join((language || this).getWordBreakSymbol()) + return this.words.join((language || this).wordBreakSymbol) } getColumnNames() { return this._getUnionNames() @@ -11761,8 +11662,8 @@ class TreeNode extends AbstractNode { // todo: return array? getPathArray? _getFirstWordPath(relativeTo) { if (this.isRoot(relativeTo)) return "" - else if (this.getParent().isRoot(relativeTo)) return this.getFirstWord() - return this.getParent()._getFirstWordPath(relativeTo) + this.getEdgeSymbol() + this.getFirstWord() + else if (this.parent.isRoot(relativeTo)) return this.firstWord + return this.parent._getFirstWordPath(relativeTo) + this.edgeSymbol + this.firstWord } getFirstWordPathRelativeTo(relativeTo) { return this._getFirstWordPath(relativeTo) @@ -11778,39 +11679,37 @@ class TreeNode extends AbstractNode { } _getPathVector(relativeTo) { if (this.isRoot(relativeTo)) return [] - const path = this.getParent()._getPathVector(relativeTo) + const path = this.parent._getPathVector(relativeTo) path.push(this.getIndex()) return path } getIndex() { - return this.getParent()._indexOfNode(this) + return this.parent._indexOfNode(this) } isTerminal() { return !this.length } _getLineHtml() { - return this.getWords() - .map((word, index) => `<span class="word${index}">${TreeUtils.stripHtml(word)}</span>`) - .join(`<span class="zIncrement">${this.getWordBreakSymbol()}</span>`) + return this.words.map((word, index) => `<span class="word${index}">${Utils.stripHtml(word)}</span>`).join(`<span class="zIncrement">${this.wordBreakSymbol}</span>`) } _getXmlContent(indentCount) { - if (this.getContent() !== undefined) return this.getContentWithChildren() + if (this.content !== undefined) return this.contentWithChildren return this.length ? `${indentCount === -1 ? "" : "\n"}${this._childrenToXml(indentCount > -1 ? indentCount + 2 : -1)}${" ".repeat(indentCount)}` : "" } _toXml(indentCount) { const indent = " ".repeat(indentCount) - const tag = this.getFirstWord() + const tag = this.firstWord return `${indent}<${tag}>${this._getXmlContent(indentCount)}</${tag}>${indentCount === -1 ? "" : "\n"}` } _toObjectTuple() { - const content = this.getContent() + const content = this.content const length = this.length const hasChildrenNoContent = content === undefined && length const hasContentAndHasChildren = content !== undefined && length // If the node has a content and a subtree return it as a string, as // Javascript object values can't be both a leaf and a tree. - const tupleValue = hasChildrenNoContent ? this.toObject() : hasContentAndHasChildren ? this.getContentWithChildren() : content - return [this.getFirstWord(), tupleValue] + const tupleValue = hasChildrenNoContent ? this.toObject() : hasContentAndHasChildren ? this.contentWithChildren : content + return [this.firstWord, tupleValue] } _indexOfNode(needleNode) { let result = -1 @@ -11840,11 +11739,11 @@ class TreeNode extends AbstractNode { } rightPad(padCharacter = " ") { const newWidth = this.getMaxLineWidth() - this.getTopDownArray().forEach(node => node._rightPad(newWidth, padCharacter)) + this.topDownArray.forEach(node => node._rightPad(newWidth, padCharacter)) return this } lengthen(numberOfLines) { - let linesToAdd = numberOfLines - this.getNumberOfLines() + let linesToAdd = numberOfLines - this.numberOfLines while (linesToAdd > 0) { this.appendLine("") linesToAdd-- @@ -11857,7 +11756,7 @@ class TreeNode extends AbstractNode { const nodeBreakSymbol = "\n" let next while ((next = treesOrStrings.shift())) { - clone.lengthen(next.getNumberOfLines()) + clone.lengthen(next.numberOfLines) clone.rightPad() next .toString() @@ -11881,9 +11780,9 @@ class TreeNode extends AbstractNode { } toBraid(treesOrStrings) { treesOrStrings.unshift(this) - const nodeDelimiter = this.getNodeBreakSymbol() + const nodeDelimiter = this.nodeBreakSymbol return new TreeNode( - TreeUtils.interweave(treesOrStrings.map(tree => tree.toString().split(nodeDelimiter))) + Utils.interweave(treesOrStrings.map(tree => tree.toString().split(nodeDelimiter))) .map(line => (line === undefined ? "" : line)) .join(nodeDelimiter) ) @@ -11896,14 +11795,14 @@ class TreeNode extends AbstractNode { ) } _hasColumns(columns) { - const words = this.getWords() + const words = this.words return columns.every((searchTerm, index) => searchTerm === words[index]) } hasWord(index, word) { return this.getWord(index) === word } getNodeByColumns(...columns) { - return this.getTopDownArray().find(node => node._hasColumns(columns)) + return this.topDownArray.find(node => node._hasColumns(columns)) } getNodeByColumn(index, name) { return this.find(node => node.getWord(index) === name) @@ -11930,7 +11829,7 @@ class TreeNode extends AbstractNode { .join("\n") } getSelectedNodes() { - return this.getTopDownArray().filter(node => node.isSelected()) + return this.topDownArray.filter(node => node.isSelected()) } clearSelection() { this.getSelectedNodes().forEach(node => node.unselectNode()) @@ -12016,7 +11915,7 @@ class TreeNode extends AbstractNode { } _getLevels() { const levels = {} - this.getTopDownArray().forEach(node => { + this.topDownArray.forEach(node => { const level = node._getIndentLevel() if (!levels[level]) levels[level] = [] levels[level].push(node) @@ -12048,6 +11947,21 @@ class TreeNode extends AbstractNode { if (!node) return undefined return node.nodeAt(indexOrIndexArray.slice(1)) } + // Flatten a tree node into an object like {twitter:"pldb", "twitter.followers":123}. + // Assumes you have a nested key/value list with no multiline strings. + toFlatObject(delimiter = ".") { + let newObject = {} + const { edgeSymbolRegex } = this + this.forEach((child, index) => { + newObject[child.getWord(0)] = child.content + child.topDownArray.forEach(node => { + const newColumnName = node.getFirstWordPathRelativeTo(this).replace(edgeSymbolRegex, delimiter) + const value = node.content + newObject[newColumnName] = value + }) + }) + return newObject + } _toObject() { const obj = {} this.forEach(node => { @@ -12056,33 +11970,28 @@ class TreeNode extends AbstractNode { }) return obj } - toHtml() { + get asHtml() { return this._childrenToHtml(0) } _toHtmlCubeLine(indents = 0, lineIndex = 0, planeIndex = 0) { const getLine = (cellIndex, word = "") => `<span class="htmlCubeSpan" style="top: calc(var(--topIncrement) * ${planeIndex} + var(--rowHeight) * ${lineIndex}); left:calc(var(--leftIncrement) * ${planeIndex} + var(--cellWidth) * ${cellIndex});">${word}</span>` let cells = [] - this.getWords().forEach((word, index) => (word ? cells.push(getLine(index + indents, word)) : "")) + this.words.forEach((word, index) => (word ? cells.push(getLine(index + indents, word)) : "")) return cells.join("") } - toHtmlCube() { - return this.map((plane, planeIndex) => - plane - .getTopDownArray() - .map((line, lineIndex) => line._toHtmlCubeLine(line.getIndentLevel() - 2, lineIndex, planeIndex)) - .join("") - ).join("") + get asHtmlCube() { + return this.map((plane, planeIndex) => plane.topDownArray.map((line, lineIndex) => line._toHtmlCubeLine(line.getIndentLevel() - 2, lineIndex, planeIndex)).join("")).join("") } _getHtmlJoinByCharacter() { - return `<span class="nodeBreakSymbol">${this.getNodeBreakSymbol()}</span>` + return `<span class="nodeBreakSymbol">${this.nodeBreakSymbol}</span>` } _childrenToHtml(indentCount) { const joinBy = this._getHtmlJoinByCharacter() return this.map(node => node._toHtml(indentCount)).join(joinBy) } _childrenToString(indentCount, language = this) { - return this.map(node => node.toString(indentCount, language)).join(language.getNodeBreakSymbol()) + return this.map(node => node.toString(indentCount, language)).join(language.nodeBreakSymbol) } childrenToString(indentCount = 0) { return this._childrenToString(indentCount) @@ -12098,7 +12007,7 @@ class TreeNode extends AbstractNode { compile() { return this.map(child => child.compile()).join(this._getChildJoinCharacter()) } - toXml() { + get asXml() { return this._childrenToXml(0) } toDisk(path) { @@ -12106,8 +12015,8 @@ class TreeNode extends AbstractNode { const format = TreeNode._getFileFormat(path) const formats = { tree: tree => tree.toString(), - csv: tree => tree.toCsv(), - tsv: tree => tree.toTsv() + csv: tree => tree.asCsv, + tsv: tree => tree.asTsv } this.require("fs").writeFileSync(path, formats[format](this), "utf8") return this @@ -12115,12 +12024,12 @@ class TreeNode extends AbstractNode { _lineToYaml(indentLevel, listTag = "") { let prefix = " ".repeat(indentLevel) if (listTag && indentLevel > 1) prefix = " ".repeat(indentLevel - 2) + listTag + " " - return prefix + `${this.getFirstWord()}:` + (this.getContent() ? " " + this.getContent() : "") + return prefix + `${this.firstWord}:` + (this.content ? " " + this.content : "") } _isYamlList() { return this.hasDuplicateFirstWords() } - toYaml() { + get asYaml() { return `%YAML 1.2 ---\n${this._childrenToYaml(0).join("\n")}` } @@ -12154,37 +12063,37 @@ class TreeNode extends AbstractNode { _childrenToYamlAssociativeArray(indentLevel) { return this.map(node => node._toYamlAssociativeArrayElement(indentLevel)) } - toJsonSubset() { + get asJsonSubset() { return JSON.stringify(this.toObject(), null, " ") } _toObjectForSerialization() { return this.length ? { - cells: this.getWords(), + cells: this.words, children: this.map(child => child._toObjectForSerialization()) } : { - cells: this.getWords() + cells: this.words } } - toJson() { + get asJson() { return JSON.stringify({ children: this.map(child => child._toObjectForSerialization()) }, null, " ") } - toGrid() { - const WordBreakSymbol = this.getWordBreakSymbol() + get asGrid() { + const WordBreakSymbol = this.wordBreakSymbol return this.toString() - .split(this.getNodeBreakSymbol()) + .split(this.nodeBreakSymbol) .map(line => line.split(WordBreakSymbol)) } - toGridJson() { - return JSON.stringify(this.toGrid(), null, 2) + get asGridJson() { + return JSON.stringify(this.asGrid, null, 2) } findNodes(firstWordPath) { // todo: can easily speed this up const map = {} if (!Array.isArray(firstWordPath)) firstWordPath = [firstWordPath] firstWordPath.forEach(path => (map[path] = true)) - return this.getTopDownArray().filter(node => { + return this.topDownArray.filter(node => { if (map[node._getFirstWordPath(this)]) return true return false }) @@ -12213,11 +12122,11 @@ class TreeNode extends AbstractNode { } getFrom(prefix) { const hit = this.filter(node => node.getLine().startsWith(prefix))[0] - if (hit) return hit.getLine().substr((prefix + this.getWordBreakSymbol()).length) + if (hit) return hit.getLine().substr((prefix + this.wordBreakSymbol).length) } get(firstWordPath) { const node = this._getNodeByPath(firstWordPath) - return node === undefined ? undefined : node.getContent() + return node === undefined ? undefined : node.content } getOneOf(keys) { for (let i = 0; i < keys.length; i++) { @@ -12229,7 +12138,7 @@ class TreeNode extends AbstractNode { // move to treenode pick(fields) { const newTree = new TreeNode(this.toString()) // todo: why not clone? - const map = TreeUtils.arrayToMap(fields) + const map = Utils.arrayToMap(fields) newTree.nodeAt(0).forEach(node => { if (!map[node.getWord(0)]) node.destroy() }) @@ -12239,19 +12148,22 @@ class TreeNode extends AbstractNode { return this._getNodesByGlobPath(query) } _getNodesByGlobPath(globPath) { - const edgeSymbol = this.getEdgeSymbol() + const edgeSymbol = this.edgeSymbol if (!globPath.includes(edgeSymbol)) { if (globPath === "*") return this.getChildren() - return this.filter(node => node.getFirstWord() === globPath) + return this.filter(node => node.firstWord === globPath) } const parts = globPath.split(edgeSymbol) const current = parts.shift() const rest = parts.join(edgeSymbol) - const matchingNodes = current === "*" ? this.getChildren() : this.filter(child => child.getFirstWord() === current) - return [].concat.apply([], matchingNodes.map(node => node._getNodesByGlobPath(rest))) + const matchingNodes = current === "*" ? this.getChildren() : this.filter(child => child.firstWord === current) + return [].concat.apply( + [], + matchingNodes.map(node => node._getNodesByGlobPath(rest)) + ) } _getNodeByPath(firstWordPath) { - const edgeSymbol = this.getEdgeSymbol() + const edgeSymbol = this.edgeSymbol if (!firstWordPath.includes(edgeSymbol)) { const index = this.indexOfLast(firstWordPath) return index === -1 ? undefined : this._nodeAt(index) @@ -12261,18 +12173,18 @@ class TreeNode extends AbstractNode { const currentNode = this._getChildrenArray()[this._getIndex()[current]] return currentNode ? currentNode._getNodeByPath(parts.join(edgeSymbol)) : undefined } - getNext() { + get next() { if (this.isRoot()) return this const index = this.getIndex() - const parent = this.getParent() + const parent = this.parent const length = parent.length const next = index + 1 return next === length ? parent._getChildrenArray()[0] : parent._getChildrenArray()[next] } - getPrevious() { + get previous() { if (this.isRoot()) return this const index = this.getIndex() - const parent = this.getParent() + const parent = this.parent const length = parent.length const prev = index - 1 return prev === -1 ? parent._getChildrenArray()[length - 1] : parent._getChildrenArray()[prev] @@ -12283,26 +12195,34 @@ class TreeNode extends AbstractNode { this.forEach(node => { if (!node.length) return undefined node.forEach(node => { - obj[node.getFirstWord()] = 1 + obj[node.firstWord] = 1 }) }) return Object.keys(obj) } getAncestorNodesByInheritanceViaExtendsKeyword(key) { - const ancestorNodes = this._getAncestorNodes((node, id) => node._getNodesByColumn(0, id), node => node.get(key), this) + const ancestorNodes = this._getAncestorNodes( + (node, id) => node._getNodesByColumn(0, id), + node => node.get(key), + this + ) ancestorNodes.push(this) return ancestorNodes } // Note: as you can probably tell by the name of this method, I don't recommend using this as it will likely be replaced by something better. getAncestorNodesByInheritanceViaColumnIndices(thisColumnNumber, extendsColumnNumber) { - const ancestorNodes = this._getAncestorNodes((node, id) => node._getNodesByColumn(thisColumnNumber, id), node => node.getWord(extendsColumnNumber), this) + const ancestorNodes = this._getAncestorNodes( + (node, id) => node._getNodesByColumn(thisColumnNumber, id), + node => node.getWord(extendsColumnNumber), + this + ) ancestorNodes.push(this) return ancestorNodes } _getAncestorNodes(getPotentialParentNodesByIdFn, getParentIdFn, cannotContainNode) { const parentId = getParentIdFn(this) if (!parentId) return [] - const potentialParentNodes = getPotentialParentNodesByIdFn(this.getParent(), parentId) + const potentialParentNodes = getPotentialParentNodesByIdFn(this.parent, parentId) if (!potentialParentNodes.length) throw new Error(`"${this.getLine()} tried to extend "${parentId}" but "${parentId}" not found.`) if (potentialParentNodes.length > 1) throw new Error(`Invalid inheritance family tree. Multiple unique ids found for "${parentId}"`) const parentNode = potentialParentNodes[0] @@ -12318,7 +12238,7 @@ class TreeNode extends AbstractNode { let node = this while (path.length) { if (!node) return names - names.push(node.nodeAt(path[0]).getFirstWord()) + names.push(node.nodeAt(path[0]).firstWord) node = node.nodeAt(path.shift()) } return names @@ -12329,7 +12249,7 @@ class TreeNode extends AbstractNode { .map((line, index) => `${index + 1} ${line}`) .join("\n") } - toCsv() { + get asCsv() { return this.toDelimited(",") } _getTypes(header) { @@ -12378,26 +12298,26 @@ class TreeNode extends AbstractNode { }) return matrix } - _toArrays(header, cellFn) { + _toArrays(columnNames, cellFn) { const skipHeaderRow = 1 - const headerArray = header.map((columnName, index) => cellFn(columnName, 0, index)) + const header = columnNames.map((columnName, index) => cellFn(columnName, 0, index)) const rows = this.map((node, rowNumber) => - header.map((columnName, columnIndex) => { + columnNames.map((columnName, columnIndex) => { const childNode = node.getNode(columnName) - const content = childNode ? childNode.getContentWithChildren() : "" + const content = childNode ? childNode.contentWithChildren : "" return cellFn(content, rowNumber + skipHeaderRow, columnIndex) }) ) return { - rows: rows, - header: headerArray + rows, + header } } _toDelimited(delimiter, header, cellFn) { const data = this._toArrays(header, cellFn) return data.header.join(delimiter) + "\n" + data.rows.map(row => row.join(delimiter)).join("\n") } - toTable() { + get asTable() { // Output a table for printing return this._toTable(100, false) } @@ -12429,10 +12349,10 @@ class TreeNode extends AbstractNode { } return this._toDelimited(" ", header, cellFn) } - toSsv() { + get asSsv() { return this.toDelimited(" ") } - toOutline() { + get asOutline() { return this._toOutline(node => node.getLine()) } toMappedOutline(nodeFn) { @@ -12475,14 +12395,14 @@ class TreeNode extends AbstractNode { // this.split("foo").join("\n") === this.toString() split(firstWord) { const constructor = this.constructor - const NodeBreakSymbol = this.getNodeBreakSymbol() - const WordBreakSymbol = this.getWordBreakSymbol() + const NodeBreakSymbol = this.nodeBreakSymbol + const WordBreakSymbol = this.wordBreakSymbol // todo: cleanup. the escaping is wierd. return this.toString() .split(new RegExp(`\\${NodeBreakSymbol}(?=${firstWord}(?:${WordBreakSymbol}|\\${NodeBreakSymbol}))`, "g")) .map(str => new constructor(str)) } - toMarkdownTable() { + get asMarkdownTable() { return this.toMarkdownTableAdvanced(this._getUnionNames(), val => val) } toMarkdownTableAdvanced(columns, formatFn) { @@ -12496,30 +12416,33 @@ class TreeNode extends AbstractNode { }) return lines.join("\n") } - toTsv() { + get asTsv() { return this.toDelimited("\t") } - getNodeBreakSymbol() { + get nodeBreakSymbol() { return "\n" } - getWordBreakSymbol() { + get wordBreakSymbol() { return " " } - getNodeBreakSymbolRegex() { - return new RegExp(this.getNodeBreakSymbol(), "g") + get edgeSymbolRegex() { + return new RegExp(this.edgeSymbol, "g") } - getEdgeSymbol() { + get nodeBreakSymbolRegex() { + return new RegExp(this.nodeBreakSymbol, "g") + } + get edgeSymbol() { return " " } _textToContentAndChildrenTuple(text) { - const lines = text.split(this.getNodeBreakSymbolRegex()) + const lines = text.split(this.nodeBreakSymbolRegex) const firstLine = lines.shift() const children = !lines.length ? undefined : lines - .map(line => (line.substr(0, 1) === this.getEdgeSymbol() ? line : this.getEdgeSymbol() + line)) + .map(line => (line.substr(0, 1) === this.edgeSymbol ? line : this.edgeSymbol + line)) .map(line => line.substr(1)) - .join(this.getNodeBreakSymbol()) + .join(this.nodeBreakSymbol) return [firstLine, children] } _getLine() { @@ -12531,7 +12454,7 @@ class TreeNode extends AbstractNode { return this } _clearChildren() { - this._deleteByIndexes(TreeUtils.getRange(0, this.length)) + this._deleteByIndexes(Utils.getRange(0, this.length)) delete this._children return this } @@ -12589,15 +12512,16 @@ class TreeNode extends AbstractNode { this._insertLineAndChildren(line, children) } _insertLineAndChildren(line, children, index = this.length) { - const nodeConstructor = this._getParser()._getNodeConstructor(line, this) - const newNode = new nodeConstructor(children, line, this) + const parser = this._getParser()._getParser(line, this) + const newNode = new parser(children, line, this) const adjustedIndex = index < 0 ? this.length + index : index this._getChildrenArray().splice(adjustedIndex, 0, newNode) if (this._index) this._makeIndex(adjustedIndex) + this.clearQuickCache() return newNode } _appendChildrenFromString(str) { - const lines = str.split(this.getNodeBreakSymbolRegex()) + const lines = str.split(this.nodeBreakSymbolRegex) const parentStack = [] let currentIndentCount = -1 let lastNode = this @@ -12615,8 +12539,8 @@ class TreeNode extends AbstractNode { } const lineContent = line.substr(currentIndentCount) const parent = parentStack[parentStack.length - 1] - const nodeConstructor = parent._getParser()._getNodeConstructor(lineContent, parent) - lastNode = new nodeConstructor(undefined, lineContent, parent) + const parser = parent._getParser()._getParser(lineContent, parent) + lastNode = new parser(undefined, lineContent, parent) parent._getChildrenArray().push(lastNode) }) } @@ -12627,21 +12551,19 @@ class TreeNode extends AbstractNode { return this._index || this._makeIndex() } getContentsArray() { - return this.map(node => node.getContent()) + return this.map(node => node.content) } - // todo: rename to getChildrenByConstructor(?) - getChildrenByNodeConstructor(constructor) { - return this.filter(child => child instanceof constructor) + getChildrenByParser(parser) { + return this.filter(child => child instanceof parser) } - getAncestorByNodeConstructor(constructor) { - if (this instanceof constructor) return this + getAncestorByParser(parser) { + if (this instanceof parser) return this if (this.isRoot()) return undefined - const parent = this.getParent() - return parent instanceof constructor ? parent : parent.getAncestorByNodeConstructor(constructor) + const parent = this.parent + return parent instanceof parser ? parent : parent.getAncestorByParser(parser) } - // todo: rename to getNodeByConstructor(?) - getNodeByType(constructor) { - return this.find(child => child instanceof constructor) + getNodeByParser(parser) { + return this.find(child => child instanceof parser) } indexOfLast(firstWord) { const result = this._getIndex()[firstWord] @@ -12653,7 +12575,7 @@ class TreeNode extends AbstractNode { const length = this.length const nodes = this._getChildrenArray() for (let index = 0; index < length; index++) { - if (nodes[index].getFirstWord() === firstWord) return index + if (nodes[index].firstWord === firstWord) return index } } // todo: rename this. it is a particular type of object. @@ -12661,7 +12583,7 @@ class TreeNode extends AbstractNode { return this._toObject() } getFirstWords() { - return this.map(node => node.getFirstWord()) + return this.map(node => node.firstWord) } _makeIndex(startAt = 0) { if (!this._index || !startAt) this._index = {} @@ -12669,7 +12591,7 @@ class TreeNode extends AbstractNode { const newIndex = this._index const length = nodes.length for (let index = startAt; index < length; index++) { - newIndex[nodes[index].getFirstWord()] = index + newIndex[nodes[index].firstWord] = index } return newIndex } @@ -12678,19 +12600,26 @@ class TreeNode extends AbstractNode { } _getIndentCount(str) { let level = 0 - const edgeChar = this.getEdgeSymbol() + const edgeChar = this.edgeSymbol while (str[level] === edgeChar) { level++ } return level } - clone() { - return new this.constructor(this.childrenToString(), this.getLine()) + clone(children = this.childrenToString(), line = this.getLine()) { + return new this.constructor(children, line) } - // todo: rename to hasFirstWord - has(firstWord) { + hasFirstWord(firstWord) { return this._hasFirstWord(firstWord) } + has(firstWordPath) { + const edgeSymbol = this.edgeSymbol + if (!firstWordPath.includes(edgeSymbol)) return this.hasFirstWord(firstWordPath) + const parts = firstWordPath.split(edgeSymbol) + const next = this.getNode(parts.shift()) + if (!next) return false + return next.has(parts.join(edgeSymbol)) + } hasNode(node) { const needle = node.toString() return this.getChildren().some(node => node.toString() === needle) @@ -12708,9 +12637,7 @@ class TreeNode extends AbstractNode { return this.getChildren().find(fn) } findLast(fn) { - return this.getChildren() - .reverse() - .find(fn) + return this.getChildren().reverse().find(fn) } every(fn) { let index = 0 @@ -12730,9 +12657,30 @@ class TreeNode extends AbstractNode { if (predicate(node) !== false) node.deepVisit(predicate) }) } + get quickCache() { + if (!this._quickCache) this._quickCache = {} + return this._quickCache + } + getCustomIndex(key) { + if (!this.quickCache.customIndexes) this.quickCache.customIndexes = {} + const customIndexes = this.quickCache.customIndexes + if (customIndexes[key]) return customIndexes[key] + const customIndex = {} + customIndexes[key] = customIndex + this.filter(file => file.has(key)).forEach(file => { + const value = file.get(key) + if (!customIndex[value]) customIndex[value] = [] + customIndex[value].push(file) + }) + return customIndex + } + clearQuickCache() { + delete this._quickCache + } // todo: protected? _clearIndex() { delete this._index + this.clearQuickCache() } slice(start, end) { return this.getChildren().slice(start, end) @@ -12751,14 +12699,14 @@ class TreeNode extends AbstractNode { return result } _getGrandParent() { - return this.isRoot() || this.getParent().isRoot() ? undefined : this.getParent().getParent() + return this.isRoot() || this.parent.isRoot() ? undefined : this.parent.parent } _getParser() { - if (!TreeNode._parsers.has(this.constructor)) TreeNode._parsers.set(this.constructor, this.createParser()) - return TreeNode._parsers.get(this.constructor) + if (!TreeNode._parserCombinators.has(this.constructor)) TreeNode._parserCombinators.set(this.constructor, this.createParserCombinator()) + return TreeNode._parserCombinators.get(this.constructor) } - createParser() { - return new Parser(this.constructor) + createParserCombinator() { + return new ParserCombinator(this.constructor) } static _makeUniqueId() { if (this._uniqueId === undefined) this._uniqueId = 0 @@ -12780,7 +12728,14 @@ class TreeNode extends AbstractNode { return this } getLineOrChildrenModifiedTime() { - return Math.max(this.getLineModifiedTime(), this.getChildArrayModifiedTime(), Math.max.apply(null, this.map(child => child.getLineOrChildrenModifiedTime()))) + return Math.max( + this.getLineModifiedTime(), + this.getChildArrayModifiedTime(), + Math.max.apply( + null, + this.map(child => child.getLineOrChildrenModifiedTime()) + ) + ) } _setVirtualParentTree(tree) { this._virtualParentTree = tree @@ -12836,7 +12791,7 @@ class TreeNode extends AbstractNode { const node = nodeOrStr instanceof TreeNode ? nodeOrStr : new TreeNode(nodeOrStr) const usedFirstWords = new Set() node.forEach(sourceNode => { - const firstWord = sourceNode.getFirstWord() + const firstWord = sourceNode.firstWord let targetNode const isAnArrayNotMap = usedFirstWords.has(firstWord) if (!this.has(firstWord)) { @@ -12846,7 +12801,7 @@ class TreeNode extends AbstractNode { } if (isAnArrayNotMap) targetNode = this.appendLine(sourceNode.getLine()) else { - targetNode = this.touchNode(firstWord).setContent(sourceNode.getContent()) + targetNode = this.touchNode(firstWord).setContent(sourceNode.content) usedFirstWords.add(firstWord) } if (sourceNode.length) targetNode.extend(sourceNode) @@ -12861,7 +12816,7 @@ class TreeNode extends AbstractNode { const map = new Map() const lastNode = clone.lastNode() lastNode.getOlderSiblings().forEach(node => map.set(node.getWord(0), node)) - lastNode.getTopDownArray().forEach(node => { + lastNode.topDownArray.forEach(node => { const replacement = map.get(node.getWord(0)) if (!replacement) return node.replaceNode(str => replacement.toString()) @@ -12872,7 +12827,7 @@ class TreeNode extends AbstractNode { const clone = this.clone() const defs = clone.findNodes(macroDefinitionWord) const allUses = clone.findNodes(macroUsageWord) - const wordBreakSymbol = clone.getWordBreakSymbol() + const wordBreakSymbol = clone.wordBreakSymbol defs.forEach(def => { const macroName = def.getWord(1) const uses = allUses.filter(node => node.hasWord(1, macroName)) @@ -12899,7 +12854,7 @@ class TreeNode extends AbstractNode { this._lineModifiedTime = this._getProcessTimeInMilliseconds() } insertWord(index, word) { - const wi = this.getWordBreakSymbol() + const wi = this.wordBreakSymbol const words = this._getLine().split(wi) words.splice(index, 0, word) this.setLine(words.join(wi)) @@ -12907,7 +12862,7 @@ class TreeNode extends AbstractNode { } deleteDuplicates() { const set = new Set() - this.getTopDownArray().forEach(node => { + this.topDownArray.forEach(node => { const str = node.toString() if (set.has(str)) node.destroy() else set.add(str) @@ -12915,7 +12870,7 @@ class TreeNode extends AbstractNode { return this } setWord(index, word) { - const wi = this.getWordBreakSymbol() + const wi = this.wordBreakSymbol const words = this._getLine().split(wi) words[index] = word this.setLine(words.join(wi)) @@ -12925,34 +12880,34 @@ class TreeNode extends AbstractNode { return this._clearChildren() } setContent(content) { - if (content === this.getContent()) return this - const newArray = [this.getFirstWord()] + if (content === this.content) return this + const newArray = [this.firstWord] if (content !== undefined) { content = content.toString() - if (content.match(this.getNodeBreakSymbol())) return this.setContentWithChildren(content) + if (content.match(this.nodeBreakSymbol)) return this.setContentWithChildren(content) newArray.push(content) } - this._setLine(newArray.join(this.getWordBreakSymbol())) + this._setLine(newArray.join(this.wordBreakSymbol)) this._updateLineModifiedTimeAndTriggerEvent() return this } prependSibling(line, children) { - return this.getParent().insertLineAndChildren(line, children, this.getIndex()) + return this.parent.insertLineAndChildren(line, children, this.getIndex()) } appendSibling(line, children) { - return this.getParent().insertLineAndChildren(line, children, this.getIndex() + 1) + return this.parent.insertLineAndChildren(line, children, this.getIndex() + 1) } setContentWithChildren(text) { // todo: deprecate - if (!text.includes(this.getNodeBreakSymbol())) { + if (!text.includes(this.nodeBreakSymbol)) { this._clearChildren() return this.setContent(text) } - const lines = text.split(this.getNodeBreakSymbolRegex()) + const lines = text.split(this.nodeBreakSymbolRegex) const firstLine = lines.shift() this.setContent(firstLine) // tood: cleanup. - const remainingString = lines.join(this.getNodeBreakSymbol()) + const remainingString = lines.join(this.nodeBreakSymbol) const children = new TreeNode(remainingString) if (!remainingString) children.appendLine("") this.setChildren(children) @@ -12964,13 +12919,13 @@ class TreeNode extends AbstractNode { setLine(line) { if (line === this.getLine()) return this // todo: clear parent TMTimes - this.getParent()._clearIndex() + this.parent._clearIndex() this._setLine(line) this._updateLineModifiedTimeAndTriggerEvent() return this } duplicate() { - return this.getParent()._insertLineAndChildren(this.getLine(), this.childrenToString(), this.getIndex() + 1) + return this.parent._insertLineAndChildren(this.getLine(), this.childrenToString(), this.getIndex() + 1) } trim() { // todo: could do this so only the trimmed rows are deleted. @@ -12978,7 +12933,7 @@ class TreeNode extends AbstractNode { return this } destroy() { - this.getParent()._deleteNode(this) + this.parent._deleteNode(this) } set(firstWordPath, text) { return this.touchNode(firstWordPath).setContentWithChildren(text) @@ -13009,6 +12964,10 @@ class TreeNode extends AbstractNode { appendLine(line) { return this._insertLineAndChildren(line) } + appendUniqueLine(line) { + if (!this.hasLine(line)) return this.appendLine(line) + return this.findLine(line) + } appendLineAndChildren(line, children) { return this._insertLineAndChildren(line, children) } @@ -13021,7 +12980,10 @@ class TreeNode extends AbstractNode { // todo: remove? getNodesByLinePrefixes(columns) { const matches = [] - this._getNodesByLineRegex(matches, columns.map(str => new RegExp("^" + str))) + this._getNodesByLineRegex( + matches, + columns.map(str => new RegExp("^" + str)) + ) return matches } nodesThatStartWith(prefix) { @@ -13066,7 +13028,7 @@ class TreeNode extends AbstractNode { return this } invert() { - this.forEach(node => node.getWords().reverse()) + this.forEach(node => node.words.reverse()) return this } _rename(oldFirstWord, newFirstWord) { @@ -13080,7 +13042,7 @@ class TreeNode extends AbstractNode { // Does not recurse. remap(map) { this.forEach(node => { - const firstWord = node.getFirstWord() + const firstWord = node.firstWord if (map[firstWord] !== undefined) node.setFirstWord(map[firstWord]) }) return this @@ -13098,12 +13060,12 @@ class TreeNode extends AbstractNode { const allNodes = this._getChildrenArray() const indexesToDelete = [] allNodes.forEach((node, index) => { - if (node.getFirstWord() === firstWord) indexesToDelete.push(index) + if (node.firstWord === firstWord) indexesToDelete.push(index) }) return this._deleteByIndexes(indexesToDelete) } delete(path = "") { - const edgeSymbol = this.getEdgeSymbol() + const edgeSymbol = this.edgeSymbol if (!path.includes(edgeSymbol)) return this._deleteAllChildNodesWithFirstWord(path) const parts = path.split(edgeSymbol) const nextFirstWord = parts.pop() @@ -13115,12 +13077,12 @@ class TreeNode extends AbstractNode { return this } _getNonMaps() { - const results = this.getTopDownArray().filter(node => node.hasDuplicateFirstWords()) + const results = this.topDownArray.filter(node => node.hasDuplicateFirstWords()) if (this.hasDuplicateFirstWords()) results.unshift(this) return results } replaceNode(fn) { - const parent = this.getParent() + const parent = this.parent const index = this.getIndex() const newNodes = new TreeNode(fn(this.toString())) const returnedNodes = [] @@ -13145,7 +13107,7 @@ class TreeNode extends AbstractNode { while (this.has(index.toString())) { index++ } - const line = index.toString() + (content === undefined ? "" : this.getWordBreakSymbol() + content) + const line = index.toString() + (content === undefined ? "" : this.wordBreakSymbol + content) return this.appendLineAndChildren(line, children) } deleteBlanks() { @@ -13159,7 +13121,7 @@ class TreeNode extends AbstractNode { return this._firstWordSort(firstWordOrder) } deleteWordAt(wordIndex) { - const words = this.getWords() + const words = this.words words.splice(wordIndex, 1) return this.setWords(words) } @@ -13176,7 +13138,7 @@ class TreeNode extends AbstractNode { } triggerAncestors(event) { if (this.isRoot()) return - const parent = this.getParent() + const parent = this.parent parent.trigger(event) parent.triggerAncestors(event) } @@ -13199,18 +13161,14 @@ class TreeNode extends AbstractNode { return this } setWords(words) { - return this.setLine(words.join(this.getWordBreakSymbol())) + return this.setLine(words.join(this.wordBreakSymbol)) } setWordsFrom(index, words) { - this.setWords( - this.getWords() - .slice(0, index) - .concat(words) - ) + this.setWords(this.words.slice(0, index).concat(words)) return this } appendWord(word) { - const words = this.getWords() + const words = this.words words.push(word) return this.setWords(words) } @@ -13222,8 +13180,8 @@ class TreeNode extends AbstractNode { map[word] = index }) this.sort((nodeA, nodeB) => { - const valA = map[nodeA.getFirstWord()] - const valB = map[nodeB.getFirstWord()] + const valA = map[nodeA.firstWord] + const valB = map[nodeB.firstWord] if (valA > valB) return nodeBFirst if (valA < valB) return nodeAFirst return secondarySortFn ? secondarySortFn(nodeA, nodeB) : 0 @@ -13238,8 +13196,8 @@ class TreeNode extends AbstractNode { return contextNode } _touchNodeByString(str) { - str = str.replace(this.getNodeBreakSymbolRegex(), "") // todo: do we want to do this sanitization? - return this._touchNode(str.split(this.getWordBreakSymbol())) + str = str.replace(this.nodeBreakSymbolRegex, "") // todo: do we want to do this sanitization? + return this._touchNode(str.split(this.wordBreakSymbol)) } touchNode(str) { return this._touchNodeByString(str) @@ -13250,6 +13208,9 @@ class TreeNode extends AbstractNode { hasLine(line) { return this.getChildren().some(node => node.getLine() === line) } + findLine(line) { + return this.getChildren().find(node => node.getLine() === line) + } getNodesByLine(line) { return this.filter(node => node.getLine() === line) } @@ -13266,8 +13227,8 @@ class TreeNode extends AbstractNode { const indices = indexOrIndices instanceof Array ? indexOrIndices : [indexOrIndices] const length = indices.length this.sort((nodeA, nodeB) => { - const wordsA = nodeA.getWords() - const wordsB = nodeB.getWords() + const wordsA = nodeA.words + const wordsB = nodeB.words for (let index = 0; index < length; index++) { const col = indices[index] const av = wordsA[col] @@ -13289,7 +13250,7 @@ class TreeNode extends AbstractNode { return this.appendWord(word) } // todo: check to ensure identical objects - addObjectsAsDelimited(arrayOfObjects, delimiter = TreeUtils._chooseDelimiter(new TreeNode(arrayOfObjects).toString())) { + addObjectsAsDelimited(arrayOfObjects, delimiter = Utils._chooseDelimiter(new TreeNode(arrayOfObjects).toString())) { const header = Object.keys(arrayOfObjects[0]) .join(delimiter) .replace(/[\n\r]/g, "") @@ -13300,11 +13261,11 @@ class TreeNode extends AbstractNode { ) return this.addUniqueRowsToNestedDelimited(header, rows) } - setChildrenAsDelimited(tree, delimiter = TreeUtils._chooseDelimiter(tree.toString())) { + setChildrenAsDelimited(tree, delimiter = Utils._chooseDelimiter(tree.toString())) { tree = tree instanceof TreeNode ? tree : new TreeNode(tree) return this.setChildren(tree.toDelimited(delimiter)) } - convertChildrenToDelimited(delimiter = TreeUtils._chooseDelimiter(this.childrenToString())) { + convertChildrenToDelimited(delimiter = Utils._chooseDelimiter(this.childrenToString())) { // todo: handle newlines!!! return this.setChildren(this.toDelimited(delimiter)) } @@ -13319,13 +13280,13 @@ class TreeNode extends AbstractNode { shiftLeft() { const grandParent = this._getGrandParent() if (!grandParent) return this - const parentIndex = this.getParent().getIndex() + const parentIndex = this.parent.getIndex() const newNode = grandParent.insertLineAndChildren(this.getLine(), this.length ? this.childrenToString() : undefined, parentIndex + 1) this.destroy() return newNode } pasteText(text) { - const parent = this.getParent() + const parent = this.parent const index = this.getIndex() const newNodes = new TreeNode(text) const firstNode = newNodes.nodeAt(0) @@ -13346,7 +13307,7 @@ class TreeNode extends AbstractNode { templateToString(obj) { // todo: compile/cache for perf? const tree = this.clone() - tree.getTopDownArray().forEach(node => { + tree.topDownArray.forEach(node => { const line = node.getLine().replace(/{([^\}]+)}/g, (match, path) => { const replacement = obj[path] if (replacement === undefined) throw new Error(`In string template no match found on line "${node.getLine()}"`) @@ -13462,8 +13423,8 @@ class TreeNode extends AbstractNode { } static serializedTreeNodeToTree(treeNode) { const language = new TreeNode() - const cellDelimiter = language.getWordBreakSymbol() - const nodeDelimiter = language.getNodeBreakSymbol() + const cellDelimiter = language.wordBreakSymbol + const nodeDelimiter = language.nodeBreakSymbol const line = treeNode.cells ? treeNode.cells.join(cellDelimiter) : undefined const tree = new TreeNode(undefined, line) if (treeNode.children) @@ -13478,8 +13439,8 @@ class TreeNode extends AbstractNode { static fromGridJson(str) { const lines = JSON.parse(str) const language = new TreeNode() - const cellDelimiter = language.getWordBreakSymbol() - const nodeDelimiter = language.getNodeBreakSymbol() + const cellDelimiter = language.wordBreakSymbol + const nodeDelimiter = language.nodeBreakSymbol return new TreeNode(lines.map(line => line.join(cellDelimiter)).join(nodeDelimiter)) } static fromSsv(str) { @@ -13696,9 +13657,20 @@ class TreeNode extends AbstractNode { } return methods[format](content) } + static fromFolder(folderPath, filepathPredicate = filepath => filepath !== ".DS_Store") { + const path = require("path") + const fs = require("fs") + const tree = new TreeNode() + const files = fs + .readdirSync(folderPath) + .map(filename => path.join(folderPath, filename)) + .filter(filepath => !fs.statSync(filepath).isDirectory() && filepathPredicate(filepath)) + .forEach(filePath => tree.appendLineAndChildren(filePath, fs.readFileSync(filePath, "utf8"))) + return tree + } } -TreeNode._parsers = new Map() -TreeNode.Parser = Parser +TreeNode._parserCombinators = new Map() +TreeNode.ParserCombinator = ParserCombinator TreeNode.iris = `sepal_length,sepal_width,petal_length,petal_width,species 6.1,3,4.9,1.8,virginica 5.6,2.7,4.2,1.3,versicolor @@ -13710,7 +13682,7 @@ TreeNode.iris = `sepal_length,sepal_width,petal_length,petal_width,species 4.9,2.5,4.5,1.7,virginica 5.1,3.5,1.4,0.2,setosa 5,3.4,1.5,0.2,setosa` -TreeNode.getVersion = () => "53.8.0" +TreeNode.getVersion = () => "74.0.0" class AbstractExtendibleTreeNode extends TreeNode { _getFromExtended(firstWordPath) { const hit = this._getNodeFromExtended(firstWordPath) @@ -13719,15 +13691,15 @@ class AbstractExtendibleTreeNode extends TreeNode { _getFamilyTree() { const tree = new TreeNode() this.forEach(node => { - const path = node._getAncestorsArray().map(node => node._getId()) + const path = node._getAncestorsArray().map(node => node.id) path.reverse() tree.touchNode(path.join(" ")) }) return tree } // todo: be more specific with the param - _getChildrenByNodeConstructorInExtended(constructor) { - return TreeUtils.flatten(this._getAncestorsArray().map(node => node.getChildrenByNodeConstructor(constructor))) + _getChildrenByParserInExtended(parser) { + return Utils.flatten(this._getAncestorsArray().map(node => node.getChildrenByParser(parser))) } _getExtendedParent() { return this._getAncestorsArray()[1] @@ -13745,11 +13717,11 @@ class AbstractExtendibleTreeNode extends TreeNode { .reverse() .join("\n") } - _doesExtend(nodeTypeId) { - return this._getAncestorSet().has(nodeTypeId) + _doesExtend(parserId) { + return this._getAncestorSet().has(parserId) } _getAncestorSet() { - if (!this._cache_ancestorSet) this._cache_ancestorSet = new Set(this._getAncestorsArray().map(def => def._getId())) + if (!this._cache_ancestorSet) this._cache_ancestorSet = new Set(this._getAncestorsArray().map(def => def.id)) return this._cache_ancestorSet } // Note: the order is: [this, parent, grandParent, ...] @@ -13757,7 +13729,7 @@ class AbstractExtendibleTreeNode extends TreeNode { this._initAncestorsArrayCache(cannotContainNodes) return this._cache_ancestorsArray } - _getIdThatThisExtends() { + get idThatThisExtends() { return this.get(TreeNotationConstants.extends) } _initAncestorsArrayCache(cannotContainNodes) { @@ -13765,9 +13737,9 @@ class AbstractExtendibleTreeNode extends TreeNode { if (cannotContainNodes && cannotContainNodes.includes(this)) throw new Error(`Loop detected: '${this.getLine()}' is the ancestor of one of its ancestors.`) cannotContainNodes = cannotContainNodes || [this] let ancestors = [this] - const extendedId = this._getIdThatThisExtends() + const extendedId = this.idThatThisExtends if (extendedId) { - const parentNode = this._getIdToNodeMap()[extendedId] + const parentNode = this.idToNodeMap[extendedId] if (!parentNode) throw new Error(`${extendedId} not found`) ancestors = ancestors.concat(parentNode._getAncestorsArray(cannotContainNodes)) } @@ -13775,17 +13747,17 @@ class AbstractExtendibleTreeNode extends TreeNode { } } class ExtendibleTreeNode extends AbstractExtendibleTreeNode { - _getIdToNodeMap() { - if (!this.isRoot()) return this.getRootNode()._getIdToNodeMap() + get idToNodeMap() { + if (!this.isRoot()) return this.root.idToNodeMap if (!this._nodeMapCache) { this._nodeMapCache = {} this.forEach(child => { - this._nodeMapCache[child._getId()] = child + this._nodeMapCache[child.id] = child }) } return this._nodeMapCache } - _getId() { + get id() { return this.getWord(0) } } @@ -13794,8 +13766,17 @@ window.ExtendibleTreeNode = ExtendibleTreeNode window.AbstractExtendibleTreeNode = AbstractExtendibleTreeNode window.TreeEvents = TreeEvents window.TreeWord = TreeWord + + +// Compiled language parsers will include these files: +const GlobalNamespaceAdditions = { + Utils: "Utils.js", + TreeNode: "TreeNode.js", + HandGrammarProgram: "GrammarLanguage.js", + GrammarBackedNode: "GrammarLanguage.js" +} var GrammarConstantsCompiler -;(function(GrammarConstantsCompiler) { +;(function (GrammarConstantsCompiler) { GrammarConstantsCompiler["stringTemplate"] = "stringTemplate" GrammarConstantsCompiler["indentCharacter"] = "indentCharacter" GrammarConstantsCompiler["catchAllCellDelimiter"] = "catchAllCellDelimiter" @@ -13803,19 +13784,12 @@ var GrammarConstantsCompiler GrammarConstantsCompiler["joinChildrenWith"] = "joinChildrenWith" GrammarConstantsCompiler["closeChildren"] = "closeChildren" })(GrammarConstantsCompiler || (GrammarConstantsCompiler = {})) -var SQLiteTypes -;(function(SQLiteTypes) { - SQLiteTypes["integer"] = "INTEGER" - SQLiteTypes["float"] = "FLOAT" - SQLiteTypes["text"] = "TEXT" -})(SQLiteTypes || (SQLiteTypes = {})) var GrammarConstantsMisc -;(function(GrammarConstantsMisc) { +;(function (GrammarConstantsMisc) { GrammarConstantsMisc["doNotSynthesize"] = "doNotSynthesize" - GrammarConstantsMisc["tableName"] = "tableName" })(GrammarConstantsMisc || (GrammarConstantsMisc = {})) var PreludeCellTypeIds -;(function(PreludeCellTypeIds) { +;(function (PreludeCellTypeIds) { PreludeCellTypeIds["anyCell"] = "anyCell" PreludeCellTypeIds["keywordCell"] = "keywordCell" PreludeCellTypeIds["extraWordCell"] = "extraWordCell" @@ -13826,14 +13800,14 @@ var PreludeCellTypeIds PreludeCellTypeIds["intCell"] = "intCell" })(PreludeCellTypeIds || (PreludeCellTypeIds = {})) var GrammarConstantsConstantTypes -;(function(GrammarConstantsConstantTypes) { +;(function (GrammarConstantsConstantTypes) { GrammarConstantsConstantTypes["boolean"] = "boolean" GrammarConstantsConstantTypes["string"] = "string" GrammarConstantsConstantTypes["int"] = "int" GrammarConstantsConstantTypes["float"] = "float" })(GrammarConstantsConstantTypes || (GrammarConstantsConstantTypes = {})) var GrammarBundleFiles -;(function(GrammarBundleFiles) { +;(function (GrammarBundleFiles) { GrammarBundleFiles["package"] = "package.json" GrammarBundleFiles["readme"] = "readme.md" GrammarBundleFiles["indexHtml"] = "index.html" @@ -13841,22 +13815,22 @@ var GrammarBundleFiles GrammarBundleFiles["testJs"] = "test.js" })(GrammarBundleFiles || (GrammarBundleFiles = {})) var GrammarCellParser -;(function(GrammarCellParser) { +;(function (GrammarCellParser) { GrammarCellParser["prefix"] = "prefix" GrammarCellParser["postfix"] = "postfix" GrammarCellParser["omnifix"] = "omnifix" })(GrammarCellParser || (GrammarCellParser = {})) var GrammarConstants -;(function(GrammarConstants) { +;(function (GrammarConstants) { // node types GrammarConstants["extensions"] = "extensions" - GrammarConstants["toolingDirective"] = "tooling" - GrammarConstants["todoComment"] = "todo" + GrammarConstants["comment"] = "//" GrammarConstants["version"] = "version" - GrammarConstants["nodeType"] = "nodeType" + GrammarConstants["parser"] = "parser" GrammarConstants["cellType"] = "cellType" GrammarConstants["grammarFileExtension"] = "grammar" - GrammarConstants["nodeTypeSuffix"] = "Node" + GrammarConstants["abstractParserPrefix"] = "abstract" + GrammarConstants["parserSuffix"] = "Parser" GrammarConstants["cellTypeSuffix"] = "Cell" // error check time GrammarConstants["regex"] = "regex" @@ -13866,39 +13840,44 @@ var GrammarConstants GrammarConstants["examples"] = "examples" GrammarConstants["min"] = "min" GrammarConstants["max"] = "max" - // baseNodeTypes - GrammarConstants["baseNodeType"] = "baseNodeType" - GrammarConstants["blobNode"] = "blobNode" - GrammarConstants["errorNode"] = "errorNode" + // baseParsers + GrammarConstants["baseParser"] = "baseParser" + GrammarConstants["blobParser"] = "blobParser" + GrammarConstants["errorParser"] = "errorParser" // parse time GrammarConstants["extends"] = "extends" - GrammarConstants["abstract"] = "abstract" GrammarConstants["root"] = "root" GrammarConstants["crux"] = "crux" GrammarConstants["cruxFromId"] = "cruxFromId" GrammarConstants["pattern"] = "pattern" GrammarConstants["inScope"] = "inScope" GrammarConstants["cells"] = "cells" + GrammarConstants["listDelimiter"] = "listDelimiter" + GrammarConstants["contentKey"] = "contentKey" + GrammarConstants["childrenKey"] = "childrenKey" + GrammarConstants["uniqueFirstWord"] = "uniqueFirstWord" GrammarConstants["catchAllCellType"] = "catchAllCellType" GrammarConstants["cellParser"] = "cellParser" - GrammarConstants["catchAllNodeType"] = "catchAllNodeType" + GrammarConstants["catchAllParser"] = "catchAllParser" GrammarConstants["constants"] = "constants" GrammarConstants["required"] = "required" GrammarConstants["single"] = "single" + GrammarConstants["uniqueLine"] = "uniqueLine" GrammarConstants["tags"] = "tags" GrammarConstants["_extendsJsClass"] = "_extendsJsClass" GrammarConstants["_rootNodeJsHeader"] = "_rootNodeJsHeader" - // default catchAll nodeType - GrammarConstants["BlobNode"] = "BlobNode" - GrammarConstants["defaultRootNode"] = "defaultRootNode" + // default catchAll parser + GrammarConstants["BlobParser"] = "BlobParser" + GrammarConstants["DefaultRootParser"] = "DefaultRootParser" // code GrammarConstants["javascript"] = "javascript" // compile time - GrammarConstants["compilerNodeType"] = "compiler" + GrammarConstants["compilerParser"] = "compiler" GrammarConstants["compilesTo"] = "compilesTo" // develop time GrammarConstants["description"] = "description" GrammarConstants["example"] = "example" + GrammarConstants["sortTemplate"] = "sortTemplate" GrammarConstants["frequency"] = "frequency" GrammarConstants["highlightScope"] = "highlightScope" })(GrammarConstants || (GrammarConstants = {})) @@ -13916,91 +13895,106 @@ class TypedWord extends TreeWord { } // todo: can we merge these methods into base TreeNode and ditch this class? class GrammarBackedNode extends TreeNode { - getDefinition() { - const handGrammarProgram = this.getHandGrammarProgram() - return this.isRoot() ? handGrammarProgram : handGrammarProgram.getNodeTypeDefinitionByNodeTypeId(this.constructor.name) - } - toSQLiteInsertStatement(primaryKeyFunction = node => node.getWord(0)) { - const def = this.getDefinition() - const tableName = def.getTableNameIfAny() || def._getId() - const columns = def.getSQLiteTableColumns() - const hits = columns.filter(colDef => this.has(colDef.columnName)) - const values = hits.map(colDef => { - const node = this.getNode(colDef.columnName) - const content = node.getContent() - return colDef.type === SQLiteTypes.text ? `"${content}"` : content - }) - hits.unshift({ columnName: "id", type: SQLiteTypes.text }) - values.unshift(`"${primaryKeyFunction(this)}"`) - return `INSERT INTO ${tableName} (${hits.map(col => col.columnName).join(",")}) VALUES (${values.join(",")});` + get definition() { + if (this._definition) return this._definition + this._definition = this.isRoot() ? this.handGrammarProgram : this.parent.definition.getParserDefinitionByParserId(this.constructor.name) + return this._definition + } + get rootGrammarTree() { + return this.definition.root } getAutocompleteResults(partialWord, cellIndex) { return cellIndex === 0 ? this._getAutocompleteResultsForFirstWord(partialWord) : this._getAutocompleteResultsForCell(partialWord, cellIndex) } - getChildInstancesOfNodeTypeId(nodeTypeId) { - return this.filter(node => node.doesExtend(nodeTypeId)) + get nodeIndex() { + // StringMap<int> {firstWord: index} + // When there are multiple tails with the same firstWord, _index stores the last content. + // todo: change the above behavior: when a collision occurs, create an array. + return this._nodeIndex || this._makeNodeIndex() } - doesExtend(nodeTypeId) { - return this.getDefinition()._doesExtend(nodeTypeId) + _clearIndex() { + delete this._nodeIndex + return super._clearIndex() } - _getErrorNodeErrors() { - return [this.getFirstWord() ? new UnknownNodeTypeError(this) : new BlankLineError(this)] + _makeIndex(startAt = 0) { + if (this._nodeIndex) this._makeNodeIndex(startAt) + return super._makeIndex(startAt) } - _getBlobNodeCatchAllNodeType() { - return BlobNode + _makeNodeIndex(startAt = 0) { + if (!this._nodeIndex || !startAt) this._nodeIndex = {} + const nodes = this._getChildrenArray() + const newIndex = this._nodeIndex + const length = nodes.length + for (let index = startAt; index < length; index++) { + const node = nodes[index] + const ancestors = Array.from(node.definition._getAncestorSet()).forEach(id => { + if (!newIndex[id]) newIndex[id] = [] + newIndex[id].push(node) + }) + } + return newIndex + } + getChildInstancesOfParserId(parserId) { + return this.nodeIndex[parserId] || [] + } + doesExtend(parserId) { + return this.definition._doesExtend(parserId) + } + _getErrorParserErrors() { + return [this.firstWord ? new UnknownParserError(this) : new BlankLineError(this)] + } + _getBlobParserCatchAllParser() { + return BlobParser } _getAutocompleteResultsForFirstWord(partialWord) { - const keywordMap = this.getDefinition().getFirstWordMapWithDefinitions() + const keywordMap = this.definition.firstWordMapWithDefinitions let keywords = Object.keys(keywordMap) if (partialWord) keywords = keywords.filter(keyword => keyword.includes(partialWord)) - return keywords.map(keyword => { - const def = keywordMap[keyword] - const description = def.getDescription() - return { - text: keyword, - displayText: keyword + (description ? " " + description : "") - } - }) + return keywords + .map(keyword => { + const def = keywordMap[keyword] + if (def.suggestInAutocomplete === false) return false + const description = def.description + return { + text: keyword, + displayText: keyword + (description ? " " + description : "") + } + }) + .filter(i => i) } _getAutocompleteResultsForCell(partialWord, cellIndex) { // todo: root should be [] correct? - const cell = this._getParsedCells()[cellIndex] + const cell = this.parsedCells[cellIndex] return cell ? cell.getAutoCompleteWords(partialWord) : [] } // note: this is overwritten by the root node of a runtime grammar program. // some of the magic that makes this all work. but maybe there's a better way. - getHandGrammarProgram() { + get handGrammarProgram() { if (this.isRoot()) throw new Error(`Root node without getHandGrammarProgram defined.`) - return this.getRootNode().getHandGrammarProgram() + return this.root.handGrammarProgram } getRunTimeEnumOptions(cell) { return undefined } _sortNodesByInScopeOrder() { - const nodeTypeOrder = this.getDefinition()._getMyInScopeNodeTypeIds() - if (!nodeTypeOrder.length) return this + const parserOrder = this.definition._getMyInScopeParserIds() + if (!parserOrder.length) return this const orderMap = {} - nodeTypeOrder.forEach((word, index) => { - orderMap[word] = index - }) - this.sort( - TreeUtils.makeSortByFn(runtimeNode => { - return orderMap[runtimeNode.getDefinition().getNodeTypeIdFromDefinition()] - }) - ) + parserOrder.forEach((word, index) => (orderMap[word] = index)) + this.sort(Utils.makeSortByFn(runtimeNode => orderMap[runtimeNode.definition.parserIdFromDefinition])) return this } get requiredNodeErrors() { const errors = [] - Object.values(this.getDefinition().getFirstWordMapWithDefinitions()).forEach(def => { - if (def.isRequired()) if (!this.getChildren().some(node => node.getDefinition() === def)) errors.push(new MissingRequiredNodeTypeError(this, def.getNodeTypeIdFromDefinition())) + Object.values(this.definition.firstWordMapWithDefinitions).forEach(def => { + if (def.isRequired() && !this.nodeIndex[def.id]) errors.push(new MissingRequiredParserError(this, def.id)) }) return errors } - getProgramAsCells() { + get programAsCells() { // todo: what is this? - return this.getTopDownArray().map(node => { - const cells = node._getParsedCells() + return this.topDownArray.map(node => { + const cells = node.parsedCells let indents = node.getIndentLevel() - 1 while (indents) { cells.unshift(undefined) @@ -14009,55 +14003,49 @@ class GrammarBackedNode extends TreeNode { return cells }) } - getProgramWidth() { - return Math.max(...this.getProgramAsCells().map(line => line.length)) + get programWidth() { + return Math.max(...this.programAsCells.map(line => line.length)) } - getAllTypedWords() { + get allTypedWords() { const words = [] - this.getTopDownArray().forEach(node => { - node.getWordTypes().forEach((cell, index) => { - words.push(new TypedWord(node, index, cell.getCellTypeId())) - }) - }) + this.topDownArray.forEach(node => node.wordTypes.forEach((cell, index) => words.push(new TypedWord(node, index, cell.cellTypeId)))) return words } findAllWordsWithCellType(cellTypeId) { - return this.getAllTypedWords().filter(typedWord => typedWord.type === cellTypeId) + return this.allTypedWords.filter(typedWord => typedWord.type === cellTypeId) } - findAllNodesWithNodeType(nodeTypeId) { - return this.getTopDownArray().filter(node => node.getDefinition().getNodeTypeIdFromDefinition() === nodeTypeId) + findAllNodesWithParser(parserId) { + return this.topDownArray.filter(node => node.definition.parserIdFromDefinition === parserId) } toCellTypeTree() { - return this.getTopDownArray() - .map(child => child.getIndentation() + child.getLineCellTypes()) - .join("\n") + return this.topDownArray.map(child => child.indentation + child.lineCellTypes).join("\n") } getParseTable(maxColumnWidth = 40) { const tree = new TreeNode(this.toCellTypeTree()) return new TreeNode( - tree.getTopDownArray().map((node, lineNumber) => { + tree.topDownArray.map((node, lineNumber) => { const sourceNode = this.nodeAtLine(lineNumber) const errs = sourceNode.getErrors() const errorCount = errs.length const obj = { lineNumber: lineNumber, - source: sourceNode.getIndentation() + sourceNode.getLine(), - nodeType: sourceNode.constructor.name, - cellTypes: node.getContent(), + source: sourceNode.indentation + sourceNode.getLine(), + parser: sourceNode.constructor.name, + cellTypes: node.content, errorCount: errorCount } - if (errorCount) obj.errorMessages = errs.map(err => err.getMessage()).join(";") + if (errorCount) obj.errorMessages = errs.map(err => err.message).join(";") return obj }) ).toFormattedTable(maxColumnWidth) } - // Helper method for selecting potential nodeTypes needed to update grammar file. - getInvalidNodeTypes() { + // Helper method for selecting potential parsers needed to update grammar file. + get invalidParsers() { return Array.from( new Set( this.getAllErrors() - .filter(err => err instanceof UnknownNodeTypeError) - .map(err => err.getNode().getFirstWord()) + .filter(err => err instanceof UnknownParserError) + .map(err => err.getNode().firstWord) ) ) } @@ -14090,7 +14078,7 @@ class GrammarBackedNode extends TreeNode { result.suggestions = result.suggestions.map(node => node.text).join(" ") return result }) - ).toTable() + ).asTable } getAutocompleteResultsAt(lineIndex, charIndex) { const lineNode = this.nodeAtLine(lineIndex) || this @@ -14107,10 +14095,10 @@ class GrammarBackedNode extends TreeNode { matches: nodeInScope.getAutocompleteResults(wordProperties.word, wordIndex) } } - _sortWithParentNodeTypesUpTop() { - const familyTree = new HandGrammarProgram(this.toString()).getNodeTypeFamilyTree() + _sortWithParentParsersUpTop() { + const familyTree = new HandGrammarProgram(this.toString()).parserFamilyTree const rank = {} - familyTree.getTopDownArray().forEach((node, index) => { + familyTree.topDownArray.forEach((node, index) => { rank[node.getWord(0)] = index }) const nodeAFirst = -1 @@ -14126,58 +14114,80 @@ class GrammarBackedNode extends TreeNode { if (this.isRoot()) { this._sortNodesByInScopeOrder() try { - this._sortWithParentNodeTypesUpTop() + this._sortWithParentParsersUpTop() } catch (err) { console.log(`Warning: ${err}`) } } - this.getTopDownArray().forEach(child => { + this.topDownArray.forEach(child => { child.format() }) return this } - getNodeTypeUsage(filepath = "") { - // returns a report on what nodeTypes from its language the program uses + sortFromSortTemplate() { + if (!this.length) return this + // Recurse + this.forEach(node => node.sortFromSortTemplate()) + const def = this.isRoot() ? this.definition.rootParserDefinition : this.definition + const { sortIndices, sortSections } = def.sortSpec + // Sort and insert section breaks + if (sortIndices.size) { + // Sort keywords + this.sort((nodeA, nodeB) => { + var _a, _b + const aIndex = (_a = sortIndices.get(nodeA.firstWord)) !== null && _a !== void 0 ? _a : sortIndices.get(nodeA.sortKey) + const bIndex = (_b = sortIndices.get(nodeB.firstWord)) !== null && _b !== void 0 ? _b : sortIndices.get(nodeB.sortKey) + if (aIndex === undefined) console.error(`sortTemplate is missing "${nodeA.firstWord}"`) + const a = aIndex !== null && aIndex !== void 0 ? aIndex : 1000 + const b = bIndex !== null && bIndex !== void 0 ? bIndex : 1000 + return a > b ? 1 : a < b ? -1 : nodeA.getLine() > nodeB.getLine() + }) + // pad sections + let currentSection = 0 + this.forEach(node => { + var _a + const nodeSection = (_a = sortSections.get(node.firstWord)) !== null && _a !== void 0 ? _a : sortSections.get(node.sortKey) + const sectionHasAdvanced = nodeSection > currentSection + if (sectionHasAdvanced) { + currentSection = nodeSection + node.prependSibling("") // Put a blank line before this section + } + }) + } + return this + } + getParserUsage(filepath = "") { + // returns a report on what parsers from its language the program uses const usage = new TreeNode() - const handGrammarProgram = this.getHandGrammarProgram() - handGrammarProgram.getValidConcreteAndAbstractNodeTypeDefinitions().forEach(def => { - const requiredCellTypeIds = def.getCellParser().getRequiredCellTypeIds() - usage.appendLine([def.getNodeTypeIdFromDefinition(), "line-id", "nodeType", requiredCellTypeIds.join(" ")].join(" ")) + const handGrammarProgram = this.handGrammarProgram + handGrammarProgram.validConcreteAndAbstractParserDefinitions.forEach(def => { + const requiredCellTypeIds = def.cellParser.getRequiredCellTypeIds() + usage.appendLine([def.parserIdFromDefinition, "line-id", "parser", requiredCellTypeIds.join(" ")].join(" ")) }) - this.getTopDownArray().forEach((node, lineNumber) => { - const stats = usage.getNode(node.getNodeTypeId()) - stats.appendLine([filepath + "-" + lineNumber, node.getWords().join(" ")].join(" ")) + this.topDownArray.forEach((node, lineNumber) => { + const stats = usage.getNode(node.parserId) + stats.appendLine([filepath + "-" + lineNumber, node.words.join(" ")].join(" ")) }) return usage } toHighlightScopeTree() { - return this.getTopDownArray() - .map(child => child.getIndentation() + child.getLineHighlightScopes()) - .join("\n") + return this.topDownArray.map(child => child.indentation + child.getLineHighlightScopes()).join("\n") } toDefinitionLineNumberTree() { - return this.getTopDownArray() - .map(child => child.getDefinition().getLineNumber() + " " + child.getIndentation() + child.getCellDefinitionLineNumbers().join(" ")) - .join("\n") + return this.topDownArray.map(child => child.definition.lineNumber + " " + child.indentation + child.cellDefinitionLineNumbers.join(" ")).join("\n") } - toCellTypeTreeWithNodeConstructorNames() { - return this.getTopDownArray() - .map(child => child.constructor.name + this.getWordBreakSymbol() + child.getIndentation() + child.getLineCellTypes()) - .join("\n") + get asCellTypeTreeWithParserIds() { + return this.topDownArray.map(child => child.constructor.name + this.wordBreakSymbol + child.indentation + child.lineCellTypes).join("\n") } - toPreludeCellTypeTreeWithNodeConstructorNames() { - return this.getTopDownArray() - .map(child => child.constructor.name + this.getWordBreakSymbol() + child.getIndentation() + child.getLineCellPreludeTypes()) - .join("\n") + toPreludeCellTypeTreeWithParserIds() { + return this.topDownArray.map(child => child.constructor.name + this.wordBreakSymbol + child.indentation + child.getLineCellPreludeTypes()).join("\n") } - getTreeWithNodeTypes() { - return this.getTopDownArray() - .map(child => child.constructor.name + this.getWordBreakSymbol() + child.getIndentation() + child.getLine()) - .join("\n") + get asTreeWithParsers() { + return this.topDownArray.map(child => child.constructor.name + this.wordBreakSymbol + child.indentation + child.getLine()).join("\n") } getCellHighlightScopeAtPosition(lineIndex, wordIndex) { this._initCellTypeCache() - const typeNode = this._cache_highlightScopeTree.getTopDownArray()[lineIndex - 1] + const typeNode = this._cache_highlightScopeTree.topDownArray[lineIndex - 1] return typeNode ? typeNode.getWord(wordIndex - 1) : undefined } _initCellTypeCache() { @@ -14187,40 +14197,47 @@ class GrammarBackedNode extends TreeNode { this._cache_highlightScopeTree = new TreeNode(this.toHighlightScopeTree()) this._cache_programCellTypeStringMTime = treeMTime } - createParser() { - return this.isRoot() - ? new TreeNode.Parser(BlobNode) - : new TreeNode.Parser( - this.getParent() - ._getParser() - ._getCatchAllNodeConstructor(this.getParent()), - {} - ) + createParserCombinator() { + return this.isRoot() ? new TreeNode.ParserCombinator(BlobParser) : new TreeNode.ParserCombinator(this.parent._getParser()._getCatchAllParser(this.parent), {}) } - getNodeTypeId() { - return this.getDefinition().getNodeTypeIdFromDefinition() + get parserId() { + return this.definition.parserIdFromDefinition } - getWordTypes() { - return this._getParsedCells().filter(cell => cell.getWord() !== undefined) + get wordTypes() { + return this.parsedCells.filter(cell => cell.getWord() !== undefined) } get cellErrors() { - return this._getParsedCells() - .map(check => check.getErrorIfAny()) - .filter(identity => identity) + return this.parsedCells.map(check => check.getErrorIfAny()).filter(identity => identity) } - get singleNodeUsedTwiceErrors() { + get singleParserUsedTwiceErrors() { const errors = [] - const parent = this.getParent() - const hits = parent.getChildInstancesOfNodeTypeId(this.getDefinition().id) + const parent = this.parent + const hits = parent.getChildInstancesOfParserId(this.definition.id) if (hits.length > 1) hits.forEach((node, index) => { - if (node === this) errors.push(new NodeTypeUsedMultipleTimesError(node)) + if (node === this) errors.push(new ParserUsedMultipleTimesError(node)) }) return errors } + get uniqueLineAppearsTwiceErrors() { + const errors = [] + const parent = this.parent + const hits = parent.getChildInstancesOfParserId(this.definition.id) + if (hits.length > 1) { + const set = new Set() + hits.forEach((node, index) => { + const line = node.getLine() + if (set.has(line)) errors.push(new ParserUsedMultipleTimesError(node)) + set.add(line) + }) + } + return errors + } get scopeErrors() { let errors = [] - if (this.getDefinition().isSingle) errors = errors.concat(this.singleNodeUsedTwiceErrors) + const def = this.definition + if (def.isSingle) errors = errors.concat(this.singleParserUsedTwiceErrors) + if (def.isUniqueLine) errors = errors.concat(this.uniqueLineAppearsTwiceErrors) const { requiredNodeErrors } = this if (requiredNodeErrors.length) errors = errors.concat(requiredNodeErrors) return errors @@ -14228,60 +14245,122 @@ class GrammarBackedNode extends TreeNode { getErrors() { return this.cellErrors.concat(this.scopeErrors) } - _getParsedCells() { - return this.getDefinition() - .getCellParser() - .getCellArray(this) + get parsedCells() { + return this.definition.cellParser.getCellArray(this) } // todo: just make a fn that computes proper spacing and then is given a node to print - getLineCellTypes() { - return this._getParsedCells() - .map(slot => slot.getCellTypeId()) - .join(" ") + get lineCellTypes() { + return this.parsedCells.map(slot => slot.cellTypeId).join(" ") } getLineCellPreludeTypes() { - return this._getParsedCells() + return this.parsedCells .map(slot => { - const def = slot._getCellTypeDefinition() + const def = slot.cellTypeDefinition //todo: cleanup - return def ? def._getPreludeKindId() : PreludeCellTypeIds.anyCell + return def ? def.preludeKindId : PreludeCellTypeIds.anyCell }) .join(" ") } getLineHighlightScopes(defaultScope = "source") { - return this._getParsedCells() - .map(slot => slot.getHighlightScope() || defaultScope) - .join(" ") + return this.parsedCells.map(slot => slot.highlightScope || defaultScope).join(" ") } - getCellDefinitionLineNumbers() { - return this._getParsedCells().map(cell => cell.getDefinitionLineNumber()) + get cellDefinitionLineNumbers() { + return this.parsedCells.map(cell => cell.definitionLineNumber) } _getCompiledIndentation() { - const indentCharacter = this.getDefinition()._getCompilerObject()[GrammarConstantsCompiler.indentCharacter] - const indent = this.getIndentation() + const indentCharacter = this.definition._getCompilerObject()[GrammarConstantsCompiler.indentCharacter] + const indent = this.indentation return indentCharacter !== undefined ? indentCharacter.repeat(indent.length) : indent } _getFields() { // fields are like cells const fields = {} this.forEach(node => { - const def = node.getDefinition() - if (def.isRequired() || def.isSingle) fields[node.getWord(0)] = node.getContent() + const def = node.definition + if (def.isRequired() || def.isSingle) fields[node.getWord(0)] = node.content }) return fields } _getCompiledLine() { - const compiler = this.getDefinition()._getCompilerObject() + const compiler = this.definition._getCompilerObject() const catchAllCellDelimiter = compiler[GrammarConstantsCompiler.catchAllCellDelimiter] const str = compiler[GrammarConstantsCompiler.stringTemplate] - return str !== undefined ? TreeUtils.formatStr(str, catchAllCellDelimiter, Object.assign(this._getFields(), this.cells)) : this.getLine() + return str !== undefined ? Utils.formatStr(str, catchAllCellDelimiter, Object.assign(this._getFields(), this.cells)) : this.getLine() + } + get listDelimiter() { + return this.definition._getFromExtended(GrammarConstants.listDelimiter) + } + get contentKey() { + return this.definition._getFromExtended(GrammarConstants.contentKey) + } + get childrenKey() { + return this.definition._getFromExtended(GrammarConstants.childrenKey) + } + get childrenAreTextBlob() { + return this.definition._isBlobParser() + } + get isArrayElement() { + return this.definition._hasFromExtended(GrammarConstants.uniqueFirstWord) ? false : !this.definition.isSingle + } + get list() { + return this.listDelimiter ? this.content.split(this.listDelimiter) : super.list + } + get typedContent() { + // todo: probably a better way to do this, perhaps by defining a cellDelimiter at the node level + // todo: this currently parse anything other than string types + if (this.listDelimiter) return this.content.split(this.listDelimiter) + const cells = this.parsedCells + if (cells.length === 2) return cells[1].parsed + return this.content + } + get typedTuple() { + const key = this.firstWord + if (this.childrenAreTextBlob) return [key, this.childrenToString()] + const { typedContent, contentKey, childrenKey } = this + if (contentKey || childrenKey) { + let obj = {} + if (childrenKey) obj[childrenKey] = this.childrenToString() + else obj = this.typedMap + if (contentKey) { + obj[contentKey] = typedContent + } + return [key, obj] + } + const hasChildren = this.length > 0 + const hasChildrenNoContent = typedContent === undefined && hasChildren + const shouldReturnValueAsObject = hasChildrenNoContent + if (shouldReturnValueAsObject) return [key, this.typedMap] + const hasChildrenAndContent = typedContent !== undefined && hasChildren + const shouldReturnValueAsContentPlusChildren = hasChildrenAndContent + // If the node has a content and a subtree return it as a string, as + // Javascript object values can't be both a leaf and a tree. + if (shouldReturnValueAsContentPlusChildren) return [key, this.contentWithChildren] + return [key, typedContent] + } + get _shouldSerialize() { + const should = this.shouldSerialize + return should === undefined ? true : should } + get typedMap() { + const obj = {} + this.forEach(node => { + if (!node._shouldSerialize) return true + const tuple = node.typedTuple + if (!node.isArrayElement) obj[tuple[0]] = tuple[1] + else { + if (!obj[tuple[0]]) obj[tuple[0]] = [] + obj[tuple[0]].push(tuple[1]) + } + }) + return obj + } + fromTypedMap() {} compile() { if (this.isRoot()) return super.compile() - const def = this.getDefinition() + const def = this.definition const indent = this._getCompiledIndentation() const compiledLine = this._getCompiledLine() - if (def.isTerminalNodeType()) return indent + compiledLine + if (def.isTerminalParser()) return indent + compiledLine const compiler = def._getCompilerObject() const openChildrenString = compiler[GrammarConstantsCompiler.openChildren] || "" const closeChildrenString = compiler[GrammarConstantsCompiler.closeChildren] || "" @@ -14294,83 +14373,80 @@ ${indent}${closeChildrenString}` // todo: remove get cells() { const cells = {} - this._getParsedCells().forEach(cell => { - const cellTypeId = cell.getCellTypeId() - if (!cell.isCatchAll()) cells[cellTypeId] = cell.getParsed() + this.parsedCells.forEach(cell => { + const cellTypeId = cell.cellTypeId + if (!cell.isCatchAll()) cells[cellTypeId] = cell.parsed else { if (!cells[cellTypeId]) cells[cellTypeId] = [] - cells[cellTypeId].push(cell.getParsed()) + cells[cellTypeId].push(cell.parsed) } }) return cells } } -class BlobNode extends GrammarBackedNode { - createParser() { - return new TreeNode.Parser(BlobNode, {}) +class BlobParser extends GrammarBackedNode { + createParserCombinator() { + return new TreeNode.ParserCombinator(BlobParser, {}) } getErrors() { return [] } } // todo: can we remove this? hard to extend. -class UnknownNodeTypeNode extends GrammarBackedNode { - createParser() { - return new TreeNode.Parser(UnknownNodeTypeNode, {}) +class UnknownParserNode extends GrammarBackedNode { + createParserCombinator() { + return new TreeNode.ParserCombinator(UnknownParserNode, {}) } getErrors() { - return [new UnknownNodeTypeError(this)] + return [new UnknownParserError(this)] } } /* A cell contains a word but also the type information for that word. */ class AbstractGrammarBackedCell { - constructor(node, index, typeDef, cellTypeId, isCatchAll, nodeTypeDef) { + constructor(node, index, typeDef, cellTypeId, isCatchAll, parserDefinitionParser) { this._typeDef = typeDef this._node = node this._isCatchAll = isCatchAll this._index = index this._cellTypeId = cellTypeId - this._nodeTypeDefinition = nodeTypeDef + this._parserDefinitionParser = parserDefinitionParser } getWord() { return this._node.getWord(this._index) } - getDefinitionLineNumber() { - return this._typeDef.getLineNumber() - } - getSQLiteType() { - return SQLiteTypes.text + get definitionLineNumber() { + return this._typeDef.lineNumber } - getCellTypeId() { + get cellTypeId() { return this._cellTypeId } getNode() { return this._node } - getCellIndex() { + get cellIndex() { return this._index } isCatchAll() { return this._isCatchAll } get min() { - return this._getCellTypeDefinition().get(GrammarConstants.min) || "0" + return this.cellTypeDefinition.get(GrammarConstants.min) || "0" } get max() { - return this._getCellTypeDefinition().get(GrammarConstants.max) || "100" + return this.cellTypeDefinition.get(GrammarConstants.max) || "100" } get placeholder() { - return this._getCellTypeDefinition().get(GrammarConstants.examples) || "" + return this.cellTypeDefinition.get(GrammarConstants.examples) || "" } - getHighlightScope() { - const definition = this._getCellTypeDefinition() - if (definition) return definition.getHighlightScope() // todo: why the undefined? + get highlightScope() { + const definition = this.cellTypeDefinition + if (definition) return definition.highlightScope // todo: why the undefined? } getAutoCompleteWords(partialWord = "") { - const cellDef = this._getCellTypeDefinition() - let words = cellDef ? cellDef._getAutocompleteWordOptions(this.getNode().getRootNode()) : [] + const cellDef = this.cellTypeDefinition + let words = cellDef ? cellDef._getAutocompleteWordOptions(this.getNode().root) : [] const runTimeOptions = this.getNode().getRunTimeEnumOptions(this) if (runTimeOptions) words = runTimeOptions.concat(words) if (partialWord) words = words.filter(word => word.includes(partialWord)) @@ -14383,13 +14459,13 @@ class AbstractGrammarBackedCell { } synthesizeCell(seed = Date.now()) { // todo: cleanup - const cellDef = this._getCellTypeDefinition() + const cellDef = this.cellTypeDefinition const enumOptions = cellDef._getFromExtended(GrammarConstants.enum) - if (enumOptions) return TreeUtils.getRandomString(1, enumOptions.split(" ")) + if (enumOptions) return Utils.getRandomString(1, enumOptions.split(" ")) return this._synthesizeCell(seed) } _getStumpEnumInput(crux) { - const cellDef = this._getCellTypeDefinition() + const cellDef = this.cellTypeDefinition const enumOptions = cellDef._getFromExtended(GrammarConstants.enum) if (!enumOptions) return undefined const options = new TreeNode( @@ -14411,20 +14487,17 @@ ${options.toString(1)}` name ${crux} placeholder ${this.placeholder}` } - _getCellTypeDefinition() { + get cellTypeDefinition() { return this._typeDef } - _getFullLine() { - return this.getNode().getLine() - } _getErrorContext() { - return this._getFullLine().split(" ")[0] // todo: WordBreakSymbol + return this.getNode().getLine().split(" ")[0] // todo: WordBreakSymbol } isValid() { const runTimeOptions = this.getNode().getRunTimeEnumOptions(this) const word = this.getWord() if (runTimeOptions) return runTimeOptions.includes(word) - return this._getCellTypeDefinition().isValid(word, this.getNode().getRootNode()) && this._isValid() + return this.cellTypeDefinition.isValid(word, this.getNode().root) && this._isValid() } getErrorIfAny() { const word = this.getWord() @@ -14440,12 +14513,12 @@ class GrammarBitCell extends AbstractGrammarBackedCell { return word === "0" || word === "1" } _synthesizeCell() { - return TreeUtils.getRandomString(1, "01".split("")) + return Utils.getRandomString(1, "01".split("")) } - getRegexString() { + get regexString() { return "[01]" } - getParsed() { + get parsed() { const word = this.getWord() return !!parseInt(word) } @@ -14469,15 +14542,12 @@ class GrammarIntCell extends GrammarNumericCell { return num.toString() === word } _synthesizeCell(seed) { - return TreeUtils.randomUniformInt(parseInt(this.min), parseInt(this.max), seed).toString() + return Utils.randomUniformInt(parseInt(this.min), parseInt(this.max), seed).toString() } - getRegexString() { + get regexString() { return "-?[0-9]+" } - getSQLiteType() { - return SQLiteTypes.integer - } - getParsed() { + get parsed() { const word = this.getWord() return parseInt(word) } @@ -14490,16 +14560,13 @@ class GrammarFloatCell extends GrammarNumericCell { const num = parseFloat(word) return !isNaN(num) && /^-?\d*(\.\d+)?$/.test(word) } - getSQLiteType() { - return SQLiteTypes.float - } _synthesizeCell(seed) { - return TreeUtils.randomUniformFloat(parseFloat(this.min), parseFloat(this.max), seed).toString() + return Utils.randomUniformFloat(parseFloat(this.min), parseFloat(this.max), seed).toString() } - getRegexString() { + get regexString() { return "-?d*(.d+)?" } - getParsed() { + get parsed() { const word = this.getWord() return parseFloat(word) } @@ -14519,15 +14586,15 @@ class GrammarBoolCell extends AbstractGrammarBackedCell { return this._trues.has(str) || this._falses.has(str) } _synthesizeCell() { - return TreeUtils.getRandomString(1, ["1", "true", "t", "yes", "0", "false", "f", "no"]) + return Utils.getRandomString(1, ["1", "true", "t", "yes", "0", "false", "f", "no"]) } _getOptions() { return Array.from(this._trues).concat(Array.from(this._falses)) } - getRegexString() { + get regexString() { return "(?:" + this._getOptions().join("|") + ")" } - getParsed() { + get parsed() { const word = this.getWord() return this._trues.has(word.toLowerCase()) } @@ -14538,20 +14605,20 @@ class GrammarAnyCell extends AbstractGrammarBackedCell { return true } _synthesizeCell() { - const examples = this._getCellTypeDefinition()._getFromExtended(GrammarConstants.examples) - if (examples) return TreeUtils.getRandomString(1, examples.split(" ")) - return this._nodeTypeDefinition.getNodeTypeIdFromDefinition() + "-" + this.constructor.name + const examples = this.cellTypeDefinition._getFromExtended(GrammarConstants.examples) + if (examples) return Utils.getRandomString(1, examples.split(" ")) + return this._parserDefinitionParser.parserIdFromDefinition + "-" + this.constructor.name } - getRegexString() { + get regexString() { return "[^ ]+" } - getParsed() { + get parsed() { return this.getWord() } } class GrammarKeywordCell extends GrammarAnyCell { _synthesizeCell() { - return this._nodeTypeDefinition._getCruxIfAny() + return this._parserDefinitionParser.cruxIfAny } } GrammarKeywordCell.defaultHighlightScope = "keyword" @@ -14566,7 +14633,7 @@ class GrammarExtraWordCellTypeCell extends AbstractGrammarBackedCell { _synthesizeCell() { return "extraWord" // should never occur? } - getParsed() { + get parsed() { return this.getWord() } getErrorIfAny() { @@ -14584,7 +14651,7 @@ class GrammarUnknownCellTypeCell extends AbstractGrammarBackedCell { _synthesizeCell() { return "extraWord" // should never occur? } - getParsed() { + get parsed() { return this.getWord() } getErrorIfAny() { @@ -14596,16 +14663,16 @@ class AbstractTreeError { this._node = node } getLineIndex() { - return this.getLineNumber() - 1 + return this.lineNumber - 1 } - getLineNumber() { + get lineNumber() { return this.getNode()._getLineNumber() // todo: handle sourcemaps } isCursorOnWord(lineIndex, characterIndex) { return lineIndex === this.getLineIndex() && this._doesCharacterIndexFallOnWord(characterIndex) } _doesCharacterIndexFallOnWord(characterIndex) { - return this.getCellIndex() === this.getNode().getWordIndexAtCharacterIndex(characterIndex) + return this.cellIndex === this.getNode().getWordIndexAtCharacterIndex(characterIndex) } // convenience method. may be removed. isBlankLineError() { @@ -14616,41 +14683,32 @@ class AbstractTreeError { return false } getIndent() { - return this.getNode().getIndentation() + return this.getNode().indentation } getCodeMirrorLineWidgetElement(onApplySuggestionCallBack = () => {}) { - const suggestion = this.getSuggestionMessage() + const suggestion = this.suggestionMessage if (this.isMissingWordError()) return this._getCodeMirrorLineWidgetElementCellTypeHints() if (suggestion) return this._getCodeMirrorLineWidgetElementWithSuggestion(onApplySuggestionCallBack, suggestion) return this._getCodeMirrorLineWidgetElementWithoutSuggestion() } - getNodeTypeId() { - return this.getNode() - .getDefinition() - .getNodeTypeIdFromDefinition() + get parserId() { + return this.getNode().definition.parserIdFromDefinition } _getCodeMirrorLineWidgetElementCellTypeHints() { const el = document.createElement("div") - el.appendChild( - document.createTextNode( - this.getIndent() + - this.getNode() - .getDefinition() - .getLineHints() - ) - ) + el.appendChild(document.createTextNode(this.getIndent() + this.getNode().definition.lineHints)) el.className = "LintCellTypeHints" return el } _getCodeMirrorLineWidgetElementWithoutSuggestion() { const el = document.createElement("div") - el.appendChild(document.createTextNode(this.getIndent() + this.getMessage())) + el.appendChild(document.createTextNode(this.getIndent() + this.message)) el.className = "LintError" return el } _getCodeMirrorLineWidgetElementWithSuggestion(onApplySuggestionCallBack, suggestion) { const el = document.createElement("div") - el.appendChild(document.createTextNode(this.getIndent() + `${this.getErrorTypeName()}. Suggestion: ${suggestion}`)) + el.appendChild(document.createTextNode(this.getIndent() + `${this.errorTypeName}. Suggestion: ${suggestion}`)) el.className = "LintErrorWithSuggestion" el.onclick = () => { this.applySuggestion() @@ -14662,41 +14720,39 @@ class AbstractTreeError { return this.getNode().getLine() } getExtension() { - return this.getNode() - .getHandGrammarProgram() - .getExtensionName() + return this.getNode().handGrammarProgram.extensionName } getNode() { return this._node } - getErrorTypeName() { + get errorTypeName() { return this.constructor.name.replace("Error", "") } - getCellIndex() { + get cellIndex() { return 0 } toObject() { return { - type: this.getErrorTypeName(), - line: this.getLineNumber(), - cell: this.getCellIndex(), - suggestion: this.getSuggestionMessage(), + type: this.errorTypeName, + line: this.lineNumber, + cell: this.cellIndex, + suggestion: this.suggestionMessage, path: this.getNode().getFirstWordPath(), - message: this.getMessage() + message: this.message } } hasSuggestion() { - return this.getSuggestionMessage() !== "" + return this.suggestionMessage !== "" } - getSuggestionMessage() { + get suggestionMessage() { return "" } toString() { - return this.getMessage() + return this.message } applySuggestion() {} - getMessage() { - return `${this.getErrorTypeName()} at line ${this.getLineNumber()} cell ${this.getCellIndex()}.` + get message() { + return `${this.errorTypeName} at line ${this.lineNumber} cell ${this.cellIndex}.` } } class AbstractCellError extends AbstractTreeError { @@ -14704,137 +14760,149 @@ class AbstractCellError extends AbstractTreeError { super(cell.getNode()) this._cell = cell } - getCell() { + get cell() { return this._cell } - getCellIndex() { - return this._cell.getCellIndex() + get cellIndex() { + return this._cell.cellIndex } - _getWordSuggestion() { - return TreeUtils.didYouMean( - this.getCell().getWord(), - this.getCell() - .getAutoCompleteWords() - .map(option => option.text) + get wordSuggestion() { + return Utils.didYouMean( + this.cell.getWord(), + this.cell.getAutoCompleteWords().map(option => option.text) ) } } -class UnknownNodeTypeError extends AbstractTreeError { - getMessage() { +class UnknownParserError extends AbstractTreeError { + get message() { const node = this.getNode() - const parentNode = node.getParent() + const parentNode = node.parent const options = parentNode._getParser().getFirstWordOptions() - return super.getMessage() + ` Invalid nodeType "${node.getFirstWord()}". Valid nodeTypes are: ${TreeUtils._listToEnglishText(options, 7)}.` + return super.message + ` Invalid parser "${node.firstWord}". Valid parsers are: ${Utils._listToEnglishText(options, 7)}.` } - _getWordSuggestion() { + get wordSuggestion() { const node = this.getNode() - const parentNode = node.getParent() - return TreeUtils.didYouMean(node.getFirstWord(), parentNode.getAutocompleteResults("", 0).map(option => option.text)) + const parentNode = node.parent + return Utils.didYouMean( + node.firstWord, + parentNode.getAutocompleteResults("", 0).map(option => option.text) + ) } - getSuggestionMessage() { - const suggestion = this._getWordSuggestion() + get suggestionMessage() { + const suggestion = this.wordSuggestion const node = this.getNode() - if (suggestion) return `Change "${node.getFirstWord()}" to "${suggestion}"` + if (suggestion) return `Change "${node.firstWord}" to "${suggestion}"` return "" } applySuggestion() { - const suggestion = this._getWordSuggestion() - if (suggestion) this.getNode().setWord(this.getCellIndex(), suggestion) + const suggestion = this.wordSuggestion + if (suggestion) this.getNode().setWord(this.cellIndex, suggestion) return this } } -class BlankLineError extends UnknownNodeTypeError { - getMessage() { - return super.getMessage() + ` Line: "${this.getNode().getLine()}". Blank lines are errors.` +class BlankLineError extends UnknownParserError { + get message() { + return super.message + ` Line: "${this.getNode().getLine()}". Blank lines are errors.` } // convenience method isBlankLineError() { return true } - getSuggestionMessage() { - return `Delete line ${this.getLineNumber()}` + get suggestionMessage() { + return `Delete line ${this.lineNumber}` } applySuggestion() { this.getNode().destroy() return this } } -class MissingRequiredNodeTypeError extends AbstractTreeError { - constructor(node, missingNodeTypeId) { +class MissingRequiredParserError extends AbstractTreeError { + constructor(node, missingParserId) { super(node) - this._missingNodeTypeId = missingNodeTypeId + this._missingParserId = missingParserId + } + get message() { + return super.message + ` A "${this._missingParserId}" is required.` } - getMessage() { - return super.getMessage() + ` A "${this._missingNodeTypeId}" is required.` +} +class ParserUsedMultipleTimesError extends AbstractTreeError { + get message() { + return super.message + ` Multiple "${this.getNode().firstWord}" found.` + } + get suggestionMessage() { + return `Delete line ${this.lineNumber}` + } + applySuggestion() { + return this.getNode().destroy() } } -class NodeTypeUsedMultipleTimesError extends AbstractTreeError { - getMessage() { - return super.getMessage() + ` Multiple "${this.getNode().getFirstWord()}" found.` +class LineAppearsMultipleTimesError extends AbstractTreeError { + get message() { + return super.message + ` "${this.getNode().getLine()}" appears multiple times.` } - getSuggestionMessage() { - return `Delete line ${this.getLineNumber()}` + get suggestionMessage() { + return `Delete line ${this.lineNumber}` } applySuggestion() { return this.getNode().destroy() } } class UnknownCellTypeError extends AbstractCellError { - getMessage() { - return super.getMessage() + ` No cellType "${this.getCell().getCellTypeId()}" found. Language grammar for "${this.getExtension()}" may need to be fixed.` + get message() { + return super.message + ` No cellType "${this.cell.cellTypeId}" found. Language grammar for "${this.getExtension()}" may need to be fixed.` } } class InvalidWordError extends AbstractCellError { - getMessage() { - return super.getMessage() + ` "${this.getCell().getWord()}" does not fit in cellType "${this.getCell().getCellTypeId()}".` + get message() { + return super.message + ` "${this.cell.getWord()}" does not fit in cellType "${this.cell.cellTypeId}".` } - getSuggestionMessage() { - const suggestion = this._getWordSuggestion() - if (suggestion) return `Change "${this.getCell().getWord()}" to "${suggestion}"` + get suggestionMessage() { + const suggestion = this.wordSuggestion + if (suggestion) return `Change "${this.cell.getWord()}" to "${suggestion}"` return "" } applySuggestion() { - const suggestion = this._getWordSuggestion() - if (suggestion) this.getNode().setWord(this.getCellIndex(), suggestion) + const suggestion = this.wordSuggestion + if (suggestion) this.getNode().setWord(this.cellIndex, suggestion) return this } } class ExtraWordError extends AbstractCellError { - getMessage() { - return super.getMessage() + ` Extra word "${this.getCell().getWord()}" in ${this.getNodeTypeId()}.` + get message() { + return super.message + ` Extra word "${this.cell.getWord()}" in ${this.parserId}.` } - getSuggestionMessage() { - return `Delete word "${this.getCell().getWord()}" at cell ${this.getCellIndex()}` + get suggestionMessage() { + return `Delete word "${this.cell.getWord()}" at cell ${this.cellIndex}` } applySuggestion() { - return this.getNode().deleteWordAt(this.getCellIndex()) + return this.getNode().deleteWordAt(this.cellIndex) } } class MissingWordError extends AbstractCellError { // todo: autocomplete suggestion - getMessage() { - return super.getMessage() + ` Missing word for cell "${this.getCell().getCellTypeId()}".` + get message() { + return super.message + ` Missing word for cell "${this.cell.cellTypeId}".` } isMissingWordError() { return true } } // todo: add standard types, enum types, from disk types -class AbstractGrammarWordTestNode extends TreeNode {} -class GrammarRegexTestNode extends AbstractGrammarWordTestNode { +class AbstractGrammarWordTestParser extends TreeNode {} +class GrammarRegexTestParser extends AbstractGrammarWordTestParser { isValid(str) { - if (!this._regex) this._regex = new RegExp("^" + this.getContent() + "$") + if (!this._regex) this._regex = new RegExp("^" + this.content + "$") return !!str.match(this._regex) } } -class GrammarReservedWordsTestNode extends AbstractGrammarWordTestNode { +class GrammarReservedWordsTestParser extends AbstractGrammarWordTestParser { isValid(str) { - if (!this._set) this._set = new Set(this.getContent().split(" ")) + if (!this._set) this._set = new Set(this.content.split(" ")) return !this._set.has(str) } } // todo: remove in favor of custom word type constructors -class EnumFromCellTypesTestNode extends AbstractGrammarWordTestNode { +class EnumFromCellTypesTestParser extends AbstractGrammarWordTestParser { _getEnumFromCellTypes(programRootNode) { const cellTypeIds = this.getWordsFrom(1) const enumGroup = cellTypeIds.join(" ") @@ -14845,8 +14913,7 @@ class EnumFromCellTypesTestNode extends AbstractGrammarWordTestNode { const map = {} const cellTypeMap = {} cellTypeIds.forEach(typeId => (cellTypeMap[typeId] = true)) - programRootNode - .getAllTypedWords() + programRootNode.allTypedWords .filter(typedWord => cellTypeMap[typedWord.type]) .forEach(typedWord => { map[typedWord.word] = true @@ -14859,71 +14926,71 @@ class EnumFromCellTypesTestNode extends AbstractGrammarWordTestNode { return this._getEnumFromCellTypes(programRootNode)[str] === true } } -class GrammarEnumTestNode extends AbstractGrammarWordTestNode { +class GrammarEnumTestNode extends AbstractGrammarWordTestParser { isValid(str) { // enum c c++ java return !!this.getOptions()[str] } getOptions() { - if (!this._map) this._map = TreeUtils.arrayToMap(this.getWordsFrom(1)) + if (!this._map) this._map = Utils.arrayToMap(this.getWordsFrom(1)) return this._map } } -class cellTypeDefinitionNode extends AbstractExtendibleTreeNode { - createParser() { +class cellTypeDefinitionParser extends AbstractExtendibleTreeNode { + createParserCombinator() { const types = {} - types[GrammarConstants.regex] = GrammarRegexTestNode - types[GrammarConstants.reservedWords] = GrammarReservedWordsTestNode - types[GrammarConstants.enumFromCellTypes] = EnumFromCellTypesTestNode + types[GrammarConstants.regex] = GrammarRegexTestParser + types[GrammarConstants.reservedWords] = GrammarReservedWordsTestParser + types[GrammarConstants.enumFromCellTypes] = EnumFromCellTypesTestParser types[GrammarConstants.enum] = GrammarEnumTestNode types[GrammarConstants.highlightScope] = TreeNode - types[GrammarConstants.todoComment] = TreeNode + types[GrammarConstants.comment] = TreeNode types[GrammarConstants.examples] = TreeNode types[GrammarConstants.min] = TreeNode types[GrammarConstants.max] = TreeNode types[GrammarConstants.description] = TreeNode types[GrammarConstants.extends] = TreeNode - return new TreeNode.Parser(undefined, types) + return new TreeNode.ParserCombinator(undefined, types) } - _getId() { + get id() { return this.getWord(0) } - _getIdToNodeMap() { - return this.getParent().getCellTypeDefinitions() + get idToNodeMap() { + return this.parent.cellTypeDefinitions } getGetter(wordIndex) { const wordToNativeJavascriptTypeParser = this.getCellConstructor().parserFunctionName - return `get ${this.getCellTypeId()}() { + return `get ${this.cellTypeId}() { return ${wordToNativeJavascriptTypeParser ? wordToNativeJavascriptTypeParser + `(this.getWord(${wordIndex}))` : `this.getWord(${wordIndex})`} }` } getCatchAllGetter(wordIndex) { const wordToNativeJavascriptTypeParser = this.getCellConstructor().parserFunctionName - return `get ${this.getCellTypeId()}() { + return `get ${this.cellTypeId}() { return ${wordToNativeJavascriptTypeParser ? `this.getWordsFrom(${wordIndex}).map(val => ${wordToNativeJavascriptTypeParser}(val))` : `this.getWordsFrom(${wordIndex})`} }` } // `this.getWordsFrom(${requireds.length + 1})` // todo: cleanup typings. todo: remove this hidden logic. have a "baseType" property? getCellConstructor() { - return this._getPreludeKind() || GrammarAnyCell + return this.preludeKind || GrammarAnyCell } - _getPreludeKind() { + get preludeKind() { return PreludeKinds[this.getWord(0)] || PreludeKinds[this._getExtendedCellTypeId()] } - _getPreludeKindId() { + get preludeKindId() { if (PreludeKinds[this.getWord(0)]) return this.getWord(0) else if (PreludeKinds[this._getExtendedCellTypeId()]) return this._getExtendedCellTypeId() return PreludeCellTypeIds.anyCell } _getExtendedCellTypeId() { const arr = this._getAncestorsArray() - return arr[arr.length - 1]._getId() + return arr[arr.length - 1].id } - getHighlightScope() { + get highlightScope() { const hs = this._getFromExtended(GrammarConstants.highlightScope) if (hs) return hs - const preludeKind = this._getPreludeKind() + const preludeKind = this.preludeKind if (preludeKind) return preludeKind.defaultHighlightScope } _getEnumOptions() { @@ -14941,15 +15008,18 @@ class cellTypeDefinitionNode extends AbstractExtendibleTreeNode { _getAutocompleteWordOptions(program) { return this._getEnumOptions() || this._getEnumFromCellTypeOptions(program) || [] } - getRegexString() { + get regexString() { // todo: enum const enumOptions = this._getEnumOptions() return this._getFromExtended(GrammarConstants.regex) || (enumOptions ? "(?:" + enumOptions.join("|") + ")" : "[^ ]*") } + _getAllTests() { + return this._getChildrenByParserInExtended(AbstractGrammarWordTestParser) + } isValid(str, programRootNode) { - return this._getChildrenByNodeConstructorInExtended(AbstractGrammarWordTestNode).every(node => node.isValid(str, programRootNode)) + return this._getAllTests().every(node => node.isValid(str, programRootNode)) } - getCellTypeId() { + get cellTypeId() { return this.getWord(0) } } @@ -14957,18 +15027,21 @@ class AbstractCellParser { constructor(definition) { this._definition = definition } - getCatchAllCellTypeId() { + get catchAllCellTypeId() { return this._definition._getFromExtended(GrammarConstants.catchAllCellType) } // todo: improve layout (use bold?) - getLineHints() { - const catchAllCellTypeId = this.getCatchAllCellTypeId() - const nodeTypeId = this._definition._getCruxIfAny() || this._definition._getId() // todo: cleanup - return `${nodeTypeId}: ${this.getRequiredCellTypeIds().join(" ")}${catchAllCellTypeId ? ` ${catchAllCellTypeId}...` : ""}` + get lineHints() { + const catchAllCellTypeId = this.catchAllCellTypeId + const parserId = this._definition.cruxIfAny || this._definition.id // todo: cleanup + return `${parserId}: ${this.getRequiredCellTypeIds().join(" ")}${catchAllCellTypeId ? ` ${catchAllCellTypeId}...` : ""}` } getRequiredCellTypeIds() { - const parameters = this._definition._getFromExtended(GrammarConstants.cells) - return parameters ? parameters.split(" ") : [] + if (!this._requiredCellTypeIds) { + const parameters = this._definition._getFromExtended(GrammarConstants.cells) + this._requiredCellTypeIds = parameters ? parameters.split(" ") : [] + } + return this._requiredCellTypeIds } _getCellTypeId(cellIndex, requiredCellTypeIds, totalWordCount) { return requiredCellTypeIds[cellIndex] @@ -14977,9 +15050,9 @@ class AbstractCellParser { return cellIndex >= numberOfRequiredCells } getCellArray(node = undefined) { - const wordCount = node ? node.getWords().length : 0 + const wordCount = node ? node.words.length : 0 const def = this._definition - const grammarProgram = def.getLanguageDefinitionProgram() + const grammarProgram = def.languageDefinitionProgram const requiredCellTypeIds = this.getRequiredCellTypeIds() const numberOfRequiredCells = requiredCellTypeIds.length const actualWordCountOrRequiredCellCount = Math.max(wordCount, numberOfRequiredCells) @@ -14987,7 +15060,7 @@ class AbstractCellParser { // A for loop instead of map because "numberOfCellsToFill" can be longer than words.length for (let cellIndex = 0; cellIndex < actualWordCountOrRequiredCellCount; cellIndex++) { const isCatchAll = this._isCatchAllCell(cellIndex, numberOfRequiredCells, wordCount) - let cellTypeId = isCatchAll ? this.getCatchAllCellTypeId() : this._getCellTypeId(cellIndex, requiredCellTypeIds, wordCount) + let cellTypeId = isCatchAll ? this.catchAllCellTypeId : this._getCellTypeId(cellIndex, requiredCellTypeIds, wordCount) let cellTypeDefinition = grammarProgram.getCellTypeDefinitionById(cellTypeId) let cellConstructor if (cellTypeDefinition) cellConstructor = cellTypeDefinition.getCellConstructor() @@ -14997,7 +15070,8 @@ class AbstractCellParser { cellTypeId = PreludeCellTypeIds.extraWordCell cellTypeDefinition = grammarProgram.getCellTypeDefinitionById(cellTypeId) } - cells[cellIndex] = new cellConstructor(node, cellIndex, cellTypeDefinition, cellTypeId, isCatchAll, def) + const anyCellConstructor = cellConstructor + cells[cellIndex] = new anyCellConstructor(node, cellIndex, cellTypeDefinition, cellTypeId, isCatchAll, def) } return cells } @@ -15016,11 +15090,11 @@ class OmnifixCellParser extends AbstractCellParser { getCellArray(node = undefined) { const cells = [] const def = this._definition - const program = node ? node.getRootNode() : undefined - const grammarProgram = def.getLanguageDefinitionProgram() - const words = node ? node.getWords() : [] + const program = node ? node.root : undefined + const grammarProgram = def.languageDefinitionProgram + const words = node ? node.words : [] const requiredCellTypeDefs = this.getRequiredCellTypeIds().map(cellTypeId => grammarProgram.getCellTypeDefinitionById(cellTypeId)) - const catchAllCellTypeId = this.getCatchAllCellTypeId() + const catchAllCellTypeId = this.catchAllCellTypeId const catchAllCellTypeDef = catchAllCellTypeId && grammarProgram.getCellTypeDefinitionById(catchAllCellTypeId) words.forEach((word, wordIndex) => { let cellConstructor @@ -15029,7 +15103,7 @@ class OmnifixCellParser extends AbstractCellParser { if (cellTypeDefinition.isValid(word, program)) { // todo: cleanup cellIndex/wordIndex stuff cellConstructor = cellTypeDefinition.getCellConstructor() - cells.push(new cellConstructor(node, wordIndex, cellTypeDefinition, cellTypeDefinition._getId(), false, def)) + cells.push(new cellConstructor(node, wordIndex, cellTypeDefinition, cellTypeDefinition.id, false, def)) requiredCellTypeDefs.splice(index, 1) return true } @@ -15044,14 +15118,14 @@ class OmnifixCellParser extends AbstractCellParser { const wordCount = words.length requiredCellTypeDefs.forEach((cellTypeDef, index) => { let cellConstructor = cellTypeDef.getCellConstructor() - cells.push(new cellConstructor(node, wordCount + index, cellTypeDef, cellTypeDef._getId(), false, def)) + cells.push(new cellConstructor(node, wordCount + index, cellTypeDef, cellTypeDef.id, false, def)) }) return cells } } -class GrammarExampleNode extends TreeNode {} -class GrammarCompilerNode extends TreeNode { - createParser() { +class GrammarExampleParser extends TreeNode {} +class GrammarCompilerParser extends TreeNode { + createParserCombinator() { const types = [ GrammarConstantsCompiler.stringTemplate, GrammarConstantsCompiler.indentCharacter, @@ -15064,37 +15138,41 @@ class GrammarCompilerNode extends TreeNode { types.forEach(type => { map[type] = TreeNode }) - return new TreeNode.Parser(undefined, map) + return new TreeNode.ParserCombinator(undefined, map) } } -class GrammarNodeTypeConstant extends TreeNode { +class AbstractParserConstantParser extends TreeNode { + constructor(children, line, parent) { + super(children, line, parent) + parent[this.identifier] = this.constantValue + } getGetter() { - return `get ${this.getIdentifier()}() { return ${this.getConstantValueAsJsText()} }` + return `get ${this.identifier}() { return ${this.constantValueAsJsText} }` } - getIdentifier() { + get identifier() { return this.getWord(1) } - getConstantValueAsJsText() { + get constantValueAsJsText() { const words = this.getWordsFrom(2) return words.length > 1 ? `[${words.join(",")}]` : words[0] } - getConstantValue() { - return JSON.parse(this.getConstantValueAsJsText()) + get constantValue() { + return JSON.parse(this.constantValueAsJsText) } } -class GrammarNodeTypeConstantInt extends GrammarNodeTypeConstant {} -class GrammarNodeTypeConstantString extends GrammarNodeTypeConstant { - getConstantValueAsJsText() { - return "`" + TreeUtils.escapeBackTicks(this.getConstantValue()) + "`" +class GrammarParserConstantInt extends AbstractParserConstantParser {} +class GrammarParserConstantString extends AbstractParserConstantParser { + get constantValueAsJsText() { + return "`" + Utils.escapeBackTicks(this.constantValue) + "`" } - getConstantValue() { + get constantValue() { return this.length ? this.childrenToString() : this.getWordsFrom(2).join(" ") } } -class GrammarNodeTypeConstantFloat extends GrammarNodeTypeConstant {} -class GrammarNodeTypeConstantBoolean extends GrammarNodeTypeConstant {} -class AbstractGrammarDefinitionNode extends AbstractExtendibleTreeNode { - createParser() { +class GrammarParserConstantFloat extends AbstractParserConstantParser {} +class GrammarParserConstantBoolean extends AbstractParserConstantParser {} +class AbstractParserDefinitionParser extends AbstractExtendibleTreeNode { + createParserCombinator() { // todo: some of these should just be on nonRootNodes const types = [ GrammarConstants.frequency, @@ -15102,59 +15180,73 @@ class AbstractGrammarDefinitionNode extends AbstractExtendibleTreeNode { GrammarConstants.cells, GrammarConstants.extends, GrammarConstants.description, - GrammarConstants.catchAllNodeType, + GrammarConstants.catchAllParser, GrammarConstants.catchAllCellType, GrammarConstants.cellParser, GrammarConstants.extensions, GrammarConstants.version, + GrammarConstants.sortTemplate, GrammarConstants.tags, GrammarConstants.crux, GrammarConstants.cruxFromId, + GrammarConstants.listDelimiter, + GrammarConstants.contentKey, + GrammarConstants.childrenKey, + GrammarConstants.uniqueFirstWord, + GrammarConstants.uniqueLine, GrammarConstants.pattern, - GrammarConstants.baseNodeType, + GrammarConstants.baseParser, GrammarConstants.required, GrammarConstants.root, GrammarConstants._extendsJsClass, GrammarConstants._rootNodeJsHeader, GrammarConstants.javascript, GrammarConstants.compilesTo, - GrammarConstants.abstract, GrammarConstants.javascript, GrammarConstants.single, - GrammarConstants.todoComment + GrammarConstants.comment ] const map = {} types.forEach(type => { map[type] = TreeNode }) - map[GrammarConstantsConstantTypes.boolean] = GrammarNodeTypeConstantBoolean - map[GrammarConstantsConstantTypes.int] = GrammarNodeTypeConstantInt - map[GrammarConstantsConstantTypes.string] = GrammarNodeTypeConstantString - map[GrammarConstantsConstantTypes.float] = GrammarNodeTypeConstantFloat - map[GrammarConstants.compilerNodeType] = GrammarCompilerNode - map[GrammarConstants.example] = GrammarExampleNode - return new TreeNode.Parser(undefined, map) + map[GrammarConstantsConstantTypes.boolean] = GrammarParserConstantBoolean + map[GrammarConstantsConstantTypes.int] = GrammarParserConstantInt + map[GrammarConstantsConstantTypes.string] = GrammarParserConstantString + map[GrammarConstantsConstantTypes.float] = GrammarParserConstantFloat + map[GrammarConstants.compilerParser] = GrammarCompilerParser + map[GrammarConstants.example] = GrammarExampleParser + return new TreeNode.ParserCombinator(undefined, map, [{ regex: HandGrammarProgram.parserFullRegex, parser: parserDefinitionParser }]) + } + get sortSpec() { + const sortSections = new Map() + const sortIndices = new Map() + const sortTemplate = this.get(GrammarConstants.sortTemplate) + if (!sortTemplate) return { sortSections, sortIndices } + sortTemplate.split(" ").forEach((section, sectionIndex) => section.split(" ").forEach(word => sortSections.set(word, sectionIndex))) + sortTemplate.split(" ").forEach((word, index) => sortIndices.set(word, index)) + return { sortSections, sortIndices } } toTypeScriptInterface(used = new Set()) { let childrenInterfaces = [] let properties = [] - const inScope = this.getFirstWordMapWithDefinitions() - const thisId = this._getId() + const inScope = this.firstWordMapWithDefinitions + const thisId = this.id used.add(thisId) Object.keys(inScope).forEach(key => { const def = inScope[key] - const map = def.getFirstWordMapWithDefinitions() - const id = def._getId() + const map = def.firstWordMapWithDefinitions + const id = def.id const optionalTag = def.isRequired() ? "" : "?" const escapedKey = key.match(/\?/) ? `"${key}"` : key - const description = def.getDescription() + const description = def.description if (Object.keys(map).length && !used.has(id)) { childrenInterfaces.push(def.toTypeScriptInterface(used)) properties.push(` ${escapedKey}${optionalTag}: ${id}`) } else properties.push(` ${escapedKey}${optionalTag}: any${description ? " // " + description : ""}`) }) properties.sort() - const description = this.getDescription() + const description = this.description const myInterface = "" return `${childrenInterfaces.join("\n")} ${description ? "// " + description : ""} @@ -15162,103 +15254,69 @@ interface ${thisId} { ${properties.join("\n")} }`.trim() } - getTableNameIfAny() { - return this.getFrom(`${GrammarConstantsConstantTypes.string} ${GrammarConstantsMisc.tableName}`) - } - getSQLiteTableColumns() { - return this._getConcreteNonErrorInScopeNodeDefinitions(this._getInScopeNodeTypeIds()).map(node => { - const firstNonKeywordCellType = node.getCellParser().getCellArray()[1] - const type = firstNonKeywordCellType ? firstNonKeywordCellType.getSQLiteType() : SQLiteTypes.text - return { - columnName: node._getIdWithoutSuffix(), - type - } - }) - } - toSQLiteTableSchema() { - const columns = this.getSQLiteTableColumns().map(columnDef => `${columnDef.columnName} ${columnDef.type}`) - return `create table ${this.getTableNameIfAny() || this._getId()} ( - id TEXT NOT NULL PRIMARY KEY, - ${columns.join(",\n ")} -);` - } - _getId() { - return this.getWord(0) - } get id() { - return this._getId() + return this.getWord(0) } - _getIdWithoutSuffix() { - return this._getId().replace(HandGrammarProgram.nodeTypeSuffixRegex, "") + get idWithoutSuffix() { + return this.id.replace(HandGrammarProgram.parserSuffixRegex, "") } - getConstantsObject() { + get constantsObject() { const obj = this._getUniqueConstantNodes() - Object.keys(obj).forEach(key => { - obj[key] = obj[key].getConstantValue() - }) + Object.keys(obj).forEach(key => (obj[key] = obj[key].constantValue)) return obj } _getUniqueConstantNodes(extended = true) { const obj = {} - const items = extended ? this._getChildrenByNodeConstructorInExtended(GrammarNodeTypeConstant) : this.getChildrenByNodeConstructor(GrammarNodeTypeConstant) + const items = extended ? this._getChildrenByParserInExtended(AbstractParserConstantParser) : this.getChildrenByParser(AbstractParserConstantParser) items.reverse() // Last definition wins. - items.forEach(node => { - obj[node.getIdentifier()] = node - }) + items.forEach(node => (obj[node.identifier] = node)) return obj } - getExamples() { - return this._getChildrenByNodeConstructorInExtended(GrammarExampleNode) + get examples() { + return this._getChildrenByParserInExtended(GrammarExampleParser) } - getNodeTypeIdFromDefinition() { + get parserIdFromDefinition() { return this.getWord(0) } - // todo: remove? just reused nodeTypeId - _getGeneratedClassName() { - return this.getNodeTypeIdFromDefinition() + // todo: remove? just reused parserId + get generatedClassName() { + return this.parserIdFromDefinition } - _hasValidNodeTypeId() { - return !!this._getGeneratedClassName() + _hasValidParserId() { + return !!this.generatedClassName } _isAbstract() { - return this.has(GrammarConstants.abstract) - } - _getConcreteDescendantDefinitions() { - const defs = this._getProgramNodeTypeDefinitionCache() - const id = this._getId() - return Object.values(defs).filter(def => { - return def._doesExtend(id) && !def._isAbstract() - }) + return this.id.startsWith(GrammarConstants.abstractParserPrefix) } - _getCruxIfAny() { - return this.get(GrammarConstants.crux) || (this._hasFromExtended(GrammarConstants.cruxFromId) ? this._getIdWithoutSuffix() : undefined) + get cruxIfAny() { + return this.get(GrammarConstants.crux) || (this._hasFromExtended(GrammarConstants.cruxFromId) ? this.idWithoutSuffix : undefined) } - _getRegexMatch() { + get regexMatch() { return this.get(GrammarConstants.pattern) } - _getFirstCellEnumOptions() { + get firstCellEnumOptions() { const firstCellDef = this._getMyCellTypeDefs()[0] return firstCellDef ? firstCellDef._getEnumOptions() : undefined } - getLanguageDefinitionProgram() { - return this.getParent() + get languageDefinitionProgram() { + return this.root } - _getCustomJavascriptMethods() { + get customJavascriptMethods() { const hasJsCode = this.has(GrammarConstants.javascript) return hasJsCode ? this.getNode(GrammarConstants.javascript).childrenToString() : "" } - getFirstWordMapWithDefinitions() { - if (!this._cache_firstWordToNodeDefMap) this._cache_firstWordToNodeDefMap = this._createParserInfo(this._getInScopeNodeTypeIds()).firstWordMap + get firstWordMapWithDefinitions() { + if (!this._cache_firstWordToNodeDefMap) this._cache_firstWordToNodeDefMap = this._createParserInfo(this._getInScopeParserIds()).firstWordMap return this._cache_firstWordToNodeDefMap } // todo: remove - getRunTimeFirstWordsInScope() { + get runTimeFirstWordsInScope() { return this._getParser().getFirstWordOptions() } _getMyCellTypeDefs() { const requiredCells = this.get(GrammarConstants.cells) if (!requiredCells) return [] - const grammarProgram = this.getLanguageDefinitionProgram() + const grammarProgram = this.languageDefinitionProgram return requiredCells.split(" ").map(cellTypeId => { const cellTypeDef = grammarProgram.getCellTypeDefinitionById(cellTypeId) if (!cellTypeDef) throw new Error(`No cellType "${cellTypeId}" found`) @@ -15266,156 +15324,153 @@ ${properties.join("\n")} }) } // todo: what happens when you have a cell getter and constant with same name? - _getCellGettersAndNodeTypeConstants() { + get cellGettersAndParserConstants() { // todo: add cellType parsings - const grammarProgram = this.getLanguageDefinitionProgram() + const grammarProgram = this.languageDefinitionProgram const getters = this._getMyCellTypeDefs().map((cellTypeDef, index) => cellTypeDef.getGetter(index)) const catchAllCellTypeId = this.get(GrammarConstants.catchAllCellType) if (catchAllCellTypeId) getters.push(grammarProgram.getCellTypeDefinitionById(catchAllCellTypeId).getCatchAllGetter(getters.length)) // Constants - Object.values(this._getUniqueConstantNodes(false)).forEach(node => { - getters.push(node.getGetter()) - }) + Object.values(this._getUniqueConstantNodes(false)).forEach(node => getters.push(node.getGetter())) return getters.join("\n") } - _createParserInfo(nodeTypeIdsInScope) { + _createParserInfo(parserIdsInScope) { const result = { firstWordMap: {}, regexTests: [] } - if (!nodeTypeIdsInScope.length) return result - const allProgramNodeTypeDefinitionsMap = this._getProgramNodeTypeDefinitionCache() - Object.keys(allProgramNodeTypeDefinitionsMap) - .filter(nodeTypeId => allProgramNodeTypeDefinitionsMap[nodeTypeId].isOrExtendsANodeTypeInScope(nodeTypeIdsInScope)) - .filter(nodeTypeId => !allProgramNodeTypeDefinitionsMap[nodeTypeId]._isAbstract()) - .forEach(nodeTypeId => { - const def = allProgramNodeTypeDefinitionsMap[nodeTypeId] - const regex = def._getRegexMatch() - const crux = def._getCruxIfAny() - const enumOptions = def._getFirstCellEnumOptions() - if (regex) result.regexTests.push({ regex: regex, nodeConstructor: def.getNodeTypeIdFromDefinition() }) + if (!parserIdsInScope.length) return result + const allProgramParserDefinitionsMap = this.programParserDefinitionCache + Object.keys(allProgramParserDefinitionsMap) + .filter(parserId => { + const def = allProgramParserDefinitionsMap[parserId] + return def.isOrExtendsAParserInScope(parserIdsInScope) && !def._isAbstract() + }) + .forEach(parserId => { + const def = allProgramParserDefinitionsMap[parserId] + const regex = def.regexMatch + const crux = def.cruxIfAny + const enumOptions = def.firstCellEnumOptions + if (regex) result.regexTests.push({ regex: regex, parser: def.parserIdFromDefinition }) else if (crux) result.firstWordMap[crux] = def else if (enumOptions) { - enumOptions.forEach(option => { - result.firstWordMap[option] = def - }) + enumOptions.forEach(option => (result.firstWordMap[option] = def)) } }) return result } - getTopNodeTypeDefinitions() { - const arr = Object.values(this.getFirstWordMapWithDefinitions()) - arr.sort(TreeUtils.makeSortByFn(definition => definition.getFrequency())) + get topParserDefinitions() { + const arr = Object.values(this.firstWordMapWithDefinitions) + arr.sort(Utils.makeSortByFn(definition => definition.frequency)) arr.reverse() return arr } - _getMyInScopeNodeTypeIds() { - const nodeTypesNode = this.getNode(GrammarConstants.inScope) - return nodeTypesNode ? nodeTypesNode.getWordsFrom(1) : [] + _getMyInScopeParserIds(target = this) { + const parsersNode = target.getNode(GrammarConstants.inScope) + const scopedDefinitionIds = target.myScopedParserDefinitions.map(def => def.id) + return parsersNode ? parsersNode.getWordsFrom(1).concat(scopedDefinitionIds) : scopedDefinitionIds } - _getInScopeNodeTypeIds() { + _getInScopeParserIds() { // todo: allow multiple of these if we allow mixins? - const ids = this._getMyInScopeNodeTypeIds() + const ids = this._getMyInScopeParserIds() const parentDef = this._getExtendedParent() - return parentDef ? ids.concat(parentDef._getInScopeNodeTypeIds()) : ids + return parentDef ? ids.concat(parentDef._getInScopeParserIds()) : ids } - // Should only one of these node types be present in the parent node? get isSingle() { - return this._hasFromExtended(GrammarConstants.single) + const hit = this._getNodeFromExtended(GrammarConstants.single) + return hit && hit.get(GrammarConstants.single) !== "false" + } + get isUniqueLine() { + const hit = this._getNodeFromExtended(GrammarConstants.uniqueLine) + return hit && hit.get(GrammarConstants.uniqueLine) !== "false" } isRequired() { return this._hasFromExtended(GrammarConstants.required) } - getNodeTypeDefinitionByNodeTypeId(nodeTypeId) { + getParserDefinitionByParserId(parserId) { // todo: return catch all? - const def = this._getProgramNodeTypeDefinitionCache()[nodeTypeId] + const def = this.programParserDefinitionCache[parserId] if (def) return def - // todo: cleanup - this.getLanguageDefinitionProgram()._addDefaultCatchAllBlobNode() - return this._getProgramNodeTypeDefinitionCache()[nodeTypeId] + this.languageDefinitionProgram._addDefaultCatchAllBlobParser() // todo: cleanup. Why did I do this? Needs to be removed or documented. + const nodeDef = this.languageDefinitionProgram.programParserDefinitionCache[parserId] + if (!nodeDef) throw new Error(`No definition found for parser id "${parserId}". Node: \n---\n${this.asString}\n---`) + return nodeDef } - isDefined(nodeTypeId) { - return !!this._getProgramNodeTypeDefinitionCache()[nodeTypeId] + isDefined(parserId) { + return !!this.programParserDefinitionCache[parserId] } - _getIdToNodeMap() { - return this._getProgramNodeTypeDefinitionCache() + get idToNodeMap() { + return this.programParserDefinitionCache } _amIRoot() { - if (this._cache_isRoot === undefined) this._cache_isRoot = this._getLanguageRootNode() === this + if (this._cache_isRoot === undefined) this._cache_isRoot = this._languageRootNode === this return this._cache_isRoot } - _getLanguageRootNode() { - return this.getParent().getRootNodeTypeDefinitionNode() + get _languageRootNode() { + return this.root.rootParserDefinition } - _isErrorNodeType() { - return this.get(GrammarConstants.baseNodeType) === GrammarConstants.errorNode + _isErrorParser() { + return this.get(GrammarConstants.baseParser) === GrammarConstants.errorParser } - _isBlobNodeType() { + _isBlobParser() { // Do not check extended classes. Only do once. - return this.get(GrammarConstants.baseNodeType) === GrammarConstants.blobNode + return this._getFromExtended(GrammarConstants.baseParser) === GrammarConstants.blobParser } - _getErrorMethodToJavascript() { - if (this._isBlobNodeType()) return "getErrors() { return [] }" // Skips parsing child nodes for perf gains. - if (this._isErrorNodeType()) return "getErrors() { return this._getErrorNodeErrors() }" + get errorMethodToJavascript() { + if (this._isBlobParser()) return "getErrors() { return [] }" // Skips parsing child nodes for perf gains. + if (this._isErrorParser()) return "getErrors() { return this._getErrorParserErrors() }" return "" } - _getParserToJavascript() { - if (this._isBlobNodeType()) + get parserAsJavascript() { + if (this._isBlobParser()) // todo: do we need this? - return "createParser() { return new jtree.TreeNode.Parser(this._getBlobNodeCatchAllNodeType())}" - const parserInfo = this._createParserInfo(this._getMyInScopeNodeTypeIds()) + return "createParserCombinator() { return new TreeNode.ParserCombinator(this._getBlobParserCatchAllParser())}" + const parserInfo = this._createParserInfo(this._getMyInScopeParserIds()) const myFirstWordMap = parserInfo.firstWordMap const regexRules = parserInfo.regexTests // todo: use constants in first word maps? // todo: cache the super extending? const firstWords = Object.keys(myFirstWordMap) const hasFirstWords = firstWords.length - const catchAllConstructor = this._getCatchAllNodeConstructorToJavascript() - if (!hasFirstWords && !catchAllConstructor && !regexRules.length) return "" + const catchAllParser = this.catchAllParserToJavascript + if (!hasFirstWords && !catchAllParser && !regexRules.length) return "" const firstWordsStr = hasFirstWords - ? `Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), {` + firstWords.map(firstWord => `"${firstWord}" : ${myFirstWordMap[firstWord].getNodeTypeIdFromDefinition()}`).join(",\n") + "})" + ? `Object.assign(Object.assign({}, super.createParserCombinator()._getFirstWordMapAsObject()), {` + firstWords.map(firstWord => `"${firstWord}" : ${myFirstWordMap[firstWord].parserIdFromDefinition}`).join(",\n") + "})" : "undefined" const regexStr = regexRules.length ? `[${regexRules .map(rule => { - return `{regex: /${rule.regex}/, nodeConstructor: ${rule.nodeConstructor}}` + return `{regex: /${rule.regex}/, parser: ${rule.parser}}` }) .join(",")}]` : "undefined" - const catchAllStr = catchAllConstructor ? catchAllConstructor : this._amIRoot() ? `this._getBlobNodeCatchAllNodeType()` : "undefined" - return `createParser() { - return new jtree.TreeNode.Parser(${catchAllStr}, ${firstWordsStr}, ${regexStr}) + const catchAllStr = catchAllParser ? catchAllParser : this._amIRoot() ? `this._getBlobParserCatchAllParser()` : "undefined" + const scopedParserJavascript = this.myScopedParserDefinitions.map(def => def.asJavascriptClass).join("\n\n") + return `createParserCombinator() {${scopedParserJavascript} + return new TreeNode.ParserCombinator(${catchAllStr}, ${firstWordsStr}, ${regexStr}) }` } - _getCatchAllNodeConstructorToJavascript() { - if (this._isBlobNodeType()) return "this._getBlobNodeCatchAllNodeType()" - const nodeTypeId = this.get(GrammarConstants.catchAllNodeType) - if (!nodeTypeId) return "" - const nodeDef = this.getNodeTypeDefinitionByNodeTypeId(nodeTypeId) - if (!nodeDef) throw new Error(`No definition found for nodeType id "${nodeTypeId}"`) - return nodeDef._getGeneratedClassName() + get myScopedParserDefinitions() { + return this.getChildrenByParser(parserDefinitionParser) + } + get catchAllParserToJavascript() { + if (this._isBlobParser()) return "this._getBlobParserCatchAllParser()" + const parserId = this.get(GrammarConstants.catchAllParser) + if (!parserId) return "" + const nodeDef = this.getParserDefinitionByParserId(parserId) + return nodeDef.generatedClassName } - _nodeDefToJavascriptClass() { - const components = [this._getParserToJavascript(), this._getErrorMethodToJavascript(), this._getCellGettersAndNodeTypeConstants(), this._getCustomJavascriptMethods()].filter(identity => identity) + get asJavascriptClass() { + const components = [this.parserAsJavascript, this.errorMethodToJavascript, this.cellGettersAndParserConstants, this.customJavascriptMethods].filter(identity => identity) + const thisClassName = this.generatedClassName if (this._amIRoot()) { - components.push(`static cachedHandGrammarProgramRoot = new jtree.HandGrammarProgram(\`${TreeUtils.escapeBackTicks( - this.getParent() - .toString() - .replace(/\\/g, "\\\\") - )}\`) - getHandGrammarProgram() { + components.push(`static cachedHandGrammarProgramRoot = new HandGrammarProgram(\`${Utils.escapeBackTicks(this.parent.toString().replace(/\\/g, "\\\\"))}\`) + get handGrammarProgram() { return this.constructor.cachedHandGrammarProgramRoot }`) - const nodeTypeMap = this.getLanguageDefinitionProgram() - .getValidConcreteAndAbstractNodeTypeDefinitions() - .map(def => { - const id = def.getNodeTypeIdFromDefinition() - return `"${id}": ${id}` - }) - .join(",\n") - components.push(`static getNodeTypeMap() { return {${nodeTypeMap} }}`) + components.push(`static rootParser = ${thisClassName}`) } - return `class ${this._getGeneratedClassName()} extends ${this._getExtendsClassName()} { + return `class ${thisClassName} extends ${this._getExtendsClassName()} { ${components.join("\n")} }` } @@ -15424,11 +15479,11 @@ ${properties.join("\n")} const hardCodedExtend = this.get(GrammarConstants._extendsJsClass) if (hardCodedExtend) return hardCodedExtend const extendedDef = this._getExtendedParent() - return extendedDef ? extendedDef._getGeneratedClassName() : "jtree.GrammarBackedNode" + return extendedDef ? extendedDef.generatedClassName : "GrammarBackedNode" } _getCompilerObject() { let obj = {} - const items = this._getChildrenByNodeConstructorInExtended(GrammarCompilerNode) + const items = this._getChildrenByParserInExtended(GrammarCompilerParser) items.reverse() // Last definition wins. items.forEach(node => { obj = Object.assign(obj, node.toObject()) // todo: what about multiline strings? @@ -15436,35 +15491,35 @@ ${properties.join("\n")} return obj } // todo: improve layout (use bold?) - getLineHints() { - return this.getCellParser().getLineHints() + get lineHints() { + return this.cellParser.lineHints } - isOrExtendsANodeTypeInScope(firstWordsInScope) { - const chain = this._getNodeTypeInheritanceSet() + isOrExtendsAParserInScope(firstWordsInScope) { + const chain = this._getParserInheritanceSet() return firstWordsInScope.some(firstWord => chain.has(firstWord)) } - isTerminalNodeType() { - return !this._getFromExtended(GrammarConstants.inScope) && !this._getFromExtended(GrammarConstants.catchAllNodeType) + isTerminalParser() { + return !this._getFromExtended(GrammarConstants.inScope) && !this._getFromExtended(GrammarConstants.catchAllParser) } - _getSublimeMatchLine() { - const regexMatch = this._getRegexMatch() + get sublimeMatchLine() { + const regexMatch = this.regexMatch if (regexMatch) return `'${regexMatch}'` - const cruxMatch = this._getCruxIfAny() - if (cruxMatch) return `'^ *${TreeUtils.escapeRegExp(cruxMatch)}(?: |$)'` - const enumOptions = this._getFirstCellEnumOptions() - if (enumOptions) return `'^ *(${TreeUtils.escapeRegExp(enumOptions.join("|"))})(?: |$)'` + const cruxMatch = this.cruxIfAny + if (cruxMatch) return `'^ *${Utils.escapeRegExp(cruxMatch)}(?: |$)'` + const enumOptions = this.firstCellEnumOptions + if (enumOptions) return `'^ *(${Utils.escapeRegExp(enumOptions.join("|"))})(?: |$)'` } // todo: refactor. move some parts to cellParser? _toSublimeMatchBlock() { const defaultHighlightScope = "source" - const program = this.getLanguageDefinitionProgram() - const cellParser = this.getCellParser() + const program = this.languageDefinitionProgram + const cellParser = this.cellParser const requiredCellTypeIds = cellParser.getRequiredCellTypeIds() - const catchAllCellTypeId = cellParser.getCatchAllCellTypeId() + const catchAllCellTypeId = cellParser.catchAllCellTypeId const firstCellTypeDef = program.getCellTypeDefinitionById(requiredCellTypeIds[0]) - const firstWordHighlightScope = (firstCellTypeDef ? firstCellTypeDef.getHighlightScope() : defaultHighlightScope) + "." + this.getNodeTypeIdFromDefinition() - const topHalf = ` '${this.getNodeTypeIdFromDefinition()}': - - match: ${this._getSublimeMatchLine()} + const firstWordHighlightScope = (firstCellTypeDef ? firstCellTypeDef.highlightScope : defaultHighlightScope) + "." + this.parserIdFromDefinition + const topHalf = ` '${this.parserIdFromDefinition}': + - match: ${this.sublimeMatchLine} scope: ${firstWordHighlightScope}` if (catchAllCellTypeId) requiredCellTypeIds.push(catchAllCellTypeId) if (!requiredCellTypeIds.length) return topHalf @@ -15472,7 +15527,7 @@ ${properties.join("\n")} .map((cellTypeId, index) => { const cellTypeDefinition = program.getCellTypeDefinitionById(cellTypeId) // todo: cleanup if (!cellTypeDefinition) throw new Error(`No ${GrammarConstants.cellType} ${cellTypeId} found`) // todo: standardize error/capture error at grammar time - return ` ${index + 1}: ${(cellTypeDefinition.getHighlightScope() || defaultHighlightScope) + "." + cellTypeDefinition.getCellTypeId()}` + return ` ${index + 1}: ${(cellTypeDefinition.highlightScope || defaultHighlightScope) + "." + cellTypeDefinition.cellTypeId}` }) .join("\n") const cellTypesToRegex = cellTypeIds => cellTypeIds.map(cellTypeId => `({{${cellTypeId}}})?`).join(" ?") @@ -15484,36 +15539,44 @@ ${captures} - match: $ pop: true` } - _getNodeTypeInheritanceSet() { - if (!this._cache_nodeTypeInheritanceSet) this._cache_nodeTypeInheritanceSet = new Set(this.getAncestorNodeTypeIdsArray()) - return this._cache_nodeTypeInheritanceSet + _getParserInheritanceSet() { + if (!this._cache_parserInheritanceSet) this._cache_parserInheritanceSet = new Set(this.ancestorParserIdsArray) + return this._cache_parserInheritanceSet } - getAncestorNodeTypeIdsArray() { - if (!this._cache_ancestorNodeTypeIdsArray) { - this._cache_ancestorNodeTypeIdsArray = this._getAncestorsArray().map(def => def.getNodeTypeIdFromDefinition()) - this._cache_ancestorNodeTypeIdsArray.reverse() + get ancestorParserIdsArray() { + if (!this._cache_ancestorParserIdsArray) { + this._cache_ancestorParserIdsArray = this._getAncestorsArray().map(def => def.parserIdFromDefinition) + this._cache_ancestorParserIdsArray.reverse() } - return this._cache_ancestorNodeTypeIdsArray + return this._cache_ancestorParserIdsArray + } + get programParserDefinitionCache() { + if (!this._cache_parserDefinitionParsers) this._cache_parserDefinitionParsers = this.isRoot || this.hasParserDefinitions ? this.makeProgramParserDefinitionCache() : this.parent.programParserDefinitionCache + return this._cache_parserDefinitionParsers } - _getProgramNodeTypeDefinitionCache() { - return this.getLanguageDefinitionProgram()._getProgramNodeTypeDefinitionCache() + get hasParserDefinitions() { + return !!this.getChildrenByParser(parserDefinitionParser).length } - getDescription() { + makeProgramParserDefinitionCache() { + const scopedParsers = this.getChildrenByParser(parserDefinitionParser) + const cache = Object.assign({}, this.parent.programParserDefinitionCache) // todo. We don't really need this. we should just lookup the parent if no local hits. + scopedParsers.forEach(parserDefinitionParser => (cache[parserDefinitionParser.parserIdFromDefinition] = parserDefinitionParser)) + return cache + } + get description() { return this._getFromExtended(GrammarConstants.description) || "" } - getFrequency() { + get frequency() { const val = this._getFromExtended(GrammarConstants.frequency) return val ? parseFloat(val) : 0 } - _getExtendedNodeTypeId() { - const ancestorIds = this.getAncestorNodeTypeIdsArray() + _getExtendedParserId() { + const ancestorIds = this.ancestorParserIdsArray if (ancestorIds.length > 1) return ancestorIds[ancestorIds.length - 2] } _toStumpString() { - const crux = this._getCruxIfAny() - const cellArray = this.getCellParser() - .getCellArray() - .filter((item, index) => index) // for now this only works for keyword langs + const crux = this.cruxIfAny + const cellArray = this.cellParser.getCellArray().filter((item, index) => index) // for now this only works for keyword langs if (!cellArray.length) // todo: remove this! just doing it for now until we refactor getCellArray to handle catchAlls better. return "" @@ -15524,64 +15587,88 @@ ${cells.toString(1)}` } toStumpString() { const nodeBreakSymbol = "\n" - return this._getConcreteNonErrorInScopeNodeDefinitions(this._getInScopeNodeTypeIds()) + return this._getConcreteNonErrorInScopeNodeDefinitions(this._getInScopeParserIds()) .map(def => def._toStumpString()) .filter(identity => identity) .join(nodeBreakSymbol) } _generateSimulatedLine(seed) { // todo: generate simulated data from catch all - const crux = this._getCruxIfAny() - return this.getCellParser() + const crux = this.cruxIfAny + return this.cellParser .getCellArray() .map((cell, index) => (!index && crux ? crux : cell.synthesizeCell(seed))) .join(" ") } - _shouldSynthesize(def, nodeTypeChain) { - if (def._isErrorNodeType() || def._isAbstract()) return false - if (nodeTypeChain.includes(def._getId())) return false + _shouldSynthesize(def, parserChain) { + if (def._isErrorParser() || def._isAbstract()) return false + if (parserChain.includes(def.id)) return false const tags = def.get(GrammarConstants.tags) if (tags && tags.includes(GrammarConstantsMisc.doNotSynthesize)) return false return true } - _getConcreteNonErrorInScopeNodeDefinitions(nodeTypeIds) { - const results = [] - nodeTypeIds.forEach(nodeTypeId => { - const def = this.getNodeTypeDefinitionByNodeTypeId(nodeTypeId) - if (def._isErrorNodeType()) return true - else if (def._isAbstract()) { - def._getConcreteDescendantDefinitions().forEach(def => results.push(def)) - } else { - results.push(def) - } + // Get all definitions in this current scope down, even ones that are scoped inside other definitions. + get inScopeAndDescendantDefinitions() { + return this.languageDefinitionProgram._collectAllDefinitions(Object.values(this.programParserDefinitionCache), []) + } + _collectAllDefinitions(defs, collection = []) { + defs.forEach(def => { + collection.push(def) + def._collectAllDefinitions(def.getChildrenByParser(parserDefinitionParser), collection) }) - return results + return collection + } + get cruxPath() { + const parentPath = this.parent.cruxPath + return (parentPath ? parentPath + " " : "") + this.cruxIfAny + } + get cruxPathAsColumnName() { + return this.cruxPath.replace(/ /g, "_") + } + // Get every definition that extends from this one, even ones that are scoped inside other definitions. + get concreteDescendantDefinitions() { + const { inScopeAndDescendantDefinitions, id } = this + return Object.values(inScopeAndDescendantDefinitions).filter(def => def._doesExtend(id) && !def._isAbstract()) + } + get concreteInScopeDescendantDefinitions() { + // Note: non-recursive. + const defs = this.programParserDefinitionCache + const id = this.id + return Object.values(defs).filter(def => def._doesExtend(id) && !def._isAbstract()) + } + _getConcreteNonErrorInScopeNodeDefinitions(parserIds) { + const defs = [] + parserIds.forEach(parserId => { + const def = this.getParserDefinitionByParserId(parserId) + if (def._isErrorParser()) return + else if (def._isAbstract()) def.concreteInScopeDescendantDefinitions.forEach(def => defs.push(def)) + else defs.push(def) + }) + return defs } // todo: refactor - synthesizeNode(nodeCount = 1, indentCount = -1, nodeTypesAlreadySynthesized = [], seed = Date.now()) { - let inScopeNodeTypeIds = this._getInScopeNodeTypeIds() - const catchAllNodeTypeId = this._getFromExtended(GrammarConstants.catchAllNodeType) - if (catchAllNodeTypeId) inScopeNodeTypeIds.push(catchAllNodeTypeId) - const thisId = this._getId() - if (!nodeTypesAlreadySynthesized.includes(thisId)) nodeTypesAlreadySynthesized.push(thisId) + synthesizeNode(nodeCount = 1, indentCount = -1, parsersAlreadySynthesized = [], seed = Date.now()) { + let inScopeParserIds = this._getInScopeParserIds() + const catchAllParserId = this._getFromExtended(GrammarConstants.catchAllParser) + if (catchAllParserId) inScopeParserIds.push(catchAllParserId) + const thisId = this.id + if (!parsersAlreadySynthesized.includes(thisId)) parsersAlreadySynthesized.push(thisId) const lines = [] while (nodeCount) { const line = this._generateSimulatedLine(seed) if (line) lines.push(" ".repeat(indentCount >= 0 ? indentCount : 0) + line) - this._getConcreteNonErrorInScopeNodeDefinitions(inScopeNodeTypeIds.filter(nodeTypeId => !nodeTypesAlreadySynthesized.includes(nodeTypeId))) - .filter(def => this._shouldSynthesize(def, nodeTypesAlreadySynthesized)) + this._getConcreteNonErrorInScopeNodeDefinitions(inScopeParserIds.filter(parserId => !parsersAlreadySynthesized.includes(parserId))) + .filter(def => this._shouldSynthesize(def, parsersAlreadySynthesized)) .forEach(def => { - const chain = nodeTypesAlreadySynthesized // .slice(0) - chain.push(def._getId()) - def.synthesizeNode(1, indentCount + 1, chain, seed).forEach(line => { - lines.push(line) - }) + const chain = parsersAlreadySynthesized // .slice(0) + chain.push(def.id) + def.synthesizeNode(1, indentCount + 1, chain, seed).forEach(line => lines.push(line)) }) nodeCount-- } return lines } - getCellParser() { + get cellParser() { if (!this._cellParser) { const cellParsingStrategy = this._getFromExtended(GrammarConstants.cellParser) if (cellParsingStrategy === GrammarCellParser.postfix) this._cellParser = new PostfixCellParser(this) @@ -15592,54 +15679,64 @@ ${cells.toString(1)}` } } // todo: remove? -class nodeTypeDefinitionNode extends AbstractGrammarDefinitionNode {} +class parserDefinitionParser extends AbstractParserDefinitionParser {} // HandGrammarProgram is a constructor that takes a grammar file, and builds a new // constructor for new language that takes files in that language to execute, compile, etc. -class HandGrammarProgram extends AbstractGrammarDefinitionNode { - createParser() { +class HandGrammarProgram extends AbstractParserDefinitionParser { + createParserCombinator() { const map = {} - map[GrammarConstants.toolingDirective] = TreeNode - map[GrammarConstants.todoComment] = TreeNode - return new TreeNode.Parser(UnknownNodeTypeNode, map, [{ regex: HandGrammarProgram.nodeTypeFullRegex, nodeConstructor: nodeTypeDefinitionNode }, { regex: HandGrammarProgram.cellTypeFullRegex, nodeConstructor: cellTypeDefinitionNode }]) - } + map[GrammarConstants.comment] = TreeNode + return new TreeNode.ParserCombinator(UnknownParserNode, map, [ + { regex: HandGrammarProgram.blankLineRegex, parser: TreeNode }, + { regex: HandGrammarProgram.parserFullRegex, parser: parserDefinitionParser }, + { regex: HandGrammarProgram.cellTypeFullRegex, parser: cellTypeDefinitionParser } + ]) + } + // rootParser // Note: this is some so far unavoidable tricky code. We need to eval the transpiled JS, in a NodeJS or browser environment. - _compileAndEvalGrammar() { - if (!this.isNodeJs()) this._cache_compiledLoadedNodeTypes = TreeUtils.appendCodeAndReturnValueOnWindow(this.toBrowserJavascript(), this.getRootNodeTypeId()).getNodeTypeMap() - else { - const path = require("path") - const code = this.toNodeJsJavascript(path.join(__dirname, "..", "index.js")) - try { - const rootNode = this._requireInVmNodeJsRootNodeTypeConstructor(code) - this._cache_compiledLoadedNodeTypes = rootNode.getNodeTypeMap() - if (!this._cache_compiledLoadedNodeTypes) throw new Error(`Failed to getNodeTypeMap`) - } catch (err) { - // todo: figure out best error pattern here for debugging - console.log(err) - // console.log(`Error in code: `) - // console.log(new TreeNode(code).toStringWithLineNumbers()) - } + _compileAndReturnRootParser() { + if (this._cache_rootParser) return this._cache_rootParser + if (!this.isNodeJs()) { + this._cache_rootParser = Utils.appendCodeAndReturnValueOnWindow(this.toBrowserJavascript(), this.rootParserId).rootParser + return this._cache_rootParser + } + const path = require("path") + const code = this.toNodeJsJavascript(__dirname) + try { + const rootNode = this._requireInVmNodeJsRootParser(code) + this._cache_rootParser = rootNode.rootParser + if (!this._cache_rootParser) throw new Error(`Failed to rootParser`) + } catch (err) { + // todo: figure out best error pattern here for debugging + console.log(err) + // console.log(`Error in code: `) + // console.log(new TreeNode(code).toStringWithLineNumbers()) } + return this._cache_rootParser } - trainModel(programs, programConstructor = this.compileAndReturnRootConstructor()) { - const nodeDefs = this.getValidConcreteAndAbstractNodeTypeDefinitions() + get cruxPath() { + return "" + } + trainModel(programs, rootParser = this.compileAndReturnRootParser()) { + const nodeDefs = this.validConcreteAndAbstractParserDefinitions const nodeDefCountIncludingRoot = nodeDefs.length + 1 - const matrix = TreeUtils.makeMatrix(nodeDefCountIncludingRoot, nodeDefCountIncludingRoot, 0) + const matrix = Utils.makeMatrix(nodeDefCountIncludingRoot, nodeDefCountIncludingRoot, 0) const idToIndex = {} const indexToId = {} nodeDefs.forEach((def, index) => { - const id = def._getId() + const id = def.id idToIndex[id] = index + 1 indexToId[index + 1] = id }) programs.forEach(code => { - const exampleProgram = new programConstructor(code) - exampleProgram.getTopDownArray().forEach(node => { - const nodeIndex = idToIndex[node.getDefinition()._getId()] - const parentNode = node.getParent() + const exampleProgram = new rootParser(code) + exampleProgram.topDownArray.forEach(node => { + const nodeIndex = idToIndex[node.definition.id] + const parentNode = node.parent if (!nodeIndex) return undefined if (parentNode.isRoot()) matrix[0][nodeIndex]++ else { - const parentIndex = idToIndex[parentNode.getDefinition()._getId()] + const parentIndex = idToIndex[parentNode.definition.id] if (!parentIndex) return undefined matrix[parentIndex][nodeIndex]++ } @@ -15652,17 +15749,17 @@ class HandGrammarProgram extends AbstractGrammarDefinitionNode { } } _mapPredictions(predictionsVector, model) { - const total = TreeUtils.sum(predictionsVector) + const total = Utils.sum(predictionsVector) const predictions = predictionsVector.slice(1).map((count, index) => { const id = model.indexToId[index + 1] return { - id: id, - def: this.getNodeTypeDefinitionByNodeTypeId(id), + id, + def: this.getParserDefinitionByParserId(id), count, prob: count / total } }) - predictions.sort(TreeUtils.makeSortByFn(prediction => prediction.count)).reverse() + predictions.sort(Utils.makeSortByFn(prediction => prediction.count)).reverse() return predictions } predictChildren(model, node) { @@ -15672,48 +15769,44 @@ class HandGrammarProgram extends AbstractGrammarDefinitionNode { return this._mapPredictions(this._predictParents(model, node), model) } _predictChildren(model, node) { - return model.matrix[node.isRoot() ? 0 : model.idToIndex[node.getDefinition()._getId()]] + return model.matrix[node.isRoot() ? 0 : model.idToIndex[node.definition.id]] } _predictParents(model, node) { if (node.isRoot()) return [] - const nodeIndex = model.idToIndex[node.getDefinition()._getId()] + const nodeIndex = model.idToIndex[node.definition.id] return model.matrix.map(row => row[nodeIndex]) } - _compileAndReturnNodeTypeMap() { - if (!this._cache_compiledLoadedNodeTypes) this._compileAndEvalGrammar() - return this._cache_compiledLoadedNodeTypes - } _setDirName(name) { this._dirName = name return this } - _requireInVmNodeJsRootNodeTypeConstructor(code) { + _requireInVmNodeJsRootParser(code) { const vm = require("vm") const path = require("path") - const jtreePath = path.join(__dirname, "..", "index.js") // todo: cleanup up try { - global.jtree = require(jtreePath) + Object.keys(GlobalNamespaceAdditions).forEach(key => { + global[key] = require("./" + GlobalNamespaceAdditions[key]) + }) global.require = require global.__dirname = this._dirName global.module = {} return vm.runInThisContext(code) } catch (err) { // todo: figure out best error pattern here for debugging - console.log(`Error in compiled grammar code for language "${this.getGrammarName()}"`) + console.log(`Error in compiled grammar code for language "${this.grammarName}"`) // console.log(new TreeNode(code).toStringWithLineNumbers()) - console.log(`jtreePath: "${jtreePath}"`) console.log(err) throw err } } - examplesToTestBlocks(programConstructor = this.compileAndReturnRootConstructor(), expectedErrorMessage = "") { + examplesToTestBlocks(rootParser = this.compileAndReturnRootParser(), expectedErrorMessage = "") { const testBlocks = {} - this.getValidConcreteAndAbstractNodeTypeDefinitions().forEach(def => - def.getExamples().forEach(example => { - const id = def._getId() + example.getContent() + this.validConcreteAndAbstractParserDefinitions.forEach(def => + def.examples.forEach(example => { + const id = def.id + example.content testBlocks[id] = equal => { - const exampleProgram = new programConstructor(example.childrenToString()) + const exampleProgram = new rootParser(example.childrenToString()) const errors = exampleProgram.getAllErrors(example._getLineNumber() + 1) equal(errors.join("\n"), expectedErrorMessage, `Expected no errors in ${id}`) } @@ -15722,14 +15815,14 @@ class HandGrammarProgram extends AbstractGrammarDefinitionNode { return testBlocks } toReadMe() { - const languageName = this.getExtensionName() - const rootNodeDef = this.getRootNodeTypeDefinitionNode() - const cellTypes = this.getCellTypeDefinitions() - const nodeTypeFamilyTree = this.getNodeTypeFamilyTree() - const exampleNode = rootNodeDef.getExamples()[0] + const languageName = this.extensionName + const rootNodeDef = this.rootParserDefinition + const cellTypes = this.cellTypeDefinitions + const parserFamilyTree = this.parserFamilyTree + const exampleNode = rootNodeDef.examples[0] return `title ${languageName} Readme -paragraph ${rootNodeDef.getDescription()} +paragraph ${rootNodeDef.description} subtitle Quick Example @@ -15739,9 +15832,9 @@ ${exampleNode ? exampleNode.childrenToString(1) : ""} subtitle Quick facts about ${languageName} list - - ${languageName} has ${nodeTypeFamilyTree.getTopDownArray().length} node types. + - ${languageName} has ${parserFamilyTree.topDownArray.length} node types. - ${languageName} has ${Object.keys(cellTypes).length} cell types - - The source code for ${languageName} is ${this.getTopDownArray().length} lines long. + - The source code for ${languageName} is ${this.topDownArray.length} lines long. subtitle Installing @@ -15756,7 +15849,7 @@ code subtitle Node Types code -${nodeTypeFamilyTree.toString(1)} +${parserFamilyTree.toString(1)} subtitle Cell Types @@ -15768,7 +15861,7 @@ subtitle Road Map paragraph Here are the "todos" present in the source code for ${languageName}: list -${this.getTopDownArray() +${this.topDownArray .filter(node => node.getWord(0) === "todo") .map(node => ` - ${node.getLine()}`) .join("\n")} @@ -15778,9 +15871,9 @@ paragraph This readme was auto-generated using the } toBundle() { const files = {} - const rootNodeDef = this.getRootNodeTypeDefinitionNode() - const languageName = this.getExtensionName() - const example = rootNodeDef.getExamples()[0] + const rootNodeDef = this.rootParserDefinition + const languageName = this.extensionName + const example = rootNodeDef.examples[0] const sampleCode = example ? example.childrenToString() : "" files[GrammarBundleFiles.package] = JSON.stringify( { @@ -15798,175 +15891,160 @@ paragraph This readme was auto-generated using the const errors = program.getAllErrors() console.log("Sample program compiled with " + errors.length + " errors.") if (errors.length) - console.log(errors.map(error => error.getMessage()))` + console.log(errors.map(error => error.message))` const nodePath = `${languageName}.node.js` files[nodePath] = this.toNodeJsJavascript() files[GrammarBundleFiles.indexJs] = `module.exports = require("./${nodePath}")` const browserPath = `${languageName}.browser.js` files[browserPath] = this.toBrowserJavascript() - files[GrammarBundleFiles.indexHtml] = `<script src="node_modules/jtree/products/jtree.browser.js"></script> + files[GrammarBundleFiles.indexHtml] = `<script src="node_modules/jtree/products/Utils.browser.js"></script> +<script src="node_modules/jtree/products/TreeNode.browser.js"></script> +<script src="node_modules/jtree/products/GrammarLanguage.browser.js"></script> <script src="${browserPath}"></script> <script> const sampleCode = \`${sampleCode.toString()}\` ${testCode} </script>` - const samplePath = "sample." + this.getExtensionName() + const samplePath = "sample." + this.extensionName files[samplePath] = sampleCode.toString() files[GrammarBundleFiles.testJs] = `const ${languageName} = require("./index.js") /*keep-line*/ const sampleCode = require("fs").readFileSync("${samplePath}", "utf8") ${testCode}` return files } - getTargetExtension() { - return this.getRootNodeTypeDefinitionNode().get(GrammarConstants.compilesTo) + get targetExtension() { + return this.rootParserDefinition.get(GrammarConstants.compilesTo) } - getCellTypeDefinitions() { - if (!this._cache_cellTypes) this._cache_cellTypes = this._getCellTypeDefinitions() - return this._cache_cellTypes + get cellTypeDefinitions() { + if (this._cache_cellTypes) return this._cache_cellTypes + const types = {} + // todo: add built in word types? + this.getChildrenByParser(cellTypeDefinitionParser).forEach(type => (types[type.cellTypeId] = type)) + this._cache_cellTypes = types + return types } getCellTypeDefinitionById(cellTypeId) { // todo: return unknownCellTypeDefinition? or is that handled somewhere else? - return this.getCellTypeDefinitions()[cellTypeId] + return this.cellTypeDefinitions[cellTypeId] } - getNodeTypeFamilyTree() { + get parserFamilyTree() { const tree = new TreeNode() - Object.values(this.getValidConcreteAndAbstractNodeTypeDefinitions()).forEach(node => { - const path = node.getAncestorNodeTypeIdsArray().join(" ") - tree.touchNode(path) - }) + Object.values(this.validConcreteAndAbstractParserDefinitions).forEach(node => tree.touchNode(node.ancestorParserIdsArray.join(" "))) return tree } - _getCellTypeDefinitions() { - const types = {} - // todo: add built in word types? - this.getChildrenByNodeConstructor(cellTypeDefinitionNode).forEach(type => (types[type.getCellTypeId()] = type)) - return types - } - getLanguageDefinitionProgram() { + get languageDefinitionProgram() { return this } - getValidConcreteAndAbstractNodeTypeDefinitions() { - return this.getChildrenByNodeConstructor(nodeTypeDefinitionNode).filter(node => node._hasValidNodeTypeId()) + get validConcreteAndAbstractParserDefinitions() { + return this.getChildrenByParser(parserDefinitionParser).filter(node => node._hasValidParserId()) } - _getLastRootNodeTypeDefinitionNode() { - return this.findLast(def => def instanceof AbstractGrammarDefinitionNode && def.has(GrammarConstants.root) && def._hasValidNodeTypeId()) + get lastRootParserDefinitionNode() { + return this.findLast(def => def instanceof AbstractParserDefinitionParser && def.has(GrammarConstants.root) && def._hasValidParserId()) } - _initRootNodeTypeDefinitionNode() { - if (this._cache_rootNodeTypeNode) return - if (!this._cache_rootNodeTypeNode) this._cache_rootNodeTypeNode = this._getLastRootNodeTypeDefinitionNode() + _initRootParserDefinitionNode() { + if (this._cache_rootParserNode) return + if (!this._cache_rootParserNode) this._cache_rootParserNode = this.lastRootParserDefinitionNode // By default, have a very permissive basic root node. // todo: whats the best design pattern to use for this sort of thing? - if (!this._cache_rootNodeTypeNode) { - this._cache_rootNodeTypeNode = this.concat(`${GrammarConstants.defaultRootNode} + if (!this._cache_rootParserNode) { + this._cache_rootParserNode = this.concat(`${GrammarConstants.DefaultRootParser} ${GrammarConstants.root} - ${GrammarConstants.catchAllNodeType} ${GrammarConstants.BlobNode}`)[0] - this._addDefaultCatchAllBlobNode() + ${GrammarConstants.catchAllParser} ${GrammarConstants.BlobParser}`)[0] + this._addDefaultCatchAllBlobParser() } } - getRootNodeTypeDefinitionNode() { - this._initRootNodeTypeDefinitionNode() - return this._cache_rootNodeTypeNode + get rootParserDefinition() { + this._initRootParserDefinitionNode() + return this._cache_rootParserNode } - // todo: whats the best design pattern to use for this sort of thing? - _addDefaultCatchAllBlobNode() { - delete this._cache_nodeTypeDefinitions - this.concat(`${GrammarConstants.BlobNode} - ${GrammarConstants.baseNodeType} ${GrammarConstants.blobNode}`) + _addDefaultCatchAllBlobParser() { + if (this._addedCatchAll) return + this._addedCatchAll = true + delete this._cache_parserDefinitionParsers + this.concat(`${GrammarConstants.BlobParser} + ${GrammarConstants.baseParser} ${GrammarConstants.blobParser}`) } - getExtensionName() { - return this.getGrammarName() + get extensionName() { + return this.grammarName } - _getId() { - return this.getRootNodeTypeId() - } - getRootNodeTypeId() { - return this.getRootNodeTypeDefinitionNode().getNodeTypeIdFromDefinition() + get id() { + return this.rootParserId } - getGrammarName() { - return this.getRootNodeTypeId().replace(HandGrammarProgram.nodeTypeSuffixRegex, "") + get rootParserId() { + return this.rootParserDefinition.parserIdFromDefinition } - _getMyInScopeNodeTypeIds() { - const nodeTypesNode = this.getRootNodeTypeDefinitionNode().getNode(GrammarConstants.inScope) - return nodeTypesNode ? nodeTypesNode.getWordsFrom(1) : [] + get grammarName() { + return this.rootParserId.replace(HandGrammarProgram.parserSuffixRegex, "") } - _getInScopeNodeTypeIds() { - const nodeTypesNode = this.getRootNodeTypeDefinitionNode().getNode(GrammarConstants.inScope) - return nodeTypesNode ? nodeTypesNode.getWordsFrom(1) : [] + _getMyInScopeParserIds() { + return super._getMyInScopeParserIds(this.rootParserDefinition) } - _initProgramNodeTypeDefinitionCache() { - if (this._cache_nodeTypeDefinitions) return undefined - this._cache_nodeTypeDefinitions = {} - this.getChildrenByNodeConstructor(nodeTypeDefinitionNode).forEach(nodeTypeDefinitionNode => { - this._cache_nodeTypeDefinitions[nodeTypeDefinitionNode.getNodeTypeIdFromDefinition()] = nodeTypeDefinitionNode - }) + _getInScopeParserIds() { + const parsersNode = this.rootParserDefinition.getNode(GrammarConstants.inScope) + return parsersNode ? parsersNode.getWordsFrom(1) : [] } - _getProgramNodeTypeDefinitionCache() { - this._initProgramNodeTypeDefinitionCache() - return this._cache_nodeTypeDefinitions + makeProgramParserDefinitionCache() { + const cache = {} + this.getChildrenByParser(parserDefinitionParser).forEach(parserDefinitionParser => (cache[parserDefinitionParser.parserIdFromDefinition] = parserDefinitionParser)) + return cache } - compileAndReturnRootConstructor() { - if (!this._cache_rootConstructorClass) { - const def = this.getRootNodeTypeDefinitionNode() - const rootNodeTypeId = def.getNodeTypeIdFromDefinition() - this._cache_rootConstructorClass = def.getLanguageDefinitionProgram()._compileAndReturnNodeTypeMap()[rootNodeTypeId] + compileAndReturnRootParser() { + if (!this._cached_rootParser) { + const rootDef = this.rootParserDefinition + this._cached_rootParser = rootDef.languageDefinitionProgram._compileAndReturnRootParser() } - return this._cache_rootConstructorClass + return this._cached_rootParser } - _getFileExtensions() { - return this.getRootNodeTypeDefinitionNode().get(GrammarConstants.extensions) - ? this.getRootNodeTypeDefinitionNode() - .get(GrammarConstants.extensions) - .split(" ") - .join(",") - : this.getExtensionName() + get fileExtensions() { + return this.rootParserDefinition.get(GrammarConstants.extensions) ? this.rootParserDefinition.get(GrammarConstants.extensions).split(" ").join(",") : this.extensionName } - toNodeJsJavascript(normalizedJtreePath = "jtree") { - return this._rootNodeDefToJavascriptClass(normalizedJtreePath, true).trim() + toNodeJsJavascript(jtreeProductsPath = "jtree/products") { + return this._rootNodeDefToJavascriptClass(jtreeProductsPath, true).trim() } toBrowserJavascript() { return this._rootNodeDefToJavascriptClass("", false).trim() } - _getProperName() { - return TreeUtils.ucfirst(this.getExtensionName()) - } - _rootNodeDefToJavascriptClass(normalizedJtreePath, forNodeJs = true) { - const defs = this.getValidConcreteAndAbstractNodeTypeDefinitions() + _rootNodeDefToJavascriptClass(jtreeProductsPath, forNodeJs = true) { + const defs = this.validConcreteAndAbstractParserDefinitions // todo: throw if there is no root node defined - const nodeTypeClasses = defs.map(def => def._nodeDefToJavascriptClass()).join("\n\n") - const rootDef = this.getRootNodeTypeDefinitionNode() + const parserClasses = defs.map(def => def.asJavascriptClass).join("\n\n") + const rootDef = this.rootParserDefinition const rootNodeJsHeader = forNodeJs && rootDef._getConcatBlockStringFromExtended(GrammarConstants._rootNodeJsHeader) - const rootName = rootDef._getGeneratedClassName() + const rootName = rootDef.generatedClassName if (!rootName) throw new Error(`Root Node Type Has No Name`) let exportScript = "" - if (forNodeJs) { + if (forNodeJs) exportScript = `module.exports = ${rootName}; ${rootName}` - } else { - exportScript = `window.${rootName} = ${rootName}` - } + else exportScript = `window.${rootName} = ${rootName}` + let nodeJsImports = `` + if (forNodeJs) + nodeJsImports = Object.keys(GlobalNamespaceAdditions) + .map(key => `const { ${key} } = require("${jtreeProductsPath}/${GlobalNamespaceAdditions[key]}")`) + .join("\n") // todo: we can expose the previous "constants" export, if needed, via the grammar, which we preserve. return `{ -${forNodeJs ? `const {jtree} = require("${normalizedJtreePath.replace(/\\/g, "\\\\")}")` : ""} +${nodeJsImports} ${rootNodeJsHeader ? rootNodeJsHeader : ""} -${nodeTypeClasses} +${parserClasses} ${exportScript} } ` } toSublimeSyntaxFile() { - const cellTypeDefs = this.getCellTypeDefinitions() + const cellTypeDefs = this.cellTypeDefinitions const variables = Object.keys(cellTypeDefs) - .map(name => ` ${name}: '${cellTypeDefs[name].getRegexString()}'`) + .map(name => ` ${name}: '${cellTypeDefs[name].regexString}'`) .join("\n") - const defs = this.getValidConcreteAndAbstractNodeTypeDefinitions().filter(kw => !kw._isAbstract()) - const nodeTypeContexts = defs.map(def => def._toSublimeMatchBlock()).join("\n\n") - const includes = defs.map(nodeTypeDef => ` - include: '${nodeTypeDef.getNodeTypeIdFromDefinition()}'`).join("\n") + const defs = this.validConcreteAndAbstractParserDefinitions.filter(kw => !kw._isAbstract()) + const parserContexts = defs.map(def => def._toSublimeMatchBlock()).join("\n\n") + const includes = defs.map(parserDef => ` - include: '${parserDef.parserIdFromDefinition}'`).join("\n") return `%YAML 1.2 --- -name: ${this.getExtensionName()} -file_extensions: [${this._getFileExtensions()}] -scope: source.${this.getExtensionName()} +name: ${this.extensionName} +file_extensions: [${this.fileExtensions}] +scope: source.${this.extensionName} variables: ${variables} @@ -15975,17 +16053,18 @@ contexts: main: ${includes} -${nodeTypeContexts}` +${parserContexts}` } } -HandGrammarProgram.makeNodeTypeId = str => TreeUtils._replaceNonAlphaNumericCharactersWithCharCodes(str).replace(HandGrammarProgram.nodeTypeSuffixRegex, "") + GrammarConstants.nodeTypeSuffix -HandGrammarProgram.makeCellTypeId = str => TreeUtils._replaceNonAlphaNumericCharactersWithCharCodes(str).replace(HandGrammarProgram.cellTypeSuffixRegex, "") + GrammarConstants.cellTypeSuffix -HandGrammarProgram.nodeTypeSuffixRegex = new RegExp(GrammarConstants.nodeTypeSuffix + "$") -HandGrammarProgram.nodeTypeFullRegex = new RegExp("^[a-zA-Z0-9_]+" + GrammarConstants.nodeTypeSuffix + "$") +HandGrammarProgram.makeParserId = str => Utils._replaceNonAlphaNumericCharactersWithCharCodes(str).replace(HandGrammarProgram.parserSuffixRegex, "") + GrammarConstants.parserSuffix +HandGrammarProgram.makeCellTypeId = str => Utils._replaceNonAlphaNumericCharactersWithCharCodes(str).replace(HandGrammarProgram.cellTypeSuffixRegex, "") + GrammarConstants.cellTypeSuffix +HandGrammarProgram.parserSuffixRegex = new RegExp(GrammarConstants.parserSuffix + "$") +HandGrammarProgram.parserFullRegex = new RegExp("^[a-zA-Z0-9_]+" + GrammarConstants.parserSuffix + "$") +HandGrammarProgram.blankLineRegex = new RegExp("^$") HandGrammarProgram.cellTypeSuffixRegex = new RegExp(GrammarConstants.cellTypeSuffix + "$") HandGrammarProgram.cellTypeFullRegex = new RegExp("^[a-zA-Z0-9_]+" + GrammarConstants.cellTypeSuffix + "$") HandGrammarProgram._languages = {} -HandGrammarProgram._nodeTypes = {} +HandGrammarProgram._parsers = {} const PreludeKinds = {} PreludeKinds[PreludeCellTypeIds.anyCell] = GrammarAnyCell PreludeKinds[PreludeCellTypeIds.keywordCell] = GrammarKeywordCell @@ -15994,55 +16073,15 @@ PreludeKinds[PreludeCellTypeIds.numberCell] = GrammarFloatCell PreludeKinds[PreludeCellTypeIds.bitCell] = GrammarBitCell PreludeKinds[PreludeCellTypeIds.boolCell] = GrammarBoolCell PreludeKinds[PreludeCellTypeIds.intCell] = GrammarIntCell -window.GrammarConstants = GrammarConstants -window.PreludeCellTypeIds = PreludeCellTypeIds -window.HandGrammarProgram = HandGrammarProgram -window.GrammarBackedNode = GrammarBackedNode -window.UnknownNodeTypeError = UnknownNodeTypeError -class Upgrader extends TreeNode { - upgradeManyInPlace(globPatterns, fromVersion, toVersion) { - this._upgradeMany(globPatterns, fromVersion, toVersion).forEach(file => file.tree.toDisk(file.path)) - return this - } - upgradeManyPreview(globPatterns, fromVersion, toVersion) { - return this._upgradeMany(globPatterns, fromVersion, toVersion) - } - _upgradeMany(globPatterns, fromVersion, toVersion) { - const glob = this.require("glob") - const files = TreeUtils.flatten(globPatterns.map(pattern => glob.sync(pattern))) - console.log(`${files.length} files to upgrade`) - return files.map(path => { - console.log("Upgrading " + path) - return { - tree: this.upgrade(TreeNode.fromDisk(path), fromVersion, toVersion), - path: path - } - }) - } - upgrade(code, fromVersion, toVersion) { - const updateFromMap = this.getUpgradeFromMap() - const semver = this.require("semver") - let fromMap - while ((fromMap = updateFromMap[fromVersion])) { - const toNextVersion = Object.keys(fromMap)[0] // todo: currently we just assume 1 step at a time - if (semver.lt(toVersion, toNextVersion)) break - const fn = Object.values(fromMap)[0] - code = fn(code) - fromVersion = toNextVersion - } - return code - } -} -window.Upgrader = Upgrader class UnknownGrammarProgram extends TreeNode { _inferRootNodeForAPrefixLanguage(grammarName) { - grammarName = HandGrammarProgram.makeNodeTypeId(grammarName) + grammarName = HandGrammarProgram.makeParserId(grammarName) const rootNode = new TreeNode(`${grammarName} ${GrammarConstants.root}`) - // note: right now we assume 1 global cellTypeMap and nodeTypeMap per grammar. But we may have scopes in the future? + // note: right now we assume 1 global cellTypeMap and parserMap per grammar. But we may have scopes in the future? const rootNodeNames = this.getFirstWords() .filter(identity => identity) - .map(word => HandGrammarProgram.makeNodeTypeId(word)) + .map(word => HandGrammarProgram.makeParserId(word)) rootNode .nodeAt(0) .touchNode(GrammarConstants.inScope) @@ -16052,33 +16091,31 @@ class UnknownGrammarProgram extends TreeNode { _renameIntegerKeywords(clone) { // todo: why are we doing this? for (let node of clone.getTopDownArrayIterator()) { - const firstWordIsAnInteger = !!node.getFirstWord().match(/^\d+$/) - const parentFirstWord = node.getParent().getFirstWord() - if (firstWordIsAnInteger && parentFirstWord) node.setFirstWord(HandGrammarProgram.makeNodeTypeId(parentFirstWord + UnknownGrammarProgram._childSuffix)) + const firstWordIsAnInteger = !!node.firstWord.match(/^\d+$/) + const parentFirstWord = node.parent.firstWord + if (firstWordIsAnInteger && parentFirstWord) node.setFirstWord(HandGrammarProgram.makeParserId(parentFirstWord + UnknownGrammarProgram._childSuffix)) } } _getKeywordMaps(clone) { const keywordsToChildKeywords = {} const keywordsToNodeInstances = {} for (let node of clone.getTopDownArrayIterator()) { - const firstWord = node.getFirstWord() + const firstWord = node.firstWord if (!keywordsToChildKeywords[firstWord]) keywordsToChildKeywords[firstWord] = {} if (!keywordsToNodeInstances[firstWord]) keywordsToNodeInstances[firstWord] = [] keywordsToNodeInstances[firstWord].push(node) - node.forEach(child => { - keywordsToChildKeywords[firstWord][child.getFirstWord()] = true - }) + node.forEach(child => (keywordsToChildKeywords[firstWord][child.firstWord] = true)) } return { keywordsToChildKeywords: keywordsToChildKeywords, keywordsToNodeInstances: keywordsToNodeInstances } } - _inferNodeTypeDef(firstWord, globalCellTypeMap, childFirstWords, instances) { - const edgeSymbol = this.getEdgeSymbol() - const nodeTypeId = HandGrammarProgram.makeNodeTypeId(firstWord) - const nodeDefNode = new TreeNode(nodeTypeId).nodeAt(0) - const childNodeTypeIds = childFirstWords.map(word => HandGrammarProgram.makeNodeTypeId(word)) - if (childNodeTypeIds.length) nodeDefNode.touchNode(GrammarConstants.inScope).setWordsFrom(1, childNodeTypeIds) + _inferParserDef(firstWord, globalCellTypeMap, childFirstWords, instances) { + const edgeSymbol = this.edgeSymbol + const parserId = HandGrammarProgram.makeParserId(firstWord) + const nodeDefNode = new TreeNode(parserId).nodeAt(0) + const childParserIds = childFirstWords.map(word => HandGrammarProgram.makeParserId(word)) + if (childParserIds.length) nodeDefNode.touchNode(GrammarConstants.inScope).setWordsFrom(1, childParserIds) const cellsForAllInstances = instances - .map(line => line.getContent()) + .map(line => line.content) .filter(identity => identity) .map(line => line.split(edgeSymbol)) const instanceCellCounts = new Set(cellsForAllInstances.map(cells => cells.length)) @@ -16087,7 +16124,12 @@ class UnknownGrammarProgram extends TreeNode { let catchAllCellType let cellTypeIds = [] for (let cellIndex = 0; cellIndex < maxCellsOnLine; cellIndex++) { - const cellType = this._getBestCellType(firstWord, instances.length, maxCellsOnLine, cellsForAllInstances.map(cells => cells[cellIndex])) + const cellType = this._getBestCellType( + firstWord, + instances.length, + maxCellsOnLine, + cellsForAllInstances.map(cells => cells[cellIndex]) + ) if (!globalCellTypeMap.has(cellType.cellTypeId)) globalCellTypeMap.set(cellType.cellTypeId, cellType.cellTypeDefinition) cellTypeIds.push(cellType.cellTypeId) } @@ -16098,7 +16140,7 @@ class UnknownGrammarProgram extends TreeNode { cellTypeIds.pop() } } - const needsCruxProperty = !firstWord.endsWith(UnknownGrammarProgram._childSuffix + "Node") // todo: cleanup + const needsCruxProperty = !firstWord.endsWith(UnknownGrammarProgram._childSuffix + GrammarConstants.parserSuffix) // todo: cleanup if (needsCruxProperty) nodeDefNode.set(GrammarConstants.crux, firstWord) if (catchAllCellType) nodeDefNode.set(GrammarConstants.catchAllCellType, catchAllCellType) const cellLine = cellTypeIds.slice() @@ -16106,14 +16148,14 @@ class UnknownGrammarProgram extends TreeNode { if (cellLine.length > 0) nodeDefNode.set(GrammarConstants.cells, cellLine.join(edgeSymbol)) //if (!catchAllCellType && cellTypeIds.length === 1) nodeDefNode.set(GrammarConstants.cells, cellTypeIds[0]) // Todo: add conditional frequencies - return nodeDefNode.getParent().toString() + return nodeDefNode.parent.toString() } // inferGrammarFileForAnSSVLanguage(grammarName: string): string { - // grammarName = HandGrammarProgram.makeNodeTypeId(grammarName) + // grammarName = HandGrammarProgram.makeParserId(grammarName) // const rootNode = new TreeNode(`${grammarName} // ${GrammarConstants.root}`) - // // note: right now we assume 1 global cellTypeMap and nodeTypeMap per grammar. But we may have scopes in the future? - // const rootNodeNames = this.getFirstWords().map(word => HandGrammarProgram.makeNodeTypeId(word)) + // // note: right now we assume 1 global cellTypeMap and parserMap per grammar. But we may have scopes in the future? + // const rootNodeNames = this.getFirstWords().map(word => HandGrammarProgram.makeParserId(word)) // rootNode // .nodeAt(0) // .touchNode(GrammarConstants.inScope) @@ -16126,25 +16168,25 @@ class UnknownGrammarProgram extends TreeNode { const { keywordsToChildKeywords, keywordsToNodeInstances } = this._getKeywordMaps(clone) const globalCellTypeMap = new Map() globalCellTypeMap.set(PreludeCellTypeIds.keywordCell, undefined) - const nodeTypeDefs = Object.keys(keywordsToChildKeywords) + const parserDefs = Object.keys(keywordsToChildKeywords) .filter(identity => identity) - .map(firstWord => this._inferNodeTypeDef(firstWord, globalCellTypeMap, Object.keys(keywordsToChildKeywords[firstWord]), keywordsToNodeInstances[firstWord])) + .map(firstWord => this._inferParserDef(firstWord, globalCellTypeMap, Object.keys(keywordsToChildKeywords[firstWord]), keywordsToNodeInstances[firstWord])) const cellTypeDefs = [] globalCellTypeMap.forEach((def, id) => cellTypeDefs.push(def ? def : id)) - const nodeBreakSymbol = this.getNodeBreakSymbol() - return this._formatCode([this._inferRootNodeForAPrefixLanguage(grammarName).toString(), cellTypeDefs.join(nodeBreakSymbol), nodeTypeDefs.join(nodeBreakSymbol)].filter(identity => identity).join("\n")) + const nodeBreakSymbol = this.nodeBreakSymbol + return this._formatCode([this._inferRootNodeForAPrefixLanguage(grammarName).toString(), cellTypeDefs.join(nodeBreakSymbol), parserDefs.join(nodeBreakSymbol)].filter(identity => identity).join("\n")) } _formatCode(code) { // todo: make this run in browser too if (!this.isNodeJs()) return code const grammarProgram = new HandGrammarProgram(TreeNode.fromDisk(__dirname + "/../langs/grammar/grammar.grammar")) - const programConstructor = grammarProgram.compileAndReturnRootConstructor() - const program = new programConstructor(code) + const rootParser = grammarProgram.compileAndReturnRootParser() + const program = new rootParser(code) return program.format().toString() } _getBestCellType(firstWord, instanceCount, maxCellsOnLine, allValues) { const asSet = new Set(allValues) - const edgeSymbol = this.getEdgeSymbol() + const edgeSymbol = this.edgeSymbol const values = Array.from(asSet).filter(identity => identity) const every = fn => { for (let index = 0; index < values.length; index++) { @@ -16177,10 +16219,18 @@ class UnknownGrammarProgram extends TreeNode { } } UnknownGrammarProgram._childSuffix = "Child" +window.GrammarConstants = GrammarConstants +window.PreludeCellTypeIds = PreludeCellTypeIds +window.HandGrammarProgram = HandGrammarProgram +window.GrammarBackedNode = GrammarBackedNode +window.UnknownParserError = UnknownParserError window.UnknownGrammarProgram = UnknownGrammarProgram + + +"use strict" // Adapted from https://github.com/NeekSandhu/codemirror-textmate/blob/master/src/tmToCm.ts var CmToken -;(function(CmToken) { +;(function (CmToken) { CmToken["Atom"] = "atom" CmToken["Attribute"] = "attribute" CmToken["Bracket"] = "bracket" @@ -16358,10 +16408,10 @@ const textMateScopeToCodeMirrorStyle = (scopeSegments, styleTree = tmToCm) => { const matchingBranch = styleTree[scopeSegments.shift()] return matchingBranch ? textMateScopeToCodeMirrorStyle(scopeSegments, matchingBranch) || matchingBranch.$ || null : null } -class TreeNotationCodeMirrorMode { - constructor(name, getProgramConstructorFn, getProgramCodeFn, codeMirrorLib = undefined) { +class GrammarCodeMirrorMode { + constructor(name, getRootParserFn, getProgramCodeFn, codeMirrorLib = undefined) { this._name = name - this._getProgramConstructorFn = getProgramConstructorFn + this._getRootParserFn = getRootParserFn this._getProgramCodeFn = getProgramCodeFn || (instance => (instance ? instance.getValue() : this._originalValue)) this._codeMirrorLib = codeMirrorLib } @@ -16369,48 +16419,48 @@ class TreeNotationCodeMirrorMode { const source = this._getProgramCodeFn(this._cmInstance) || "" if (!this._cachedProgram || this._cachedSource !== source) { this._cachedSource = source - this._cachedProgram = new (this._getProgramConstructorFn())(source) + this._cachedProgram = new (this._getRootParserFn())(source) } return this._cachedProgram } _getExcludedIntelliSenseTriggerKeys() { return { - "8": "backspace", - "9": "tab", - "13": "enter", - "16": "shift", - "17": "ctrl", - "18": "alt", - "19": "pause", - "20": "capslock", - "27": "escape", - "33": "pageup", - "34": "pagedown", - "35": "end", - "36": "home", - "37": "left", - "38": "up", - "39": "right", - "40": "down", - "45": "insert", - "46": "delete", - "91": "left window key", - "92": "right window key", - "93": "select", - "112": "f1", - "113": "f2", - "114": "f3", - "115": "f4", - "116": "f5", - "117": "f6", - "118": "f7", - "119": "f8", - "120": "f9", - "121": "f10", - "122": "f11", - "123": "f12", - "144": "numlock", - "145": "scrolllock" + 8: "backspace", + 9: "tab", + 13: "enter", + 16: "shift", + 17: "ctrl", + 18: "alt", + 19: "pause", + 20: "capslock", + 27: "escape", + 33: "pageup", + 34: "pagedown", + 35: "end", + 36: "home", + 37: "left", + 38: "up", + 39: "right", + 40: "down", + 45: "insert", + 46: "delete", + 91: "left window key", + 92: "right window key", + 93: "select", + 112: "f1", + 113: "f2", + 114: "f3", + 115: "f4", + 116: "f5", + 117: "f6", + 118: "f7", + 119: "f8", + 120: "f9", + 121: "f10", + 122: "f11", + 123: "f12", + 144: "numlock", + 145: "scrolllock" } } token(stream, state) { @@ -16513,150 +16563,139 @@ class TreeNotationCodeMirrorMode { state.cellIndex = 0 } } -window.TreeNotationCodeMirrorMode = TreeNotationCodeMirrorMode -class jtree {} -jtree.GrammarBackedNode = GrammarBackedNode -jtree.GrammarConstants = GrammarConstants -jtree.Utils = TreeUtils -jtree.UnknownNodeTypeError = UnknownNodeTypeError -jtree.TestRacer = TestRacer -jtree.TreeEvents = TreeEvents -jtree.TreeNode = TreeNode -jtree.ExtendibleTreeNode = ExtendibleTreeNode -jtree.HandGrammarProgram = HandGrammarProgram -jtree.UnknownGrammarProgram = UnknownGrammarProgram -jtree.TreeNotationCodeMirrorMode = TreeNotationCodeMirrorMode -jtree.getVersion = () => TreeNode.getVersion() -window.jtree = jtree +window.GrammarCodeMirrorMode = GrammarCodeMirrorMode { - class stumpNode extends jtree.GrammarBackedNode { - createParser() { - return new jtree.TreeNode.Parser( - errorNode, - Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { - blockquote: htmlTagNode, - colgroup: htmlTagNode, - datalist: htmlTagNode, - fieldset: htmlTagNode, - menuitem: htmlTagNode, - noscript: htmlTagNode, - optgroup: htmlTagNode, - progress: htmlTagNode, - styleTag: htmlTagNode, - template: htmlTagNode, - textarea: htmlTagNode, - titleTag: htmlTagNode, - address: htmlTagNode, - article: htmlTagNode, - caption: htmlTagNode, - details: htmlTagNode, - section: htmlTagNode, - summary: htmlTagNode, - button: htmlTagNode, - canvas: htmlTagNode, - dialog: htmlTagNode, - figure: htmlTagNode, - footer: htmlTagNode, - header: htmlTagNode, - hgroup: htmlTagNode, - iframe: htmlTagNode, - keygen: htmlTagNode, - legend: htmlTagNode, - object: htmlTagNode, - option: htmlTagNode, - output: htmlTagNode, - script: htmlTagNode, - select: htmlTagNode, - source: htmlTagNode, - strong: htmlTagNode, - aside: htmlTagNode, - embed: htmlTagNode, - input: htmlTagNode, - label: htmlTagNode, - meter: htmlTagNode, - param: htmlTagNode, - small: htmlTagNode, - table: htmlTagNode, - tbody: htmlTagNode, - tfoot: htmlTagNode, - thead: htmlTagNode, - track: htmlTagNode, - video: htmlTagNode, - abbr: htmlTagNode, - area: htmlTagNode, - base: htmlTagNode, - body: htmlTagNode, - code: htmlTagNode, - form: htmlTagNode, - head: htmlTagNode, - html: htmlTagNode, - link: htmlTagNode, - main: htmlTagNode, - mark: htmlTagNode, - menu: htmlTagNode, - meta: htmlTagNode, - ruby: htmlTagNode, - samp: htmlTagNode, - span: htmlTagNode, - time: htmlTagNode, - bdi: htmlTagNode, - bdo: htmlTagNode, - col: htmlTagNode, - del: htmlTagNode, - dfn: htmlTagNode, - div: htmlTagNode, - img: htmlTagNode, - ins: htmlTagNode, - kbd: htmlTagNode, - map: htmlTagNode, - nav: htmlTagNode, - pre: htmlTagNode, - rtc: htmlTagNode, - sub: htmlTagNode, - sup: htmlTagNode, - var: htmlTagNode, - wbr: htmlTagNode, - br: htmlTagNode, - dd: htmlTagNode, - dl: htmlTagNode, - dt: htmlTagNode, - em: htmlTagNode, - h1: htmlTagNode, - h2: htmlTagNode, - h3: htmlTagNode, - h4: htmlTagNode, - h5: htmlTagNode, - h6: htmlTagNode, - hr: htmlTagNode, - li: htmlTagNode, - ol: htmlTagNode, - rb: htmlTagNode, - rp: htmlTagNode, - rt: htmlTagNode, - td: htmlTagNode, - th: htmlTagNode, - tr: htmlTagNode, - ul: htmlTagNode, - a: htmlTagNode, - b: htmlTagNode, - i: htmlTagNode, - p: htmlTagNode, - q: htmlTagNode, - s: htmlTagNode, - u: htmlTagNode + class stumpParser extends GrammarBackedNode { + createParserCombinator() { + return new TreeNode.ParserCombinator( + errorParser, + Object.assign(Object.assign({}, super.createParserCombinator()._getFirstWordMapAsObject()), { + blockquote: htmlTagParser, + colgroup: htmlTagParser, + datalist: htmlTagParser, + fieldset: htmlTagParser, + menuitem: htmlTagParser, + noscript: htmlTagParser, + optgroup: htmlTagParser, + progress: htmlTagParser, + styleTag: htmlTagParser, + template: htmlTagParser, + textarea: htmlTagParser, + titleTag: htmlTagParser, + address: htmlTagParser, + article: htmlTagParser, + caption: htmlTagParser, + details: htmlTagParser, + section: htmlTagParser, + summary: htmlTagParser, + button: htmlTagParser, + canvas: htmlTagParser, + dialog: htmlTagParser, + figure: htmlTagParser, + footer: htmlTagParser, + header: htmlTagParser, + hgroup: htmlTagParser, + iframe: htmlTagParser, + keygen: htmlTagParser, + legend: htmlTagParser, + object: htmlTagParser, + option: htmlTagParser, + output: htmlTagParser, + script: htmlTagParser, + select: htmlTagParser, + source: htmlTagParser, + strong: htmlTagParser, + aside: htmlTagParser, + embed: htmlTagParser, + input: htmlTagParser, + label: htmlTagParser, + meter: htmlTagParser, + param: htmlTagParser, + small: htmlTagParser, + table: htmlTagParser, + tbody: htmlTagParser, + tfoot: htmlTagParser, + thead: htmlTagParser, + track: htmlTagParser, + video: htmlTagParser, + abbr: htmlTagParser, + area: htmlTagParser, + base: htmlTagParser, + body: htmlTagParser, + code: htmlTagParser, + form: htmlTagParser, + head: htmlTagParser, + html: htmlTagParser, + link: htmlTagParser, + main: htmlTagParser, + mark: htmlTagParser, + menu: htmlTagParser, + meta: htmlTagParser, + ruby: htmlTagParser, + samp: htmlTagParser, + span: htmlTagParser, + time: htmlTagParser, + bdi: htmlTagParser, + bdo: htmlTagParser, + col: htmlTagParser, + del: htmlTagParser, + dfn: htmlTagParser, + div: htmlTagParser, + img: htmlTagParser, + ins: htmlTagParser, + kbd: htmlTagParser, + map: htmlTagParser, + nav: htmlTagParser, + pre: htmlTagParser, + rtc: htmlTagParser, + sub: htmlTagParser, + sup: htmlTagParser, + var: htmlTagParser, + wbr: htmlTagParser, + br: htmlTagParser, + dd: htmlTagParser, + dl: htmlTagParser, + dt: htmlTagParser, + em: htmlTagParser, + h1: htmlTagParser, + h2: htmlTagParser, + h3: htmlTagParser, + h4: htmlTagParser, + h5: htmlTagParser, + h6: htmlTagParser, + hr: htmlTagParser, + li: htmlTagParser, + ol: htmlTagParser, + rb: htmlTagParser, + rp: htmlTagParser, + rt: htmlTagParser, + td: htmlTagParser, + th: htmlTagParser, + tr: htmlTagParser, + ul: htmlTagParser, + a: htmlTagParser, + b: htmlTagParser, + i: htmlTagParser, + p: htmlTagParser, + q: htmlTagParser, + s: htmlTagParser, + u: htmlTagParser, }), - [{ regex: /^$/, nodeConstructor: blankLineNode }, { regex: /^[a-zA-Z0-9_]+Component/, nodeConstructor: componentDefinitionNode }] + [ + { regex: /^$/, parser: blankLineParser }, + { regex: /^[a-zA-Z0-9_]+Component/, parser: componentDefinitionParser }, + ] ) } compile() { - return this.toHtml() + return this.asHtml } _getHtmlJoinByCharacter() { return "" } - static cachedHandGrammarProgramRoot = new jtree.HandGrammarProgram(`tooling onsave jtree build produceLang stump + static cachedHandGrammarProgramRoot = new HandGrammarProgram(`// Cell parsers anyCell keywordCell emptyCell @@ -16680,23 +16719,25 @@ htmlAttributeNameCell bernKeywordCell enum bern extends keywordCell -stumpNode + +// Line parsers +stumpParser root description A prefix Tree Language that compiles to HTML. - catchAllNodeType errorNode - inScope htmlTagNode blankLineNode + catchAllParser errorParser + inScope htmlTagParser blankLineParser example div h1 hello world compilesTo html javascript compile() { - return this.toHtml() + return this.asHtml } _getHtmlJoinByCharacter() { return "" } -blankLineNode +blankLineParser pattern ^$ tags doNotSynthesize cells emptyCell @@ -16705,15 +16746,15 @@ blankLineNode return "" } getTextContent() {return ""} -htmlTagNode - inScope bernNode htmlTagNode htmlAttributeNode blankLineNode +htmlTagParser + inScope bernParser htmlTagParser htmlAttributeParser blankLineParser catchAllCellType anyHtmlContentCell cells htmlTagNameCell javascript - isHtmlTagNode = true + isHtmlTagParser = true getTag() { // we need to remove the "Tag" bit to handle the style and title attribute/tag conflict. - const firstWord = this.getFirstWord() + const firstWord = this.firstWord const map = { titleTag: "title", styleTag: "style" @@ -16723,7 +16764,7 @@ htmlTagNode _getHtmlJoinByCharacter() { return "" } - toHtmlWithSuids() { + asHtmlWithSuids() { return this._toHtml(undefined, true) } _getOneLiner() { @@ -16739,25 +16780,25 @@ htmlTagNode get domElement() { var elem = document.createElement(this.getTag()) elem.setAttribute("stumpUid", this._getUid()) - this.filter(node => node.isAttributeNode) - .forEach(child => elem.setAttribute(child.getFirstWord(), child.getContent())) + this.filter(node => node.isAttributeParser) + .forEach(child => elem.setAttribute(child.firstWord, child.content)) elem.innerHTML = this.has("bern") ? this.getNode("bern").childrenToString() : this._getOneLiner() - this.filter(node => node.isHtmlTagNode) + this.filter(node => node.isHtmlTagParser) .forEach(child => elem.appendChild(child.domElement)) return elem } _toHtml(indentCount, withSuid) { const tag = this.getTag() const children = this.map(child => child._toHtml(indentCount + 1, withSuid)).join("") - const attributesStr = this.filter(node => node.isAttributeNode) + const attributesStr = this.filter(node => node.isAttributeParser) .map(child => child.getAttribute()) .join("") const indent = " ".repeat(indentCount) const collapse = this.shouldCollapse() - const indentForChildNodes = !collapse && this.getChildInstancesOfNodeTypeId("htmlTagNode").length > 0 + const indentForChildParsers = !collapse && this.getChildInstancesOfParserId("htmlTagParser").length > 0 const suid = withSuid ? \` stumpUid="\${this._getUid()}"\` : "" const oneLiner = this._getOneLiner() - return \`\${!collapse ? indent : ""}<\${tag}\${attributesStr}\${suid}>\${oneLiner}\${indentForChildNodes ? "\\n" : ""}\${children}</\${tag}>\${collapse ? "" : "\\n"}\` + return \`\${!collapse ? indent : ""}<\${tag}\${attributesStr}\${suid}>\${oneLiner}\${indentForChildParsers ? "\\n" : ""}\${children}</\${tag}>\${collapse ? "" : "\\n"}\` } removeCssStumpNode() { return this.removeStumpNode() @@ -16767,31 +16808,31 @@ htmlTagNode return this.destroy() } getNodeByGuid(guid) { - return this.getTopDownArray().find(node => node._getUid() === guid) + return this.topDownArray.find(node => node._getUid() === guid) } addClassToStumpNode(className) { - const classNode = this.touchNode("class") - const words = classNode.getWordsFrom(1) + const classParser = this.touchNode("class") + const words = classParser.getWordsFrom(1) // note: we call add on shadow regardless, because at the moment stump may have gotten out of // sync with shadow, if things modified the dom. todo: cleanup. this.getShadow().addClassToShadow(className) if (words.includes(className)) return this words.push(className) - classNode.setContent(words.join(this.getWordBreakSymbol())) + classParser.setContent(words.join(this.wordBreakSymbol)) return this } removeClassFromStumpNode(className) { - const classNode = this.getNode("class") - if (!classNode) return this - const newClasses = classNode.getWords().filter(word => word !== className) - if (!newClasses.length) classNode.destroy() - else classNode.setContent(newClasses.join(" ")) + const classParser = this.getNode("class") + if (!classParser) return this + const newClasses = classParser.words.filter(word => word !== className) + if (!newClasses.length) classParser.destroy() + else classParser.setContent(newClasses.join(" ")) this.getShadow().removeClassFromShadow(className) return this } stumpNodeHasClass(className) { - const classNode = this.getNode("class") - return classNode && classNode.getWords().includes(className) ? true : false + const classParser = this.getNode("class") + return classParser && classParser.words.includes(className) ? true : false } isStumpNodeCheckbox() { return this.get("type") === "checkbox" @@ -16807,10 +16848,10 @@ htmlTagNode return this.insertChildNode(text, index) } insertChildNode(text, index) { - const singleNode = new jtree.TreeNode(text).getChildren()[0] + const singleNode = new TreeNode(text).getChildren()[0] const newNode = this.insertLineAndChildren(singleNode.getLine(), singleNode.childrenToString(), index) - const stumpNodeIndex = this.filter(node => node.isHtmlTagNode).indexOf(newNode) - this.getShadow().insertHtmlNode(newNode, stumpNodeIndex) + const stumpParserIndex = this.filter(node => node.isHtmlTagParser).indexOf(newNode) + this.getShadow().insertHtmlNode(newNode, stumpParserIndex) return newNode } isInputType() { @@ -16820,7 +16861,7 @@ htmlTagNode return this.findStumpNodesByChild(line)[0] } findStumpNodeByChildString(line) { - return this.getTopDownArray().find(node => + return this.topDownArray.find(node => node .map(child => child.getLine()) .join("\\n") @@ -16831,31 +16872,31 @@ htmlTagNode return this._findStumpNodesByBase(firstWord)[0] } _findStumpNodesByBase(firstWord) { - return this.getTopDownArray().filter(node => node.doesExtend("htmlTagNode") && node.getFirstWord() === firstWord) + return this.topDownArray.filter(node => node.doesExtend("htmlTagParser") && node.firstWord === firstWord) } hasLine(line) { return this.getChildren().some(node => node.getLine() === line) } findStumpNodesByChild(line) { - return this.getTopDownArray().filter(node => node.doesExtend("htmlTagNode") && node.hasLine(line)) + return this.topDownArray.filter(node => node.doesExtend("htmlTagParser") && node.hasLine(line)) } findStumpNodesWithClass(className) { - return this.getTopDownArray().filter( + return this.topDownArray.filter( node => - node.doesExtend("htmlTagNode") && + node.doesExtend("htmlTagParser") && node.has("class") && node .getNode("class") - .getWords() + .words .includes(className) ) } getShadowClass() { - return this.getParent().getShadowClass() + return this.parent.getShadowClass() } // todo: should not be here getStumpNodeTreeComponent() { - return this._treeComponent || this.getParent().getStumpNodeTreeComponent() + return this._treeComponent || this.parent.getStumpNodeTreeComponent() } // todo: should not be here setStumpNodeTreeComponent(treeComponent) { @@ -16872,76 +16913,64 @@ htmlTagNode // todo return this } - toHtml() { + get asHtml() { return this._toHtml() } -errorNode - baseNodeType errorNode -componentDefinitionNode - extends htmlTagNode +errorParser + baseParser errorParser +componentDefinitionParser + extends htmlTagParser pattern ^[a-zA-Z0-9_]+Component cells componentTagNameCell javascript getTag() { return "div" } -htmlAttributeNode +htmlAttributeParser javascript _toHtml() { return "" } getTextContent() {return ""} getAttribute() { - return \` \${this.getFirstWord()}="\${this.getContent()}"\` + return \` \${this.firstWord}="\${this.content}"\` } - boolean isAttributeNode true + boolean isAttributeParser true boolean isTileAttribute true - catchAllNodeType errorNode + catchAllParser errorParser catchAllCellType attributeValueCell cells htmlAttributeNameCell stumpExtendedAttributeNameCell extends htmlAttributeNameCell enum collapse blurCommand changeCommand clickCommand contextMenuCommand doubleClickCommand keyUpCommand lineClickCommand lineShiftClickCommand shiftClickCommand -stumpExtendedAttributeNode - description Node types not present in HTML but included in stump. - extends htmlAttributeNode +stumpExtendedAttributeParser + description Parser types not present in HTML but included in stump. + extends htmlAttributeParser cells stumpExtendedAttributeNameCell -lineOfHtmlContentNode +lineOfHtmlContentParser boolean isTileAttribute true - catchAllNodeType lineOfHtmlContentNode + catchAllParser lineOfHtmlContentParser catchAllCellType anyHtmlContentCell javascript getTextContent() {return this.getLine()} -bernNode +bernParser boolean isTileAttribute true - todo Rename this node type + // todo Rename this node type description This is a node where you can put any HTML content. It is called "bern" until someone comes up with a better name. - catchAllNodeType lineOfHtmlContentNode + catchAllParser lineOfHtmlContentParser javascript _toHtml() { return this.childrenToString() } getTextContent() {return ""} cells bernKeywordCell`) - getHandGrammarProgram() { + get handGrammarProgram() { return this.constructor.cachedHandGrammarProgramRoot } - static getNodeTypeMap() { - return { - stumpNode: stumpNode, - blankLineNode: blankLineNode, - htmlTagNode: htmlTagNode, - errorNode: errorNode, - componentDefinitionNode: componentDefinitionNode, - htmlAttributeNode: htmlAttributeNode, - stumpExtendedAttributeNode: stumpExtendedAttributeNode, - lineOfHtmlContentNode: lineOfHtmlContentNode, - bernNode: bernNode - } - } + static rootParser = stumpParser } - class blankLineNode extends jtree.GrammarBackedNode { + class blankLineParser extends GrammarBackedNode { get emptyCell() { return this.getWord(0) } @@ -16953,297 +16982,300 @@ bernNode } } - class htmlTagNode extends jtree.GrammarBackedNode { - createParser() { - return new jtree.TreeNode.Parser( + class htmlTagParser extends GrammarBackedNode { + createParserCombinator() { + return new TreeNode.ParserCombinator( undefined, - Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { - blockquote: htmlTagNode, - colgroup: htmlTagNode, - datalist: htmlTagNode, - fieldset: htmlTagNode, - menuitem: htmlTagNode, - noscript: htmlTagNode, - optgroup: htmlTagNode, - progress: htmlTagNode, - styleTag: htmlTagNode, - template: htmlTagNode, - textarea: htmlTagNode, - titleTag: htmlTagNode, - address: htmlTagNode, - article: htmlTagNode, - caption: htmlTagNode, - details: htmlTagNode, - section: htmlTagNode, - summary: htmlTagNode, - button: htmlTagNode, - canvas: htmlTagNode, - dialog: htmlTagNode, - figure: htmlTagNode, - footer: htmlTagNode, - header: htmlTagNode, - hgroup: htmlTagNode, - iframe: htmlTagNode, - keygen: htmlTagNode, - legend: htmlTagNode, - object: htmlTagNode, - option: htmlTagNode, - output: htmlTagNode, - script: htmlTagNode, - select: htmlTagNode, - source: htmlTagNode, - strong: htmlTagNode, - aside: htmlTagNode, - embed: htmlTagNode, - input: htmlTagNode, - label: htmlTagNode, - meter: htmlTagNode, - param: htmlTagNode, - small: htmlTagNode, - table: htmlTagNode, - tbody: htmlTagNode, - tfoot: htmlTagNode, - thead: htmlTagNode, - track: htmlTagNode, - video: htmlTagNode, - abbr: htmlTagNode, - area: htmlTagNode, - base: htmlTagNode, - body: htmlTagNode, - code: htmlTagNode, - form: htmlTagNode, - head: htmlTagNode, - html: htmlTagNode, - link: htmlTagNode, - main: htmlTagNode, - mark: htmlTagNode, - menu: htmlTagNode, - meta: htmlTagNode, - ruby: htmlTagNode, - samp: htmlTagNode, - span: htmlTagNode, - time: htmlTagNode, - bdi: htmlTagNode, - bdo: htmlTagNode, - col: htmlTagNode, - del: htmlTagNode, - dfn: htmlTagNode, - div: htmlTagNode, - img: htmlTagNode, - ins: htmlTagNode, - kbd: htmlTagNode, - map: htmlTagNode, - nav: htmlTagNode, - pre: htmlTagNode, - rtc: htmlTagNode, - sub: htmlTagNode, - sup: htmlTagNode, - var: htmlTagNode, - wbr: htmlTagNode, - br: htmlTagNode, - dd: htmlTagNode, - dl: htmlTagNode, - dt: htmlTagNode, - em: htmlTagNode, - h1: htmlTagNode, - h2: htmlTagNode, - h3: htmlTagNode, - h4: htmlTagNode, - h5: htmlTagNode, - h6: htmlTagNode, - hr: htmlTagNode, - li: htmlTagNode, - ol: htmlTagNode, - rb: htmlTagNode, - rp: htmlTagNode, - rt: htmlTagNode, - td: htmlTagNode, - th: htmlTagNode, - tr: htmlTagNode, - ul: htmlTagNode, - a: htmlTagNode, - b: htmlTagNode, - i: htmlTagNode, - p: htmlTagNode, - q: htmlTagNode, - s: htmlTagNode, - u: htmlTagNode, - oncanplaythrough: htmlAttributeNode, - ondurationchange: htmlAttributeNode, - onloadedmetadata: htmlAttributeNode, - contenteditable: htmlAttributeNode, - "accept-charset": htmlAttributeNode, - onbeforeunload: htmlAttributeNode, - onvolumechange: htmlAttributeNode, - onbeforeprint: htmlAttributeNode, - oncontextmenu: htmlAttributeNode, - autocomplete: htmlAttributeNode, - onafterprint: htmlAttributeNode, - onhashchange: htmlAttributeNode, - onloadeddata: htmlAttributeNode, - onmousewheel: htmlAttributeNode, - onratechange: htmlAttributeNode, - ontimeupdate: htmlAttributeNode, - oncuechange: htmlAttributeNode, - ondragenter: htmlAttributeNode, - ondragleave: htmlAttributeNode, - ondragstart: htmlAttributeNode, - onloadstart: htmlAttributeNode, - onmousedown: htmlAttributeNode, - onmousemove: htmlAttributeNode, - onmouseover: htmlAttributeNode, - placeholder: htmlAttributeNode, - formaction: htmlAttributeNode, - "http-equiv": htmlAttributeNode, - novalidate: htmlAttributeNode, - ondblclick: htmlAttributeNode, - ondragover: htmlAttributeNode, - onkeypress: htmlAttributeNode, - onmouseout: htmlAttributeNode, - onpagehide: htmlAttributeNode, - onpageshow: htmlAttributeNode, - onpopstate: htmlAttributeNode, - onprogress: htmlAttributeNode, - spellcheck: htmlAttributeNode, - accesskey: htmlAttributeNode, - autofocus: htmlAttributeNode, - draggable: htmlAttributeNode, - maxlength: htmlAttributeNode, - oncanplay: htmlAttributeNode, - ondragend: htmlAttributeNode, - onemptied: htmlAttributeNode, - oninvalid: htmlAttributeNode, - onkeydown: htmlAttributeNode, - onmouseup: htmlAttributeNode, - onoffline: htmlAttributeNode, - onplaying: htmlAttributeNode, - onseeking: htmlAttributeNode, - onstalled: htmlAttributeNode, - onstorage: htmlAttributeNode, - onsuspend: htmlAttributeNode, - onwaiting: htmlAttributeNode, - translate: htmlAttributeNode, - autoplay: htmlAttributeNode, - controls: htmlAttributeNode, - datetime: htmlAttributeNode, - disabled: htmlAttributeNode, - download: htmlAttributeNode, - dropzone: htmlAttributeNode, - hreflang: htmlAttributeNode, - multiple: htmlAttributeNode, - onchange: htmlAttributeNode, - ononline: htmlAttributeNode, - onresize: htmlAttributeNode, - onscroll: htmlAttributeNode, - onsearch: htmlAttributeNode, - onseeked: htmlAttributeNode, - onselect: htmlAttributeNode, - onsubmit: htmlAttributeNode, - ontoggle: htmlAttributeNode, - onunload: htmlAttributeNode, - property: htmlAttributeNode, - readonly: htmlAttributeNode, - required: htmlAttributeNode, - reversed: htmlAttributeNode, - selected: htmlAttributeNode, - tabindex: htmlAttributeNode, - bgcolor: htmlAttributeNode, - charset: htmlAttributeNode, - checked: htmlAttributeNode, - colspan: htmlAttributeNode, - content: htmlAttributeNode, - default: htmlAttributeNode, - dirname: htmlAttributeNode, - enctype: htmlAttributeNode, - headers: htmlAttributeNode, - onabort: htmlAttributeNode, - onclick: htmlAttributeNode, - onended: htmlAttributeNode, - onerror: htmlAttributeNode, - onfocus: htmlAttributeNode, - oninput: htmlAttributeNode, - onkeyup: htmlAttributeNode, - onpaste: htmlAttributeNode, - onpause: htmlAttributeNode, - onreset: htmlAttributeNode, - onwheel: htmlAttributeNode, - optimum: htmlAttributeNode, - pattern: htmlAttributeNode, - preload: htmlAttributeNode, - rowspan: htmlAttributeNode, - sandbox: htmlAttributeNode, - srclang: htmlAttributeNode, - accept: htmlAttributeNode, - action: htmlAttributeNode, - border: htmlAttributeNode, - coords: htmlAttributeNode, - height: htmlAttributeNode, - hidden: htmlAttributeNode, - method: htmlAttributeNode, - onblur: htmlAttributeNode, - oncopy: htmlAttributeNode, - ondrag: htmlAttributeNode, - ondrop: htmlAttributeNode, - onload: htmlAttributeNode, - onplay: htmlAttributeNode, - poster: htmlAttributeNode, - srcdoc: htmlAttributeNode, - srcset: htmlAttributeNode, - target: htmlAttributeNode, - usemap: htmlAttributeNode, - align: htmlAttributeNode, - async: htmlAttributeNode, - class: htmlAttributeNode, - color: htmlAttributeNode, - defer: htmlAttributeNode, - ismap: htmlAttributeNode, - media: htmlAttributeNode, - muted: htmlAttributeNode, - oncut: htmlAttributeNode, - scope: htmlAttributeNode, - shape: htmlAttributeNode, - sizes: htmlAttributeNode, - start: htmlAttributeNode, - style: htmlAttributeNode, - title: htmlAttributeNode, - value: htmlAttributeNode, - width: htmlAttributeNode, - cols: htmlAttributeNode, - high: htmlAttributeNode, - href: htmlAttributeNode, - kind: htmlAttributeNode, - lang: htmlAttributeNode, - list: htmlAttributeNode, - loop: htmlAttributeNode, - name: htmlAttributeNode, - open: htmlAttributeNode, - rows: htmlAttributeNode, - size: htmlAttributeNode, - step: htmlAttributeNode, - type: htmlAttributeNode, - wrap: htmlAttributeNode, - alt: htmlAttributeNode, - dir: htmlAttributeNode, - for: htmlAttributeNode, - low: htmlAttributeNode, - max: htmlAttributeNode, - min: htmlAttributeNode, - rel: htmlAttributeNode, - src: htmlAttributeNode, - id: htmlAttributeNode, - lineShiftClickCommand: stumpExtendedAttributeNode, - contextMenuCommand: stumpExtendedAttributeNode, - doubleClickCommand: stumpExtendedAttributeNode, - shiftClickCommand: stumpExtendedAttributeNode, - lineClickCommand: stumpExtendedAttributeNode, - changeCommand: stumpExtendedAttributeNode, - clickCommand: stumpExtendedAttributeNode, - keyUpCommand: stumpExtendedAttributeNode, - blurCommand: stumpExtendedAttributeNode, - collapse: stumpExtendedAttributeNode, - bern: bernNode + Object.assign(Object.assign({}, super.createParserCombinator()._getFirstWordMapAsObject()), { + blockquote: htmlTagParser, + colgroup: htmlTagParser, + datalist: htmlTagParser, + fieldset: htmlTagParser, + menuitem: htmlTagParser, + noscript: htmlTagParser, + optgroup: htmlTagParser, + progress: htmlTagParser, + styleTag: htmlTagParser, + template: htmlTagParser, + textarea: htmlTagParser, + titleTag: htmlTagParser, + address: htmlTagParser, + article: htmlTagParser, + caption: htmlTagParser, + details: htmlTagParser, + section: htmlTagParser, + summary: htmlTagParser, + button: htmlTagParser, + canvas: htmlTagParser, + dialog: htmlTagParser, + figure: htmlTagParser, + footer: htmlTagParser, + header: htmlTagParser, + hgroup: htmlTagParser, + iframe: htmlTagParser, + keygen: htmlTagParser, + legend: htmlTagParser, + object: htmlTagParser, + option: htmlTagParser, + output: htmlTagParser, + script: htmlTagParser, + select: htmlTagParser, + source: htmlTagParser, + strong: htmlTagParser, + aside: htmlTagParser, + embed: htmlTagParser, + input: htmlTagParser, + label: htmlTagParser, + meter: htmlTagParser, + param: htmlTagParser, + small: htmlTagParser, + table: htmlTagParser, + tbody: htmlTagParser, + tfoot: htmlTagParser, + thead: htmlTagParser, + track: htmlTagParser, + video: htmlTagParser, + abbr: htmlTagParser, + area: htmlTagParser, + base: htmlTagParser, + body: htmlTagParser, + code: htmlTagParser, + form: htmlTagParser, + head: htmlTagParser, + html: htmlTagParser, + link: htmlTagParser, + main: htmlTagParser, + mark: htmlTagParser, + menu: htmlTagParser, + meta: htmlTagParser, + ruby: htmlTagParser, + samp: htmlTagParser, + span: htmlTagParser, + time: htmlTagParser, + bdi: htmlTagParser, + bdo: htmlTagParser, + col: htmlTagParser, + del: htmlTagParser, + dfn: htmlTagParser, + div: htmlTagParser, + img: htmlTagParser, + ins: htmlTagParser, + kbd: htmlTagParser, + map: htmlTagParser, + nav: htmlTagParser, + pre: htmlTagParser, + rtc: htmlTagParser, + sub: htmlTagParser, + sup: htmlTagParser, + var: htmlTagParser, + wbr: htmlTagParser, + br: htmlTagParser, + dd: htmlTagParser, + dl: htmlTagParser, + dt: htmlTagParser, + em: htmlTagParser, + h1: htmlTagParser, + h2: htmlTagParser, + h3: htmlTagParser, + h4: htmlTagParser, + h5: htmlTagParser, + h6: htmlTagParser, + hr: htmlTagParser, + li: htmlTagParser, + ol: htmlTagParser, + rb: htmlTagParser, + rp: htmlTagParser, + rt: htmlTagParser, + td: htmlTagParser, + th: htmlTagParser, + tr: htmlTagParser, + ul: htmlTagParser, + a: htmlTagParser, + b: htmlTagParser, + i: htmlTagParser, + p: htmlTagParser, + q: htmlTagParser, + s: htmlTagParser, + u: htmlTagParser, + oncanplaythrough: htmlAttributeParser, + ondurationchange: htmlAttributeParser, + onloadedmetadata: htmlAttributeParser, + contenteditable: htmlAttributeParser, + "accept-charset": htmlAttributeParser, + onbeforeunload: htmlAttributeParser, + onvolumechange: htmlAttributeParser, + onbeforeprint: htmlAttributeParser, + oncontextmenu: htmlAttributeParser, + autocomplete: htmlAttributeParser, + onafterprint: htmlAttributeParser, + onhashchange: htmlAttributeParser, + onloadeddata: htmlAttributeParser, + onmousewheel: htmlAttributeParser, + onratechange: htmlAttributeParser, + ontimeupdate: htmlAttributeParser, + oncuechange: htmlAttributeParser, + ondragenter: htmlAttributeParser, + ondragleave: htmlAttributeParser, + ondragstart: htmlAttributeParser, + onloadstart: htmlAttributeParser, + onmousedown: htmlAttributeParser, + onmousemove: htmlAttributeParser, + onmouseover: htmlAttributeParser, + placeholder: htmlAttributeParser, + formaction: htmlAttributeParser, + "http-equiv": htmlAttributeParser, + novalidate: htmlAttributeParser, + ondblclick: htmlAttributeParser, + ondragover: htmlAttributeParser, + onkeypress: htmlAttributeParser, + onmouseout: htmlAttributeParser, + onpagehide: htmlAttributeParser, + onpageshow: htmlAttributeParser, + onpopstate: htmlAttributeParser, + onprogress: htmlAttributeParser, + spellcheck: htmlAttributeParser, + accesskey: htmlAttributeParser, + autofocus: htmlAttributeParser, + draggable: htmlAttributeParser, + maxlength: htmlAttributeParser, + oncanplay: htmlAttributeParser, + ondragend: htmlAttributeParser, + onemptied: htmlAttributeParser, + oninvalid: htmlAttributeParser, + onkeydown: htmlAttributeParser, + onmouseup: htmlAttributeParser, + onoffline: htmlAttributeParser, + onplaying: htmlAttributeParser, + onseeking: htmlAttributeParser, + onstalled: htmlAttributeParser, + onstorage: htmlAttributeParser, + onsuspend: htmlAttributeParser, + onwaiting: htmlAttributeParser, + translate: htmlAttributeParser, + autoplay: htmlAttributeParser, + controls: htmlAttributeParser, + datetime: htmlAttributeParser, + disabled: htmlAttributeParser, + download: htmlAttributeParser, + dropzone: htmlAttributeParser, + hreflang: htmlAttributeParser, + multiple: htmlAttributeParser, + onchange: htmlAttributeParser, + ononline: htmlAttributeParser, + onresize: htmlAttributeParser, + onscroll: htmlAttributeParser, + onsearch: htmlAttributeParser, + onseeked: htmlAttributeParser, + onselect: htmlAttributeParser, + onsubmit: htmlAttributeParser, + ontoggle: htmlAttributeParser, + onunload: htmlAttributeParser, + property: htmlAttributeParser, + readonly: htmlAttributeParser, + required: htmlAttributeParser, + reversed: htmlAttributeParser, + selected: htmlAttributeParser, + tabindex: htmlAttributeParser, + bgcolor: htmlAttributeParser, + charset: htmlAttributeParser, + checked: htmlAttributeParser, + colspan: htmlAttributeParser, + content: htmlAttributeParser, + default: htmlAttributeParser, + dirname: htmlAttributeParser, + enctype: htmlAttributeParser, + headers: htmlAttributeParser, + onabort: htmlAttributeParser, + onclick: htmlAttributeParser, + onended: htmlAttributeParser, + onerror: htmlAttributeParser, + onfocus: htmlAttributeParser, + oninput: htmlAttributeParser, + onkeyup: htmlAttributeParser, + onpaste: htmlAttributeParser, + onpause: htmlAttributeParser, + onreset: htmlAttributeParser, + onwheel: htmlAttributeParser, + optimum: htmlAttributeParser, + pattern: htmlAttributeParser, + preload: htmlAttributeParser, + rowspan: htmlAttributeParser, + sandbox: htmlAttributeParser, + srclang: htmlAttributeParser, + accept: htmlAttributeParser, + action: htmlAttributeParser, + border: htmlAttributeParser, + coords: htmlAttributeParser, + height: htmlAttributeParser, + hidden: htmlAttributeParser, + method: htmlAttributeParser, + onblur: htmlAttributeParser, + oncopy: htmlAttributeParser, + ondrag: htmlAttributeParser, + ondrop: htmlAttributeParser, + onload: htmlAttributeParser, + onplay: htmlAttributeParser, + poster: htmlAttributeParser, + srcdoc: htmlAttributeParser, + srcset: htmlAttributeParser, + target: htmlAttributeParser, + usemap: htmlAttributeParser, + align: htmlAttributeParser, + async: htmlAttributeParser, + class: htmlAttributeParser, + color: htmlAttributeParser, + defer: htmlAttributeParser, + ismap: htmlAttributeParser, + media: htmlAttributeParser, + muted: htmlAttributeParser, + oncut: htmlAttributeParser, + scope: htmlAttributeParser, + shape: htmlAttributeParser, + sizes: htmlAttributeParser, + start: htmlAttributeParser, + style: htmlAttributeParser, + title: htmlAttributeParser, + value: htmlAttributeParser, + width: htmlAttributeParser, + cols: htmlAttributeParser, + high: htmlAttributeParser, + href: htmlAttributeParser, + kind: htmlAttributeParser, + lang: htmlAttributeParser, + list: htmlAttributeParser, + loop: htmlAttributeParser, + name: htmlAttributeParser, + open: htmlAttributeParser, + rows: htmlAttributeParser, + size: htmlAttributeParser, + step: htmlAttributeParser, + type: htmlAttributeParser, + wrap: htmlAttributeParser, + alt: htmlAttributeParser, + dir: htmlAttributeParser, + for: htmlAttributeParser, + low: htmlAttributeParser, + max: htmlAttributeParser, + min: htmlAttributeParser, + rel: htmlAttributeParser, + src: htmlAttributeParser, + id: htmlAttributeParser, + lineShiftClickCommand: stumpExtendedAttributeParser, + contextMenuCommand: stumpExtendedAttributeParser, + doubleClickCommand: stumpExtendedAttributeParser, + shiftClickCommand: stumpExtendedAttributeParser, + lineClickCommand: stumpExtendedAttributeParser, + changeCommand: stumpExtendedAttributeParser, + clickCommand: stumpExtendedAttributeParser, + keyUpCommand: stumpExtendedAttributeParser, + blurCommand: stumpExtendedAttributeParser, + collapse: stumpExtendedAttributeParser, + bern: bernParser, }), - [{ regex: /^$/, nodeConstructor: blankLineNode }, { regex: /^[a-zA-Z0-9_]+Component/, nodeConstructor: componentDefinitionNode }] + [ + { regex: /^$/, parser: blankLineParser }, + { regex: /^[a-zA-Z0-9_]+Component/, parser: componentDefinitionParser }, + ] ) } get htmlTagNameCell() { @@ -17252,20 +17284,20 @@ bernNode get anyHtmlContentCell() { return this.getWordsFrom(1) } - isHtmlTagNode = true + isHtmlTagParser = true getTag() { // we need to remove the "Tag" bit to handle the style and title attribute/tag conflict. - const firstWord = this.getFirstWord() + const firstWord = this.firstWord const map = { titleTag: "title", - styleTag: "style" + styleTag: "style", } return map[firstWord] || firstWord } _getHtmlJoinByCharacter() { return "" } - toHtmlWithSuids() { + asHtmlWithSuids() { return this._toHtml(undefined, true) } _getOneLiner() { @@ -17281,23 +17313,25 @@ bernNode get domElement() { var elem = document.createElement(this.getTag()) elem.setAttribute("stumpUid", this._getUid()) - this.filter(node => node.isAttributeNode).forEach(child => elem.setAttribute(child.getFirstWord(), child.getContent())) + this.filter((node) => node.isAttributeParser).forEach((child) => elem.setAttribute(child.firstWord, child.content)) elem.innerHTML = this.has("bern") ? this.getNode("bern").childrenToString() : this._getOneLiner() - this.filter(node => node.isHtmlTagNode).forEach(child => elem.appendChild(child.domElement)) + this.filter((node) => node.isHtmlTagParser).forEach((child) => elem.appendChild(child.domElement)) return elem } _toHtml(indentCount, withSuid) { const tag = this.getTag() - const children = this.map(child => child._toHtml(indentCount + 1, withSuid)).join("") - const attributesStr = this.filter(node => node.isAttributeNode) - .map(child => child.getAttribute()) + const children = this.map((child) => child._toHtml(indentCount + 1, withSuid)).join("") + const attributesStr = this.filter((node) => node.isAttributeParser) + .map((child) => child.getAttribute()) .join("") const indent = " ".repeat(indentCount) const collapse = this.shouldCollapse() - const indentForChildNodes = !collapse && this.getChildInstancesOfNodeTypeId("htmlTagNode").length > 0 + const indentForChildParsers = !collapse && this.getChildInstancesOfParserId("htmlTagParser").length > 0 const suid = withSuid ? ` stumpUid="${this._getUid()}"` : "" const oneLiner = this._getOneLiner() - return `${!collapse ? indent : ""}<${tag}${attributesStr}${suid}>${oneLiner}${indentForChildNodes ? "\n" : ""}${children}</${tag}>${collapse ? "" : "\n"}` + return `${!collapse ? indent : ""}<${tag}${attributesStr}${suid}>${oneLiner}${indentForChildParsers ? "\n" : ""}${children}</${tag}>${ + collapse ? "" : "\n" + }` } removeCssStumpNode() { return this.removeStumpNode() @@ -17307,31 +17341,31 @@ bernNode return this.destroy() } getNodeByGuid(guid) { - return this.getTopDownArray().find(node => node._getUid() === guid) + return this.topDownArray.find((node) => node._getUid() === guid) } addClassToStumpNode(className) { - const classNode = this.touchNode("class") - const words = classNode.getWordsFrom(1) + const classParser = this.touchNode("class") + const words = classParser.getWordsFrom(1) // note: we call add on shadow regardless, because at the moment stump may have gotten out of // sync with shadow, if things modified the dom. todo: cleanup. this.getShadow().addClassToShadow(className) if (words.includes(className)) return this words.push(className) - classNode.setContent(words.join(this.getWordBreakSymbol())) + classParser.setContent(words.join(this.wordBreakSymbol)) return this } removeClassFromStumpNode(className) { - const classNode = this.getNode("class") - if (!classNode) return this - const newClasses = classNode.getWords().filter(word => word !== className) - if (!newClasses.length) classNode.destroy() - else classNode.setContent(newClasses.join(" ")) + const classParser = this.getNode("class") + if (!classParser) return this + const newClasses = classParser.words.filter((word) => word !== className) + if (!newClasses.length) classParser.destroy() + else classParser.setContent(newClasses.join(" ")) this.getShadow().removeClassFromShadow(className) return this } stumpNodeHasClass(className) { - const classNode = this.getNode("class") - return classNode && classNode.getWords().includes(className) ? true : false + const classParser = this.getNode("class") + return classParser && classParser.words.includes(className) ? true : false } isStumpNodeCheckbox() { return this.get("type") === "checkbox" @@ -17347,10 +17381,10 @@ bernNode return this.insertChildNode(text, index) } insertChildNode(text, index) { - const singleNode = new jtree.TreeNode(text).getChildren()[0] + const singleNode = new TreeNode(text).getChildren()[0] const newNode = this.insertLineAndChildren(singleNode.getLine(), singleNode.childrenToString(), index) - const stumpNodeIndex = this.filter(node => node.isHtmlTagNode).indexOf(newNode) - this.getShadow().insertHtmlNode(newNode, stumpNodeIndex) + const stumpParserIndex = this.filter((node) => node.isHtmlTagParser).indexOf(newNode) + this.getShadow().insertHtmlNode(newNode, stumpParserIndex) return newNode } isInputType() { @@ -17360,9 +17394,9 @@ bernNode return this.findStumpNodesByChild(line)[0] } findStumpNodeByChildString(line) { - return this.getTopDownArray().find(node => + return this.topDownArray.find((node) => node - .map(child => child.getLine()) + .map((child) => child.getLine()) .join("\n") .includes(line) ) @@ -17371,31 +17405,23 @@ bernNode return this._findStumpNodesByBase(firstWord)[0] } _findStumpNodesByBase(firstWord) { - return this.getTopDownArray().filter(node => node.doesExtend("htmlTagNode") && node.getFirstWord() === firstWord) + return this.topDownArray.filter((node) => node.doesExtend("htmlTagParser") && node.firstWord === firstWord) } hasLine(line) { - return this.getChildren().some(node => node.getLine() === line) + return this.getChildren().some((node) => node.getLine() === line) } findStumpNodesByChild(line) { - return this.getTopDownArray().filter(node => node.doesExtend("htmlTagNode") && node.hasLine(line)) + return this.topDownArray.filter((node) => node.doesExtend("htmlTagParser") && node.hasLine(line)) } findStumpNodesWithClass(className) { - return this.getTopDownArray().filter( - node => - node.doesExtend("htmlTagNode") && - node.has("class") && - node - .getNode("class") - .getWords() - .includes(className) - ) + return this.topDownArray.filter((node) => node.doesExtend("htmlTagParser") && node.has("class") && node.getNode("class").words.includes(className)) } getShadowClass() { - return this.getParent().getShadowClass() + return this.parent.getShadowClass() } // todo: should not be here getStumpNodeTreeComponent() { - return this._treeComponent || this.getParent().getStumpNodeTreeComponent() + return this._treeComponent || this.parent.getStumpNodeTreeComponent() } // todo: should not be here setStumpNodeTreeComponent(treeComponent) { @@ -17412,18 +17438,18 @@ bernNode // todo return this } - toHtml() { + get asHtml() { return this._toHtml() } } - class errorNode extends jtree.GrammarBackedNode { + class errorParser extends GrammarBackedNode { getErrors() { - return this._getErrorNodeErrors() + return this._getErrorParserErrors() } } - class componentDefinitionNode extends htmlTagNode { + class componentDefinitionParser extends htmlTagParser { get componentTagNameCell() { return this.getWord(0) } @@ -17432,9 +17458,9 @@ bernNode } } - class htmlAttributeNode extends jtree.GrammarBackedNode { - createParser() { - return new jtree.TreeNode.Parser(errorNode, undefined, undefined) + class htmlAttributeParser extends GrammarBackedNode { + createParserCombinator() { + return new TreeNode.ParserCombinator(errorParser, undefined, undefined) } get htmlAttributeNameCell() { return this.getWord(0) @@ -17445,7 +17471,7 @@ bernNode get isTileAttribute() { return true } - get isAttributeNode() { + get isAttributeParser() { return true } _toHtml() { @@ -17455,19 +17481,19 @@ bernNode return "" } getAttribute() { - return ` ${this.getFirstWord()}="${this.getContent()}"` + return ` ${this.firstWord}="${this.content}"` } } - class stumpExtendedAttributeNode extends htmlAttributeNode { + class stumpExtendedAttributeParser extends htmlAttributeParser { get stumpExtendedAttributeNameCell() { return this.getWord(0) } } - class lineOfHtmlContentNode extends jtree.GrammarBackedNode { - createParser() { - return new jtree.TreeNode.Parser(lineOfHtmlContentNode, undefined, undefined) + class lineOfHtmlContentParser extends GrammarBackedNode { + createParserCombinator() { + return new TreeNode.ParserCombinator(lineOfHtmlContentParser, undefined, undefined) } get anyHtmlContentCell() { return this.getWordsFrom(0) @@ -17480,9 +17506,9 @@ bernNode } } - class bernNode extends jtree.GrammarBackedNode { - createParser() { - return new jtree.TreeNode.Parser(lineOfHtmlContentNode, undefined, undefined) + class bernParser extends GrammarBackedNode { + createParserCombinator() { + return new TreeNode.ParserCombinator(lineOfHtmlContentParser, undefined, undefined) } get bernKeywordCell() { return this.getWord(0) @@ -17498,16 +17524,16 @@ bernNode } } - window.stumpNode = stumpNode + window.stumpParser = stumpParser } { - class hakonNode extends jtree.GrammarBackedNode { - createParser() { - return new jtree.TreeNode.Parser( - selectorNode, - Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { comment: commentNode }), + class hakonParser extends GrammarBackedNode { + createParserCombinator() { + return new TreeNode.ParserCombinator( + selectorParser, + Object.assign(Object.assign({}, super.createParserCombinator()._getFirstWordMapAsObject()), { comment: commentParser }), undefined ) } @@ -17515,12 +17541,12 @@ bernNode return "" } compile() { - return this.getTopDownArray() - .filter(node => node.isSelectorNode) - .map(child => child.compile()) + return this.topDownArray + .filter((node) => node.isSelectorParser) + .map((child) => child.compile()) .join("") } - static cachedHandGrammarProgramRoot = new jtree.HandGrammarProgram(`tooling onsave jtree build produceLang hakon + static cachedHandGrammarProgramRoot = new HandGrammarProgram(`// Cell Parsers anyCell keywordCell commentKeywordCell @@ -17534,7 +17560,7 @@ cssValueCell selectorCell highlightScope keyword.control examples body h1 - todo add html tags, css and ids selector regexes, etc + // todo add html tags, css and ids selector regexes, etc vendorPrefixPropertyKeywordCell description Properties like -moz-column-fill highlightScope variable.function @@ -17542,26 +17568,28 @@ vendorPrefixPropertyKeywordCell propertyKeywordCell highlightScope variable.function extends keywordCell - todo Where are these coming from? Can we add a url link + // todo Where are these coming from? Can we add a url link enum align-content align-items align-self all animation animation-delay animation-direction animation-duration animation-fill-mode animation-iteration-count animation-name animation-play-state animation-timing-function backface-visibility background background-attachment background-blend-mode background-clip background-color background-image background-origin background-position background-repeat background-size border border-bottom border-bottom-color border-bottom-left-radius border-bottom-right-radius border-bottom-style border-bottom-width border-collapse border-color border-image border-image-outset border-image-repeat border-image-slice border-image-source border-image-width border-left border-left-color border-left-style border-left-width border-radius border-right border-right-color border-right-style border-right-width border-spacing border-style border-top border-top-color border-top-left-radius border-top-right-radius border-top-style border-top-width border-width bottom box-shadow box-sizing break-inside caption-side clear clip color column-count column-fill column-gap column-rule column-rule-color column-rule-style column-rule-width column-span column-width columns content counter-increment counter-reset cursor direction display empty-cells fill filter flex flex-basis flex-direction flex-flow flex-grow flex-shrink flex-wrap float font @font-face font-family font-size font-size-adjust font-stretch font-style font-variant font-weight hanging-punctuation height hyphens justify-content @keyframes left letter-spacing line-height list-style list-style-image list-style-position list-style-type margin margin-bottom margin-left margin-right margin-top max-height max-width @media min-height min-width nav-down nav-index nav-left nav-right nav-up opacity order outline outline-color outline-offset outline-style outline-width overflow overflow-x overflow-y padding padding-bottom padding-left padding-right padding-top page-break-after page-break-before page-break-inside perspective perspective-origin position quotes resize right tab-size table-layout text-align text-align-last text-decoration text-decoration-color text-decoration-line text-decoration-style text-indent text-justify text-overflow text-shadow text-transform top transform transform-origin transform-style transition transition-delay transition-duration transition-property transition-timing-function unicode-bidi vertical-align visibility white-space width word-break word-spacing word-wrap z-index overscroll-behavior-x user-select -ms-touch-action -webkit-user-select -webkit-touch-callout -moz-user-select touch-action -ms-user-select -khtml-user-select gap grid-auto-flow grid-column grid-column-end grid-column-gap grid-column-start grid-gap grid-row grid-row-end grid-row-gap grid-row-start grid-template-columns grid-template-rows justify-items justify-self errorCell highlightScope invalid commentCell highlightScope comment -hakonNode + +// Line Parsers +hakonParser root - todo Add variables? + // todo Add variables? description A prefix Tree Language that compiles to CSS compilesTo css - inScope commentNode - catchAllNodeType selectorNode + inScope commentParser + catchAllParser selectorParser javascript getSelector() { return "" } compile() { - return this.getTopDownArray() - .filter(node => node.isSelectorNode) + return this.topDownArray + .filter(node => node.isSelectorParser) .map(child => child.compile()) .join("") } @@ -17574,37 +17602,37 @@ hakonNode &:hover color blue font-size 17px -propertyNode +propertyParser catchAllCellType cssValueCell - catchAllNodeType errorNode + catchAllParser errorParser javascript compile(spaces) { - return \`\${spaces}\${this.getFirstWord()}: \${this.getContent()};\` + return \`\${spaces}\${this.firstWord}: \${this.content};\` } cells propertyKeywordCell -variableNode - extends propertyNode +variableParser + extends propertyParser pattern -- -browserPrefixPropertyNode - extends propertyNode +browserPrefixPropertyParser + extends propertyParser pattern ^\\-\\w.+ cells vendorPrefixPropertyKeywordCell -errorNode - catchAllNodeType errorNode +errorParser + catchAllParser errorParser catchAllCellType errorCell - baseNodeType errorNode -commentNode + baseParser errorParser +commentParser cells commentKeywordCell catchAllCellType commentCell - catchAllNodeType commentNode -selectorNode - inScope propertyNode variableNode commentNode - catchAllNodeType selectorNode - boolean isSelectorNode true + catchAllParser commentParser +selectorParser + inScope propertyParser variableParser commentParser + catchAllParser selectorParser + boolean isSelectorParser true javascript getSelector() { - const parentSelector = this.getParent().getSelector() - return this.getFirstWord() + const parentSelector = this.parent.getSelector() + return this.firstWord .split(",") .map(part => { if (part.startsWith("&")) return parentSelector + part.substr(1) @@ -17613,33 +17641,23 @@ selectorNode .join(",") } compile() { - const propertyNodes = this.getChildren().filter(node => node.doesExtend("propertyNode")) - if (!propertyNodes.length) return "" + const propertyParsers = this.getChildren().filter(node => node.doesExtend("propertyParser")) + if (!propertyParsers.length) return "" const spaces = " " return \`\${this.getSelector()} { - \${propertyNodes.map(child => child.compile(spaces)).join("\\n")} + \${propertyParsers.map(child => child.compile(spaces)).join("\\n")} }\\n\` } cells selectorCell`) - getHandGrammarProgram() { + get handGrammarProgram() { return this.constructor.cachedHandGrammarProgramRoot } - static getNodeTypeMap() { - return { - hakonNode: hakonNode, - propertyNode: propertyNode, - variableNode: variableNode, - browserPrefixPropertyNode: browserPrefixPropertyNode, - errorNode: errorNode, - commentNode: commentNode, - selectorNode: selectorNode - } - } + static rootParser = hakonParser } - class propertyNode extends jtree.GrammarBackedNode { - createParser() { - return new jtree.TreeNode.Parser(errorNode, undefined, undefined) + class propertyParser extends GrammarBackedNode { + createParserCombinator() { + return new TreeNode.ParserCombinator(errorParser, undefined, undefined) } get propertyKeywordCell() { return this.getWord(0) @@ -17648,33 +17666,33 @@ selectorNode return this.getWordsFrom(1) } compile(spaces) { - return `${spaces}${this.getFirstWord()}: ${this.getContent()};` + return `${spaces}${this.firstWord}: ${this.content};` } } - class variableNode extends propertyNode {} + class variableParser extends propertyParser {} - class browserPrefixPropertyNode extends propertyNode { + class browserPrefixPropertyParser extends propertyParser { get vendorPrefixPropertyKeywordCell() { return this.getWord(0) } } - class errorNode extends jtree.GrammarBackedNode { - createParser() { - return new jtree.TreeNode.Parser(errorNode, undefined, undefined) + class errorParser extends GrammarBackedNode { + createParserCombinator() { + return new TreeNode.ParserCombinator(errorParser, undefined, undefined) } getErrors() { - return this._getErrorNodeErrors() + return this._getErrorParserErrors() } get errorCell() { return this.getWordsFrom(0) } } - class commentNode extends jtree.GrammarBackedNode { - createParser() { - return new jtree.TreeNode.Parser(commentNode, undefined, undefined) + class commentParser extends GrammarBackedNode { + createParserCombinator() { + return new TreeNode.ParserCombinator(commentParser, undefined, undefined) } get commentKeywordCell() { return this.getWord(0) @@ -17684,251 +17702,254 @@ selectorNode } } - class selectorNode extends jtree.GrammarBackedNode { - createParser() { - return new jtree.TreeNode.Parser( - selectorNode, - Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { - "border-bottom-right-radius": propertyNode, - "transition-timing-function": propertyNode, - "animation-iteration-count": propertyNode, - "animation-timing-function": propertyNode, - "border-bottom-left-radius": propertyNode, - "border-top-right-radius": propertyNode, - "border-top-left-radius": propertyNode, - "background-attachment": propertyNode, - "background-blend-mode": propertyNode, - "text-decoration-color": propertyNode, - "text-decoration-style": propertyNode, - "overscroll-behavior-x": propertyNode, - "-webkit-touch-callout": propertyNode, - "grid-template-columns": propertyNode, - "animation-play-state": propertyNode, - "text-decoration-line": propertyNode, - "animation-direction": propertyNode, - "animation-fill-mode": propertyNode, - "backface-visibility": propertyNode, - "background-position": propertyNode, - "border-bottom-color": propertyNode, - "border-bottom-style": propertyNode, - "border-bottom-width": propertyNode, - "border-image-outset": propertyNode, - "border-image-repeat": propertyNode, - "border-image-source": propertyNode, - "hanging-punctuation": propertyNode, - "list-style-position": propertyNode, - "transition-duration": propertyNode, - "transition-property": propertyNode, - "-webkit-user-select": propertyNode, - "animation-duration": propertyNode, - "border-image-slice": propertyNode, - "border-image-width": propertyNode, - "border-right-color": propertyNode, - "border-right-style": propertyNode, - "border-right-width": propertyNode, - "perspective-origin": propertyNode, - "-khtml-user-select": propertyNode, - "grid-template-rows": propertyNode, - "background-origin": propertyNode, - "background-repeat": propertyNode, - "border-left-color": propertyNode, - "border-left-style": propertyNode, - "border-left-width": propertyNode, - "column-rule-color": propertyNode, - "column-rule-style": propertyNode, - "column-rule-width": propertyNode, - "counter-increment": propertyNode, - "page-break-before": propertyNode, - "page-break-inside": propertyNode, - "grid-column-start": propertyNode, - "background-color": propertyNode, - "background-image": propertyNode, - "border-top-color": propertyNode, - "border-top-style": propertyNode, - "border-top-width": propertyNode, - "font-size-adjust": propertyNode, - "list-style-image": propertyNode, - "page-break-after": propertyNode, - "transform-origin": propertyNode, - "transition-delay": propertyNode, - "-ms-touch-action": propertyNode, - "-moz-user-select": propertyNode, - "animation-delay": propertyNode, - "background-clip": propertyNode, - "background-size": propertyNode, - "border-collapse": propertyNode, - "justify-content": propertyNode, - "list-style-type": propertyNode, - "text-align-last": propertyNode, - "text-decoration": propertyNode, - "transform-style": propertyNode, - "-ms-user-select": propertyNode, - "grid-column-end": propertyNode, - "grid-column-gap": propertyNode, - "animation-name": propertyNode, - "border-spacing": propertyNode, - "flex-direction": propertyNode, - "letter-spacing": propertyNode, - "outline-offset": propertyNode, - "padding-bottom": propertyNode, - "text-transform": propertyNode, - "vertical-align": propertyNode, - "grid-auto-flow": propertyNode, - "grid-row-start": propertyNode, - "align-content": propertyNode, - "border-bottom": propertyNode, - "border-radius": propertyNode, - "counter-reset": propertyNode, - "margin-bottom": propertyNode, - "outline-color": propertyNode, - "outline-style": propertyNode, - "outline-width": propertyNode, - "padding-right": propertyNode, - "text-overflow": propertyNode, - "justify-items": propertyNode, - "border-color": propertyNode, - "border-image": propertyNode, - "border-right": propertyNode, - "border-style": propertyNode, - "border-width": propertyNode, - "break-inside": propertyNode, - "caption-side": propertyNode, - "column-count": propertyNode, - "column-width": propertyNode, - "font-stretch": propertyNode, - "font-variant": propertyNode, - "margin-right": propertyNode, - "padding-left": propertyNode, - "table-layout": propertyNode, - "text-justify": propertyNode, - "unicode-bidi": propertyNode, - "word-spacing": propertyNode, - "touch-action": propertyNode, - "grid-row-end": propertyNode, - "grid-row-gap": propertyNode, - "justify-self": propertyNode, - "align-items": propertyNode, - "border-left": propertyNode, - "column-fill": propertyNode, - "column-rule": propertyNode, - "column-span": propertyNode, - "empty-cells": propertyNode, - "flex-shrink": propertyNode, - "font-family": propertyNode, - "font-weight": propertyNode, - "line-height": propertyNode, - "margin-left": propertyNode, - "padding-top": propertyNode, - perspective: propertyNode, - "text-indent": propertyNode, - "text-shadow": propertyNode, - "white-space": propertyNode, - "user-select": propertyNode, - "grid-column": propertyNode, - "align-self": propertyNode, - background: propertyNode, - "border-top": propertyNode, - "box-shadow": propertyNode, - "box-sizing": propertyNode, - "column-gap": propertyNode, - "flex-basis": propertyNode, - "@font-face": propertyNode, - "font-style": propertyNode, - "@keyframes": propertyNode, - "list-style": propertyNode, - "margin-top": propertyNode, - "max-height": propertyNode, - "min-height": propertyNode, - "overflow-x": propertyNode, - "overflow-y": propertyNode, - "text-align": propertyNode, - transition: propertyNode, - visibility: propertyNode, - "word-break": propertyNode, - animation: propertyNode, - direction: propertyNode, - "flex-flow": propertyNode, - "flex-grow": propertyNode, - "flex-wrap": propertyNode, - "font-size": propertyNode, - "max-width": propertyNode, - "min-width": propertyNode, - "nav-index": propertyNode, - "nav-right": propertyNode, - transform: propertyNode, - "word-wrap": propertyNode, - "nav-down": propertyNode, - "nav-left": propertyNode, - overflow: propertyNode, - position: propertyNode, - "tab-size": propertyNode, - "grid-gap": propertyNode, - "grid-row": propertyNode, - columns: propertyNode, - content: propertyNode, - display: propertyNode, - hyphens: propertyNode, - opacity: propertyNode, - outline: propertyNode, - padding: propertyNode, - "z-index": propertyNode, - border: propertyNode, - bottom: propertyNode, - cursor: propertyNode, - filter: propertyNode, - height: propertyNode, - margin: propertyNode, - "@media": propertyNode, - "nav-up": propertyNode, - quotes: propertyNode, - resize: propertyNode, - clear: propertyNode, - color: propertyNode, - float: propertyNode, - order: propertyNode, - right: propertyNode, - width: propertyNode, - clip: propertyNode, - fill: propertyNode, - flex: propertyNode, - font: propertyNode, - left: propertyNode, - all: propertyNode, - top: propertyNode, - gap: propertyNode, - "": propertyNode, - comment: commentNode + class selectorParser extends GrammarBackedNode { + createParserCombinator() { + return new TreeNode.ParserCombinator( + selectorParser, + Object.assign(Object.assign({}, super.createParserCombinator()._getFirstWordMapAsObject()), { + "border-bottom-right-radius": propertyParser, + "transition-timing-function": propertyParser, + "animation-iteration-count": propertyParser, + "animation-timing-function": propertyParser, + "border-bottom-left-radius": propertyParser, + "border-top-right-radius": propertyParser, + "border-top-left-radius": propertyParser, + "background-attachment": propertyParser, + "background-blend-mode": propertyParser, + "text-decoration-color": propertyParser, + "text-decoration-style": propertyParser, + "overscroll-behavior-x": propertyParser, + "-webkit-touch-callout": propertyParser, + "grid-template-columns": propertyParser, + "animation-play-state": propertyParser, + "text-decoration-line": propertyParser, + "animation-direction": propertyParser, + "animation-fill-mode": propertyParser, + "backface-visibility": propertyParser, + "background-position": propertyParser, + "border-bottom-color": propertyParser, + "border-bottom-style": propertyParser, + "border-bottom-width": propertyParser, + "border-image-outset": propertyParser, + "border-image-repeat": propertyParser, + "border-image-source": propertyParser, + "hanging-punctuation": propertyParser, + "list-style-position": propertyParser, + "transition-duration": propertyParser, + "transition-property": propertyParser, + "-webkit-user-select": propertyParser, + "animation-duration": propertyParser, + "border-image-slice": propertyParser, + "border-image-width": propertyParser, + "border-right-color": propertyParser, + "border-right-style": propertyParser, + "border-right-width": propertyParser, + "perspective-origin": propertyParser, + "-khtml-user-select": propertyParser, + "grid-template-rows": propertyParser, + "background-origin": propertyParser, + "background-repeat": propertyParser, + "border-left-color": propertyParser, + "border-left-style": propertyParser, + "border-left-width": propertyParser, + "column-rule-color": propertyParser, + "column-rule-style": propertyParser, + "column-rule-width": propertyParser, + "counter-increment": propertyParser, + "page-break-before": propertyParser, + "page-break-inside": propertyParser, + "grid-column-start": propertyParser, + "background-color": propertyParser, + "background-image": propertyParser, + "border-top-color": propertyParser, + "border-top-style": propertyParser, + "border-top-width": propertyParser, + "font-size-adjust": propertyParser, + "list-style-image": propertyParser, + "page-break-after": propertyParser, + "transform-origin": propertyParser, + "transition-delay": propertyParser, + "-ms-touch-action": propertyParser, + "-moz-user-select": propertyParser, + "animation-delay": propertyParser, + "background-clip": propertyParser, + "background-size": propertyParser, + "border-collapse": propertyParser, + "justify-content": propertyParser, + "list-style-type": propertyParser, + "text-align-last": propertyParser, + "text-decoration": propertyParser, + "transform-style": propertyParser, + "-ms-user-select": propertyParser, + "grid-column-end": propertyParser, + "grid-column-gap": propertyParser, + "animation-name": propertyParser, + "border-spacing": propertyParser, + "flex-direction": propertyParser, + "letter-spacing": propertyParser, + "outline-offset": propertyParser, + "padding-bottom": propertyParser, + "text-transform": propertyParser, + "vertical-align": propertyParser, + "grid-auto-flow": propertyParser, + "grid-row-start": propertyParser, + "align-content": propertyParser, + "border-bottom": propertyParser, + "border-radius": propertyParser, + "counter-reset": propertyParser, + "margin-bottom": propertyParser, + "outline-color": propertyParser, + "outline-style": propertyParser, + "outline-width": propertyParser, + "padding-right": propertyParser, + "text-overflow": propertyParser, + "justify-items": propertyParser, + "border-color": propertyParser, + "border-image": propertyParser, + "border-right": propertyParser, + "border-style": propertyParser, + "border-width": propertyParser, + "break-inside": propertyParser, + "caption-side": propertyParser, + "column-count": propertyParser, + "column-width": propertyParser, + "font-stretch": propertyParser, + "font-variant": propertyParser, + "margin-right": propertyParser, + "padding-left": propertyParser, + "table-layout": propertyParser, + "text-justify": propertyParser, + "unicode-bidi": propertyParser, + "word-spacing": propertyParser, + "touch-action": propertyParser, + "grid-row-end": propertyParser, + "grid-row-gap": propertyParser, + "justify-self": propertyParser, + "align-items": propertyParser, + "border-left": propertyParser, + "column-fill": propertyParser, + "column-rule": propertyParser, + "column-span": propertyParser, + "empty-cells": propertyParser, + "flex-shrink": propertyParser, + "font-family": propertyParser, + "font-weight": propertyParser, + "line-height": propertyParser, + "margin-left": propertyParser, + "padding-top": propertyParser, + perspective: propertyParser, + "text-indent": propertyParser, + "text-shadow": propertyParser, + "white-space": propertyParser, + "user-select": propertyParser, + "grid-column": propertyParser, + "align-self": propertyParser, + background: propertyParser, + "border-top": propertyParser, + "box-shadow": propertyParser, + "box-sizing": propertyParser, + "column-gap": propertyParser, + "flex-basis": propertyParser, + "@font-face": propertyParser, + "font-style": propertyParser, + "@keyframes": propertyParser, + "list-style": propertyParser, + "margin-top": propertyParser, + "max-height": propertyParser, + "min-height": propertyParser, + "overflow-x": propertyParser, + "overflow-y": propertyParser, + "text-align": propertyParser, + transition: propertyParser, + visibility: propertyParser, + "word-break": propertyParser, + animation: propertyParser, + direction: propertyParser, + "flex-flow": propertyParser, + "flex-grow": propertyParser, + "flex-wrap": propertyParser, + "font-size": propertyParser, + "max-width": propertyParser, + "min-width": propertyParser, + "nav-index": propertyParser, + "nav-right": propertyParser, + transform: propertyParser, + "word-wrap": propertyParser, + "nav-down": propertyParser, + "nav-left": propertyParser, + overflow: propertyParser, + position: propertyParser, + "tab-size": propertyParser, + "grid-gap": propertyParser, + "grid-row": propertyParser, + columns: propertyParser, + content: propertyParser, + display: propertyParser, + hyphens: propertyParser, + opacity: propertyParser, + outline: propertyParser, + padding: propertyParser, + "z-index": propertyParser, + border: propertyParser, + bottom: propertyParser, + cursor: propertyParser, + filter: propertyParser, + height: propertyParser, + margin: propertyParser, + "@media": propertyParser, + "nav-up": propertyParser, + quotes: propertyParser, + resize: propertyParser, + clear: propertyParser, + color: propertyParser, + float: propertyParser, + order: propertyParser, + right: propertyParser, + width: propertyParser, + clip: propertyParser, + fill: propertyParser, + flex: propertyParser, + font: propertyParser, + left: propertyParser, + all: propertyParser, + top: propertyParser, + gap: propertyParser, + "": propertyParser, + comment: commentParser, }), - [{ regex: /--/, nodeConstructor: variableNode }, { regex: /^\-\w.+/, nodeConstructor: browserPrefixPropertyNode }] + [ + { regex: /--/, parser: variableParser }, + { regex: /^\-\w.+/, parser: browserPrefixPropertyParser }, + ] ) } get selectorCell() { return this.getWord(0) } - get isSelectorNode() { + get isSelectorParser() { return true } getSelector() { - const parentSelector = this.getParent().getSelector() - return this.getFirstWord() + const parentSelector = this.parent.getSelector() + return this.firstWord .split(",") - .map(part => { + .map((part) => { if (part.startsWith("&")) return parentSelector + part.substr(1) return parentSelector ? parentSelector + " " + part : part }) .join(",") } compile() { - const propertyNodes = this.getChildren().filter(node => node.doesExtend("propertyNode")) - if (!propertyNodes.length) return "" + const propertyParsers = this.getChildren().filter((node) => node.doesExtend("propertyParser")) + if (!propertyParsers.length) return "" const spaces = " " return `${this.getSelector()} { -${propertyNodes.map(child => child.compile(spaces)).join("\n")} +${propertyParsers.map((child) => child.compile(spaces)).join("\n")} }\n` } } - window.hakonNode = hakonNode + window.hakonParser = hakonParser } @@ -17986,7 +18007,7 @@ WillowConstants.checkedSelector = ":checked" WillowConstants.contenteditable = "contenteditable" WillowConstants.inputTypes = ["input", "textarea"] var CacheType -;(function(CacheType) { +;(function (CacheType) { CacheType["inBrowserMemory"] = "inBrowserMemory" })(CacheType || (CacheType = {})) class WillowHTTPResponse { @@ -18053,9 +18074,7 @@ class AbstractWillowShadow { return this } getShadowParent() { - return this.getShadowStumpNode() - .getParent() - .getShadow() + return this.getShadowStumpNode().parent.getShadow() } getPositionAndDimensions(gridSize = 1) { const offset = this.getShadowOffset() @@ -18171,7 +18190,7 @@ class WillowMousetrap { bind() {} } // this one should have no document, window, $, et cetera. -class AbstractWillowBrowser extends stumpNode { +class AbstractWillowBrowser extends stumpParser { constructor(fullHtmlPageUrlIncludingProtocolAndFileName) { super(`${WillowConstants.tags.html} ${WillowConstants.tags.head} @@ -18275,7 +18294,7 @@ class AbstractWillowBrowser extends stumpNode { return this._fullHtmlPageUrlIncludingProtocolAndFileName } getAppWebPageParentFolderWithoutTrailingSlash() { - return jtree.Utils.getPathWithoutFileName(this._fullHtmlPageUrlIncludingProtocolAndFileName) + return Utils.getPathWithoutFileName(this._fullHtmlPageUrlIncludingProtocolAndFileName) } _makeRelativeUrlAbsolute(url) { if (url.startsWith("http://") || url.startsWith("https://")) return url @@ -18334,13 +18353,13 @@ class AbstractWillowBrowser extends stumpNode { async appendScript(url) {} getWindowTitle() { // todo: deep getNodeByBase/withBase/type/word or something? - const nodes = this.getTopDownArray() - const titleNode = nodes.find(node => node.getFirstWord() === WillowConstants.titleTag) - return titleNode ? titleNode.getContent() : "" + const nodes = this.topDownArray + const titleNode = nodes.find(node => node.firstWord === WillowConstants.titleTag) + return titleNode ? titleNode.content : "" } setWindowTitle(value) { - const nodes = this.getTopDownArray() - const headNode = nodes.find(node => node.getFirstWord() === WillowConstants.tags.head) + const nodes = this.topDownArray + const headNode = nodes.find(node => node.firstWord === WillowConstants.tags.head) headNode.touchNode(WillowConstants.titleTag).setContent(value) return this } @@ -18351,7 +18370,7 @@ class AbstractWillowBrowser extends stumpNode { // noop in willow } getPageHtml() { - return this.getHtmlStumpNode().toHtmlWithSuids() + return this.getHtmlStumpNode().asHtmlWithSuids() } getStumpNodeFromElement(el) {} setPasteHandler(fn) { @@ -18516,7 +18535,7 @@ class WillowBrowserShadow extends AbstractWillowShadow { return this } onShadowEventWithSelector(event, selector, fn) { - this.element.addEventListener(event, function(evt) { + this.element.addEventListener(event, function (evt) { let target = evt.target while (target !== null) { if (target.matches(selector)) { @@ -18667,13 +18686,13 @@ class RealWillowBrowser extends AbstractWillowBrowser { } _appendScript(url) { //https://bradb.net/blog/promise-based-js-script-loader/ - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { let resolved = false const scriptEl = document.createElement("script") scriptEl.type = "text/javascript" scriptEl.src = url scriptEl.async = true - scriptEl.onload = scriptEl.onreadystatechange = function() { + scriptEl.onload = scriptEl.onreadystatechange = function () { if (!resolved && (!this.readyState || this.readyState == "complete")) { resolved = true resolve(this) @@ -18754,7 +18773,7 @@ class RealWillowBrowser extends AbstractWillowBrowser { // Add the help, and then hopefull we'll get a dragover event on the dragOverHelp, then // 50ms later, add the dragleave handler, and from now on drag leave will only happen on the help // div - setTimeout(function() { + setTimeout(function () { bodyShadow.onShadowEvent(BrowserEvents.dragleave, dragleaveHandler) }, 50) } @@ -18786,7 +18805,7 @@ class RealWillowBrowser extends AbstractWillowBrowser { bodyShadow.onShadowEvent(BrowserEvents.dragover, dragoverHandler) bodyShadow.onShadowEvent(BrowserEvents.drop, dropHandler) // todo: why do we do this? - bodyShadow.onShadowEvent(BrowserEvents.dragenter, function(event) { + bodyShadow.onShadowEvent(BrowserEvents.dragenter, function (event) { event.preventDefault() event.stopPropagation() }) @@ -18829,13 +18848,13 @@ class RealWillowBrowser extends AbstractWillowBrowser { } class AbstractTheme { hakonToCss(str) { - const hakonProgram = new hakonNode(str) + const hakonProgram = new hakonParser(str) // console.log(hakonProgram.getAllErrors()) return hakonProgram.compile() } } class DefaultTheme extends AbstractTheme {} -class AbstractTreeComponent extends jtree.GrammarBackedNode { +class AbstractTreeComponentParser extends GrammarBackedNode { async startWhenReady() { if (this.isNodeJs()) return this.start() document.addEventListener( @@ -18901,16 +18920,16 @@ class AbstractTreeComponent extends jtree.GrammarBackedNode { _getHtmlOnlyNodes() { const nodes = [] this.willowBrowser.getHtmlStumpNode().deepVisit(node => { - if (node.getFirstWord() === "styleTag" || (node.getContent() || "").startsWith("<svg ")) return false + if (node.firstWord === "styleTag" || (node.content || "").startsWith("<svg ")) return false nodes.push(node) }) return nodes } getStumpNodeStringWithoutCssAndSvg() { // todo: cleanup. feels hacky. - const clone = new jtree.TreeNode(this.willowBrowser.getHtmlStumpNode().toString()) - clone.getTopDownArray().forEach(node => { - if (node.getFirstWord() === "styleTag" || (node.getContent() || "").startsWith("<svg ")) node.destroy() + const clone = new TreeNode(this.willowBrowser.getHtmlStumpNode().toString()) + clone.topDownArray.forEach(node => { + if (node.firstWord === "styleTag" || (node.content || "").startsWith("<svg ")) node.destroy() }) return clone.toString() } @@ -18932,7 +18951,7 @@ class AbstractTreeComponent extends jtree.GrammarBackedNode { this._onCommandWillRun() // todo: remove. currently used by ohayo let treeComponent = stumpNode.getStumpNodeTreeComponent() while (!treeComponent[commandMethod]) { - const parent = treeComponent.getParent() + const parent = treeComponent.parent if (parent === treeComponent) throw new Error(`Unknown command "${commandMethod}"`) if (!parent) debugger treeComponent = parent @@ -18954,28 +18973,28 @@ class AbstractTreeComponent extends jtree.GrammarBackedNode { this._executeCommandOnStumpNode(stumpNode, stumpNode.getStumpNodeAttr(attr)) return false } - bodyShadow.onShadowEventWithSelector(BrowserEvents.contextmenu, `[${WillowConstants.contextMenuCommand}]`, function(target, evt) { + bodyShadow.onShadowEventWithSelector(BrowserEvents.contextmenu, `[${WillowConstants.contextMenuCommand}]`, function (target, evt) { if (evt.ctrlKey) return true app._setMouseEvent(evt) // todo: remove? return checkAndExecute(target, WillowConstants.contextMenuCommand, evt) }) - bodyShadow.onShadowEventWithSelector(BrowserEvents.click, `[${WillowConstants.clickCommand}]`, function(target, evt) { + bodyShadow.onShadowEventWithSelector(BrowserEvents.click, `[${WillowConstants.clickCommand}]`, function (target, evt) { if (evt.shiftKey) return checkAndExecute(this, WillowConstants.shiftClickCommand, evt) app._setMouseEvent(evt) // todo: remove? return checkAndExecute(target, WillowConstants.clickCommand, evt) }) - bodyShadow.onShadowEventWithSelector(BrowserEvents.dblclick, `[${WillowConstants.doubleClickCommand}]`, function(target, evt) { + bodyShadow.onShadowEventWithSelector(BrowserEvents.dblclick, `[${WillowConstants.doubleClickCommand}]`, function (target, evt) { if (evt.target !== evt.currentTarget) return true // direct dblclicks only app._setMouseEvent(evt) // todo: remove? return checkAndExecute(target, WillowConstants.doubleClickCommand, evt) }) - bodyShadow.onShadowEventWithSelector(BrowserEvents.blur, `[${WillowConstants.blurCommand}]`, function(target, evt) { + bodyShadow.onShadowEventWithSelector(BrowserEvents.blur, `[${WillowConstants.blurCommand}]`, function (target, evt) { return checkAndExecute(target, WillowConstants.blurCommand, evt) }) - bodyShadow.onShadowEventWithSelector(BrowserEvents.keyup, `[${WillowConstants.keyUpCommand}]`, function(target, evt) { + bodyShadow.onShadowEventWithSelector(BrowserEvents.keyup, `[${WillowConstants.keyUpCommand}]`, function (target, evt) { return checkAndExecute(target, WillowConstants.keyUpCommand, evt) }) - bodyShadow.onShadowEventWithSelector(BrowserEvents.change, `[${WillowConstants.changeCommand}]`, function(target, evt) { + bodyShadow.onShadowEventWithSelector(BrowserEvents.change, `[${WillowConstants.changeCommand}]`, function (target, evt) { return checkAndExecute(target, WillowConstants.changeCommand, evt) }) } @@ -18994,7 +19013,7 @@ class AbstractTreeComponent extends jtree.GrammarBackedNode { toggleTreeComponentFrameworkDebuggerCommand() { // todo: move somewhere else? // todo: cleanup - const app = this.getRootNode() + const app = this.root const node = app.getNode("TreeComponentFrameworkDebuggerComponent") if (node) { node.unmountAndDestroy() @@ -19010,7 +19029,7 @@ class AbstractTreeComponent extends jtree.GrammarBackedNode { return "" } getTheme() { - if (!this.isRoot()) return this.getRootNode().getTheme() + if (!this.isRoot()) return this.root.getTheme() if (!this._theme) this._theme = new DefaultTheme() return this._theme } @@ -19025,7 +19044,7 @@ class AbstractTreeComponent extends jtree.GrammarBackedNode { }) } getMessageBuffer() { - if (!this._messageBuffer) this._messageBuffer = new jtree.TreeNode() + if (!this._messageBuffer) this._messageBuffer = new TreeNode() return this._messageBuffer } // todo: move this to tree class? or other higher level class? @@ -19039,11 +19058,11 @@ class AbstractTreeComponent extends jtree.GrammarBackedNode { // todo: cleanup! return this.addStumpCodeMessageToLog(`div class OhayoError - bern${jtree.TreeNode.nest(errorMessage, 2)}`) + bern${TreeNode.nest(errorMessage, 2)}`) } logMessageText(message = "") { const pre = `pre - bern${jtree.TreeNode.nest(message, 2)}` + bern${TreeNode.nest(message, 2)}` return this.addStumpCodeMessageToLog(pre) } unmount() { @@ -19067,14 +19086,14 @@ class AbstractTreeComponent extends jtree.GrammarBackedNode { class ${this.getCssClassNames().join(" ")}` } getCssClassNames() { - return this._getJavascriptPrototypeChainUpTo("AbstractTreeComponent") + return this._getJavascriptPrototypeChainUpTo("AbstractTreeComponentParser") } treeComponentWillMount() {} async treeComponentDidMount() { - AbstractTreeComponent._mountedTreeComponents++ + AbstractTreeComponentParser._mountedTreeComponents++ } treeComponentDidUnmount() { - AbstractTreeComponent._mountedTreeComponents-- + AbstractTreeComponentParser._mountedTreeComponents-- } treeComponentWillUnmount() {} getNewestTimeToRender() { @@ -19086,7 +19105,7 @@ class AbstractTreeComponent extends jtree.GrammarBackedNode { } async treeComponentDidUpdate() {} _getChildTreeComponents() { - return this.getChildrenByNodeConstructor(AbstractTreeComponent) + return this.getChildrenByParser(AbstractTreeComponentParser) } _hasChildrenTreeComponents() { return this._getChildTreeComponents().length > 0 @@ -19104,7 +19123,7 @@ class AbstractTreeComponent extends jtree.GrammarBackedNode { toPlainHtml(containerId) { return `<div id="${containerId}"> <style>${this.getTheme().hakonToCss(this.toHakonCode())}</style> -${new stumpNode(this.toStumpCode()).compile()} +${new stumpParser(this.toStumpCode()).compile()} </div>` } _updateAndGetUpdateReport() { @@ -19122,7 +19141,7 @@ ${new stumpNode(this.toStumpCode()).compile()} return reasonForUpdatingOrNot } _updateHtml() { - const stumpNodeToMountOn = this._htmlStumpNode.getParent() + const stumpNodeToMountOn = this._htmlStumpNode.parent const currentIndex = this._htmlStumpNode.getIndex() this._removeHtml() this._mountHtml(stumpNodeToMountOn, this._toLoadedOrLoadingStumpCode(), currentIndex) @@ -19135,7 +19154,7 @@ ${new stumpNode(this.toStumpCode()).compile()} toggle(firstWord, contentOptions) { const currentNode = this.getNode(firstWord) if (!contentOptions) return currentNode ? currentNode.unmountAndDestroy() : this.appendLine(firstWord) - const currentContent = currentNode === undefined ? undefined : currentNode.getContent() + const currentContent = currentNode === undefined ? undefined : currentNode.content const index = contentOptions.indexOf(currentContent) const newContent = index === -1 || index + 1 === contentOptions.length ? contentOptions[0] : contentOptions[index + 1] this.delete(firstWord) @@ -19147,7 +19166,7 @@ ${new stumpNode(this.toStumpCode()).compile()} } toggleAndRender(firstWord, contentOptions) { this.toggle(firstWord, contentOptions) - this.getRootNode().renderAndGetRenderReport() + this.root.renderAndGetRenderReport() } _getFirstOutdatedDependency(lastRenderedTime = this._getLastRenderedTime() || 0) { return this.getDependencies().find(dep => dep.getLineModifiedTime() > lastRenderedTime) @@ -19194,7 +19213,7 @@ ${new stumpNode(this.toStumpCode()).compile()} }) } toStumpLoadingCode() { - return `div Loading ${this.getFirstWord()}... + return `div Loading ${this.firstWord}... class ${this.getCssClassNames().join(" ")} id ${this.getTreeComponentId()}` } @@ -19233,10 +19252,10 @@ ${new stumpNode(this.toStumpCode()).compile()} // todo: only insert css once per class? have a set? this._cssStumpNode = this._getPageHeadStump().insertCssChildNode(`styleTag for ${this.constructor.name} - bern${jtree.TreeNode.nest(css, 2)}`) + bern${TreeNode.nest(css, 2)}`) } _getPageHeadStump() { - return this.getRootNode().willowBrowser.getHeadStumpNode() + return this.root.willowBrowser.getHeadStumpNode() } _removeCss() { if (!this._cssStumpNode) return this @@ -19275,11 +19294,11 @@ ${new stumpNode(this.toStumpCode()).compile()} } let str = `${this.getWord(0) || this.constructor.name} ${isUpdateOp ? "update" : "mount"} ${treeComponentUpdateReport.shouldUpdate} ${treeComponentUpdateReport.reason}` childResults.forEach(child => (str += "\n" + child.toString(1))) - return new jtree.TreeNode(str) + return new TreeNode(str) } } -AbstractTreeComponent._mountedTreeComponents = 0 -class TreeComponentFrameworkDebuggerComponent extends AbstractTreeComponent { +AbstractTreeComponentParser._mountedTreeComponents = 0 +class TreeComponentFrameworkDebuggerComponent extends AbstractTreeComponentParser { toHakonCode() { return `.TreeComponentFrameworkDebuggerComponent position fixed @@ -19301,7 +19320,7 @@ class TreeComponentFrameworkDebuggerComponent extends AbstractTreeComponent { opacity 1` } toStumpCode() { - const app = this.getRootNode() + const app = this.root return `div class TreeComponentFrameworkDebuggerComponent div x @@ -19311,13 +19330,13 @@ class TreeComponentFrameworkDebuggerComponent extends AbstractTreeComponent { span This app is powered by the a Tree Component Framework href https://github.com/treenotation/jtree/tree/main/treeComponentFramework - p ${app.getNumberOfLines()} components loaded. ${WillowBrowser._stumpsOnPage} stumps on page. + p ${app.numberOfLines} components loaded. ${WillowBrowser._stumpsOnPage} stumps on page. pre bern ${app.toString(3)}` } } -class AbstractGithubTriangleComponent extends AbstractTreeComponent { +class AbstractGithubTriangleComponent extends AbstractTreeComponentParser { constructor() { super(...arguments) this.githubLink = `https://github.com/treenotation/jtree` @@ -19338,6 +19357,6 @@ class AbstractGithubTriangleComponent extends AbstractTreeComponent { } } window.AbstractGithubTriangleComponent = AbstractGithubTriangleComponent -window.AbstractTreeComponent = AbstractTreeComponent +window.AbstractTreeComponentParser = AbstractTreeComponentParser window.WillowBrowser = WillowBrowser window.TreeComponentFrameworkDebuggerComponent = TreeComponentFrameworkDebuggerComponent diff --git a/dist/simoji.js b/dist/simoji.js index c2e84e3..3b2c6a6 100644 --- a/dist/simoji.js +++ b/dist/simoji.js @@ -3,284 +3,285 @@ const yodash = {} + yodash.parseInts = (arr, start) => arr.map((item, index) => (index >= start ? parseInt(item) : item)) yodash.getRandomAngle = randomNumberGenerator => { - const r1 = randomNumberGenerator() - const r2 = randomNumberGenerator() - if (r1 > 0.5) return r2 > 0.5 ? Directions.North : Directions.South - return r2 > 0.5 ? Directions.West : Directions.East + const r1 = randomNumberGenerator() + const r2 = randomNumberGenerator() + if (r1 > 0.5) return r2 > 0.5 ? Directions.North : Directions.South + return r2 > 0.5 ? Directions.West : Directions.East } yodash.flipAngle = angle => { - let newAngle = "" - if (angle.includes(Directions.North)) newAngle += Directions.South - else if (angle.includes(Directions.South)) newAngle += Directions.North - if (angle.includes(Directions.East)) newAngle += Directions.West - else if (angle.includes(Directions.West)) newAngle += Directions.East - return newAngle + let newAngle = "" + if (angle.includes(Directions.North)) newAngle += Directions.South + else if (angle.includes(Directions.South)) newAngle += Directions.North + if (angle.includes(Directions.East)) newAngle += Directions.West + else if (angle.includes(Directions.West)) newAngle += Directions.East + return newAngle } yodash.compare = (left, operator, right) => { - if (operator === "=") return left == right - if (operator === "<") return left < right - if (operator === ">") return left > right - if (operator === "<=") return left <= right - if (operator === ">=") return left >= right + if (operator === "=") return left == right + if (operator === "<") return left < right + if (operator === ">") return left > right + if (operator === "<=") return left <= right + if (operator === ">=") return left >= right - return false + return false } yodash.compileAgentClassDeclarationsAndMap = program => { - const clone = program.clone() - clone.filter(node => node.getNodeTypeId() !== NodeTypes.agentDefinitionNode).forEach(node => node.destroy()) - clone.agentKeywordMap = {} - clone.agentTypes.forEach((node, index) => (clone.agentKeywordMap[node.getWord(0)] = `simAgent${index}`)) - const compiled = clone.compile() - const agentMap = Object.keys(clone.agentKeywordMap) - .map(key => `"${key}":${clone.agentKeywordMap[key]}`) - .join(",") - return `${compiled} + const clone = program.clone() + clone.filter(node => node.parserId !== ParserTypes.agentDefinitionParser).forEach(node => node.destroy()) + clone.agentKeywordMap = {} + clone.agentTypes.forEach((node, index) => (clone.agentKeywordMap[node.firstWord] = `simAgent${index}`)) + const compiled = clone.compile() + const agentMap = Object.keys(clone.agentKeywordMap) + .map(key => `"${key}":${clone.agentKeywordMap[key]}`) + .join(",") + return `${compiled} const map = {${agentMap}}; map;` } yodash.patchExperimentAndReplaceSymbols = (program, experiment) => { - const clone = program.clone() - // drop experiment nodes - clone.filter(node => node.getNodeTypeId() === NodeTypes.experimentNode).forEach(node => node.destroy()) - // Append current experiment - if (experiment) clone.concat(experiment.childrenToString()) - // Build symbol table - const symbolTable = {} - clone - .filter(node => node.getNodeTypeId() === NodeTypes.settingDefinitionNode) - .forEach(node => { - symbolTable[node.getWord(0)] = node.getContent() - node.destroy() - }) - // Find and replace - let withVarsReplaced = clone.toString() - Object.keys(symbolTable).forEach(key => { - withVarsReplaced = withVarsReplaced.replaceAll(key, symbolTable[key]) - }) - return withVarsReplaced + const clone = program.clone() + // drop experiment nodes + clone.filter(node => node.parserId === ParserTypes.experimentParser).forEach(node => node.destroy()) + // Append current experiment + if (experiment) clone.concat(experiment.childrenToString()) + // Build symbol table + const symbolTable = {} + clone + .filter(node => node.parserId === ParserTypes.settingDefinitionParser) + .forEach(node => { + symbolTable[node.firstWord] = node.content + node.destroy() + }) + // Find and replace + let withVarsReplaced = clone.toString() + Object.keys(symbolTable).forEach(key => { + withVarsReplaced = withVarsReplaced.replaceAll(key, symbolTable[key]) + }) + return withVarsReplaced } yodash.getBestAngle = (targets, position) => { - let closest = Infinity - let target - targets.forEach(candidate => { - const pos = candidate.position - const distance = math.distance([pos.down, pos.right], [position.down, position.right]) - if (distance < closest) { - closest = distance - target = candidate - } - }) - const heading = target.position - return yodash.angle(position.down, position.right, heading.down, heading.right) + let closest = Infinity + let target + targets.forEach(candidate => { + const pos = candidate.position + const distance = math.distance([pos.down, pos.right], [position.down, position.right]) + if (distance < closest) { + closest = distance + target = candidate + } + }) + const heading = target.position + return yodash.angle(position.down, position.right, heading.down, heading.right) } yodash.angle = (cx, cy, ex, ey) => { - const dy = ey - cy - const dx = ex - cx - let theta = Math.atan2(dy, dx) // range (-PI, PI] - theta *= 180 / Math.PI // rads to degs, range (-180, 180] - //if (theta < 0) theta = 360 + theta; // range [0, 360) - let angle = "" - - if (Math.abs(theta) > 90) angle += Directions.North - else angle += Directions.South - if (theta < 0) angle += Directions.West - else angle += Directions.East - return angle + const dy = ey - cy + const dx = ex - cx + let theta = Math.atan2(dy, dx) // range (-PI, PI] + theta *= 180 / Math.PI // rads to degs, range (-180, 180] + //if (theta < 0) theta = 360 + theta; // range [0, 360) + let angle = "" + + if (Math.abs(theta) > 90) angle += Directions.North + else angle += Directions.South + if (theta < 0) angle += Directions.West + else angle += Directions.East + return angle } yodash.getRandomLocation = (rows, cols, randomNumberGenerator) => { - const maxRight = cols - const maxBottom = rows - const right = Math.round(randomNumberGenerator() * maxRight) - const down = Math.round(randomNumberGenerator() * maxBottom) - return { right, down } + const maxRight = cols + const maxBottom = rows + const right = Math.round(randomNumberGenerator() * maxRight) + const down = Math.round(randomNumberGenerator() * maxBottom) + return { right, down } } yodash.getRandomLocationHash = (rows, cols, occupiedSpots, randomNumberGenerator) => { - const { right, down } = yodash.getRandomLocation(rows, cols, randomNumberGenerator) - const hash = yodash.makePositionHash({ right, down }) - if (occupiedSpots && occupiedSpots.has(hash)) - return yodash.getRandomLocationHash(rows, cols, occupiedSpots, randomNumberGenerator) - return hash + const { right, down } = yodash.getRandomLocation(rows, cols, randomNumberGenerator) + const hash = yodash.makePositionHash({ right, down }) + if (occupiedSpots && occupiedSpots.has(hash)) + return yodash.getRandomLocationHash(rows, cols, occupiedSpots, randomNumberGenerator) + return hash } yodash.fill = (rows, cols, occupiedSpots, emoji) => { - const board = [] - while (rows >= 0) { - let col = cols - while (col >= 0) { - const hash = yodash.makePositionHash({ right: col, down: rows }) - col-- - if (occupiedSpots.has(hash)) continue - board.push(`${emoji} ${hash}`) - } - rows-- - } - return board.join("\n") + const board = [] + while (rows >= 0) { + let col = cols + while (col >= 0) { + const hash = yodash.makePositionHash({ right: col, down: rows }) + col-- + if (occupiedSpots.has(hash)) continue + board.push(`${emoji} ${hash}`) + } + rows-- + } + return board.join("\n") } yodash.positionsAdjacentTo = position => { - let { right, down } = position - const positions = [] - down-- - positions.push({ down, right }) - right-- - positions.push({ down, right }) - right++ - right++ - positions.push({ down, right }) - down++ - positions.push({ down, right }) - right-- - right-- - positions.push({ down, right }) - down++ - positions.push({ down, right }) - right++ - positions.push({ down, right }) - right++ - positions.push({ down, right }) - return positions + let { right, down } = position + const positions = [] + down-- + positions.push({ down, right }) + right-- + positions.push({ down, right }) + right++ + right++ + positions.push({ down, right }) + down++ + positions.push({ down, right }) + right-- + right-- + positions.push({ down, right }) + down++ + positions.push({ down, right }) + right++ + positions.push({ down, right }) + right++ + positions.push({ down, right }) + return positions } yodash.makePositionHash = position => `${position.down + "⬇️ " + position.right + "➡️"}` yodash.makeRectangle = (character = "🧱", width = 20, height = 20, startRight = 0, startDown = 0) => { - if (width < 1 || height < 1) { - return "" - } - const cells = [] - let row = 0 - while (row < height) { - let col = 0 - while (col < width) { - const isPerimeter = row === 0 || row === height - 1 || col === 0 || col === width - 1 - if (isPerimeter) - cells.push( - `${character} ${yodash.makePositionHash({ - down: startDown + row, - right: startRight + col - })}` - ) - col++ - } - row++ - } - return cells.join("\n") + if (width < 1 || height < 1) { + return "" + } + const cells = [] + let row = 0 + while (row < height) { + let col = 0 + while (col < width) { + const isPerimeter = row === 0 || row === height - 1 || col === 0 || col === width - 1 + if (isPerimeter) + cells.push( + `${character} ${yodash.makePositionHash({ + down: startDown + row, + right: startRight + col + })}` + ) + col++ + } + row++ + } + return cells.join("\n") } yodash.parsePosition = words => { - return { - down: parseInt(words.find(word => word.includes("⬇️")).slice(0, -1)), - right: parseInt(words.find(word => word.includes("➡️")).slice(0, -1)) - } + return { + down: parseInt(words.find(word => word.includes("⬇️")).slice(0, -1)), + right: parseInt(words.find(word => word.includes("➡️")).slice(0, -1)) + } } yodash.draw = str => { - const lines = str.split("\n") - const output = [] - for (let index = 0; index < lines.length; index++) { - const words = lines[index].split(" ") - for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { - const word = words[wordIndex] - if (word !== "") output.push(`${word} ${yodash.makePositionHash({ down: index, right: wordIndex })}`) - } - } - return output.join("\n") + const lines = str.split("\n") + const output = [] + for (let index = 0; index < lines.length; index++) { + const words = lines[index].split(" ") + for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { + const word = words[wordIndex] + if (word !== "") output.push(`${word} ${yodash.makePositionHash({ down: index, right: wordIndex })}`) + } + } + return output.join("\n") } yodash.updateOccupiedSpots = (board, occupiedSpots) => { - new TreeNode(board).forEach(line => { - occupiedSpots.add(yodash.makePositionHash(yodash.parsePosition(line.getWords()))) - }) + new TreeNode(board).forEach(line => { + occupiedSpots.add(yodash.makePositionHash(yodash.parsePosition(line.words))) + }) } yodash.getAllAvailableSpots = (rows, cols, occupiedSpots, rowStart = 0, colStart = 0) => { - const availablePositions = [] - let down = rows - while (down >= rowStart) { - let right = cols - while (right >= colStart) { - const hash = yodash.makePositionHash({ right, down }) - if (!occupiedSpots.has(hash)) availablePositions.push({ right, down, hash }) - right-- - } - down-- - } - return availablePositions + const availablePositions = [] + let down = rows + while (down >= rowStart) { + let right = cols + while (right >= colStart) { + const hash = yodash.makePositionHash({ right, down }) + if (!occupiedSpots.has(hash)) availablePositions.push({ right, down, hash }) + right-- + } + down-- + } + return availablePositions } yodash.parsePercent = str => parseFloat(str.replace("%", "")) / 100 yodash.insertClusteredRandomAgents = ( - randomNumberGenerator, - amount, - char, - rows, - cols, - occupiedSpots, - originRow, - originColumn + randomNumberGenerator, + amount, + char, + rows, + cols, + occupiedSpots, + originRow, + originColumn ) => { - const availableSpots = yodash.getAllAvailableSpots(rows, cols, occupiedSpots) - const spots = yodash.sampleFrom(availableSpots, amount * 10, randomNumberGenerator) - const origin = originColumn - ? { down: parseInt(originRow), right: parseInt(originColumn) } - : yodash.getRandomLocation(rows, cols, randomNumberGenerator) - const sortedByDistance = lodash.sortBy(spots, spot => - math.distance([origin.down, origin.right], [spot.down, spot.right]) - ) - - return sortedByDistance - .slice(0, amount) - .map(spot => { - const { hash } = spot - occupiedSpots.add(hash) - return `${char} ${hash}` - }) - .join("\n") + const availableSpots = yodash.getAllAvailableSpots(rows, cols, occupiedSpots) + const spots = yodash.sampleFrom(availableSpots, amount * 10, randomNumberGenerator) + const origin = originColumn + ? { down: parseInt(originRow), right: parseInt(originColumn) } + : yodash.getRandomLocation(rows, cols, randomNumberGenerator) + const sortedByDistance = lodash.sortBy(spots, spot => + math.distance([origin.down, origin.right], [spot.down, spot.right]) + ) + + return sortedByDistance + .slice(0, amount) + .map(spot => { + const { hash } = spot + occupiedSpots.add(hash) + return `${char} ${hash}` + }) + .join("\n") } yodash.getRandomNumberGenerator = seed => () => { - const semiRand = Math.sin(seed++) * 10000 - return semiRand - Math.floor(semiRand) + const semiRand = Math.sin(seed++) * 10000 + return semiRand - Math.floor(semiRand) } yodash.sampleFrom = (collection, howMany, randomNumberGenerator) => - shuffleArray(collection, randomNumberGenerator).slice(0, howMany) + shuffleArray(collection, randomNumberGenerator).slice(0, howMany) const shuffleArray = (array, randomNumberGenerator) => { - const clonedArr = array.slice() - for (let index = clonedArr.length - 1; index > 0; index--) { - const replacerIndex = Math.floor(randomNumberGenerator() * (index + 1)) - ;[clonedArr[index], clonedArr[replacerIndex]] = [clonedArr[replacerIndex], clonedArr[index]] - } - return clonedArr + const clonedArr = array.slice() + for (let index = clonedArr.length - 1; index > 0; index--) { + const replacerIndex = Math.floor(randomNumberGenerator() * (index + 1)) + ;[clonedArr[index], clonedArr[replacerIndex]] = [clonedArr[replacerIndex], clonedArr[index]] + } + return clonedArr } yodash.pick = (tree, fields) => { - const newTree = tree.clone() - const map = TreeUtils.arrayToMap(fields) - newTree.forEach(node => { - if (!map[node.getWord(0)]) node.destroy() - }) + const newTree = tree.clone() + const map = Utils.arrayToMap(fields) + newTree.forEach(node => { + if (!map[node.firstWord]) node.destroy() + }) - return newTree + return newTree } yodash.flatten = tree => { - const newTree = new jtree.TreeNode() - tree.forEach(node => node.forEach(child => newTree.appendNode(child))) - return newTree + const newTree = new TreeNode() + tree.forEach(node => node.forEach(child => newTree.appendNode(child))) + return newTree } window.yodash = yodash @@ -291,7 +292,7 @@ window.yodash = yodash var jQuery -class AbstractContextMenuComponent extends AbstractTreeComponent { +class AbstractContextMenuComponent extends AbstractTreeComponentParser { toHakonCode() { const theme = this.getTheme() return `.AbstractContextMenuComponent @@ -314,14 +315,14 @@ class AbstractContextMenuComponent extends AbstractTreeComponent { } toStumpCode() { - return new jtree.TreeNode(`div + return new TreeNode(`div class AbstractContextMenuComponent {constructorName} {body}`).templateToString({ constructorName: this.constructor.name, body: this.getContextMenuBodyStumpCode() }) } treeComponentDidMount() { const container = this.getStumpNode() - const app = this.getRootNode() + const app = this.root const { willowBrowser } = app const bodyShadow = willowBrowser.getBodyStumpNode().getShadow() const unmountOnClick = function() { @@ -343,7 +344,7 @@ class AbstractContextMenuComponent extends AbstractTreeComponent { top = undefined get left() { - return this.getRootNode().getMouseEvent().clientX + return this.root.getMouseEvent().clientX } _getContextMenuPosition(windowWidth, windowHeight, x, y, shadow) { @@ -378,7 +379,7 @@ window.AbstractContextMenuComponent = AbstractContextMenuComponent const SelectedClass = "selected" -class Agent extends jtree.TreeNode { +class Agent extends TreeNode { get name() { return this._name ?? this.icon } @@ -390,8 +391,8 @@ class Agent extends jtree.TreeNode { } get definitionWithBehaviors() { - if (!this.behaviors.length) return this.board.simojiProgram.getNode(this.getWord(0)) - const behaviors = yodash.flatten(yodash.pick(this.board.simojiProgram, [this.getWord(0), ...this.behaviors])) + if (!this.behaviors.length) return this.board.simojiProgram.getNode(this.firstWord) + const behaviors = yodash.flatten(yodash.pick(this.board.simojiProgram, [this.firstWord, ...this.behaviors])) return behaviors } @@ -412,7 +413,7 @@ class Agent extends jtree.TreeNode { const { neighorCount } = this neighborConditions.forEach(conditionAndCommandsBlock => { - const [emoji, operator, count] = conditionAndCommandsBlock.getWords() + const [emoji, operator, count] = conditionAndCommandsBlock.words const actual = neighorCount[emoji] if (!yodash.compare(actual ?? 0, operator, count)) return conditionAndCommandsBlock.forEach(command => this._executeCommand(this, command)) @@ -430,7 +431,7 @@ class Agent extends jtree.TreeNode { for (let pos of yodash.positionsAdjacentTo(this.position)) { const hits = agentPositionMap.get(yodash.makePositionHash(pos)) ?? [] for (let target of hits) { - const targetId = target.getWord(0) + const targetId = target.firstWord const commandBlock = touchMap.getNode(targetId) if (commandBlock) { commandBlock.forEach(command => this._executeCommand(target, command)) @@ -446,7 +447,7 @@ class Agent extends jtree.TreeNode { this.getCommandBlocks(Keywords.onHit).forEach(hitMap => { if (this.skip(hitMap.getWord(1))) return targets.forEach(target => { - const targetId = target.getWord(0) + const targetId = target.firstWord const commandBlock = hitMap.getNode(targetId) if (commandBlock) commandBlock.forEach(command => this._executeCommand(target, command)) }) @@ -458,7 +459,7 @@ class Agent extends jtree.TreeNode { } _executeCommand(target, instruction) { - const commandName = instruction.getWord(0) + const commandName = instruction.firstWord if (this[commandName]) this[commandName](target, instruction) // board commands else this.board[commandName](instruction) @@ -506,7 +507,7 @@ class Agent extends jtree.TreeNode { } _replaceWith(newObject) { - this.getParent().appendLine(`${newObject} ${this.positionHash}`) + this.parent.appendLine(`${newObject} ${this.positionHash}`) this.remove() } @@ -556,10 +557,6 @@ class Agent extends jtree.TreeNode { } } - get root() { - return this.getRootNode() - } - set position(value) { if (this.board.isSolidAgent(value)) return this.bouncy ? this.bounce() : this const newLine = this.getLine() @@ -570,7 +567,7 @@ class Agent extends jtree.TreeNode { } get board() { - return this.getParent() + return this.parent } get maxRight() { @@ -596,7 +593,7 @@ class Agent extends jtree.TreeNode { } get position() { - return yodash.parsePosition(this.getWords()) + return yodash.parsePosition(this.words) } get positionHash() { @@ -604,7 +601,7 @@ class Agent extends jtree.TreeNode { } get gridSize() { - return this.getParent().gridSize + return this.parent.gridSize } get selected() { @@ -645,8 +642,9 @@ class Agent extends jtree.TreeNode { get inlineStyle() { const { gridSize, health } = this const opacity = health === undefined ? "" : `opacity:${this.health / this.startHealth};` - return `top:${this.top * gridSize}px;left:${this.left * - gridSize}px;font-size:${gridSize}px;line-height: ${gridSize}px;${opacity};${this.style ?? ""}` + return `top:${this.top * gridSize}px;left:${ + this.left * gridSize + }px;font-size:${gridSize}px;line-height: ${gridSize}px;${opacity};${this.style ?? ""}` } toElement() { @@ -682,7 +680,7 @@ class Agent extends jtree.TreeNode { kickIt(target) { target.angle = this.angle - target.tickStack = new jtree.TreeNode(`1 + target.tickStack = new TreeNode(`1 move move move @@ -715,7 +713,7 @@ class Agent extends jtree.TreeNode { } narrate(subject, command) { - this.root.log(`${this.getWord(0)} ${command.getContent()}`) + this.root.log(`${this.firstWord} ${command.content}`) } shoot() { @@ -802,12 +800,12 @@ window.Agent = Agent -class AgentPaletteComponent extends AbstractTreeComponent { +class AgentPaletteComponent extends AbstractTreeComponentParser { toStumpCode() { - const root = this.getRootNode() + const root = this.root const { agentToInsert } = root const items = this.paletteItems - .map(item => item.getWord(0)) + .map(item => item.firstWord) .map( word => ` div ${word} class ${agentToInsert === word ? "ActiveAgent" : ""} @@ -820,16 +818,16 @@ ${items}` } get paletteItems() { - return this.getRootNode().allAgentTypes.filter(item => !item.has("noPalette")) + return this.root.allAgentTypes.filter(item => !item.has("noPalette")) } changeAgentBrushCommand(x) { - this.getRootNode().changeAgentBrushCommand(x) + this.root.changeAgentBrushCommand(x) this.setContent(Date.now()).renderAndGetRenderReport() } getDependencies() { - return [this.getRootNode().board] + return [this.root.board] } } @@ -847,8 +845,8 @@ let nodeJsPrefix = "" // prettier-ignore -class BoardErrorNode extends AbstractTreeComponent { - _isErrorNodeType() { +class BoardErrorParser extends AbstractTreeComponentParser { + _isErrorParser() { return true } toStumpCode() { @@ -858,17 +856,17 @@ class BoardErrorNode extends AbstractTreeComponent { } } -class leftStartPosition extends jtree.TreeNode { +class leftStartPosition extends TreeNode { get width() { return parseInt(this.getWord(1)) } } -class BoardComponent extends AbstractTreeComponent { +class BoardComponent extends AbstractTreeComponentParser { // override default parser creation. _getParser() { if (!this._parser) - this._parser = new jtree.TreeNode.Parser(BoardErrorNode, { + this._parser = new TreeNode.ParserCombinator(BoardErrorParser, { ...this.agentMap, GridComponent, BoardStyleComponent, @@ -909,7 +907,7 @@ class BoardComponent extends AbstractTreeComponent { } get populationCsv() { - const csv = new TreeNode(this._populationCounts).toCsv() + const csv = new TreeNode(this._populationCounts).asCsv // add 0's for missing values return csv .split("\n") @@ -939,7 +937,7 @@ class BoardComponent extends AbstractTreeComponent { if (blocks) blocks.forEach(block => block - .filter(node => node.doesExtend(NodeTypes.abstractInjectCommandNode)) + .filter(node => node.doesExtend(ParserTypes.abstractInjectCommandParser)) .forEach(command => this.runInjectCommand(command)) ) } @@ -1005,7 +1003,7 @@ class BoardComponent extends AbstractTreeComponent { if (this.resetAfterLoop) { this.resetAfterLoop = false - this.getRootNode().resetAllCommand() + this.root.resetAllCommand() } } @@ -1037,7 +1035,7 @@ class BoardComponent extends AbstractTreeComponent { } get root() { - return this.getParent() + return this.parent } get ticksPerSecond() { @@ -1048,10 +1046,10 @@ class BoardComponent extends AbstractTreeComponent { occupiedSpots = new Set() runInjectCommand(command) { - this[command.getNodeTypeId()](command) + this[command.parserId](command) } - insertClusterNode(commandNode) { + insertClusterParser(commandNode) { this.concat( yodash.insertClusteredRandomAgents( this.randomNumberGenerator, @@ -1066,28 +1064,28 @@ class BoardComponent extends AbstractTreeComponent { ) } - insertAtNode(commandNode) { + insertAtParser(commandNode) { this.appendLine(`${commandNode.getWord(1)} ${commandNode.getWord(3)} ${commandNode.getWord(2)}`) // TODO: update occupied spots cache? } - rectangleDrawNode(commandNode) { - const newLines = yodash.makeRectangle(...yodash.parseInts(commandNode.getWords().slice(1), 1)) + rectangleDrawParser(commandNode) { + const newLines = yodash.makeRectangle(...yodash.parseInts(commandNode.words.slice(1), 1)) this.concat(newLines) // TODO: update occupied spots cache? } - pasteDrawNode(commandNode) { + pasteDrawParser(commandNode) { const newSpots = new TreeNode(commandNode.childrenToString()) yodash.updateOccupiedSpots(newSpots, this.occupiedSpots) this.concat(newSpots) } - fillNode(commandNode) { + fillParser(commandNode) { this.concat(yodash.fill(this.rows, this.cols, this.occupiedSpots, commandNode.getWord(1))) } - drawNode(commandNode) { + drawParser(commandNode) { const { occupiedSpots } = this const spots = yodash.draw(commandNode.childrenToString()) yodash.updateOccupiedSpots(spots, occupiedSpots) @@ -1105,7 +1103,7 @@ class BoardComponent extends AbstractTreeComponent { return this._rng } - insertNode(commandNode) { + insertParser(commandNode) { const { rows, cols, occupiedSpots } = this const emoji = commandNode.getWord(2) let amount = commandNode.getWord(1) @@ -1128,7 +1126,7 @@ class BoardComponent extends AbstractTreeComponent { const emoji = commands.getWord(1) if (emoji && this.has(emoji)) return commands.forEach(instruction => { - this[instruction.getWord(0)](instruction) + this[instruction.firstWord](instruction) }) }) } @@ -1138,7 +1136,7 @@ class BoardComponent extends AbstractTreeComponent { const probability = commands.getWord(1) if (probability && this.randomNumberGenerator() > parseFloat(probability)) return commands.forEach(instruction => { - this[instruction.getWord(0)](instruction) + this[instruction.firstWord](instruction) }) }) } @@ -1152,7 +1150,7 @@ class BoardComponent extends AbstractTreeComponent { } get agents() { - return this.getTopDownArray().filter(node => node instanceof Agent) + return this.topDownArray.filter(node => node instanceof Agent) } get agentPositionMap() { @@ -1244,7 +1242,7 @@ class BoardComponent extends AbstractTreeComponent { get experimentTitle() { if (!this.hasMultipleBoards) return "" - return this.root.mainExperiment.findNodes(Keywords.experiment)[this.boardIndex].getContent() ?? "" + return this.root.mainExperiment.findNodes(Keywords.experiment)[this.boardIndex].content ?? "" } startInterval() { @@ -1291,7 +1289,7 @@ class BoardComponent extends AbstractTreeComponent { } alert(command) { - const message = command.getContent() + const message = command.content if (!this.isNodeJs()) // todo: willow should shim this alert(message) @@ -1309,13 +1307,13 @@ class BoardComponent extends AbstractTreeComponent { } log(command) { - this.root.log(command.getContent()) + this.root.log(command.content) } } -class BoardStyleComponent extends AbstractTreeComponent { - createParser() { - return new jtree.TreeNode.Parser(TreeNode) +class BoardStyleComponent extends AbstractTreeComponentParser { + createParserCombinator() { + return new TreeNode.ParserCombinator(TreeNode) } toStumpCode() { @@ -1340,9 +1338,9 @@ window.BoardComponent = BoardComponent -class BottomBarComponent extends AbstractTreeComponent { - createParser() { - return new jtree.TreeNode.Parser(undefined, { +class BottomBarComponent extends AbstractTreeComponentParser { + createParserCombinator() { + return new TreeNode.ParserCombinator(undefined, { PlayButtonComponent, ReportButtonComponent, ResetButtonComponent @@ -1355,15 +1353,15 @@ window.BottomBarComponent = BottomBarComponent -class EditorHandleComponent extends AbstractTreeComponent { +class EditorHandleComponent extends AbstractTreeComponentParser { get left() { - return this.getRootNode().editor.width + return this.root.editor.width } makeDraggable() { if (this.isNodeJs()) return - const root = this.getRootNode() + const root = this.root jQuery(this.getStumpNode().getShadow().element).draggable({ axis: "x", drag: function(event, ui) { @@ -1392,7 +1390,7 @@ class EditorHandleComponent extends AbstractTreeComponent { } getDependencies() { - return [this.getRootNode().editor] + return [this.root.editor] } } @@ -1401,7 +1399,7 @@ window.EditorHandleComponent = EditorHandleComponent -const ExampleSims = new jtree.TreeNode() +const ExampleSims = new TreeNode() // prettier-ignore @@ -1414,7 +1412,8 @@ window.ExampleSims = ExampleSims -const Categories = new jtree.TreeNode(`🦠 Epidemiology + +const Categories = new TreeNode(`🦠 Epidemiology virus covid19 🌲 Forests @@ -1442,10 +1441,10 @@ class ExampleMenuComponent extends AbstractContextMenuComponent { return category .map(node => { - const name = node.getFirstWord() + const name = node.firstWord const program = ExampleSims.getNode(name) const icon = program.childrenToString().match(/(\p{Extended_Pictographic}+)/u)[1] - const properName = jtree.Utils.ucfirst(name) + const properName = Utils.ucfirst(name) return `a ${icon} &nbsp; ${properName} clickCommand loadExampleCommand ${name} class ExampleButton` @@ -1456,17 +1455,17 @@ class ExampleMenuComponent extends AbstractContextMenuComponent { // Align these to below and to the left of the clicked button top = 28 get left() { - const evt = this.getRootNode().getMouseEvent() + const evt = this.root.getMouseEvent() return evt.clientX - evt.offsetX } } -class ExamplesComponent extends AbstractTreeComponent { +class ExamplesComponent extends AbstractTreeComponentParser { toStumpCode() { const categories = Categories.map(category => { - const icon = category.getFirstWord() - const name = category.getContent() - const firstFile = category.nodeAt(0).getFirstWord() + const icon = category.firstWord + const name = category.content + const firstFile = category.nodeAt(0).firstWord return ` a ${icon} href index.html#example%20${firstFile} title ${name} @@ -1478,10 +1477,10 @@ ${categories}` } async openCategoryCommand(icon) { - const root = this.getRootNode() + const root = this.root const category = Categories.getNode(icon) - const firstFile = category.nodeAt(0).getFirstWord() - this.getRootNode().toggleAndRender(`${ExampleMenuComponent.name} ${icon}`) + const firstFile = category.nodeAt(0).firstWord + this.root.toggleAndRender(`${ExampleMenuComponent.name} ${icon}`) } } @@ -1493,9 +1492,9 @@ window.ExampleMenuComponent = ExampleMenuComponent -class GridComponent extends AbstractTreeComponent { +class GridComponent extends AbstractTreeComponentParser { gridClickCommand(down, right) { - return this.getParent().insertAgentAtCommand(right, down) + return this.parent.insertAgentAtCommand(right, down) } makeBlock(down, right, gridSize) { @@ -1506,7 +1505,7 @@ class GridComponent extends AbstractTreeComponent { } toStumpCode() { - const { cols, rows, gridSize } = this.getParent() + const { cols, rows, gridSize } = this.parent let blocks = "" let rs = rows while (rs >= 0) { @@ -1530,7 +1529,7 @@ window.GridComponent = GridComponent -class AbstractModalTreeComponent extends AbstractTreeComponent { +class AbstractModalTreeComponent extends AbstractTreeComponentParser { toHakonCode() { return `.modalBackground position fixed @@ -1568,7 +1567,7 @@ class AbstractModalTreeComponent extends AbstractTreeComponent { } toStumpCode() { - return new jtree.TreeNode(`section + return new TreeNode(`section clickCommand unmountAndDestroyCommand class modalBackground section @@ -1595,9 +1594,9 @@ window.HelpModalComponent = HelpModalComponent -class PlayButtonComponent extends AbstractTreeComponent { +class PlayButtonComponent extends AbstractTreeComponentParser { get isStarted() { - return this.getRootNode().isRunning + return this.root.isRunning } toStumpCode() { @@ -1612,7 +1611,7 @@ window.PlayButtonComponent = PlayButtonComponent -class ReportButtonComponent extends AbstractTreeComponent { +class ReportButtonComponent extends AbstractTreeComponentParser { toStumpCode() { return `span Δ title Generate Report @@ -1626,7 +1625,7 @@ window.ReportButtonComponent = ReportButtonComponent -class ResetButtonComponent extends AbstractTreeComponent { +class ResetButtonComponent extends AbstractTreeComponentParser { toStumpCode() { return `span ≪ title Clear and reset @@ -1635,8 +1634,8 @@ class ResetButtonComponent extends AbstractTreeComponent { } resetAllCommand() { - this.getRootNode().pauseAllCommand() - this.getRootNode().resetAllCommand() + this.root.pauseAllCommand() + this.root.resetAllCommand() } } @@ -1647,12 +1646,12 @@ window.ResetButtonComponent = ResetButtonComponent -class RightBarComponent extends AbstractTreeComponent { - createParser() { - return new jtree.TreeNode.Parser(undefined, { - AgentPaletteComponent - }) - } +class RightBarComponent extends AbstractTreeComponentParser { + createParserCombinator() { + return new TreeNode.ParserCombinator(undefined, { + AgentPaletteComponent + }) + } } window.RightBarComponent = RightBarComponent @@ -1660,7 +1659,7 @@ window.RightBarComponent = RightBarComponent -class ShareComponent extends AbstractTreeComponent { +class ShareComponent extends AbstractTreeComponentParser { toStumpCode() { return `div class ShareComponent @@ -1671,13 +1670,13 @@ class ShareComponent extends AbstractTreeComponent { } getDependencies() { - return [this.getRootNode().firstProgram] + return [this.root.firstProgram] } get link() { const url = new URL(typeof location === "undefined" ? "http://localhost/" : location.href) // todo: TCF should provide shim for this url.hash = "" - return url.toString() + this.getRootNode().urlHash + return url.toString() + this.root.urlHash } } @@ -1699,7 +1698,7 @@ class CodeMirrorShim { } } -class SimEditorComponent extends AbstractTreeComponent { +class SimEditorComponent extends AbstractTreeComponentParser { toStumpCode() { return `div class ${SimEditorComponent.name} @@ -1711,9 +1710,9 @@ class SimEditorComponent extends AbstractTreeComponent { id codeErrorsConsole` } - createParser() { - return new jtree.TreeNode.Parser(undefined, { - value: jtree.TreeNode + createParserCombinator() { + return new TreeNode.ParserCombinator(undefined, { + value: TreeNode }) } @@ -1728,11 +1727,11 @@ class SimEditorComponent extends AbstractTreeComponent { const code = this.codeMirrorValue if (this._code === code) return this._code = code - const root = this.getRootNode() + const root = this.root root.pauseAllCommand() // this._updateLocalStorage() - this.program = new simojiCompiler(code) + this.program = new simojiParser(code) const errs = this.program.getAllErrors() const errMessage = errs.length ? `${errs.length} errors` : "&nbsp;" @@ -1755,7 +1754,7 @@ class SimEditorComponent extends AbstractTreeComponent { this._onCodeKeyUp() }) this.codeWidgets.push( - this.codeMirrorInstance.addLineWidget(err.getLineNumber() - 1, el, { coverGutter: false, noHScroll: false }) + this.codeMirrorInstance.addLineWidget(err.lineNumber - 1, el, { coverGutter: false, noHScroll: false }) ) }) const info = this.codeMirrorInstance.getScrollInfo() @@ -1770,7 +1769,7 @@ class SimEditorComponent extends AbstractTreeComponent { } loadFromEditor() { - this.getRootNode().loadNewSim(this._code) + this.root.loadNewSim(this._code) } get simCode() { @@ -1801,12 +1800,7 @@ class SimEditorComponent extends AbstractTreeComponent { _initCodeMirror() { if (this.isNodeJs()) return (this.codeMirrorInstance = new CodeMirrorShim()) - this.codeMirrorInstance = new jtree.TreeNotationCodeMirrorMode( - "custom", - () => simojiCompiler, - undefined, - CodeMirror - ) + this.codeMirrorInstance = new GrammarCodeMirrorMode("custom", () => simojiParser, undefined, CodeMirror) .register() .fromTextAreaWithAutocomplete(document.getElementById("EditorTextarea"), { lineWrapping: false, @@ -1868,8 +1862,8 @@ const MIN_GRID_ROWS = 10 // prettier-ignore -class githubTriangleComponent extends AbstractTreeComponent { - githubLink = `https://github.com/publicdomaincompany/simoji` +class githubTriangleComponent extends AbstractTreeComponentParser { + githubLink = `https://github.com/breck7/simoji` toHakonCode() { return `.AbstractGithubTriangleComponent display block @@ -1889,8 +1883,8 @@ class githubTriangleComponent extends AbstractTreeComponent { } } -class ErrorNode extends AbstractTreeComponent { - _isErrorNodeType() { +class ErrorParser extends AbstractTreeComponentParser { + _isErrorParser() { return true } toStumpCode() { @@ -1900,9 +1894,9 @@ class ErrorNode extends AbstractTreeComponent { } } -class SimojiApp extends AbstractTreeComponent { - createParser() { - return new jtree.TreeNode.Parser(ErrorNode, { +class SimojiApp extends AbstractTreeComponentParser { + createParserCombinator() { + return new TreeNode.ParserCombinator(ErrorParser, { TopBarComponent, githubTriangleComponent, SimEditorComponent, @@ -1959,7 +1953,7 @@ class SimojiApp extends AbstractTreeComponent { try { program - .filter(node => node.doesExtend(NodeTypes.abstractInjectCommandNode)) + .filter(node => node.doesExtend(ParserTypes.abstractInjectCommandParser)) .forEach(command => board.runInjectCommand(command)) } catch (err) { if (this.verbose) console.error(err) @@ -2040,8 +2034,8 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : } dumpErrorsCommand() { - const errs = new simojiCompiler(this.simCode).getAllErrors() - console.log(new jtree.TreeNode(errs.map(err => err.toObject())).toFormattedTable(200)) + const errs = new simojiParser(this.simCode).getAllErrors() + console.log(new TreeNode(errs.map(err => err.toObject())).toFormattedTable(200)) } get boards() { @@ -2053,7 +2047,7 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : } get mainExperiment() { - return new simojiCompiler(this.simCode) + return new simojiParser(this.simCode) } get simojiPrograms() { @@ -2066,7 +2060,7 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : this._simojiPrograms.push(yodash.patchExperimentAndReplaceSymbols(mainExperiment, experiment)) }) // Evaluate the variables - this._simojiPrograms = this._simojiPrograms.map(program => new simojiCompiler(program.toString())) + this._simojiPrograms = this._simojiPrograms.map(program => new simojiParser(program.toString())) } return this._simojiPrograms } @@ -2110,7 +2104,7 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : const keyboardShortcuts = this._getKeyboardShortcuts() Object.keys(keyboardShortcuts).forEach(key => { - willowBrowser.getMousetrap().bind(key, function(evt) { + willowBrowser.getMousetrap().bind(key, function (evt) { keyboardShortcuts[key]() // todo: handle the below when we need to if (evt.preventDefault) evt.preventDefault() @@ -2147,14 +2141,7 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : if (!this.selection.length) return "" - const str = this.selection - .map(agent => - agent - .getWords() - .slice(0, 3) - .join(" ") - ) - .join("\n") + const str = this.selection.map(agent => agent.words.slice(0, 3).join(" ")).join("\n") evt.preventDefault() evt.clipboardData.setData("text/plain", str) @@ -2213,7 +2200,7 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : } get urlHash() { - const tree = new jtree.TreeNode() + const tree = new TreeNode() tree.appendLineAndChildren(UrlKeys.simoji, this.simCode ?? "") return "#" + encodeURIComponent(tree.toString()) } @@ -2292,12 +2279,12 @@ ${styleNode ? styleNode.toString().replace("style", BoardStyleComponent.name) : get isSnapshotOn() { // technically also needs rows and column settings - return new jtree.TreeNode(this.simCode).has(Keywords.seed) + return new TreeNode(this.simCode).has(Keywords.seed) } // Save the current random play for reproducibility and shareability snapShotCommand() { - const newCode = new jtree.TreeNode(this.simCode) + const newCode = new TreeNode(this.simCode) const boards = this.boards // todo: buggy. we should rename the board class to experiment, or rename experiment keyword to board. @@ -2383,7 +2370,7 @@ SimojiApp.setupApp = (simojiCode, windowWidth = 1000, windowHeight = 1000) => { typeof localStorage !== "undefined" ? localStorage.getItem(LocalStorageKeys.editorStartWidth) ?? SIZES.EDITOR_WIDTH : SIZES.EDITOR_WIDTH - const startState = new jtree.TreeNode(`${githubTriangleComponent.name} + const startState = new TreeNode(`${githubTriangleComponent.name} ${TopBarComponent.name} ${LogoComponent.name} ${ShareComponent.name} @@ -2413,13 +2400,13 @@ window.SimojiApp = SimojiApp -class TitleComponent extends AbstractTreeComponent { +class TitleComponent extends AbstractTreeComponentParser { get question() { return this.app.mainExperiment.get(Keywords.question) ?? "" } get app() { - return this.getRootNode() + return this.root } getDependencies() { @@ -2443,9 +2430,9 @@ window.TitleComponent = TitleComponent -class TopBarComponent extends AbstractTreeComponent { - createParser() { - return new jtree.TreeNode.Parser(undefined, { +class TopBarComponent extends AbstractTreeComponentParser { + createParserCombinator() { + return new TreeNode.ParserCombinator(undefined, { LogoComponent, ShareComponent, ExamplesComponent @@ -2453,7 +2440,7 @@ class TopBarComponent extends AbstractTreeComponent { } } -class LogoComponent extends AbstractTreeComponent { +class LogoComponent extends AbstractTreeComponentParser { toStumpCode() { return `a ❔ href cheatSheet.html @@ -2462,7 +2449,7 @@ class LogoComponent extends AbstractTreeComponent { } toggleHelpCommand() { - this.getRootNode().toggleHelpCommand() + this.root.toggleHelpCommand() } } @@ -2509,12 +2496,12 @@ Directions.East = "East" Directions.South = "South" Directions.West = "West" -const NodeTypes = {} +const ParserTypes = {} -NodeTypes.agentDefinitionNode = "agentDefinitionNode" -NodeTypes.experimentNode = "experimentNode" -NodeTypes.settingDefinitionNode = "settingDefinitionNode" -NodeTypes.abstractInjectCommandNode = "abstractInjectCommandNode" +ParserTypes.agentDefinitionParser = "agentDefinitionParser" +ParserTypes.experimentParser = "experimentParser" +ParserTypes.settingDefinitionParser = "settingDefinitionParser" +ParserTypes.abstractInjectCommandParser = "abstractInjectCommandParser" window.Keywords = Keywords @@ -2524,7 +2511,7 @@ window.UrlKeys = UrlKeys window.Directions = Directions -window.NodeTypes = NodeTypes +window.ParserTypes = ParserTypes const DEFAULT_SIM = "fire" @@ -2533,7 +2520,8 @@ const DEFAULT_SIM = "fire" -class BrowserGlue extends AbstractTreeComponent { + +class BrowserGlue extends AbstractTreeComponentParser { async fetchAndLoadSimCodeFromUrlCommand(url) { const simCode = await this.fetchText(url) return simCode @@ -2551,7 +2539,7 @@ class BrowserGlue extends AbstractTreeComponent { async fetchSimCode() { const hash = this.willowBrowser.getHash().substr(1) - const deepLink = new jtree.TreeNode(decodeURIComponent(hash)) + const deepLink = new TreeNode(decodeURIComponent(hash)) const example = deepLink.get(UrlKeys.example) const fromUrl = deepLink.get(UrlKeys.url) const simojiCode = deepLink.getNode(UrlKeys.simoji) @@ -2579,7 +2567,7 @@ class BrowserGlue extends AbstractTreeComponent { } async init(grammarCode, theExamples) { - window.simojiCompiler = new jtree.HandGrammarProgram(grammarCode).compileAndReturnRootConstructor() + window.simojiParser = new HandGrammarProgram(grammarCode).compileAndReturnRootParser() ExampleSims.setChildren(theExamples) const simCode = await this.fetchSimCode() diff --git a/package-lock.json b/package-lock.json index 33b0bb5..4135f6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "2.0.0", "dependencies": { "jquery": "^3.6.0", - "jtree": "^53.8.0", + "jtree": "^74.0.0", "lodash": "^4.17.21", "mathjs": "^9.4.4", "minimist": "^1.2.5" @@ -21,7 +21,7 @@ "tap": "^15.0.9" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@babel/code-frame": { @@ -409,18 +409,13 @@ "node": ">=8" } }, - "node_modules/@types/d3-format": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.4.2.tgz", - "integrity": "sha512-WeGCHAs7PHdZYq6lwl/+jsl+Nfc1J2W1kNcMeIMYzQsT6mtBDBgtJ/rcdjZ0k0rVIvqEZqhhuD5TK/v3P2gFHQ==" - }, "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" @@ -515,7 +510,12 @@ "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "node_modules/asn1": { "version": "0.2.4", @@ -597,29 +597,33 @@ } }, "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { - "bytes": "3.1.0", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -663,9 +667,9 @@ "dev": true }, "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { "node": ">= 0.8" } @@ -692,6 +696,9 @@ "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/camelcase": { @@ -851,23 +858,43 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.6" } }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { "node": ">= 0.6" } @@ -882,9 +909,9 @@ } }, "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { "node": ">= 0.6" } @@ -892,12 +919,12 @@ "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" }, "node_modules/core-util-is": { "version": "1.0.2", @@ -938,11 +965,6 @@ "node": ">= 8" } }, - "node_modules/d3-format": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", - "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" - }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -998,17 +1020,30 @@ } }, "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dezalgo": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } }, "node_modules/diff": { "version": "4.0.2", @@ -1032,7 +1067,7 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { "version": "1.3.780", @@ -1049,7 +1084,7 @@ "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { "node": ">= 0.8" } @@ -1072,7 +1107,7 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "node_modules/escape-latex": { "version": "1.2.0", @@ -1104,7 +1139,7 @@ "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { "node": ">= 0.6" } @@ -1116,37 +1151,38 @@ "dev": true }, "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dependencies": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -1155,6 +1191,25 @@ "node": ">= 0.10.0" } }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -1183,9 +1238,9 @@ "dev": true }, "node_modules/fast-safe-stringify": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.8.tgz", - "integrity": "sha512-lXatBjf3WPjmWD6DpIZxkeSsCOwqI0maYMpgDlx8g4U2qi4lbjA9oH/HD2a87G+KfsUmo5WbJFmqBZlPxtptag==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, "node_modules/fill-range": { "version": "7.0.1", @@ -1200,16 +1255,16 @@ } }, "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { @@ -1272,9 +1327,9 @@ } }, "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -1285,9 +1340,18 @@ } }, "node_modules/formidable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", - "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "dependencies": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } }, "node_modules/forwarded": { "version": "0.2.0", @@ -1308,7 +1372,7 @@ "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { "node": ">= 0.6" } @@ -1373,13 +1437,16 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-package-type": { @@ -1404,6 +1471,7 @@ "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1486,11 +1554,14 @@ } }, "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/hasha": { @@ -1506,6 +1577,14 @@ "node": ">=8" } }, + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "engines": { + "node": ">=8" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -1513,18 +1592,18 @@ "dev": true }, "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/http-signature": { @@ -1575,15 +1654,16 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ipaddr.js": { "version": "1.9.1", @@ -1931,24 +2011,66 @@ } }, "node_modules/jtree": { - "version": "53.8.0", - "resolved": "https://registry.npmjs.org/jtree/-/jtree-53.8.0.tgz", - "integrity": "sha512-HUtrF+0FMakMGbUktqGlwAkURkaRdSa9WBj1lek3T0INT+S2GdyPoZvtIdVZSBqYhqtTamXSMD7WsTt19x+xuA==", - "dependencies": { - "@types/d3-format": "^1.3.1", - "d3-format": "^1.3.2", - "express": "*", - "glob": "^7.1.4", - "mkdirp": "^0.5.6", - "moment": "^2.29.3", - "moment-parseformat": "^3.0.0", - "prettier": "^1.18.2", + "version": "74.0.0", + "resolved": "https://registry.npmjs.org/jtree/-/jtree-74.0.0.tgz", + "integrity": "sha512-jKyvI60geVzmtfqahfG4kszQp2zg50N7JvtLTxaRamZpqi3J+a+3UduSEZvzBN8k7qSvcR7rA0TBbXSBinCOPw==", + "dependencies": { + "express": "^4.18.2", + "glob": "^9.3.4", + "mkdirp": "^2.1.6", + "prettier": "^2.8.7", "recursive-readdir-sync": "^1.0.6", - "semver": "^6.1.1", - "superagent": "^5.1.0" + "superagent": "^8.0.9" }, - "bin": { - "jtree": "products/commandLineApp.node.js" + "engines": { + "node": ">=16.0" + } + }, + "node_modules/jtree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/jtree/node_modules/glob": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.4.tgz", + "integrity": "sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==", + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jtree/node_modules/minimatch": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.3.tgz", + "integrity": "sha512-tEEvU9TkZgnFDCtpnrEYnPsjT7iUx42aXfs4bzmQ5sMA09/6hZY0jeZcGkXyDagiBOvkUjNo8Viom+Me6+2x7g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jtree/node_modules/minipass": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", + "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", + "engines": { + "node": ">=8" } }, "node_modules/lcov-parse": { @@ -2030,14 +2152,11 @@ } }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/make-dir": { @@ -2077,7 +2196,7 @@ "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { "node": ">= 0.6" } @@ -2085,12 +2204,12 @@ "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "engines": { "node": ">= 0.6" } @@ -2107,19 +2226,19 @@ } }, "node_modules/mime-db": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", - "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.31", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", - "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "mime-db": "1.48.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -2129,6 +2248,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2154,38 +2274,28 @@ } }, "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "mkdirp": "dist/cjs/src/bin.js" + }, "engines": { - "node": "*" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/moment-parseformat": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/moment-parseformat/-/moment-parseformat-3.1.1.tgz", - "integrity": "sha512-uEkXh5w9WDM8xNksZYeGpbZgrcG9qlpTg+f4ftHyOIis4+U08p31zOUzk3YCv2n+c6MUpW+Nb3GLCpjoEuHqJw==" - }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { "node": ">= 0.6" } @@ -2286,14 +2396,17 @@ } }, "node_modules/object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==" + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { "ee-first": "1.1.1" }, @@ -2414,6 +2527,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -2427,10 +2541,33 @@ "node": ">=8" } }, + "node_modules/path-scurry": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.6.3.tgz", + "integrity": "sha512-RAmB+n30SlN+HnNx6EbcpoDy9nwdpcGPnEKrJnu6GZoDWBdIjo1UQMVtW2ybtC7LC2oKLcMq8y5g8WnKLiod9g==", + "dependencies": { + "lru-cache": "^7.14.1", + "minipass": "^4.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", + "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", + "engines": { + "node": ">=8" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "node_modules/performance-now": { "version": "2.1.0", @@ -2460,14 +2597,17 @@ } }, "node_modules/prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", "bin": { "prettier": "bin-prettier.js" }, "engines": { - "node": ">=4" + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, "node_modules/process-on-spawn": { @@ -2521,11 +2661,17 @@ } }, "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/range-parser": { @@ -2537,12 +2683,12 @@ } }, "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -2570,19 +2716,6 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -2710,7 +2843,8 @@ "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -2726,47 +2860,48 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" }, "engines": { "node": ">= 0.8.0" @@ -2779,9 +2914,9 @@ "dev": true }, "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/shebang-command": { "version": "2.0.0", @@ -2812,6 +2947,9 @@ "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/signal-exit": { @@ -2904,26 +3042,13 @@ } }, "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "engines": { - "node": ">= 0.6" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" + "node": ">= 0.8" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, "node_modules/string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -2959,41 +3084,56 @@ } }, "node_modules/superagent": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-5.3.1.tgz", - "integrity": "sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==", + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz", + "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==", "dependencies": { "component-emitter": "^1.3.0", - "cookiejar": "^2.1.2", - "debug": "^4.1.1", - "fast-safe-stringify": "^2.0.7", - "form-data": "^3.0.0", - "formidable": "^1.2.2", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", "methods": "^1.1.2", - "mime": "^2.4.6", - "qs": "^6.9.4", - "readable-stream": "^3.6.0", - "semver": "^7.3.2" + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" }, "engines": { - "node": ">= 7.0.0" + "node": ">=6.4.0 <13 || >=14" } }, "node_modules/superagent/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/superagent/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, "node_modules/superagent/node_modules/mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "bin": { "mime": "cli.js" }, @@ -3006,21 +3146,10 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/superagent/node_modules/qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - } - }, "node_modules/superagent/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -5067,9 +5196,9 @@ } }, "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "engines": { "node": ">=0.6" } @@ -5183,7 +5312,7 @@ "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "engines": { "node": ">= 0.8" } @@ -5197,15 +5326,10 @@ "punycode": "^2.1.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "engines": { "node": ">= 0.4.0" } @@ -5222,7 +5346,7 @@ "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "engines": { "node": ">= 0.8" } @@ -5803,18 +5927,13 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, - "@types/d3-format": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.4.2.tgz", - "integrity": "sha512-WeGCHAs7PHdZYq6lwl/+jsl+Nfc1J2W1kNcMeIMYzQsT6mtBDBgtJ/rcdjZ0k0rVIvqEZqhhuD5TK/v3P2gFHQ==" - }, "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" } }, "aggregate-error": { @@ -5891,7 +6010,12 @@ "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "asn1": { "version": "0.2.4", @@ -5958,26 +6082,29 @@ "dev": true }, "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "requires": { - "bytes": "3.1.0", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" } }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6012,9 +6139,9 @@ "dev": true }, "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "caching-transform": { "version": "4.0.0", @@ -6167,20 +6294,28 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" }, "convert-source-map": { "version": "1.8.0", @@ -6192,19 +6327,19 @@ } }, "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" }, "core-util-is": { "version": "1.0.2", @@ -6236,11 +6371,6 @@ "which": "^2.0.1" } }, - "d3-format": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", - "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" - }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -6284,14 +6414,23 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "dezalgo": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } }, "diff": { "version": "4.0.2", @@ -6312,7 +6451,7 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { "version": "1.3.780", @@ -6329,7 +6468,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "es6-error": { "version": "4.1.1", @@ -6346,7 +6485,7 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "escape-latex": { "version": "1.2.0", @@ -6368,7 +6507,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "events-to-array": { "version": "1.1.2", @@ -6377,40 +6516,48 @@ "dev": true }, "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "requires": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "extend": { @@ -6438,9 +6585,9 @@ "dev": true }, "fast-safe-stringify": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.8.tgz", - "integrity": "sha512-lXatBjf3WPjmWD6DpIZxkeSsCOwqI0maYMpgDlx8g4U2qi4lbjA9oH/HD2a87G+KfsUmo5WbJFmqBZlPxtptag==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, "fill-range": { "version": "7.0.1", @@ -6452,16 +6599,16 @@ } }, "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" } }, @@ -6509,9 +6656,9 @@ "dev": true }, "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -6519,9 +6666,15 @@ } }, "formidable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", - "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "requires": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + } }, "forwarded": { "version": "0.2.0", @@ -6536,7 +6689,7 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "fromentries": { "version": "1.3.2", @@ -6586,13 +6739,13 @@ "dev": true }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" } }, "get-package-type": { @@ -6614,6 +6767,7 @@ "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -6675,9 +6829,9 @@ "dev": true }, "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, "hasha": { "version": "5.2.2", @@ -6689,6 +6843,11 @@ "type-fest": "^0.8.0" } }, + "hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==" + }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -6696,15 +6855,15 @@ "dev": true }, "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" } }, "http-signature": { @@ -6742,15 +6901,16 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ipaddr.js": { "version": "1.9.1", @@ -7024,21 +7184,50 @@ } }, "jtree": { - "version": "53.8.0", - "resolved": "https://registry.npmjs.org/jtree/-/jtree-53.8.0.tgz", - "integrity": "sha512-HUtrF+0FMakMGbUktqGlwAkURkaRdSa9WBj1lek3T0INT+S2GdyPoZvtIdVZSBqYhqtTamXSMD7WsTt19x+xuA==", - "requires": { - "@types/d3-format": "^1.3.1", - "d3-format": "^1.3.2", - "express": "*", - "glob": "^7.1.4", - "mkdirp": "^0.5.6", - "moment": "^2.29.3", - "moment-parseformat": "^3.0.0", - "prettier": "^1.18.2", + "version": "74.0.0", + "resolved": "https://registry.npmjs.org/jtree/-/jtree-74.0.0.tgz", + "integrity": "sha512-jKyvI60geVzmtfqahfG4kszQp2zg50N7JvtLTxaRamZpqi3J+a+3UduSEZvzBN8k7qSvcR7rA0TBbXSBinCOPw==", + "requires": { + "express": "^4.18.2", + "glob": "^9.3.4", + "mkdirp": "^2.1.6", + "prettier": "^2.8.7", "recursive-readdir-sync": "^1.0.6", - "semver": "^6.1.1", - "superagent": "^5.1.0" + "superagent": "^8.0.9" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.4.tgz", + "integrity": "sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==", + "requires": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + } + }, + "minimatch": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.3.tgz", + "integrity": "sha512-tEEvU9TkZgnFDCtpnrEYnPsjT7iUx42aXfs4bzmQ5sMA09/6hZY0jeZcGkXyDagiBOvkUjNo8Viom+Me6+2x7g==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minipass": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", + "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==" + } } }, "lcov-parse": { @@ -7105,12 +7294,9 @@ } }, "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==" }, "make-dir": { "version": "3.1.0", @@ -7140,17 +7326,17 @@ "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, "mime": { "version": "1.6.0", @@ -7158,22 +7344,23 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", - "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { - "version": "2.1.31", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", - "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "1.48.0" + "mime-db": "1.52.0" } }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -7193,32 +7380,19 @@ } }, "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "requires": { - "minimist": "^1.2.6" - } - }, - "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" - }, - "moment-parseformat": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/moment-parseformat/-/moment-parseformat-3.1.1.tgz", - "integrity": "sha512-uEkXh5w9WDM8xNksZYeGpbZgrcG9qlpTg+f4ftHyOIis4+U08p31zOUzk3YCv2n+c6MUpW+Nb3GLCpjoEuHqJw==" + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==" }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, "node-preload": { "version": "0.2.1", @@ -7295,14 +7469,14 @@ "dev": true }, "object-inspect": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==" + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" }, "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "requires": { "ee-first": "1.1.1" } @@ -7395,7 +7569,8 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true }, "path-key": { "version": "3.1.1", @@ -7403,10 +7578,26 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, + "path-scurry": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.6.3.tgz", + "integrity": "sha512-RAmB+n30SlN+HnNx6EbcpoDy9nwdpcGPnEKrJnu6GZoDWBdIjo1UQMVtW2ybtC7LC2oKLcMq8y5g8WnKLiod9g==", + "requires": { + "lru-cache": "^7.14.1", + "minipass": "^4.0.2" + }, + "dependencies": { + "minipass": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", + "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==" + } + } + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "performance-now": { "version": "2.1.0", @@ -7430,9 +7621,9 @@ } }, "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==" + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==" }, "process-on-spawn": { "version": "1.0.0", @@ -7476,9 +7667,12 @@ "dev": true }, "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } }, "range-parser": { "version": "1.2.1", @@ -7486,12 +7680,12 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } @@ -7513,16 +7707,6 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -7628,7 +7812,8 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -7643,44 +7828,45 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true }, "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "dependencies": { "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" } }, "set-blocking": { @@ -7690,9 +7876,9 @@ "dev": true }, "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "shebang-command": { "version": "2.0.0", @@ -7796,24 +7982,9 @@ } }, "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, "string-width": { "version": "2.1.1", @@ -7841,53 +8012,52 @@ "dev": true }, "superagent": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-5.3.1.tgz", - "integrity": "sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==", + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz", + "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==", "requires": { "component-emitter": "^1.3.0", - "cookiejar": "^2.1.2", - "debug": "^4.1.1", - "fast-safe-stringify": "^2.0.7", - "form-data": "^3.0.0", - "formidable": "^1.2.2", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", "methods": "^1.1.2", - "mime": "^2.4.6", - "qs": "^6.9.4", - "readable-stream": "^3.6.0", - "semver": "^7.3.2" + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" }, "dependencies": { "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" } }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, "mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "qs": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz", - "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==", - "requires": { - "side-channel": "^1.0.4" - } - }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "requires": { "lru-cache": "^6.0.0" } @@ -9323,9 +9493,9 @@ } }, "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "tough-cookie": { "version": "2.5.0", @@ -9417,7 +9587,7 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "uri-js": { "version": "4.4.1", @@ -9428,15 +9598,10 @@ "punycode": "^2.1.0" } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { "version": "3.4.0", @@ -9447,7 +9612,7 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, "verror": { "version": "1.10.0", diff --git a/package.json b/package.json index eb37e8f..b59e123 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ }, "dependencies": { "jquery": "^3.6.0", - "jtree": "^53.8.0", + "jtree": "^74.0.0", "lodash": "^4.17.21", "mathjs": "^9.4.4", "minimist": "^1.2.5" @@ -32,8 +32,10 @@ "tap": "^15.0.9" }, "scripts": { + "build": "./build.js", "buildCoverageReport": "tap testAll.js", "buildCoverageHtmlReport": "tap testAll.js --coverage-report=lcov", + "local": "./server.js", "test": "node testAll.js" }, "repository": { diff --git a/readme.scroll b/readme.scroll index 4d98bd5..2a88fcd 100644 --- a/readme.scroll +++ b/readme.scroll @@ -54,7 +54,7 @@ startColumns 2 ## Development ? What is the dev loop like? -1. Start the dev server with `node server.js`. +1. Start the dev server with `npm run local`. 2. Open `localhost/dev.html` 3. Edit files 4. Refresh diff --git a/server.js b/server.js index 83592f0..657e641 100755 --- a/server.js +++ b/server.js @@ -4,7 +4,7 @@ const express = require("express") const { readFile } = require("fs") const { TypeScriptRewriter } = require("jtree/products/TypeScriptRewriter.js") const stamp = require("jtree/products/stamp.nodejs.js") -const { jtree } = require("jtree") +const { TreeNode } = require("jtree/products/TreeNode.js") const { getExamples } = require("./examples") class Server { diff --git a/simoji.grammar b/simoji.grammar index 94ed7e9..a941896 100644 --- a/simoji.grammar +++ b/simoji.grammar @@ -46,14 +46,14 @@ propertyNameCell angleCell enum North South East West NorthWest NorthEast SouthWest SouthEast highlightScope constant.numeric -errorNode - baseNodeType errorNode -simojiNode +errorParser + baseParser errorParser +simojiParser extensions simoji description A Tree Language that compiles to a TreeComponentFramework app. root - inScope abstractIgnoreNode abstractSetupNode abstractInjectCommandNode onTickNode onExtinctNode behaviorDefinitionNode experimentNode settingDefinitionNode - catchAllNodeType agentDefinitionNode + inScope abstractIgnoreParser abstractSetupParser abstractInjectCommandParser onTickParser onExtinctParser behaviorDefinitionParser experimentParser settingDefinitionParser + catchAllParser agentDefinitionParser compilesTo javascript example 🦋 @@ -69,373 +69,373 @@ simojiNode insert 2 💡 javascript get agentTypes() { - return this.filter(node => node.getNodeTypeId() === "agentDefinitionNode") + return this.filter(node => node.parserId === "agentDefinitionParser") } -experimentNode - crux experiment +experimentParser + cruxFromId cells keywordCell - inScope abstractIgnoreNode abstractSetupNode abstractInjectCommandNode onTickNode onExtinctNode settingDefinitionNode + inScope abstractIgnoreParser abstractSetupParser abstractInjectCommandParser onTickParser onExtinctParser settingDefinitionParser catchAllCellType stringCell -abstractSetupNode -atTimeNode - crux atTime +abstractSetupParser +atTimeParser + cruxFromId description Run commands at a certain tick. cells keywordCell integerCell - extends abstractSetupNode - inScope abstractInjectCommandNode -abstractSetupNumberNode + extends abstractSetupParser + inScope abstractInjectCommandParser +abstractSetupNumberParser cells keywordCell integerCell - extends abstractSetupNode + extends abstractSetupParser javascript compile() { return "" } -sizeNode +sizeParser description Size of a grid cell in pixels. Min is 10. Max is 200. - extends abstractSetupNumberNode - crux size -rowsNode + extends abstractSetupNumberParser + cruxFromId +rowsParser description Number of rows in the grid. Default is based on screen size. - extends abstractSetupNumberNode - crux rows -columnsNode + extends abstractSetupNumberParser + cruxFromId +columnsParser description Number of columns in the grid. Default is based on screen size. - extends abstractSetupNumberNode - crux columns -seedNode + extends abstractSetupNumberParser + cruxFromId +seedParser description If you'd like reproducible runs set a seed for the random number generator. - extends abstractSetupNumberNode - crux seed -ticksPerSecondNode + extends abstractSetupNumberParser + cruxFromId +ticksPerSecondParser description Time in milliseconds of one step. - extends abstractSetupNumberNode - crux ticksPerSecond -reportNode - crux report + extends abstractSetupNumberParser + cruxFromId +reportParser + cruxFromId description Define a custom report template. - catchAllNodeType ohayoLineNode - extends abstractSetupNode + catchAllParser ohayoLineParser + extends abstractSetupParser cells keywordCell javascript compile() { return "" } -styleNode +styleParser description Optional CSS to load in BoardStyleComponent - extends abstractSetupNode + extends abstractSetupParser cells keywordCell - crux style - catchAllNodeType styleLineNode + cruxFromId + catchAllParser styleLineParser javascript compile() { return "" } -questionNode - crux question +questionParser + cruxFromId description What are you trying to figure out? cells keywordCell catchAllCellType stringCell - extends abstractSetupNode -abstractInjectCommandNode -fillNode + extends abstractSetupParser +abstractInjectCommandParser +fillParser description Fill all blank cells with this agent. - extends abstractInjectCommandNode + extends abstractInjectCommandParser cells keywordCell emojiCell - crux fill -drawNode - extends abstractInjectCommandNode + cruxFromId +drawParser + extends abstractInjectCommandParser cells keywordCell - crux draw - catchAllNodeType drawLineNode -insertNode - extends abstractInjectCommandNode + cruxFromId + catchAllParser drawLineParser +insertParser + extends abstractInjectCommandParser cells keywordCell integerOrPercentCell emojiCell - crux insert -insertAtNode - extends insertNode + cruxFromId +insertAtParser + extends insertParser description Insert at X Y cells keywordCell emojiCell positionCell positionCell - crux insertAt -insertClusterNode - extends insertNode - crux insertCluster + cruxFromId +insertClusterParser + extends insertParser + cruxFromId catchAllCellType integerCell -rectangleDrawNode - extends abstractInjectCommandNode +rectangleDrawParser + extends abstractInjectCommandParser cells keywordCell emojiCell integerCell integerCell catchAllCellType integerCell crux rectangle -pasteDrawNode - extends abstractInjectCommandNode +pasteDrawParser + extends abstractInjectCommandParser cells keywordCell crux paste - catchAllNodeType pasteLineNode -drawLineNode + catchAllParser pasteLineParser +drawLineParser catchAllCellType emojiCell -pasteLineNode +pasteLineParser catchAllCellType anyCell - catchAllNodeType pasteLineNode -agentDefinitionNode - inScope abstractIgnoreNode abstractEventNode abstractAgentAttributeNode abstractBehaviorAttributeNode + catchAllParser pasteLineParser +agentDefinitionParser + inScope abstractIgnoreParser abstractEventParser abstractAgentAttributeParser behaviorAttributeParser cells keywordCell - catchAllNodeType errorNode + catchAllParser errorParser compiler stringTemplate javascript compile() { - const root = this.getRootNode() - const name = root.agentKeywordMap[this.getWord(0)] + const root = this.root + const name = root.agentKeywordMap[this.firstWord] const normal = super.compile() - const behaviors = this.filter(node => node.getNodeTypeId() === "abstractBehaviorAttributeNode") + const behaviors = this.filter(node => node.parserId === "behaviorAttributeParser") .map(behavior => `"${behavior.getLine()}"`) .join(",") return `class ${name} extends Agent { - icon = "${this.getWord(0)}" + icon = "${this.firstWord}" behaviors = [${behaviors}] ${normal} }` } -abstractCommandNode +abstractCommandParser cells keywordCell -abstractSubjectObjectCommandNode - extends abstractCommandNode -replaceWithCommandNode - extends abstractSubjectObjectCommandNode +abstractSubjectObjectCommandParser + extends abstractCommandParser +replaceWithCommandParser + extends abstractSubjectObjectCommandParser crux replaceWith cells keywordCell emojiCell -kickItCommandNode - extends abstractSubjectObjectCommandNode +kickItCommandParser + extends abstractSubjectObjectCommandParser crux kickIt -shootCommandNode - extends abstractSubjectObjectCommandNode +shootCommandParser + extends abstractSubjectObjectCommandParser crux shoot -pickItUpCommandNode - extends abstractSubjectObjectCommandNode +pickItUpCommandParser + extends abstractSubjectObjectCommandParser crux pickItUp -spawnCommandNode +spawnCommandParser crux spawn - extends abstractCommandNode + extends abstractCommandParser cells keywordCell emojiCell catchAllCellType positionCell -moveToEmptySpotCommandNode +moveToEmptySpotCommandParser crux moveToEmptySpot - extends abstractCommandNode + extends abstractCommandParser cells keywordCell -removeCommandNode +removeCommandParser description Remove this agent from the board. crux remove - extends abstractCommandNode + extends abstractCommandParser cells keywordCell -javascriptCommandNode +javascriptCommandParser description An escape hatch so you can write custom javascript in a pinch. - extends abstractCommandNode + extends abstractCommandParser crux javascript - catchAllNodeType javascriptLineNode + catchAllParser javascriptLineParser cells keywordCell -alertCommandNode - extends abstractCommandNode +alertCommandParser + extends abstractCommandParser crux alert catchAllCellType stringCell -logCommandNode - extends abstractCommandNode +logCommandParser + extends abstractCommandParser crux log catchAllCellType stringCell -narrateCommandNode - extends abstractCommandNode +narrateCommandParser + extends abstractCommandParser crux narrate catchAllCellType stringCell -pauseCommandNode - extends abstractCommandNode +pauseCommandParser + extends abstractCommandParser crux pause -decreaseCommandNode - extends abstractCommandNode +decreaseCommandParser + extends abstractCommandParser description Decrease a property by 1. crux decrease cells keywordCell propertyNameCell -increaseCommandNode - extends abstractCommandNode +increaseCommandParser + extends abstractCommandParser description Increase a property by 1. crux increase cells keywordCell propertyNameCell -moveCommandNode - extends abstractCommandNode +moveCommandParser + extends abstractCommandParser crux move -turnRandomlyCommandNode - extends abstractCommandNode +turnRandomlyCommandParser + extends abstractCommandParser crux turnRandomly -jitterCommandNode - extends abstractCommandNode +jitterCommandParser + extends abstractCommandParser crux jitter -turnTowardCommandNode +turnTowardCommandParser description Turn to the closest agent of a certain type. - extends abstractCommandNode + extends abstractCommandParser crux turnToward cells keywordCell emojiCell -turnFromCommandNode +turnFromCommandParser description Turn away from the closest agent of a certain type. - extends abstractCommandNode + extends abstractCommandParser crux turnFrom cells keywordCell emojiCell -learnCommandNode +learnCommandParser crux learn - extends abstractCommandNode + extends abstractCommandParser cells keywordCell behaviorNameCell -unlearnCommandNode +unlearnCommandParser crux unlearn - extends abstractCommandNode + extends abstractCommandParser cells keywordCell behaviorNameCell -abstractAgentAttributeNode +abstractAgentAttributeParser cells keywordCell -abstractStringAttributeNode - extends abstractAgentAttributeNode +stringAttributeParser + extends abstractAgentAttributeParser pattern ^\w+ .+$ catchAllCellType stringCell javascript compile() { - return `${this.getWord(0)} = "${this.getWord(1)}"` + return `${this.firstWord} = "${this.getWord(1)}"` } -angleNode - extends abstractStringAttributeNode +angleParser + extends stringAttributeParser cells keywordCell angleCell - crux angle -agentStyleNode + cruxFromId +agentStyleParser description Provide custom CSS for an agent type. - extends abstractStringAttributeNode + extends stringAttributeParser cells keywordCell cssCell crux style -agentHtmlNode +agentHtmlParser description Provide custom HTML for each rendered agent. - extends abstractStringAttributeNode + extends stringAttributeParser cells keywordCell htmlCell crux html -abstractBooleanAttributeNode +abstractBooleanAttributeParser description A boolean attribute. - extends abstractAgentAttributeNode + extends abstractAgentAttributeParser javascript compile() { - return `${this.getWord(0)} = true` + return `${this.firstWord} = true` } -noPaletteNode - extends abstractBooleanAttributeNode +noPaletteParser + extends abstractBooleanAttributeParser cruxFromId description Don't show this agent in the palette. -solidTraitNode +solidTraitParser description If set other agents won't pass through these. - extends abstractBooleanAttributeNode + extends abstractBooleanAttributeParser crux solid -bouncyTraitNode +bouncyTraitParser description If set other agents will bounce off this after a collision. - extends abstractBooleanAttributeNode + extends abstractBooleanAttributeParser crux bouncy -abstractIntegerAttributeNode - extends abstractAgentAttributeNode +abstractIntegerAttributeParser + extends abstractAgentAttributeParser description An integer attribute. cells keywordCell integerCell javascript compile() { - return `${this.getWord(0)} = ${this.getWord(1)}` + return `${this.firstWord} = ${this.getWord(1)}` } -customIntegerAttributeNode +customIntegerAttributeParser pattern ^\w+ \d+$ - extends abstractIntegerAttributeNode -healthNode - extends abstractIntegerAttributeNode - crux health -settingDefinitionNode + extends abstractIntegerAttributeParser +healthParser + extends abstractIntegerAttributeParser + cruxFromId +settingDefinitionParser description Define a configurable input. cells keywordCell settingValueCell pattern ^\w+Setting .+$ -ohayoLineNode +ohayoLineParser description Data visualization code written for Ohayo. catchAllCellType ohayoCell -styleLineNode +styleLineParser catchAllCellType cssCell - catchAllNodeType styleLineNode -targetEmojiNode - inScope abstractCommandNode + catchAllParser styleLineParser +targetEmojiParser + inScope abstractCommandParser cells emojiCell -abstractEventNode +abstractEventParser cells keywordCell catchAllCellType probabilityCell javascript compile() { return `` } -abstractInteractionEventNode - extends abstractEventNode - catchAllNodeType targetEmojiNode -onHitNode - extends abstractInteractionEventNode - crux onHit +abstractInteractionEventParser + extends abstractEventParser + catchAllParser targetEmojiParser +onHitParser + extends abstractInteractionEventParser + cruxFromId description Define what happens when this agent collides with other agents. -onTouchNode - extends abstractInteractionEventNode - crux onTouch +onTouchParser + extends abstractInteractionEventParser + cruxFromId description Define what happens when this agent is adjacent to other agents. -onNeighborsNode +onNeighborsParser description Define what happens when a certain amount of neighbors are nearby. - extends abstractInteractionEventNode - inScope emojiAndNeighborConditionNode - crux onNeighbors -onDeathNode - extends abstractEventNode - crux onDeath - inScope abstractCommandNode + extends abstractInteractionEventParser + inScope emojiAndNeighborConditionParser + cruxFromId +onDeathParser + extends abstractEventParser + cruxFromId + inScope abstractCommandParser description Define what happens when this agent runs out of health. -onTickNode - extends abstractEventNode - crux onTick - inScope abstractCommandNode +onTickParser + extends abstractEventParser + cruxFromId + inScope abstractCommandParser description Define what happens each tick. -emojiAndNeighborConditionNode - inScope abstractCommandNode +emojiAndNeighborConditionParser + inScope abstractCommandParser pattern ^.+ (<|>|=|<=|>=)+ .+$ cells emojiCell conditionalOperatorCell neighborCountCell -onExtinctNode - crux onExtinct - inScope abstractCommandNode +onExtinctParser + cruxFromId + inScope abstractCommandParser cells keywordCell emojiCell description Define what happens when a type of agent goes extinct from the board. javascript compile() { return "" } -abstractIgnoreNode +abstractIgnoreParser tags doNotSynthesize javascript compile () { return "" } -commentNode - extends abstractIgnoreNode +commentParser + extends abstractIgnoreParser catchAllCellType commentCell - crux comment - catchAllNodeType commentLineNode -commentAliasNode + cruxFromId + catchAllParser commentLineParser +commentAliasParser description Alternate alias for a comment. crux # - extends commentNode -blankLineNode - extends abstractIgnoreNode + extends commentParser +blankLineParser + extends abstractIgnoreParser description Blank lines compile do nothing. cells blankCell pattern ^$ -commentLineNode +commentLineParser catchAllCellType commentCell -javascriptLineNode +javascriptLineParser catchAllCellType javascriptCell -abstractBehaviorAttributeNode +behaviorAttributeParser cells behaviorNameCell pattern ^.*Behavior$ javascript compile() { return "" } -behaviorDefinitionNode - inScope abstractIgnoreNode abstractEventNode +behaviorDefinitionParser + inScope abstractIgnoreParser abstractEventParser cells behaviorNameCell pattern ^.*Behavior$ - catchAllNodeType errorNode + catchAllParser errorParser javascript compile() { return "" diff --git a/yodash.js b/yodash.js index be73f88..131e8b0 100644 --- a/yodash.js +++ b/yodash.js @@ -1,7 +1,8 @@ const yodash = {} const lodash = require("lodash") const math = require("mathjs") -const { Directions, NodeTypes } = require("./components/Types.js") +const { Utils } = require("jtree/products/Utils.js") +const { Directions, ParserTypes } = require("./components/Types.js") yodash.parseInts = (arr, start) => arr.map((item, index) => (index >= start ? parseInt(item) : item)) @@ -33,9 +34,9 @@ yodash.compare = (left, operator, right) => { yodash.compileAgentClassDeclarationsAndMap = program => { const clone = program.clone() - clone.filter(node => node.getNodeTypeId() !== NodeTypes.agentDefinitionNode).forEach(node => node.destroy()) + clone.filter(node => node.parserId !== ParserTypes.agentDefinitionParser).forEach(node => node.destroy()) clone.agentKeywordMap = {} - clone.agentTypes.forEach((node, index) => (clone.agentKeywordMap[node.getWord(0)] = `simAgent${index}`)) + clone.agentTypes.forEach((node, index) => (clone.agentKeywordMap[node.firstWord] = `simAgent${index}`)) const compiled = clone.compile() const agentMap = Object.keys(clone.agentKeywordMap) .map(key => `"${key}":${clone.agentKeywordMap[key]}`) @@ -48,15 +49,15 @@ yodash.compileAgentClassDeclarationsAndMap = program => { yodash.patchExperimentAndReplaceSymbols = (program, experiment) => { const clone = program.clone() // drop experiment nodes - clone.filter(node => node.getNodeTypeId() === NodeTypes.experimentNode).forEach(node => node.destroy()) + clone.filter(node => node.parserId === ParserTypes.experimentParser).forEach(node => node.destroy()) // Append current experiment if (experiment) clone.concat(experiment.childrenToString()) // Build symbol table const symbolTable = {} clone - .filter(node => node.getNodeTypeId() === NodeTypes.settingDefinitionNode) + .filter(node => node.parserId === ParserTypes.settingDefinitionParser) .forEach(node => { - symbolTable[node.getWord(0)] = node.getContent() + symbolTable[node.firstWord] = node.content node.destroy() }) // Find and replace @@ -200,7 +201,7 @@ yodash.draw = str => { yodash.updateOccupiedSpots = (board, occupiedSpots) => { new TreeNode(board).forEach(line => { - occupiedSpots.add(yodash.makePositionHash(yodash.parsePosition(line.getWords()))) + occupiedSpots.add(yodash.makePositionHash(yodash.parsePosition(line.words))) }) } @@ -269,16 +270,16 @@ const shuffleArray = (array, randomNumberGenerator) => { yodash.pick = (tree, fields) => { const newTree = tree.clone() - const map = TreeUtils.arrayToMap(fields) + const map = Utils.arrayToMap(fields) newTree.forEach(node => { - if (!map[node.getWord(0)]) node.destroy() + if (!map[node.firstWord]) node.destroy() }) return newTree } yodash.flatten = tree => { - const newTree = new jtree.TreeNode() + const newTree = new TreeNode() tree.forEach(node => node.forEach(child => newTree.appendNode(child))) return newTree } ------------------------------------------------------------
commit f07d27eaa36dd061f1640281c81089f1402ead70
Author: Breck Yunits <breck7@gmail.com> Date: Sat Apr 8 19:13:30 2023 -1000 Link update diff --git a/cheatSheet.scroll b/cheatSheet.scroll index 4fe9376..2232ecc 100644 --- a/cheatSheet.scroll +++ b/cheatSheet.scroll @@ -5,7 +5,7 @@ title Simoji Quickstart Guide startColumns 3 * Simoji is a language and tool for thinkers to write shareable simulations using 😃's. You write your simulations in a simple language using mostly Emojis and then click play. Simoji is public domain, open source, and suggestions welcome! - https://github.com/publicdomaincompany/simoji open source + https://github.com/breck7/simoji open source # Example Program code @@ -120,7 +120,7 @@ pipeTable # Getting Involved * The source code for Simoji and all development happens on Github. - https://github.com/publicdomaincompany/simoji Github + https://github.com/breck7/simoji Github # Printable Version link ./cheatSheet.pdf diff --git a/components/SimojiApp.js b/components/SimojiApp.js index f0f6a5a..ee8b2c5 100644 --- a/components/SimojiApp.js +++ b/components/SimojiApp.js @@ -32,7 +32,7 @@ const MIN_GRID_ROWS = 10 /*NODE_JS_ONLY*/ const simojiCompiler = jtree.compileGrammarFileAtPathAndReturnRootConstructor( __dirname + "/../simoji.grammar") class githubTriangleComponent extends AbstractTreeComponent { - githubLink = `https://github.com/publicdomaincompany/simoji` + githubLink = `https://github.com/breck7/simoji` toHakonCode() { return `.AbstractGithubTriangleComponent display block diff --git a/package.json b/package.json index 934c71f..eb37e8f 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "bin": { "simoji": "./cli.js" }, + "engineStrict": true, "engines": { "node": ">=16.0.0" }, @@ -37,11 +38,11 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/publicdomaincompany/simoji.git" + "url": "git+https://github.com/breck7/simoji.git" }, "author": "", "bugs": { - "url": "https://github.com/publicdomaincompany/simoji/issues" + "url": "https://github.com/breck7/simoji/issues" }, - "homepage": "https://github.com/publicdomaincompany/simoji#readme" + "homepage": "https://github.com/breck7/simoji#readme" } ------------------------------------------------------------
commit b319ce273d8bdbdcead50f8512951a163584fb00
Author: Breck Yunits <breck7@gmail.com> Date: Sat Apr 8 19:11:57 2023 -1000 Prettier diff --git a/build.js b/build.js index 7cd5975..48af45b 100755 --- a/build.js +++ b/build.js @@ -27,22 +27,22 @@ ourPaths.unshift(__dirname + "/yodash.js") ourPaths.push(__dirname + "/BrowserGlue.js") const simCode = ourPaths - .map(path => { - const code = Disk.read(path) + .map(path => { + const code = Disk.read(path) - return new TypeScriptRewriter(code) - .removeRequires() - .removeNodeJsOnlyLines() - .changeNodeExportsToWindowExports() - .getString() - }) - .join("\n\n") + return new TypeScriptRewriter(code) + .removeRequires() + .removeNodeJsOnlyLines() + .changeNodeExportsToWindowExports() + .getString() + }) + .join("\n\n") Disk.write(__dirname + "/dist/simoji.js", simCode) const SimConstants = { - grammar: Disk.read(__dirname + "/simoji.grammar"), - examples: getExamples() + grammar: Disk.read(__dirname + "/simoji.grammar"), + examples: getExamples() } Disk.write(__dirname + "/dist/constants.js", `const SimConstants = ` + JSON.stringify(SimConstants)) diff --git a/cli.js b/cli.js index 662cef1..4abeb9e 100755 --- a/cli.js +++ b/cli.js @@ -9,50 +9,50 @@ const VERSION = packageJson.version const CommandFnDecoratorSuffix = "Command" class SimojiCli { - execute(args = []) { - this.log(`\n🧫🧫🧫 WELCOME TO SIMOJI (v${VERSION}) 🧫🧫🧫`) - const command = args[0] - const filename = args[1] - const commandName = `${command}${CommandFnDecoratorSuffix}` - const cwd = process.cwd() - if (this[commandName]) return this[commandName](cwd, filename) - - if (Disk.exists(cwd + "/" + command)) return this.runCommand(cwd, command) - - if (!command) this.log(`\nNo command provided. Running help command.`) - else this.log(`\nUnknown command or file '${commandName}' provided. Running help command.`) - return this.helpCommand() - } - - get _allCommands() { - return Object.getOwnPropertyNames(Object.getPrototypeOf(this)) - .filter(word => word.endsWith(CommandFnDecoratorSuffix)) - .sort() - } - - async runCommand(cwd, filename) { - const fullPath = cwd + "/" + filename - if (!Disk.exists(fullPath)) return this.log(`❌ file '${fullPath}' not found.`) - this.log(`\n⏳ Running '${fullPath}'...\n`) - const code = Disk.read(filename) - const app = SimojiApp.setupApp(code) - app.verbose = false - await app.runUntilPause() - } - - helpCommand() { - return this.log( - `\nThis is the Simoji help page.\n\nCommands you can run:\n\n${this._allCommands - .map(comm => `➡️ ` + comm.replace(CommandFnDecoratorSuffix, "")) - .join("\n")}\n` - ) - } - - verbose = true - log(message) { - if (this.verbose) console.log(message) - return message - } + execute(args = []) { + this.log(`\n🧫🧫🧫 WELCOME TO SIMOJI (v${VERSION}) 🧫🧫🧫`) + const command = args[0] + const filename = args[1] + const commandName = `${command}${CommandFnDecoratorSuffix}` + const cwd = process.cwd() + if (this[commandName]) return this[commandName](cwd, filename) + + if (Disk.exists(cwd + "/" + command)) return this.runCommand(cwd, command) + + if (!command) this.log(`\nNo command provided. Running help command.`) + else this.log(`\nUnknown command or file '${commandName}' provided. Running help command.`) + return this.helpCommand() + } + + get _allCommands() { + return Object.getOwnPropertyNames(Object.getPrototypeOf(this)) + .filter(word => word.endsWith(CommandFnDecoratorSuffix)) + .sort() + } + + async runCommand(cwd, filename) { + const fullPath = cwd + "/" + filename + if (!Disk.exists(fullPath)) return this.log(`❌ file '${fullPath}' not found.`) + this.log(`\n⏳ Running '${fullPath}'...\n`) + const code = Disk.read(filename) + const app = SimojiApp.setupApp(code) + app.verbose = false + await app.runUntilPause() + } + + helpCommand() { + return this.log( + `\nThis is the Simoji help page.\n\nCommands you can run:\n\n${this._allCommands + .map(comm => `➡️ ` + comm.replace(CommandFnDecoratorSuffix, "")) + .join("\n")}\n` + ) + } + + verbose = true + log(message) { + if (this.verbose) console.log(message) + return message + } } if (module && !module.parent) new SimojiCli().execute(parseArgs(process.argv.slice(2))._) diff --git a/components/RightBar.js b/components/RightBar.js index 1938ebc..fe75b99 100644 --- a/components/RightBar.js +++ b/components/RightBar.js @@ -3,11 +3,11 @@ const { jtree } = require("jtree") const { AgentPaletteComponent } = require("./AgentPalette.js") class RightBarComponent extends AbstractTreeComponent { - createParser() { - return new jtree.TreeNode.Parser(undefined, { - AgentPaletteComponent - }) - } + createParser() { + return new jtree.TreeNode.Parser(undefined, { + AgentPaletteComponent + }) + } } module.exports = { RightBarComponent } diff --git a/components/SimojiApp.test.node.js b/components/SimojiApp.test.node.js index 5b11f20..8152577 100755 --- a/components/SimojiApp.test.node.js +++ b/components/SimojiApp.test.node.js @@ -11,66 +11,66 @@ const simojiCompiler = jtree.compileGrammarFileAtPathAndReturnRootConstructor(gr const testTree = {} testTree.simojiGrammar = areEqual => { - const errs = new grammarNode(Disk.read(grammarPath)).getAllErrors().map(err => err.toObject()) - if (errs.length) console.log(new jtree.TreeNode(errs).toFormattedTable(60)) - areEqual(errs.length, 0, "no grammar errors") + const errs = new grammarNode(Disk.read(grammarPath)).getAllErrors().map(err => err.toObject()) + if (errs.length) console.log(new jtree.TreeNode(errs).toFormattedTable(60)) + areEqual(errs.length, 0, "no grammar errors") } testTree.simGrammarErrors = areEqual => { - const errs = Disk.getFiles(examplesPath) - .map(path => { - const code = Disk.read(path) - const program = new simojiCompiler(code) - const errors = program.getAllErrors() - areEqual(errors.length, 0) - return errors.map(err => { - return { filename: path, ...err.toObject() } - }) - }) - .flat() - if (errs.length) console.log(new jtree.TreeNode(errs).toFormattedTable(60)) + const errs = Disk.getFiles(examplesPath) + .map(path => { + const code = Disk.read(path) + const program = new simojiCompiler(code) + const errors = program.getAllErrors() + areEqual(errors.length, 0) + return errors.map(err => { + return { filename: path, ...err.toObject() } + }) + }) + .flat() + if (errs.length) console.log(new jtree.TreeNode(errs).toFormattedTable(60)) } testTree.SimojiApp = areEqual => { - const app = SimojiApp.setupApp("") - areEqual(!!app, true) + const app = SimojiApp.setupApp("") + areEqual(!!app, true) } testTree.loadNewSim = async areEqual => { - // Arrange - const app = SimojiApp.setupApp("") - app.verbose = false - await app.start() + // Arrange + const app = SimojiApp.setupApp("") + app.verbose = false + await app.start() - // Act - app.pasteCodeCommand(`😃 + // Act + app.pasteCodeCommand(`😃 insert 200 😃`) - const boardState1 = app.board.toString() + const boardState1 = app.board.toString() - areEqual(app.board.populationCount["😃"], 200) + areEqual(app.board.populationCount["😃"], 200) - // Act - app.resetAllCommand() + // Act + app.resetAllCommand() - const boardState2 = app.board.toString() + const boardState2 = app.board.toString() - // Race condition is possible but pigs more likely to fly first. - areEqual(boardState1 === boardState2, false, "Boards should have changed") - areEqual(app.simojiPrograms[0].getAllErrors().length, 0, "program is valid") + // Race condition is possible but pigs more likely to fly first. + areEqual(boardState1 === boardState2, false, "Boards should have changed") + areEqual(app.simojiPrograms[0].getAllErrors().length, 0, "program is valid") - // Act - app.pasteCodeCommand(`😃 + // Act + app.pasteCodeCommand(`😃 insert insert 10 😃 `) - areEqual(app.simojiPrograms[0].getAllErrors().length, 2, "invalid programs dont crash") + areEqual(app.simojiPrograms[0].getAllErrors().length, 2, "invalid programs dont crash") } module.exports = { testTree } const runTree = testTree => { - const tap = require("tap") - Object.keys(testTree).forEach(key => { - testTree[key](tap.equal) - }) + const tap = require("tap") + Object.keys(testTree).forEach(key => { + testTree[key](tap.equal) + }) } if (module && !module.parent) runTree(testTree) diff --git a/examples.js b/examples.js index 0bf8317..b27db20 100644 --- a/examples.js +++ b/examples.js @@ -2,13 +2,13 @@ const stamp = require("jtree/products/stamp.nodejs.js") const { Disk } = require("jtree/products/Disk.node.js") const getExamples = () => - Disk.getFiles(__dirname + "/examples/") - .map(path => { - const name = Disk.getFileName(path.replace(".simoji", "")) - return name + `\n ` + Disk.read(path).replace(/\n/g, "\n ") - }) - .filter(i => i) - .join("\n") - .trim() + Disk.getFiles(__dirname + "/examples/") + .map(path => { + const name = Disk.getFileName(path.replace(".simoji", "")) + return name + `\n ` + Disk.read(path).replace(/\n/g, "\n ") + }) + .filter(i => i) + .join("\n") + .trim() module.exports = { getExamples } diff --git a/package.json b/package.json index 4fd7618..934c71f 100644 --- a/package.json +++ b/package.json @@ -7,14 +7,18 @@ "example": "examples" }, "prettier": { + "useTabs": false, + "tabWidth": 2, + "semi": false, "printWidth": 120, - "semi": false + "trailingComma": "none", + "arrowParens": "avoid" }, "bin": { "simoji": "./cli.js" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" }, "dependencies": { "jquery": "^3.6.0", diff --git a/readme.scroll b/readme.scroll index f0c964b..4d98bd5 100644 --- a/readme.scroll +++ b/readme.scroll @@ -54,7 +54,6 @@ startColumns 2 ## Development ? What is the dev loop like? - 1. Start the dev server with `node server.js`. 2. Open `localhost/dev.html` 3. Edit files diff --git a/server.js b/server.js index b676139..83592f0 100755 --- a/server.js +++ b/server.js @@ -8,33 +8,33 @@ const { jtree } = require("jtree") const { getExamples } = require("./examples") class Server { - start(port = 80) { - const app = express() + start(port = 80) { + const app = express() - app.get("/*.js", (req, res) => { - const filename = req.path.substr(1) - readFile(__dirname + "/" + filename, "utf8", (err, code) => { - if (err) throw err - res.send( - new TypeScriptRewriter(code) - .removeRequires() - .removeNodeJsOnlyLines() - .changeNodeExportsToWindowExports() - .getString() - ) - }) - }) + app.get("/*.js", (req, res) => { + const filename = req.path.substr(1) + readFile(__dirname + "/" + filename, "utf8", (err, code) => { + if (err) throw err + res.send( + new TypeScriptRewriter(code) + .removeRequires() + .removeNodeJsOnlyLines() + .changeNodeExportsToWindowExports() + .getString() + ) + }) + }) - app.get("/examples", (req, res) => { - res.send(getExamples()) - }) + app.get("/examples", (req, res) => { + res.send(getExamples()) + }) - app.use(express.static(__dirname + "/")) + app.use(express.static(__dirname + "/")) - app.listen(port, () => { - console.log(`Running Simoji Dev Server. cmd+dblclick: http://localhost:${port}/dev.html`) - }) - } + app.listen(port, () => { + console.log(`Running Simoji Dev Server. cmd+dblclick: http://localhost:${port}/dev.html`) + }) + } } const server = new Server() diff --git a/testAll.js b/testAll.js index 58522d0..545669b 100644 --- a/testAll.js +++ b/testAll.js @@ -1,8 +1,8 @@ const runTree = testTree => { - const tap = require("tap") - Object.keys(testTree).forEach(key => { - testTree[key](tap.equal) - }) + const tap = require("tap") + Object.keys(testTree).forEach(key => { + testTree[key](tap.equal) + }) } runTree({ ...require("./yodash.test.node.js").testTree, ...require("./components/SimojiApp.test.node.js").testTree }) diff --git a/yodash.js b/yodash.js index ded1dba..be73f88 100644 --- a/yodash.js +++ b/yodash.js @@ -6,281 +6,281 @@ const { Directions, NodeTypes } = require("./components/Types.js") yodash.parseInts = (arr, start) => arr.map((item, index) => (index >= start ? parseInt(item) : item)) yodash.getRandomAngle = randomNumberGenerator => { - const r1 = randomNumberGenerator() - const r2 = randomNumberGenerator() - if (r1 > 0.5) return r2 > 0.5 ? Directions.North : Directions.South - return r2 > 0.5 ? Directions.West : Directions.East + const r1 = randomNumberGenerator() + const r2 = randomNumberGenerator() + if (r1 > 0.5) return r2 > 0.5 ? Directions.North : Directions.South + return r2 > 0.5 ? Directions.West : Directions.East } yodash.flipAngle = angle => { - let newAngle = "" - if (angle.includes(Directions.North)) newAngle += Directions.South - else if (angle.includes(Directions.South)) newAngle += Directions.North - if (angle.includes(Directions.East)) newAngle += Directions.West - else if (angle.includes(Directions.West)) newAngle += Directions.East - return newAngle + let newAngle = "" + if (angle.includes(Directions.North)) newAngle += Directions.South + else if (angle.includes(Directions.South)) newAngle += Directions.North + if (angle.includes(Directions.East)) newAngle += Directions.West + else if (angle.includes(Directions.West)) newAngle += Directions.East + return newAngle } yodash.compare = (left, operator, right) => { - if (operator === "=") return left == right - if (operator === "<") return left < right - if (operator === ">") return left > right - if (operator === "<=") return left <= right - if (operator === ">=") return left >= right + if (operator === "=") return left == right + if (operator === "<") return left < right + if (operator === ">") return left > right + if (operator === "<=") return left <= right + if (operator === ">=") return left >= right - return false + return false } yodash.compileAgentClassDeclarationsAndMap = program => { - const clone = program.clone() - clone.filter(node => node.getNodeTypeId() !== NodeTypes.agentDefinitionNode).forEach(node => node.destroy()) - clone.agentKeywordMap = {} - clone.agentTypes.forEach((node, index) => (clone.agentKeywordMap[node.getWord(0)] = `simAgent${index}`)) - const compiled = clone.compile() - const agentMap = Object.keys(clone.agentKeywordMap) - .map(key => `"${key}":${clone.agentKeywordMap[key]}`) - .join(",") - return `${compiled} + const clone = program.clone() + clone.filter(node => node.getNodeTypeId() !== NodeTypes.agentDefinitionNode).forEach(node => node.destroy()) + clone.agentKeywordMap = {} + clone.agentTypes.forEach((node, index) => (clone.agentKeywordMap[node.getWord(0)] = `simAgent${index}`)) + const compiled = clone.compile() + const agentMap = Object.keys(clone.agentKeywordMap) + .map(key => `"${key}":${clone.agentKeywordMap[key]}`) + .join(",") + return `${compiled} const map = {${agentMap}}; map;` } yodash.patchExperimentAndReplaceSymbols = (program, experiment) => { - const clone = program.clone() - // drop experiment nodes - clone.filter(node => node.getNodeTypeId() === NodeTypes.experimentNode).forEach(node => node.destroy()) - // Append current experiment - if (experiment) clone.concat(experiment.childrenToString()) - // Build symbol table - const symbolTable = {} - clone - .filter(node => node.getNodeTypeId() === NodeTypes.settingDefinitionNode) - .forEach(node => { - symbolTable[node.getWord(0)] = node.getContent() - node.destroy() - }) - // Find and replace - let withVarsReplaced = clone.toString() - Object.keys(symbolTable).forEach(key => { - withVarsReplaced = withVarsReplaced.replaceAll(key, symbolTable[key]) - }) - return withVarsReplaced + const clone = program.clone() + // drop experiment nodes + clone.filter(node => node.getNodeTypeId() === NodeTypes.experimentNode).forEach(node => node.destroy()) + // Append current experiment + if (experiment) clone.concat(experiment.childrenToString()) + // Build symbol table + const symbolTable = {} + clone + .filter(node => node.getNodeTypeId() === NodeTypes.settingDefinitionNode) + .forEach(node => { + symbolTable[node.getWord(0)] = node.getContent() + node.destroy() + }) + // Find and replace + let withVarsReplaced = clone.toString() + Object.keys(symbolTable).forEach(key => { + withVarsReplaced = withVarsReplaced.replaceAll(key, symbolTable[key]) + }) + return withVarsReplaced } yodash.getBestAngle = (targets, position) => { - let closest = Infinity - let target - targets.forEach(candidate => { - const pos = candidate.position - const distance = math.distance([pos.down, pos.right], [position.down, position.right]) - if (distance < closest) { - closest = distance - target = candidate - } - }) - const heading = target.position - return yodash.angle(position.down, position.right, heading.down, heading.right) + let closest = Infinity + let target + targets.forEach(candidate => { + const pos = candidate.position + const distance = math.distance([pos.down, pos.right], [position.down, position.right]) + if (distance < closest) { + closest = distance + target = candidate + } + }) + const heading = target.position + return yodash.angle(position.down, position.right, heading.down, heading.right) } yodash.angle = (cx, cy, ex, ey) => { - const dy = ey - cy - const dx = ex - cx - let theta = Math.atan2(dy, dx) // range (-PI, PI] - theta *= 180 / Math.PI // rads to degs, range (-180, 180] - //if (theta < 0) theta = 360 + theta; // range [0, 360) - let angle = "" + const dy = ey - cy + const dx = ex - cx + let theta = Math.atan2(dy, dx) // range (-PI, PI] + theta *= 180 / Math.PI // rads to degs, range (-180, 180] + //if (theta < 0) theta = 360 + theta; // range [0, 360) + let angle = "" - if (Math.abs(theta) > 90) angle += Directions.North - else angle += Directions.South - if (theta < 0) angle += Directions.West - else angle += Directions.East - return angle + if (Math.abs(theta) > 90) angle += Directions.North + else angle += Directions.South + if (theta < 0) angle += Directions.West + else angle += Directions.East + return angle } yodash.getRandomLocation = (rows, cols, randomNumberGenerator) => { - const maxRight = cols - const maxBottom = rows - const right = Math.round(randomNumberGenerator() * maxRight) - const down = Math.round(randomNumberGenerator() * maxBottom) - return { right, down } + const maxRight = cols + const maxBottom = rows + const right = Math.round(randomNumberGenerator() * maxRight) + const down = Math.round(randomNumberGenerator() * maxBottom) + return { right, down } } yodash.getRandomLocationHash = (rows, cols, occupiedSpots, randomNumberGenerator) => { - const { right, down } = yodash.getRandomLocation(rows, cols, randomNumberGenerator) - const hash = yodash.makePositionHash({ right, down }) - if (occupiedSpots && occupiedSpots.has(hash)) - return yodash.getRandomLocationHash(rows, cols, occupiedSpots, randomNumberGenerator) - return hash + const { right, down } = yodash.getRandomLocation(rows, cols, randomNumberGenerator) + const hash = yodash.makePositionHash({ right, down }) + if (occupiedSpots && occupiedSpots.has(hash)) + return yodash.getRandomLocationHash(rows, cols, occupiedSpots, randomNumberGenerator) + return hash } yodash.fill = (rows, cols, occupiedSpots, emoji) => { - const board = [] - while (rows >= 0) { - let col = cols - while (col >= 0) { - const hash = yodash.makePositionHash({ right: col, down: rows }) - col-- - if (occupiedSpots.has(hash)) continue - board.push(`${emoji} ${hash}`) - } - rows-- - } - return board.join("\n") + const board = [] + while (rows >= 0) { + let col = cols + while (col >= 0) { + const hash = yodash.makePositionHash({ right: col, down: rows }) + col-- + if (occupiedSpots.has(hash)) continue + board.push(`${emoji} ${hash}`) + } + rows-- + } + return board.join("\n") } yodash.positionsAdjacentTo = position => { - let { right, down } = position - const positions = [] - down-- - positions.push({ down, right }) - right-- - positions.push({ down, right }) - right++ - right++ - positions.push({ down, right }) - down++ - positions.push({ down, right }) - right-- - right-- - positions.push({ down, right }) - down++ - positions.push({ down, right }) - right++ - positions.push({ down, right }) - right++ - positions.push({ down, right }) - return positions + let { right, down } = position + const positions = [] + down-- + positions.push({ down, right }) + right-- + positions.push({ down, right }) + right++ + right++ + positions.push({ down, right }) + down++ + positions.push({ down, right }) + right-- + right-- + positions.push({ down, right }) + down++ + positions.push({ down, right }) + right++ + positions.push({ down, right }) + right++ + positions.push({ down, right }) + return positions } yodash.makePositionHash = position => `${position.down + "⬇️ " + position.right + "➡️"}` yodash.makeRectangle = (character = "🧱", width = 20, height = 20, startRight = 0, startDown = 0) => { - if (width < 1 || height < 1) { - return "" - } - const cells = [] - let row = 0 - while (row < height) { - let col = 0 - while (col < width) { - const isPerimeter = row === 0 || row === height - 1 || col === 0 || col === width - 1 - if (isPerimeter) - cells.push( - `${character} ${yodash.makePositionHash({ - down: startDown + row, - right: startRight + col - })}` - ) - col++ - } - row++ - } - return cells.join("\n") + if (width < 1 || height < 1) { + return "" + } + const cells = [] + let row = 0 + while (row < height) { + let col = 0 + while (col < width) { + const isPerimeter = row === 0 || row === height - 1 || col === 0 || col === width - 1 + if (isPerimeter) + cells.push( + `${character} ${yodash.makePositionHash({ + down: startDown + row, + right: startRight + col + })}` + ) + col++ + } + row++ + } + return cells.join("\n") } yodash.parsePosition = words => { - return { - down: parseInt(words.find(word => word.includes("⬇️")).slice(0, -1)), - right: parseInt(words.find(word => word.includes("➡️")).slice(0, -1)) - } + return { + down: parseInt(words.find(word => word.includes("⬇️")).slice(0, -1)), + right: parseInt(words.find(word => word.includes("➡️")).slice(0, -1)) + } } yodash.draw = str => { - const lines = str.split("\n") - const output = [] - for (let index = 0; index < lines.length; index++) { - const words = lines[index].split(" ") - for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { - const word = words[wordIndex] - if (word !== "") output.push(`${word} ${yodash.makePositionHash({ down: index, right: wordIndex })}`) - } - } - return output.join("\n") + const lines = str.split("\n") + const output = [] + for (let index = 0; index < lines.length; index++) { + const words = lines[index].split(" ") + for (let wordIndex = 0; wordIndex < words.length; wordIndex++) { + const word = words[wordIndex] + if (word !== "") output.push(`${word} ${yodash.makePositionHash({ down: index, right: wordIndex })}`) + } + } + return output.join("\n") } yodash.updateOccupiedSpots = (board, occupiedSpots) => { - new TreeNode(board).forEach(line => { - occupiedSpots.add(yodash.makePositionHash(yodash.parsePosition(line.getWords()))) - }) + new TreeNode(board).forEach(line => { + occupiedSpots.add(yodash.makePositionHash(yodash.parsePosition(line.getWords()))) + }) } yodash.getAllAvailableSpots = (rows, cols, occupiedSpots, rowStart = 0, colStart = 0) => { - const availablePositions = [] - let down = rows - while (down >= rowStart) { - let right = cols - while (right >= colStart) { - const hash = yodash.makePositionHash({ right, down }) - if (!occupiedSpots.has(hash)) availablePositions.push({ right, down, hash }) - right-- - } - down-- - } - return availablePositions + const availablePositions = [] + let down = rows + while (down >= rowStart) { + let right = cols + while (right >= colStart) { + const hash = yodash.makePositionHash({ right, down }) + if (!occupiedSpots.has(hash)) availablePositions.push({ right, down, hash }) + right-- + } + down-- + } + return availablePositions } yodash.parsePercent = str => parseFloat(str.replace("%", "")) / 100 yodash.insertClusteredRandomAgents = ( - randomNumberGenerator, - amount, - char, - rows, - cols, - occupiedSpots, - originRow, - originColumn + randomNumberGenerator, + amount, + char, + rows, + cols, + occupiedSpots, + originRow, + originColumn ) => { - const availableSpots = yodash.getAllAvailableSpots(rows, cols, occupiedSpots) - const spots = yodash.sampleFrom(availableSpots, amount * 10, randomNumberGenerator) - const origin = originColumn - ? { down: parseInt(originRow), right: parseInt(originColumn) } - : yodash.getRandomLocation(rows, cols, randomNumberGenerator) - const sortedByDistance = lodash.sortBy(spots, spot => - math.distance([origin.down, origin.right], [spot.down, spot.right]) - ) + const availableSpots = yodash.getAllAvailableSpots(rows, cols, occupiedSpots) + const spots = yodash.sampleFrom(availableSpots, amount * 10, randomNumberGenerator) + const origin = originColumn + ? { down: parseInt(originRow), right: parseInt(originColumn) } + : yodash.getRandomLocation(rows, cols, randomNumberGenerator) + const sortedByDistance = lodash.sortBy(spots, spot => + math.distance([origin.down, origin.right], [spot.down, spot.right]) + ) - return sortedByDistance - .slice(0, amount) - .map(spot => { - const { hash } = spot - occupiedSpots.add(hash) - return `${char} ${hash}` - }) - .join("\n") + return sortedByDistance + .slice(0, amount) + .map(spot => { + const { hash } = spot + occupiedSpots.add(hash) + return `${char} ${hash}` + }) + .join("\n") } yodash.getRandomNumberGenerator = seed => () => { - const semiRand = Math.sin(seed++) * 10000 - return semiRand - Math.floor(semiRand) + const semiRand = Math.sin(seed++) * 10000 + return semiRand - Math.floor(semiRand) } yodash.sampleFrom = (collection, howMany, randomNumberGenerator) => - shuffleArray(collection, randomNumberGenerator).slice(0, howMany) + shuffleArray(collection, randomNumberGenerator).slice(0, howMany) const shuffleArray = (array, randomNumberGenerator) => { - const clonedArr = array.slice() - for (let index = clonedArr.length - 1; index > 0; index--) { - const replacerIndex = Math.floor(randomNumberGenerator() * (index + 1)) - ;[clonedArr[index], clonedArr[replacerIndex]] = [clonedArr[replacerIndex], clonedArr[index]] - } - return clonedArr + const clonedArr = array.slice() + for (let index = clonedArr.length - 1; index > 0; index--) { + const replacerIndex = Math.floor(randomNumberGenerator() * (index + 1)) + ;[clonedArr[index], clonedArr[replacerIndex]] = [clonedArr[replacerIndex], clonedArr[index]] + } + return clonedArr } yodash.pick = (tree, fields) => { - const newTree = tree.clone() - const map = TreeUtils.arrayToMap(fields) - newTree.forEach(node => { - if (!map[node.getWord(0)]) node.destroy() - }) + const newTree = tree.clone() + const map = TreeUtils.arrayToMap(fields) + newTree.forEach(node => { + if (!map[node.getWord(0)]) node.destroy() + }) - return newTree + return newTree } yodash.flatten = tree => { - const newTree = new jtree.TreeNode() - tree.forEach(node => node.forEach(child => newTree.appendNode(child))) - return newTree + const newTree = new jtree.TreeNode() + tree.forEach(node => node.forEach(child => newTree.appendNode(child))) + return newTree } module.exports = { yodash } diff --git a/yodash.test.node.js b/yodash.test.node.js index 852d095..5be76b6 100755 --- a/yodash.test.node.js +++ b/yodash.test.node.js @@ -5,28 +5,28 @@ const { yodash } = require("./yodash.js") const testTree = {} testTree.getRandomAngle = areEqual => { - areEqual(yodash.getRandomAngle(Math.random).match(/(East|West|North|South)/).length, 2) + areEqual(yodash.getRandomAngle(Math.random).match(/(East|West|North|South)/).length, 2) } testTree.makeRectangle = areEqual => { - const expected = `😀 0⬇️ 0➡️ + const expected = `😀 0⬇️ 0➡️ 😀 0⬇️ 1➡️ 😀 1⬇️ 0➡️ 😀 1⬇️ 1➡️` - areEqual(yodash.makeRectangle("😀", 2, 2), expected) - areEqual( - yodash.makeRectangle("🚪", 2, 1, 1, 1), - `🚪 1⬇️ 1➡️ + areEqual(yodash.makeRectangle("😀", 2, 2), expected) + areEqual( + yodash.makeRectangle("🚪", 2, 1, 1, 1), + `🚪 1⬇️ 1➡️ 🚪 1⬇️ 2➡️` - ) + ) } module.exports = { testTree } const runTree = testTree => { - const tap = require("tap") - Object.keys(testTree).forEach(key => { - testTree[key](tap.equal) - }) + const tap = require("tap") + Object.keys(testTree).forEach(key => { + testTree[key](tap.equal) + }) } if (module && !module.parent) runTree(testTree) ------------------------------------------------------------
commit b8bf3f5ec7386f99d806581677478344cfb53cd6
Author: Breck Yunits <breck7@gmail.com> Date: Sat Apr 8 19:07:48 2023 -1000 Update docs diff --git a/cheatSheet.html b/cheatSheet.html index 201a018..76ecba5 100644 --- a/cheatSheet.html +++ b/cheatSheet.html @@ -2,13 +2,13 @@ <title>Simoji Quickstart Guide</title> <script>/* This HTML was generated by 📜 Scroll v68.0.0. http://scroll.pub */</script> <style>@media print {.doNotPrint {display: none !important;}}</style> -<link rel="canonical" href="cheatSheet.html"></link> +<link rel="canonical" href="https://simoji.pub/cheatSheet.html"></link> <meta charset="iso-8859-1"></meta> <meta name="viewport" content="width=device-width,initial-scale=1"></meta> -<meta name="description" content="Simoji is a language and tool for thinkers to write shareable simulations using 😃's. You write your simulations in a simple language using mostly Emojis and then click play. Simoji is public domain, open source, and suggestions welcome!"></meta> +<meta name="description" content="Design quick simulations with Emojis"></meta> <meta name="generator" content="Scroll v68.0.0"></meta> <meta property="og:title" content="Simoji Quickstart Guide"></meta> -<meta property="og:description" content="Simoji is a language and tool for thinkers to write shareable simulations using 😃's. You write your simulations in a simple language using mostly Emojis and then click play. Simoji is public domain, open source, and suggestions welcome!"></meta> +<meta property="og:description" content="Design quick simulations with Emojis"></meta> <meta property="og:image" content=""></meta> <meta name="twitter:card" content="summary_large_image"></meta> @@ -303,8 +303,16 @@ h4.scrollQuestion { color: rgba(204,204,204,.5); } </style> +<div class="gazetteHeader doNotPrint"> + <a class="gazettePrevPageLink" href="readme.html"><</a> + <a class="gazetteTopLeftBar" href="index.html"><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12.7166 3.79541C12.2835 3.49716 11.7165 3.49716 11.2834 3.79541L4.14336 8.7121C3.81027 8.94146 3.60747 9.31108 3.59247 9.70797C3.54064 11.0799 3.4857 13.4824 3.63658 15.1877C3.7504 16.4742 4.05336 18.1747 4.29944 19.4256C4.41371 20.0066 4.91937 20.4284 5.52037 20.4284H8.84433C8.98594 20.4284 9.10074 20.3111 9.10074 20.1665V15.9754C9.10074 14.9627 9.90433 14.1417 10.8956 14.1417H13.4091C14.4004 14.1417 15.204 14.9627 15.204 15.9754V20.1665C15.204 20.3111 15.3188 20.4284 15.4604 20.4284H18.4796C19.0806 20.4284 19.5863 20.0066 19.7006 19.4256C19.9466 18.1747 20.2496 16.4742 20.3634 15.1877C20.5143 13.4824 20.4594 11.0799 20.4075 9.70797C20.3925 9.31108 20.1897 8.94146 19.8566 8.7121L12.7166 3.79541ZM10.4235 2.49217C11.3764 1.83602 12.6236 1.83602 13.5765 2.49217L20.7165 7.40886C21.4457 7.91098 21.9104 8.73651 21.9448 9.64736C21.9966 11.0178 22.0564 13.5119 21.8956 15.3292C21.7738 16.7067 21.4561 18.4786 21.2089 19.7353C20.9461 21.0711 19.7924 22.0001 18.4796 22.0001H15.4604C14.4691 22.0001 13.6655 21.1791 13.6655 20.1665V15.9754C13.6655 15.8307 13.5507 15.7134 13.4091 15.7134H10.8956C10.754 15.7134 10.6392 15.8307 10.6392 15.9754V20.1665C10.6392 21.1791 9.83561 22.0001 8.84433 22.0001H5.52037C4.20761 22.0001 3.05389 21.0711 2.79113 19.7353C2.54392 18.4786 2.22624 16.7067 2.10437 15.3292C1.94358 13.5119 2.00338 11.0178 2.05515 9.64736C2.08957 8.73652 2.55427 7.91098 3.28346 7.40886L10.4235 2.49217Z"/></svg></a> + <a class="gazetteTopRightBar" href="https://github.com/breck7/simoji"><svg xmlns="http://www.w3.org/2000/svg" width="92pt" height="92pt" viewBox="0 0 92 92"><path d="M90.156 41.965 50.036 1.848a5.913 5.913 0 0 0-8.368 0l-8.332 8.332 10.566 10.566a7.03 7.03 0 0 1 7.23 1.684 7.043 7.043 0 0 1 1.673 7.277l10.183 10.184a7.026 7.026 0 0 1 7.278 1.672 7.04 7.04 0 0 1 0 9.957 7.045 7.045 0 0 1-9.961 0 7.038 7.038 0 0 1-1.532-7.66l-9.5-9.497V59.36a7.04 7.04 0 0 1 1.86 11.29 7.04 7.04 0 0 1-9.957 0 7.04 7.04 0 0 1 0-9.958 7.034 7.034 0 0 1 2.308-1.539V33.926a7.001 7.001 0 0 1-2.308-1.535 7.049 7.049 0 0 1-1.516-7.7L29.242 14.273 1.734 41.777a5.918 5.918 0 0 0 0 8.371L41.855 90.27a5.92 5.92 0 0 0 8.368 0l39.933-39.934a5.925 5.925 0 0 0 0-8.371"/></g></svg></a> + <a class="gazetteNextPageLink" href="releaseNotes.html">></a> +</div> + <div class="scrollSection"><h1 class="scrollTitle"><a href="cheatSheet.html">Simoji Quickstart Guide</a></h1> </div> +<div class="scrollColumns" style="column-width:35ch;column-count:3;max-width:145ch;"> <p class="scrollParagraph">Simoji is a language and tool for thinkers to write shareable simulations using 😃's. You write your simulations in a simple language using mostly Emojis and then click play. Simoji is public domain, <a href="https://github.com/publicdomaincompany/simoji">open source</a>, and suggestions welcome!</p> <div class="scrollSection"><h3 class="scrollParagraph">Example Program</h3> <code class="scrollCodeBlock">comment Define an ant agent @@ -435,4 +443,20 @@ experiment <p class="scrollParagraph">The source code for Simoji and all development happens on <a href="https://github.com/publicdomaincompany/simoji">Github</a>.</p> </div> <div class="scrollSection"><h3 class="scrollParagraph"><a href="./cheatSheet.pdf">Printable Version</a></h3> +</div> +<div class="scrollKeyboardNav" style="display:none;"><a href="readme.html">readme.html</a> · cheatSheet.html · <a href="releaseNotes.html">releaseNotes.html</a><script>document.addEventListener('keydown', function(event) { + if (document.activeElement !== document.body) return + const getLinks = () => document.getElementsByClassName("scrollKeyboardNav")[0].getElementsByTagName("a") + if (event.key === "ArrowLeft") + getLinks()[0].click() + else if (event.key === "ArrowRight") + getLinks()[1].click() + });</script></div> +</div><p class="scrollViewSource doNotPrint"> + <a href="cheatSheet.scroll">View source</a> +</p> +<div class="gazetteFooter doNotPrint"> + <a href="mailto:breck7+simoji@gmail.com"><svg viewBox="3 5 24 20" width="24" height="20" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(1, 0, 0, 1, 0, -289.0625)"><path style="opacity:1;stroke:none;stroke-width:0.49999997;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M 5 5 C 4.2955948 5 3.6803238 5.3628126 3.3242188 5.9101562 L 14.292969 16.878906 C 14.696939 17.282876 15.303061 17.282876 15.707031 16.878906 L 26.675781 5.9101562 C 26.319676 5.3628126 25.704405 5 25 5 L 5 5 z M 3 8.4140625 L 3 23 C 3 24.108 3.892 25 5 25 L 25 25 C 26.108 25 27 24.108 27 23 L 27 8.4140625 L 17.121094 18.292969 C 15.958108 19.455959 14.041892 19.455959 12.878906 18.292969 L 3 8.4140625 z " transform="translate(0,289.0625)" id="rect4592"/></g></svg></a> + <a href="https://github.com/breck7/simoji"><svg xmlns="http://www.w3.org/2000/svg" width="92pt" height="92pt" viewBox="0 0 92 92"><path d="M90.156 41.965 50.036 1.848a5.913 5.913 0 0 0-8.368 0l-8.332 8.332 10.566 10.566a7.03 7.03 0 0 1 7.23 1.684 7.043 7.043 0 0 1 1.673 7.277l10.183 10.184a7.026 7.026 0 0 1 7.278 1.672 7.04 7.04 0 0 1 0 9.957 7.045 7.045 0 0 1-9.961 0 7.038 7.038 0 0 1-1.532-7.66l-9.5-9.497V59.36a7.04 7.04 0 0 1 1.86 11.29 7.04 7.04 0 0 1-9.957 0 7.04 7.04 0 0 1 0-9.958 7.034 7.034 0 0 1 2.308-1.539V33.926a7.001 7.001 0 0 1-2.308-1.535 7.049 7.049 0 0 1-1.516-7.7L29.242 14.273 1.734 41.777a5.918 5.918 0 0 0 0 8.371L41.855 90.27a5.92 5.92 0 0 0 8.368 0l39.933-39.934a5.925 5.925 0 0 0 0-8.371"/></g></svg></a> + <a href="https://scroll.pub" class="gazetteScrollLink">Built with Scroll v68.0.0</a> </div> \ No newline at end of file diff --git a/cheatSheet.scroll b/cheatSheet.scroll index a250c08..4fe9376 100644 --- a/cheatSheet.scroll +++ b/cheatSheet.scroll @@ -1,8 +1,9 @@ -metaTags -gazetteCss +import header.scroll title Simoji Quickstart Guide +startColumns 3 + * Simoji is a language and tool for thinkers to write shareable simulations using 😃's. You write your simulations in a simple language using mostly Emojis and then click play. Simoji is public domain, open source, and suggestions welcome! https://github.com/publicdomaincompany/simoji open source @@ -123,3 +124,5 @@ pipeTable # Printable Version link ./cheatSheet.pdf + +import footer.scroll diff --git a/footer.scroll b/footer.scroll index fd62e77..ee3818d 100644 --- a/footer.scroll +++ b/footer.scroll @@ -1,2 +1,3 @@ importOnly +keyboardNav gazetteFooter \ No newline at end of file diff --git a/header.scroll b/header.scroll index 134ceaf..aeb8e25 100644 --- a/header.scroll +++ b/header.scroll @@ -1,4 +1,8 @@ importOnly metaTags gazetteCss -gazetteHeader \ No newline at end of file +gazetteHeader +description Design quick simulations with Emojis +git https://github.com/breck7/simoji +baseUrl https://simoji.pub/ +email breck7+simoji@gmail.com \ No newline at end of file diff --git a/scroll.settings b/scroll.settings deleted file mode 100644 index e3b8258..0000000 --- a/scroll.settings +++ /dev/null @@ -1,8 +0,0 @@ -title Simoji -description Design quick simulations with Emojis -github https://github.com/publicdomaincompany/simoji -git https://github.com/publicdomaincompany/simoji/blob/main -twitter https://twitter.com/publicdomainpub -email simoji@publicdomaincompany.com -baseUrl https://simoji.pub/ -ignoreGrammarFiles \ No newline at end of file ------------------------------------------------------------
commit 386daab4faf3949799496607324486530d7aeba5
Author: Breck Yunits <breck7@gmail.com> Date: Sat Apr 8 19:01:05 2023 -1000 Update Scroll docs diff --git a/cheatSheet.html b/cheatSheet.html index 385e8f1..201a018 100644 --- a/cheatSheet.html +++ b/cheatSheet.html @@ -1,135 +1,117 @@ -<!doctype html> -<!-- - - This page was compiled by 📜 Scroll, the public domain - static site publishing software. - - https://scroll.pub - - Generally you don't want to edit it by hand. - - Scroll v22.3.0 - ---> -<html lang="en-US"> - <head> - <meta charset="utf-8"></meta> - <title>Simoji Quickstart Guide - Simoji</title> - <meta name="viewport" content="width=device-width,initial-scale=1"></meta> - <meta name="description" content="Design quick simulations with Emojis"></meta> - <meta name="generator" content="Scroll v22.3.0"></meta> - <meta property="og:title" content="Simoji Quickstart Guide"></meta> - <meta property="og:description" content="Simoji is a language and tool for thinkers to write shareable simulations using 😃's. You write your simulations in a simple language using mostly Emojis and then click play. Simoji is public domain, open source and suggestions welcome!"></meta> - <meta property="og:image" content=""></meta> - <meta name="twitter:card" content="summary_large_image"></meta> - <style>html,body,div,span,h1,h2,h3,h4,p,ol,ul,table { +<meta charset="utf-8"></meta> +<title>Simoji Quickstart Guide</title> +<script>/* This HTML was generated by 📜 Scroll v68.0.0. http://scroll.pub */</script> +<style>@media print {.doNotPrint {display: none !important;}}</style> +<link rel="canonical" href="cheatSheet.html"></link> +<meta charset="iso-8859-1"></meta> +<meta name="viewport" content="width=device-width,initial-scale=1"></meta> +<meta name="description" content="Simoji is a language and tool for thinkers to write shareable simulations using 😃's. You write your simulations in a simple language using mostly Emojis and then click play. Simoji is public domain, open source, and suggestions welcome!"></meta> +<meta name="generator" content="Scroll v68.0.0"></meta> +<meta property="og:title" content="Simoji Quickstart Guide"></meta> +<meta property="og:description" content="Simoji is a language and tool for thinkers to write shareable simulations using 😃's. You write your simulations in a simple language using mostly Emojis and then click play. Simoji is public domain, open source, and suggestions welcome!"></meta> +<meta property="og:image" content=""></meta> +<meta name="twitter:card" content="summary_large_image"></meta> + +<style>html,body,div,span,h1,h2,h3,h4,p,ol,ul,li,table,figure { margin: 0; padding: 0; border: 0; vertical-align: baseline; border-spacing: 0; } +li { + list-style-position: inside; + margin-top: .4em; + line-height: 1.2em; +} +a { + text-decoration-color: transparent; +} +a:hover { + text-decoration-color: initial; +} +sup,sub { + vertical-align: baseline; + position: relative; + top: -0.6em; +} +sub { + top: 0.6em; +} html { + padding: 4px; background-color: rgb(244,244,244); font-family: Exchange,Georgia,serif; color: #000; font-size: 14px; hyphens: auto; } -.scrollHeaderComponent { - border-bottom: 1px solid rgb(204,204,204); - text-align: center; - padding-bottom: 8px; -} -.scrollNameComponent a { - text-decoration: none; - color: #000; -} -.scrollTopRightBarComponent { - text-align: right; - position: absolute; - right: 25px; - top: 3px; -} -.scrollSocialMediaIconsComponent svg { - width: 30px; - fill: rgba(204,204,204, .5); - margin-left: 15px; -} -.scrollSocialMediaIconsComponent svg:hover { - fill: #333; -} -.scrollFooterComponent { - border-top: 1px solid rgb(204,204,204); - margin-top: 8px; - padding-top: 8px; - text-align: center; +p { + margin-top: 0.4em; + line-height: 1.4em; } -.scrollCommunityLinkComponent { +.scrollQuote { + break-inside: avoid; display: block; - font-family: Verdana; - font-weight: 100; - margin: .5em; - padding-bottom: 1em; - text-decoration: none; - color: rgba(204,204,204,.5); + margin: .5em 0; + padding: .5em; + background: rgba(204,204,204,.5); + white-space: pre-line; + border-left: .5em solid rgba(204,204,204,.8); +} +code { + font-size: 90%; + background-color: rgba(204,204,204,.5); + padding: 2px 4px; + border-radius: 4px; } -.scrollIndexPageComponent,.scrollArticlePageComponent { +.scrollColumns { column-count: auto; column-fill: balance; column-width: 35ch; column-gap: 20px; - column-rule: 1px solid rgb(204,204,204); padding-left: 20px; padding-right: 20px; margin: auto; } -.scrollArticlePageComponent { - column-rule: none; - padding-top: 8px; -} -.scrollIndexPageArticleContainerComponent { - border-bottom: 1px solid rgb(204,204,204); +.scrollSnippetContainer { padding: 1ch 0; break-inside: avoid; text-align: justify; - margin-bottom: .5em; } -.scrollTitleComponent { +.scrollTitle { text-align: center; - font-size: 24px; margin-bottom: .25em; } -.scrollTitleComponent a { - text-decoration: none; +.scrollTitle a { color: #000; } -.scrollArticleDateComponent { +.scrollDateline { font-style: italic; font-size: 80%; } -.scrollParagraphComponent { - margin-top: 0.4em; - line-height: 1.4em; -} -.scrollQuoteComponent { +.scrollSection { break-inside: avoid; - display: block; - margin: .5em 0; - padding: .5em; - background: rgba(204,204,204,.5); - white-space: pre-line; - border-left: .5em solid rgba(204,204,204,.8); } -.scrollSectionComponent,.scrollSubsectionComponent { +.scrollSection h3 { + margin-top: 1em; text-align: center; +} +.scrollSection h4 { margin-top: 1em; + text-align: center; } -.scrollQuestionComponent { +h4.scrollQuestion { text-align: left; - margin-top: 2em; } -.scrollCodeBlockComponent { +.scrollNoteLink { + opacity: .4; +} +.scrollHoverNote { + text-decoration: underline dashed 1px rgba(0,0,0,.1); + cursor: default; +} +.scrollCodeBlock { overflow: auto; font-size: 80%; hyphens: none; @@ -139,9 +121,33 @@ html { display: block; margin: .5em 0; padding: .5em; - background: rgba(204,204,204,.5); + border-radius: 0; + position: relative; +} +.scrollCodeBlock:hover .scrollCopyButton { + opacity: .5; +} +.scrollCodeBlock:hover .scrollCopyButton:hover { + opacity: .8; +} +.scrollCodeBlock:hover .scrollCopyButton:active { + opacity: 1; +} +.scrollCopyButton { + position: absolute; + top: 2px; + right: 2px; + font-size: 14px; + cursor: pointer; + opacity: 0; } -.scrollTableComponent { +.scrollCopyButton::after { + content: "[ ]"; +} +.scrollCopiedButton::after { + content: "[✓]"; +} +.scrollTable { table-layout: fixed; margin: .5em 0; overflow: hidden; @@ -150,75 +156,158 @@ html { hyphens: none; border: 1px solid rgba(224,224,224,.8); } -.scrollTableComponent td,.scrollTableComponent th { +.scrollTable td,.scrollTable th { padding: 3px; overflow: hidden; } -.scrollTableComponent th { +.scrollTable th { border-bottom: 2px solid rgba(0,0,0,.6); text-align: left; } -.scrollTableComponent tr:nth-child(even) { +.scrollTable tr:nth-child(even) { background: rgba(224,224,224,.6); } -.scrollUnorderedListComponent,.scrollOrderedListComponent { - text-align: left; - line-height: 1.4em; - padding-left: 1em; - margin-top: 0.4em; +.scrollByLine { + font-size: 12px; + font-style: italic; + margin: 4px 0; + text-align: center; } -.scrollArticleSourceLinkComponent { +.scrollViewSource { text-align: center; font-size: 80%; margin: 0; margin-top: 0.4em; line-height: 1.4em; + margin-bottom: 1em; } -.scrollArticleSourceLinkComponent a { +.scrollViewSource a { color: #000; - text-decoration: none; } -.scrollDialogueComponent span { +.scrollContinueReadingLink { + display: block; + text-align: center; +} +.scrollCaptionedFigure { + display: block; + text-align: center; +} +.scrollCaptionedFigure img { + max-width: 98%; + height: auto; +} +.scrollCaptionedFigure figcaption { + font-style: italic; +} +.scrollDashboard { + width: 100%; + font-size: 30px; + text-align: center; + font-weight: bold; + break-inside: avoid; + margin-top: 8px; + margin-bottom: 8px; +} +.scrollDashboard td { + width: 33.3%; + border: 1px solid #e8e8e8; +} +.scrollDashboard span { + font-size: 20p;; + display: block; +} +.scrollChat span { font-family: Verdana; margin-top: 5px; padding: 5px 20px; border-radius: 15px; display: inline-block; } -.scrollDialogueComponentLeft { +.scrollChatLeft { text-align: left; } -.scrollDialogueComponentLeft span { +.scrollChatLeft span { background: rgba(204,204,204, .5); } -.scrollDialogueComponentRight { +.scrollChatRight { text-align: right; } -.scrollDialogueComponentRight span { +.scrollChatRight span { color: white; background: rgb(0,132,255); } -.scrollImageComponent { - display: block; +.scrollYouTubeHolder { + position: relative; + width: 100%; + height: 0; + padding-bottom: 56.25%; +} +.scrollYouTubeEmbed { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.gazetteHeader svg { + width: 30px; + height: 30px; + fill: rgba(204,204,204,.8); +} +.gazetteHeader svg:hover { + fill: #333; +} +.gazetteHeader a { + color: rgba(204,204,204,.8); + position: absolute; + font-size: 30px; + line-height: 27px; + text-decoration: none; +} +.gazetteHeader a:hover { + color: #333; +} +.gazetteHeader .gazetteTopLeftBar { + text-align: left; + left: 25px; +} +.gazetteHeader .gazetteTopRightBar { + text-align: right; + right: 25px; +} +.gazetteHeader a.gazettePrevPageLink { + left: 3px; +} +.gazetteHeader a.gazetteNextPageLink { + right: 3px; +} +.gazetteFooter { + margin-top: 8px; + padding-top: 8px; text-align: center; } -.scrollImageComponent img { - max-width: 35ch; - height: auto; +.gazetteFooter svg { + width: 30px; + height: 30px; + fill: rgba(204,204,204, .5); + padding: 0 7px; +} +.gazetteFooter svg:hover { + fill: #333; +} +.gazetteScrollLink { + display: block; + font-family: Verdana; + font-weight: 100; + margin: .5em; + color: rgba(204,204,204,.5); } </style> -</head> - <body> - <div class="scrollArticlePageComponent" style=""><h1 class="scrollTitleComponent"><a href="cheatSheet.html">Simoji Quickstart Guide</a></h1> - - - - -<p class="scrollParagraphComponent">Simoji is a language and tool for thinkers to write shareable simulations using 😃's. You write your simulations in a simple language using mostly Emojis and then click play. Simoji is public domain, open <a href="https://github.com/publicdomaincompany/simoji">source</a> and suggestions welcome!</p> - -<h4 class="scrollSubsectionComponent">Example Program</h4> - -<code class="scrollCodeBlockComponent">comment Define an ant agent +<div class="scrollSection"><h1 class="scrollTitle"><a href="cheatSheet.html">Simoji Quickstart Guide</a></h1> +</div> +<p class="scrollParagraph">Simoji is a language and tool for thinkers to write shareable simulations using 😃's. You write your simulations in a simple language using mostly Emojis and then click play. Simoji is public domain, <a href="https://github.com/publicdomaincompany/simoji">open source</a>, and suggestions welcome!</p> +<div class="scrollSection"><h3 class="scrollParagraph">Example Program</h3> +<code class="scrollCodeBlock">comment Define an ant agent 🐜 comment Ants turn then move one space per tick onTick @@ -241,101 +330,67 @@ comment Define an ant hill comment Set up the board. insert 3 🥖 insert 1 ⛰</code> - - -<h3 class="scrollSectionComponent">Concepts</h3> - -<h4 class="scrollSubsectionComponent">Board</h4> - -<p class="scrollParagraphComponent">The Board is the rectangle on which your simulation takes place. It has a width and height and is divided into a grid.</p> - -<h4 class="scrollSubsectionComponent">Agents</h4> - -<p class="scrollParagraphComponent">Agents are the key concept in Simoji. Everything you see on your board is an agent. In the code above, the ant, hill, and food are all agent types.</p> - -<h4 class="scrollSubsectionComponent">Events</h4> - -<p class="scrollParagraphComponent">Events are blocks of commands that execute during the running of the experiments. Probabilities can be assigned so blocks run stochastically.</p> - -<ul class="scrollUnorderedListComponent"> -<li>onTick</li> -<li>onHit</li> -<li>onTouch</li> -<li>onDeath</li> -</ul> - -<p class="scrollParagraphComponent">Some events can also happen on the board level:</p> - -<ul class="scrollUnorderedListComponent"> -<li>onTick</li> -<li>onExtinct</li> -</ul> - -<h4 class="scrollSubsectionComponent">Commands</h4> - -<ul class="scrollUnorderedListComponent"> -<li>kickIt</li> -<li>replaceWith</li> -<li>spawn</li> -<li>remove</li> -<li>pickItUp</li> -<li>turnRandomly</li> -<li>turnToward</li> -</ul> - -<h4 class="scrollSubsectionComponent">Attributes</h4> - -<p class="scrollParagraphComponent">Agents can have attributes. You can define your own. Some are built in like:</p> - -<ul class="scrollUnorderedListComponent"> -<li>health</li> -</ul> - -<p class="scrollParagraphComponent">Some attributes are booleans with only 2 states like:</p> - -<ul class="scrollUnorderedListComponent"> -<li>solid</li> -<li>bouncy</li> -</ul> - -<h4 class="scrollSubsectionComponent">Experiments</h4> - -<p class="scrollParagraphComponent">You can run multiple boards at the same time using the `experiment` keyword. You can tweak any words in your experiments. Usually you want to change Settings. The maximum boards at one time is currently 4.</p> - -<h4 class="scrollSubsectionComponent">Parameters</h4> - -<p class="scrollParagraphComponent">You can define parameters that you use throughout your simulation code. This makes it easy to tweak them in experiments. For example:</p> - -<code class="scrollCodeBlockComponent">lightningFrequencySetting .1 +</div> +<div class="scrollSection"><h3 class="scrollParagraph">Concepts</h3> +<div class="scrollSection"><h4 class="scrollParagraph">Board</h4> +<p class="scrollParagraph">The Board is the rectangle on which your simulation takes place. It has a width and height and is divided into a grid.</p> +</div></div> +<div class="scrollSection"><h4 class="scrollParagraph">Agents</h4> +<p class="scrollParagraph">Agents are the key concept in Simoji. Everything you see on your board is an agent. In the code above, the ant, hill, and food are all agent types.</p> +</div> +<div class="scrollSection"><h4 class="scrollParagraph">Events</h4> +<p class="scrollParagraph">Events are blocks of commands that execute during the running of the experiments. Probabilities can be assigned so blocks run stochastically.</p> +<ul style="text-indent:0px;"><li >onTick</li> +<li >onHit</li> +<li >onTouch</li> +<li >onDeath</li></ul> +</div> +<p class="scrollParagraph">Some events can also happen on the board level:</p> +<ul style="text-indent:0px;"><li >onTick</li> +<li >onExtinct</li></ul> +<div class="scrollSection"><h4 class="scrollParagraph">Commands</h4> +<ul style="text-indent:0px;"><li >kickIt</li> +<li >replaceWith</li> +<li >spawn</li> +<li >remove</li> +<li >pickItUp</li> +<li >turnRandomly</li> +<li >turnToward</li></ul> +</div> +<div class="scrollSection"><h4 class="scrollParagraph">Attributes</h4> +<p class="scrollParagraph">Agents can have attributes. You can define your own. Some are built in like:</p> +<ul style="text-indent:0px;"><li >health</li></ul> +</div> +<p class="scrollParagraph">Some attributes are booleans with only 2 states like:</p> +<ul style="text-indent:0px;"><li >solid</li> +<li >bouncy</li></ul> +<div class="scrollSection"><h4 class="scrollParagraph">Experiments</h4> +<p class="scrollParagraph">You can run multiple boards at the same time using the <code>experiment</code> keyword. You can tweak any words in your experiments. Usually you want to change Settings. The maximum boards at one time is currently 4.</p> +</div> +<div class="scrollSection"><h4 class="scrollParagraph">Parameters</h4> +<p class="scrollParagraph">You can define parameters that you use throughout your simulation code. This makes it easy to tweak them in experiments. For example:</p> +</div> +<code class="scrollCodeBlock">lightningFrequencySetting .1 experiment comment Lots of lightning lightningFrequencySetting .5</code> - -<h4 class="scrollSubsectionComponent">Reports</h4> - -<p class="scrollParagraphComponent">Data is collected during the running of every experiment. You can export this data to CSV and/or start analyizing it immediately in the Ohayo app.</p> - -<h3 class="scrollSectionComponent">Board Setup</h3> - -<p class="scrollParagraphComponent">You can setup your board with the following commands.</p> - -<ul class="scrollUnorderedListComponent"> -<li>insert</li> -<li>paste</li> -</ul> - -<h3 class="scrollSectionComponent">Agent Palette</h3> - -<p class="scrollParagraphComponent">You can drop new Agents onto your board using the Agent Palette on the right side of your screen.</p> - -<h3 class="scrollSectionComponent">Tree Notation</h3> - -<p class="scrollParagraphComponent">Simoji the language is a <a href="https://treenotation.org">TreeLanguage</a>. There are no visible syntax characters. Indentation is used for parent/child relationships. Here is the <a href="https://jtree.treenotation.org/designer#url%20https%3A%2F%2Fsimoji.pub%2Fsimoji.grammar">grammar</a>.</p> - -<h3 class="scrollSectionComponent">Keyboard shortcuts</h3> - -<table class="scrollTableComponent"><thead><tr><th>Combo</th> +<div class="scrollSection"><h4 class="scrollParagraph">Reports</h4> +<p class="scrollParagraph">Data is collected during the running of every experiment. You can export this data to CSV and/or start analyizing it immediately in the Ohayo app.</p> +</div> +<div class="scrollSection"><h4 class="scrollParagraph">Board Setup</h4> +<p class="scrollParagraph">You can setup your board with the following commands.</p> +<ul style="text-indent:0px;"><li >insert</li> +<li >paste</li></ul> +</div> +<div class="scrollSection"><h4 class="scrollParagraph">Agent Palette</h4> +<p class="scrollParagraph">You can drop new Agents onto your board using the Agent Palette on the right side of your screen.</p> +</div> +<div class="scrollSection"><h3 class="scrollParagraph">Tree Notation</h3> +<p class="scrollParagraph">Simoji the language is a <a href="https://treenotation.org">Tree Language</a>. There are no visible syntax characters. Indentation is used for parent/child relationships. Here is the <a href="https://jtree.treenotation.org/designer#url%20https%3A%2F%2Fsimoji.pub%2Fsimoji.grammar">grammar</a>.</p> +</div> +<div class="scrollSection"><h3 class="scrollParagraph">Keyboard shortcuts</h3> +<table class="scrollTable"><thead><tr><th>Combo</th> <th>Command</th> </tr></thead> @@ -369,22 +424,15 @@ experiment <tr><td>Backspace</td> <td>Delete selection</td> </tr></tbody></table> - -<h3 class="scrollSectionComponent">Sharing Your Simulations</h3> - -<p class="scrollParagraphComponent">At the top of the page you should see a link that you can copy and paste to share your sim. When you update your simulation code that link will update.</p> - -<h4 class="scrollSubsectionComponent">Loading a Simulation from a URL</h4> - -<p class="scrollParagraphComponent">You can load any simulation from a publicly accessible URL by prefixing it with: `https://simoji.pub/#url `. For example: https://simoji.pub/#url%20https://simoji.pub/examples/eatTheBacon.simoji</p> - -<h3 class="scrollSectionComponent">Getting Involved</h3> - -<p class="scrollParagraphComponent">The source code for Simoji and all development happens on <a href="https://github.com/publicdomaincompany/simoji">Github</a>.</p> - -<h3 class="scrollSectionComponent">Printable Version</h3> - -<p class="scrollParagraphComponent">This cheat sheet is also available as a <a href="cheatSheet.pdf">printable</a> version.</p> -<p class="scrollArticleSourceLinkComponent"><a href="https://github.com/publicdomaincompany/simoji/blob/main/cheatSheet.scroll">Article source</a></p></div> -</body> -</html> +</div> +<div class="scrollSection"><h3 class="scrollParagraph">Sharing Your Simulations</h3> +<p class="scrollParagraph">At the top of the page you should see a link that you can copy and paste to share your sim. When you update your simulation code that link will update.</p> +</div> +<div class="scrollSection"><h4 class="scrollParagraph">Loading a Simulation from a URL</h4> +<p class="scrollParagraph">You can load any simulation from a publicly accessible URL by prefixing it with: <code><a href="https://simoji.pub/#url" target="<em>blank">https://simoji.pub/#url</a> </code>. For example: <a href="https://simoji.pub/#url%20https://simoji.pub/examples/eatTheBacon.simoji" target="</em>blank">https://simoji.pub/#url%20https://simoji.pub/examples/eatTheBacon.simoji</a></p> +</div> +<div class="scrollSection"><h3 class="scrollParagraph">Getting Involved</h3> +<p class="scrollParagraph">The source code for Simoji and all development happens on <a href="https://github.com/publicdomaincompany/simoji">Github</a>.</p> +</div> +<div class="scrollSection"><h3 class="scrollParagraph"><a href="./cheatSheet.pdf">Printable Version</a></h3> +</div> \ No newline at end of file diff --git a/cheatSheet.scroll b/cheatSheet.scroll index b4624b0..a250c08 100644 --- a/cheatSheet.scroll +++ b/cheatSheet.scroll @@ -1,13 +1,12 @@ -title Simoji Quickstart Guide -skipIndexPage -header -footer +metaTags +gazetteCss -paragraph - Simoji is a language and tool for thinkers to write shareable simulations using 😃's. You write your simulations in a simple language using mostly Emojis and then click play. Simoji is public domain, open source🔗github.com/publicdomaincompany/simoji and suggestions welcome! +title Simoji Quickstart Guide -subsection Example Program +* Simoji is a language and tool for thinkers to write shareable simulations using 😃's. You write your simulations in a simple language using mostly Emojis and then click play. Simoji is public domain, open source, and suggestions welcome! + https://github.com/publicdomaincompany/simoji open source +# Example Program code comment Define an ant agent 🐜 @@ -34,71 +33,46 @@ code insert 1 ⛰ -section Concepts - -subsection Board - -paragraph - The Board is the rectangle on which your simulation takes place. It has a width and height and is divided into a grid. - -subsection Agents - -paragraph - Agents are the key concept in Simoji. Everything you see on your board is an agent. In the code above, the ant, hill, and food are all agent types. - -subsection Events - -paragraph - Events are blocks of commands that execute during the running of the experiments. Probabilities can be assigned so blocks run stochastically. - -list - - onTick - - onHit - - onTouch - - onDeath - -paragraph - Some events can also happen on the board level: - -list - - onTick - - onExtinct - -subsection Commands +# Concepts +## Board +* The Board is the rectangle on which your simulation takes place. It has a width and height and is divided into a grid. -list - - kickIt - - replaceWith - - spawn - - remove - - pickItUp - - turnRandomly - - turnToward +## Agents +* Agents are the key concept in Simoji. Everything you see on your board is an agent. In the code above, the ant, hill, and food are all agent types. -subsection Attributes +## Events +* Events are blocks of commands that execute during the running of the experiments. Probabilities can be assigned so blocks run stochastically. +- onTick +- onHit +- onTouch +- onDeath -paragraph - Agents can have attributes. You can define your own. Some are built in like: +* Some events can also happen on the board level: +- onTick +- onExtinct -list - - health +## Commands +- kickIt +- replaceWith +- spawn +- remove +- pickItUp +- turnRandomly +- turnToward -paragraph - Some attributes are booleans with only 2 states like: +## Attributes +* Agents can have attributes. You can define your own. Some are built in like: +- health -list - - solid - - bouncy +* Some attributes are booleans with only 2 states like: +- solid +- bouncy -subsection Experiments +## Experiments +* You can run multiple boards at the same time using the `experiment` keyword. You can tweak any words in your experiments. Usually you want to change Settings. The maximum boards at one time is currently 4. -paragraph - You can run multiple boards at the same time using the `experiment` keyword. You can tweak any words in your experiments. Usually you want to change Settings. The maximum boards at one time is currently 4. - -subsection Parameters - -paragraph - You can define parameters that you use throughout your simulation code. This makes it easy to tweak them in experiments. For example: +## Parameters +* You can define parameters that you use throughout your simulation code. This makes it easy to tweak them in experiments. For example: code lightningFrequencySetting .1 @@ -107,32 +81,23 @@ code comment Lots of lightning lightningFrequencySetting .5 -subsection Reports - -paragraph - Data is collected during the running of every experiment. You can export this data to CSV and/or start analyizing it immediately in the Ohayo app. - -section Board Setup - -paragraph - You can setup your board with the following commands. - -list - - insert - - paste +## Reports +* Data is collected during the running of every experiment. You can export this data to CSV and/or start analyizing it immediately in the Ohayo app. -section Agent Palette +## Board Setup +* You can setup your board with the following commands. +- insert +- paste -paragraph - You can drop new Agents onto your board using the Agent Palette on the right side of your screen. +## Agent Palette +* You can drop new Agents onto your board using the Agent Palette on the right side of your screen. -section Tree Notation - -paragraph - Simoji the language is a TreeLanguage🔗treenotation.org. There are no visible syntax characters. Indentation is used for parent/child relationships. Here is the grammar🔗jtree.treenotation.org/designer#url%20https%3A%2F%2Fsimoji.pub%2Fsimoji.grammar. - -section Keyboard shortcuts +# Tree Notation +* Simoji the language is a Tree Language. There are no visible syntax characters. Indentation is used for parent/child relationships. Here is the grammar. + https://treenotation.org Tree Language + https://jtree.treenotation.org/designer#url%20https%3A%2F%2Fsimoji.pub%2Fsimoji.grammar grammar +# Keyboard shortcuts pipeTable Combo|Command Spacebar|Play/Pause @@ -146,22 +111,15 @@ pipeTable Arrows|Move selection Backspace|Delete selection -section Sharing Your Simulations - -paragraph - At the top of the page you should see a link that you can copy and paste to share your sim. When you update your simulation code that link will update. - -subsection Loading a Simulation from a URL - -paragraph - You can load any simulation from a publicly accessible URL by prefixing it with: `https://simoji.pub/#url `. For example: https://simoji.pub/#url%20https://simoji.pub/examples/eatTheBacon.simoji - -section Getting Involved +# Sharing Your Simulations +* At the top of the page you should see a link that you can copy and paste to share your sim. When you update your simulation code that link will update. -paragraph - The source code for Simoji and all development happens on Github🔗github.com/publicdomaincompany/simoji. +## Loading a Simulation from a URL +* You can load any simulation from a publicly accessible URL by prefixing it with: `https://simoji.pub/#url `. For example: https://simoji.pub/#url%20https://simoji.pub/examples/eatTheBacon.simoji -section Printable Version +# Getting Involved +* The source code for Simoji and all development happens on Github. + https://github.com/publicdomaincompany/simoji Github -paragraph - This cheat sheet is also available as a printable🔗./cheatSheet.pdf version. +# Printable Version + link ./cheatSheet.pdf diff --git a/footer.scroll b/footer.scroll new file mode 100644 index 0000000..fd62e77 --- /dev/null +++ b/footer.scroll @@ -0,0 +1,2 @@ +importOnly +gazetteFooter \ No newline at end of file diff --git a/header.scroll b/header.scroll new file mode 100644 index 0000000..134ceaf --- /dev/null +++ b/header.scroll @@ -0,0 +1,4 @@ +importOnly +metaTags +gazetteCss +gazetteHeader \ No newline at end of file diff --git a/readme.scroll b/readme.scroll index bbffdd4..f0c964b 100644 --- a/readme.scroll +++ b/readme.scroll @@ -1,40 +1,59 @@ +import header.scroll + title Simoji - Write simulations with Emojis -paragraph - An early work in progress. - -section Documentation - -paragraph - Check out the QuickStart🔗./cheatSheet.html Guide. - -section Related Work - -list - - Agent Based Modelling and Simulation tools: A review🔗www2.econ.iastate.edu/tesfatsi/ABMSoftwareReview.AbarEtAl2017.pdf of the state-of-art software - - Comparison🔗en.wikipedia.org/wiki/Comparison_of_agent-based_modeling_software of agent-based modeling software - - NetLogo🔗www.netlogoweb.org/launch#http://www.netlogoweb.org/assets/modelslib/Sample%20Models/Biology/Flocking%20Vee%20Formations.nlogo - 2D/3D, Web/Desktop, Open - - GAMA🔗gama-platform.github.io - - GAML🔗gama-platform.github.io/wiki/Introduction Language - Desktop, OpenSource - - EmojiSimulator🔗ncase.me/sim/ - After shipping I learned Nicky had a similar idea 5+ years before me and executed very well! - - InsightMaker🔗insightmaker.comSource - - AnyLogic🔗www.anylogic.com - 2D/3D - - SARL🔗en.wikipedia.org/wiki/SARL_language - - Framsticks🔗www.framsticks.com/ - 3D, Desktop - - Hash.ai🔗hash.ai - - SOAR🔗en.wikipedia.org/wiki/Soar_(cognitive_architecture) - - JADE🔗en.wikipedia.org/wiki/Java_Agent_Development_Framework - - MASON🔗cs.gmu.edu/~eclab/projects/mason/ - - REPAST🔗repast.github.io - - Simulations in Mathematica🔗demonstrations.wolfram.com/SugarscapeAgentBasedModeling/ - - Swarm🔗swarm.org/wiki/Swarm_main_page - - Altreva🔗www.altreva.com - - sandspiel🔗sandspiel.club - - The-Powder-Toy🔗github.com/The-Powder-Toy/The-Powder-Toy - -section Development - -question What is the dev loop like? +## A work in progress. + +startColumns 2 + +# Documentation +* QuickStart + link ./cheatSheet.html + +# Related Work +- Agent Based Modelling and Simulation tools: A review of the state-of-art software + https://www2.econ.iastate.edu/tesfatsi/ABMSoftwareReview.AbarEtAl2017.pdf +- Comparison of agent-based modeling software + https://en.wikipedia.org/wiki/Comparison_of_agent-based_modeling_software +- NetLogo - 2D/3D, Web/Desktop, Open + https://www.netlogoweb.org/launch#http://www.netlogoweb.org/assets/modelslib/Sample%20Models/Biology/Flocking%20Vee%20Formations.nlogo NetLogo +- GAMA + https://gama-platform.github.io +- GAML Language - Desktop, OpenSource + https://gama-platform.github.io/wiki/Introduction GAML Language +- EmojiSimulator - After shipping I learned Nicky had a similar idea 5+ years before me and executed very well! + https://ncase.me/sim/ EmojiSimulator +- InsightMaker + https://insightmaker.com +- AnyLogic - 2D/3D + https://www.anylogic.com AnyLogic +- SARL + https://en.wikipedia.org/wiki/SARL_language +- Framsticks - 3D, Desktop + https://www.framsticks.com/ Framsticks +- Hash.ai + https://hash.ai +- SOAR + https://en.wikipedia.org/wiki/Soar_(cognitive_architecture) +- JADE + https://en.wikipedia.org/wiki/Java_Agent_Development_Framework +- MASON + https://cs.gmu.edu/~eclab/projects/mason/ +- REPAST + https://repast.github.io +- Simulations in Mathematica + https://demonstrations.wolfram.com/SugarscapeAgentBasedModeling/ +- Swarm + https://swarm.org/wiki/Swarm_main_page +- Altreva + https://www.altreva.com +- sandspiel + https://sandspiel.club +- The-Powder-Toy + https://github.com/The-Powder-Toy/The-Powder-Toy + +## Development +? What is the dev loop like? 1. Start the dev server with `node server.js`. 2. Open `localhost/dev.html` @@ -43,15 +62,15 @@ question What is the dev loop like? 5. Run tests with `npm test` 6. Build distribution with `./build.js` -section Roadmap +## Roadmap +- Run sims with 8x as many agents +- Run 8x more sims at once +- Write 8x more kinds of sims with the same number of words +- Write models that are 8x less wrong +- Generate 8x better distributables -list - - Run sims with 8x as many agents - - Run 8x more sims at once - - Write 8x more kinds of sims with the same number of words - - Write models that are 8x less wrong - - Generate 8x better distributables +# ❤️ Public Domain ❤️ -section ❤️ Public Domain ❤️ +endColumns -skipIndexPage \ No newline at end of file +import footer.scroll diff --git a/releaseNotes.scroll b/releaseNotes.scroll index 9863139..ba810a3 100644 --- a/releaseNotes.scroll +++ b/releaseNotes.scroll @@ -1,13 +1,16 @@ -title Release Notes -skipIndexPage - -paragraph - Here's a list of the notable changes in Simoji. - -section 2.0.0 07-30-2021 -list - - 🎉 fill command - - 🎉 kickIt behaves a little smarter - - 🧹 kickIt now uses a tick command stack internally - - ⚠️ speed is gone. you now need to explcitly move agents in their onTick. - - ⚠️ mass, diameter, force removed \ No newline at end of file +import header.scroll +title Simoji Release Notes +startColumns + +* Here's a list of the notable changes in Simoji. + +# 2.0.0 07-30-2021 +- 🎉 fill command +- 🎉 kickIt behaves a little smarter +- 🧹 kickIt now uses a tick command stack internally +- ⚠️ speed is gone. you now need to explcitly move agents in their onTick. +- ⚠️ mass, diameter, force removed + +endColumns + +import footer.scroll