UNPKG

5.83 kBJavaScriptView Raw
1import { isNode } from '../../utils/is'
2import { forEach, map } from '../../utils/array'
3import { factory } from '../../utils/factory'
4
5const name = 'BlockNode'
6const dependencies = [
7 'ResultSet',
8 'Node'
9]
10
11export const createBlockNode = /* #__PURE__ */ factory(name, dependencies, ({ ResultSet, Node }) => {
12 /**
13 * @constructor BlockNode
14 * @extends {Node}
15 * Holds a set with blocks
16 * @param {Array.<{node: Node} | {node: Node, visible: boolean}>} blocks
17 * An array with blocks, where a block is constructed as an Object
18 * with properties block, which is a Node, and visible, which is
19 * a boolean. The property visible is optional and is true by default
20 */
21 function BlockNode (blocks) {
22 if (!(this instanceof BlockNode)) {
23 throw new SyntaxError('Constructor must be called with the new operator')
24 }
25
26 // validate input, copy blocks
27 if (!Array.isArray(blocks)) throw new Error('Array expected')
28 this.blocks = blocks.map(function (block) {
29 const node = block && block.node
30 const visible = block && block.visible !== undefined ? block.visible : true
31
32 if (!isNode(node)) throw new TypeError('Property "node" must be a Node')
33 if (typeof visible !== 'boolean') throw new TypeError('Property "visible" must be a boolean')
34
35 return {
36 node: node,
37 visible: visible
38 }
39 })
40 }
41
42 BlockNode.prototype = new Node()
43
44 BlockNode.prototype.type = 'BlockNode'
45
46 BlockNode.prototype.isBlockNode = true
47
48 /**
49 * Compile a node into a JavaScript function.
50 * This basically pre-calculates as much as possible and only leaves open
51 * calculations which depend on a dynamic scope with variables.
52 * @param {Object} math Math.js namespace with functions and constants.
53 * @param {Object} argNames An object with argument names as key and `true`
54 * as value. Used in the SymbolNode to optimize
55 * for arguments from user assigned functions
56 * (see FunctionAssignmentNode) or special symbols
57 * like `end` (see IndexNode).
58 * @return {function} Returns a function which can be called like:
59 * evalNode(scope: Object, args: Object, context: *)
60 */
61 BlockNode.prototype._compile = function (math, argNames) {
62 const evalBlocks = map(this.blocks, function (block) {
63 return {
64 evaluate: block.node._compile(math, argNames),
65 visible: block.visible
66 }
67 })
68
69 return function evalBlockNodes (scope, args, context) {
70 const results = []
71
72 forEach(evalBlocks, function evalBlockNode (block) {
73 const result = block.evaluate(scope, args, context)
74 if (block.visible) {
75 results.push(result)
76 }
77 })
78
79 return new ResultSet(results)
80 }
81 }
82
83 /**
84 * Execute a callback for each of the child blocks of this node
85 * @param {function(child: Node, path: string, parent: Node)} callback
86 */
87 BlockNode.prototype.forEach = function (callback) {
88 for (let i = 0; i < this.blocks.length; i++) {
89 callback(this.blocks[i].node, 'blocks[' + i + '].node', this)
90 }
91 }
92
93 /**
94 * Create a new BlockNode having it's childs be the results of calling
95 * the provided callback function for each of the childs of the original node.
96 * @param {function(child: Node, path: string, parent: Node): Node} callback
97 * @returns {BlockNode} Returns a transformed copy of the node
98 */
99 BlockNode.prototype.map = function (callback) {
100 const blocks = []
101 for (let i = 0; i < this.blocks.length; i++) {
102 const block = this.blocks[i]
103 const node = this._ifNode(callback(block.node, 'blocks[' + i + '].node', this))
104 blocks[i] = {
105 node: node,
106 visible: block.visible
107 }
108 }
109 return new BlockNode(blocks)
110 }
111
112 /**
113 * Create a clone of this node, a shallow copy
114 * @return {BlockNode}
115 */
116 BlockNode.prototype.clone = function () {
117 const blocks = this.blocks.map(function (block) {
118 return {
119 node: block.node,
120 visible: block.visible
121 }
122 })
123
124 return new BlockNode(blocks)
125 }
126
127 /**
128 * Get string representation
129 * @param {Object} options
130 * @return {string} str
131 * @override
132 */
133 BlockNode.prototype._toString = function (options) {
134 return this.blocks.map(function (param) {
135 return param.node.toString(options) + (param.visible ? '' : ';')
136 }).join('\n')
137 }
138
139 /**
140 * Get a JSON representation of the node
141 * @returns {Object}
142 */
143 BlockNode.prototype.toJSON = function () {
144 return {
145 mathjs: 'BlockNode',
146 blocks: this.blocks
147 }
148 }
149
150 /**
151 * Instantiate an BlockNode from its JSON representation
152 * @param {Object} json An object structured like
153 * `{"mathjs": "BlockNode", blocks: [{node: ..., visible: false}, ...]}`,
154 * where mathjs is optional
155 * @returns {BlockNode}
156 */
157 BlockNode.fromJSON = function (json) {
158 return new BlockNode(json.blocks)
159 }
160
161 /**
162 * Get HTML representation
163 * @param {Object} options
164 * @return {string} str
165 * @override
166 */
167 BlockNode.prototype.toHTML = function (options) {
168 return this.blocks.map(function (param) {
169 return param.node.toHTML(options) + (param.visible ? '' : '<span class="math-separator">;</span>')
170 }).join('<span class="math-separator"><br /></span>')
171 }
172
173 /**
174 * Get LaTeX representation
175 * @param {Object} options
176 * @return {string} str
177 */
178 BlockNode.prototype._toTex = function (options) {
179 return this.blocks.map(function (param) {
180 return param.node.toTex(options) + (param.visible ? '' : ';')
181 }).join('\\;\\;\n')
182 }
183
184 return BlockNode
185}, { isClass: true, isNode: true })