UNPKG

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