UNPKG

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