UNPKG

7.54 kBJavaScriptView Raw
1const K = require('kcore');
2require('./KNateAbstractStatic');
3
4const COMPACT = true;
5// hard LB - always present.
6const LBH = '\n';
7// soft LB - present only when COMPACT = false
8const LB = COMPACT ? '' : '\n';
9
10// -----------------------------------------------------------------------------
11// Prototypes
12// -----------------------------------------------------------------------------
13
14K.NateXmlAbstractProto = Object.assign({}, K.NateAbstractProto,
15{
16 NOTHING: 0,
17 JUST_CHILDREN: 1,
18 JUST_THE_INNER: 2,
19 OPEN_CLOSE_AT_ONCE: 3,
20 OPEN_THEN_CLOSE: 4,
21
22 escapeXmlTags(unsafe) {
23 // https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
24 return unsafe
25 .replace(/&/g, '&')
26 .replace(/</g, '&lt;')
27 .replace(/>/g, '&gt;');
28 },
29
30 commonAttributeSetter(nNode, key, value) {
31 if (!nNode.isAttributeNamesCaseSensitive) {
32 key = key.toLowerCase();
33 }
34 nNode.tagParamsList[key] = value
35 },
36
37 commonInnerContentSetter(nNode, key, value) {
38 nNode._confirmThereAreNoNateChildren()
39
40 if (K.Object.isUndefinedOrNull(value) || (value === '')) {
41 // Clear inner content, enable newXxx() calls to create nate-childs.
42 nNode.tagInnerContent = ''
43 nNode.isNateChildAllowed = true
44
45 } else {
46 // Set new inner content - disable newXxx() calls to forbid nate-childs.
47 nNode.tagInnerContent = value
48 nNode.isNateChildAllowed = false
49 }
50 },
51
52 commonOnOffAttributeSetter(nNode, key, value) {
53 if (value) {
54 nNode.tagParamsList[key] = key
55 } else if (nNode.tagParamsList[key]) {
56 delete nNode.tagParamsList[key]
57 }
58 },
59
60 init(parent, namespace)
61 {
62 K.NateAbstractProto.init.call(this, parent, namespace);
63
64 this.enabled = true;
65 this.tagOpenCloseMode = K.NateXmlAbstractProto.JUST_CHILDREN;
66 this.tagParamsList = {};
67
68 this.isTagNameCaseSensitive = false;
69 this.isAttributeNamesCaseSensitive = false;
70 this.addSlashToOpenCloseAtOnceTag = false;
71 },
72
73 _getDefaultNamespace() {
74 // Default namespace for all NateXmlStatic elements used when there is no
75 // parent item.
76 return K.nateXmlStaticNamespace
77 },
78
79 setText(txt)
80 {
81 this.tagInnerContent = txt;
82
83 return this;
84 },
85
86 setTag(tagName, tagParams, tagInnerContent, tagOpenCloseMode)
87 {
88 if (this.isTagNameCaseSensitive)
89 {
90 this.tagName = tagName
91 }
92 else
93 {
94 this.tagName = tagName ? tagName.toLowerCase() : tagName;
95 }
96
97 this.tagOpenCloseMode = tagOpenCloseMode;
98
99 //
100 // Set inner text.
101 //
102
103 if (K.Object.isUndefinedOrNull(tagInnerContent))
104 {
105 // Inner text not specified - just init with empty string.
106 this.tagInnerContent = '';
107 }
108 else if (K.Object.isString(tagInnerContent))
109 {
110 // Inner text specified as pure string - just save it as is
111 // for render time.
112 this.tagInnerContent = tagInnerContent;
113 }
114 else if (typeof tagInnerContent === 'number')
115 {
116 // Inner text specified as number - convert it to string.
117 this.tagInnerContent = tagInnerContent.toString();
118 }
119 else
120 {
121 // Unexpected inner text type.
122 this.tagInnerContent = '[error: wrong inner text type]';
123
124 console.log('NateXmlAbstract::setTag(', tagName, '): PANIC! Unexpected inner text type', typeof(tagInnerContent));
125 }
126
127 //
128 // Set params list.
129 //
130
131 if (K.Object.isUndefinedOrNull(tagParams))
132 {
133 // Do nothing with params - just leave them alone.
134 }
135 else if (K.Object.isObject(tagParams))
136 {
137 K.Object.merge(this.tagParamsList, tagParams);
138 }
139 else
140 {
141 console.log('NateXmlAbstract::setTag(', tagName, '): PANIC! Invalid params list:', tagParams);
142 }
143 },
144
145 tagOpenClose(tag, params)
146 {
147 const rv = this.getNamespace().createNate(this);
148
149 rv.setTag(tag, params, null, K.NateXmlAbstractProto.OPEN_THEN_CLOSE);
150
151 return rv;
152 },
153
154 tagOpenCloseAtOnce(tagName, params)
155 {
156 const rv = this.getNamespace().createNate(this);
157
158 rv.setTag(tagName, params, null, K.NateXmlAbstractProto.OPEN_CLOSE_AT_ONCE);
159
160 return rv;
161 },
162
163 _renderParams()
164 {
165 let rv = ''
166
167 for (let paramKey in this.tagParamsList) {
168 const value = this.tagParamsList[paramKey]
169 // If this is "boolean attribute", according to "2.4.2. Boolean attributes" of "HTML 5.3" spec
170 // (http://w3c.github.io/html/infrastructure.html#sec-boolean-attributes)
171 // then we just add the key without value.
172 // For example in <script async> - the "async" is boolean
173 // another example <input type="checkbox" checked> - the "checked" is boolean
174 if (typeof value === 'boolean') {
175 if (value) {
176 rv += ' ' + paramKey
177 }
178 } else {
179 rv += ' ' + paramKey + '="' + value + '"'
180 }
181 }
182
183 return rv
184 },
185
186 render()
187 {
188 let rv = '';
189
190 if (this.enabled)
191 {
192 switch (this.tagOpenCloseMode)
193 {
194 case K.NateXmlAbstractProto.JUST_THE_INNER:
195 {
196 rv = this.tagInnerContent;
197 rv += K.NateAbstractProto.render.call(this); // TODO: super.render()
198
199 break;
200 }
201
202 case K.NateXmlAbstractProto.OPEN_CLOSE_AT_ONCE:
203 case K.NateXmlAbstractProto.OPEN_THEN_CLOSE:
204 {
205 rv = '<' + this.tagName;
206 rv += this._renderParams();
207
208 if (this.tagOpenCloseMode == K.NateXmlAbstractProto.OPEN_CLOSE_AT_ONCE)
209 {
210 if (this.addSlashToOpenCloseAtOnceTag)
211 {
212 rv += '/>';
213 }
214 else
215 {
216 rv += '>';
217 }
218 }
219 else
220 {
221 rv += '>';
222 rv += K.NateAbstractProto.render.call(this); // TODO: super.render()
223 rv += this.tagInnerContent;
224 rv += '</' + this.tagName + '>' + LB;
225 }
226
227 break;
228 }
229
230 case K.NateXmlAbstractProto.JUST_CHILDREN:
231 {
232 rv += K.NateAbstractProto.render.call(this); // TODO: super.render()
233
234 break;
235 }
236 }
237 }
238
239 return rv;
240 }
241});
242
243// -----------------------------------------------------------------------------
244// Common setters
245// -----------------------------------------------------------------------------
246
247K.nateXmlStaticNamespace =
248 K.BSC.nateNullNamespace.createExtension('XmlStatic', {nateProto: K.NateXmlAbstractProto})
249
250K.nateXmlStaticNamespace.addSetter('text', K.NateXmlAbstractProto.commonInnerContentSetter)
251
252K.nateXmlStaticNamespace.addSetter('enabled', (nNode, key, value) => {
253 nNode.enabled = value
254})
255
256K.nateXmlStaticNamespace.addSetter('on', (nNode, key, value) => {
257 nNode.set('enabled', value)
258})
259
260// -----------------------------------------------------------------------------
261// Factory functions
262// -----------------------------------------------------------------------------
263
264K.NateXmlAbstract = function(parent, namespace)
265{
266 const thiz = Object.create(K.NateXmlAbstractProto);
267
268 K.BSC.NateAbstractProto.init.call(thiz, parent, namespace);
269 K.NateAbstractProto.init.call(thiz, parent);
270
271 thiz.init(parent);
272
273 return thiz;
274}