1 | 'use strict'
|
2 |
|
3 | const stringify = require('../../utils/string').stringify
|
4 | const escape = require('../../utils/string').escape
|
5 | const isSafeProperty = require('../../utils/customs').isSafeProperty
|
6 | const hasOwnProperty = require('../../utils/object').hasOwnProperty
|
7 |
|
8 | function factory (type, config, load, typed) {
|
9 | const Node = load(require('./Node'))
|
10 |
|
11 | |
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | function ObjectNode (properties) {
|
18 | if (!(this instanceof ObjectNode)) {
|
19 | throw new SyntaxError('Constructor must be called with the new operator')
|
20 | }
|
21 |
|
22 | this.properties = properties || {}
|
23 |
|
24 |
|
25 | if (properties) {
|
26 | if (!(typeof properties === 'object') || !Object.keys(properties).every(function (key) {
|
27 | return type.isNode(properties[key])
|
28 | })) {
|
29 | throw new TypeError('Object containing Nodes expected')
|
30 | }
|
31 | }
|
32 | }
|
33 |
|
34 | ObjectNode.prototype = new Node()
|
35 |
|
36 | ObjectNode.prototype.type = 'ObjectNode'
|
37 |
|
38 | ObjectNode.prototype.isObjectNode = true
|
39 |
|
40 | |
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 | ObjectNode.prototype._compile = function (math, argNames) {
|
54 | const evalEntries = {}
|
55 |
|
56 | for (const key in this.properties) {
|
57 | if (hasOwnProperty(this.properties, key)) {
|
58 |
|
59 |
|
60 | const stringifiedKey = stringify(key)
|
61 | const parsedKey = JSON.parse(stringifiedKey)
|
62 | if (!isSafeProperty(this.properties, parsedKey)) {
|
63 | throw new Error('No access to property "' + parsedKey + '"')
|
64 | }
|
65 |
|
66 | evalEntries[parsedKey] = this.properties[key]._compile(math, argNames)
|
67 | }
|
68 | }
|
69 |
|
70 | return function evalObjectNode (scope, args, context) {
|
71 | const obj = {}
|
72 |
|
73 | for (const key in evalEntries) {
|
74 | if (hasOwnProperty(evalEntries, key)) {
|
75 | obj[key] = evalEntries[key](scope, args, context)
|
76 | }
|
77 | }
|
78 |
|
79 | return obj
|
80 | }
|
81 | }
|
82 |
|
83 | |
84 |
|
85 |
|
86 |
|
87 | ObjectNode.prototype.forEach = function (callback) {
|
88 | for (const key in this.properties) {
|
89 | if (this.properties.hasOwnProperty(key)) {
|
90 | callback(this.properties[key], 'properties[' + stringify(key) + ']', this)
|
91 | }
|
92 | }
|
93 | }
|
94 |
|
95 | |
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 | ObjectNode.prototype.map = function (callback) {
|
102 | const properties = {}
|
103 | for (const key in this.properties) {
|
104 | if (this.properties.hasOwnProperty(key)) {
|
105 | properties[key] = this._ifNode(callback(this.properties[key],
|
106 | 'properties[' + stringify(key) + ']', this))
|
107 | }
|
108 | }
|
109 | return new ObjectNode(properties)
|
110 | }
|
111 |
|
112 | |
113 |
|
114 |
|
115 |
|
116 | ObjectNode.prototype.clone = function () {
|
117 | const properties = {}
|
118 | for (const key in this.properties) {
|
119 | if (this.properties.hasOwnProperty(key)) {
|
120 | properties[key] = this.properties[key]
|
121 | }
|
122 | }
|
123 | return new ObjectNode(properties)
|
124 | }
|
125 |
|
126 | |
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 | ObjectNode.prototype._toString = function (options) {
|
133 | const entries = []
|
134 | for (const key in this.properties) {
|
135 | if (this.properties.hasOwnProperty(key)) {
|
136 | entries.push(stringify(key) + ': ' + this.properties[key].toString(options))
|
137 | }
|
138 | }
|
139 | return '{' + entries.join(', ') + '}'
|
140 | }
|
141 |
|
142 | |
143 |
|
144 |
|
145 |
|
146 | ObjectNode.prototype.toJSON = function () {
|
147 | return {
|
148 | mathjs: 'ObjectNode',
|
149 | properties: this.properties
|
150 | }
|
151 | }
|
152 |
|
153 | |
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 | ObjectNode.fromJSON = function (json) {
|
161 | return new ObjectNode(json.properties)
|
162 | }
|
163 |
|
164 | |
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 | ObjectNode.prototype.toHTML = function (options) {
|
171 | const entries = []
|
172 | for (const key in this.properties) {
|
173 | if (this.properties.hasOwnProperty(key)) {
|
174 | entries.push('<span class="math-symbol math-property">' + escape(key) + '</span>' + '<span class="math-operator math-assignment-operator math-property-assignment-operator math-binary-operator">:</span>' + this.properties[key].toHTML(options))
|
175 | }
|
176 | }
|
177 | return '<span class="math-parenthesis math-curly-parenthesis">{</span>' + entries.join('<span class="math-separator">,</span>') + '<span class="math-parenthesis math-curly-parenthesis">}</span>'
|
178 | }
|
179 |
|
180 | |
181 |
|
182 |
|
183 |
|
184 |
|
185 | ObjectNode.prototype._toTex = function (options) {
|
186 | const entries = []
|
187 | for (const key in this.properties) {
|
188 | if (this.properties.hasOwnProperty(key)) {
|
189 | entries.push('\\mathbf{' + key + ':} & ' + this.properties[key].toTex(options) + '\\\\')
|
190 | }
|
191 | }
|
192 | return `\\left\\{\\begin{array}{ll}${entries.join('\n')}\\end{array}\\right\\}`
|
193 | }
|
194 |
|
195 | return ObjectNode
|
196 | }
|
197 |
|
198 | exports.name = 'ObjectNode'
|
199 | exports.path = 'expression.node'
|
200 | exports.factory = factory
|