1 | const fs = require('fs');
|
2 | const coffee = require('coffeescript');
|
3 | const K = require('kcore');
|
4 |
|
5 | require('./KBSCNateNamespace.js');
|
6 | require('./KBSCNateHtml.js');
|
7 | require('./KNateAbstractStatic.js');
|
8 | require('./KNateXmlStatic.js');
|
9 | require('./KNateSvgStatic.js');
|
10 |
|
11 | const COMPACT = true;
|
12 |
|
13 | const LBH = '\n';
|
14 |
|
15 | const LB = COMPACT ? '' : '\n';
|
16 |
|
17 |
|
18 | let RESOURCE_CACHE_ON = true;
|
19 |
|
20 | const RESOURCE_CACHE_STATE_PENDING = Symbol('pending');
|
21 | const RESOURCE_CACHE = {};
|
22 | const RESOURCE_COMPILED_CACHE = {};
|
23 |
|
24 | const EMIT_RESOURCE_LOADING_TO_CONSOLE_LOG = false;
|
25 | const EMIT_STATS_TO_CONSOLE_LOG = false;
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | let RESOURCES_LOAD_PENDING_CNT = 0;
|
33 | const RESOURCES_LOAD_PENDING_LIMIT = 128;
|
34 | const RESOURCES_WAITING_OBJECTS = [];
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 | K.JsCollectorProto =
|
41 | {
|
42 | init()
|
43 | {
|
44 | this.js = '';
|
45 | },
|
46 |
|
47 | _generateJsTagFromText(jsTxt)
|
48 | {
|
49 | return '<script>' + LBH + jsTxt + LBH + '</script>' + LB;
|
50 | },
|
51 |
|
52 | getAllAsTag()
|
53 | {
|
54 | let rv = '';
|
55 |
|
56 | if (this.js != '')
|
57 | {
|
58 | rv = this._generateJsTagFromText(this.js);
|
59 | }
|
60 |
|
61 | return rv;
|
62 | },
|
63 |
|
64 | getAllAsOnLoadCode()
|
65 | {
|
66 | if (this.js != '')
|
67 | {
|
68 | K.Error.reportNotImplemented('K.JsCollector::getAllAsOnLoadCode()');
|
69 | }
|
70 |
|
71 | return '';
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | },
|
83 |
|
84 | pushFromTxt(txt)
|
85 | {
|
86 | this.js += txt.trim();
|
87 | }
|
88 | };
|
89 |
|
90 | K.NateHtmlProto = Object.assign({}, K.NateXmlAbstractProto,
|
91 | {
|
92 | setResourceCacheEnabled(onOff)
|
93 | {
|
94 | RESOURCE_CACHE_ON = onOff;
|
95 | },
|
96 |
|
97 | nl2br(text) {
|
98 | return text.replace(/(?:\r\n|\r|\n)/g, '<br>');
|
99 | },
|
100 |
|
101 | escapeHtmlContent(unsafe)
|
102 | {
|
103 |
|
104 | return K.NateXmlAbstractProto.escapeXmlTags(unsafe)
|
105 | },
|
106 |
|
107 | escapeHtmlAttribute(unsafe)
|
108 | {
|
109 |
|
110 |
|
111 | return K.NateHtmlProto.escapeHtmlContent(unsafe)
|
112 | .replace(/"/g, '"')
|
113 | .replace(/'/g, ''');
|
114 | },
|
115 |
|
116 | _getDefaultNamespace() {
|
117 |
|
118 |
|
119 | return K.nateHtmlStaticNamespace
|
120 | }
|
121 | });
|
122 |
|
123 | K.NateHtmlElemProto = Object.assign({}, K.NateHtmlProto,
|
124 | {
|
125 | ON_CLICK_EVENT_BLOCKER: K.LinksUtil.getOnClickEventBlockerFunctionCodeAsText(),
|
126 |
|
127 | _concatParamStrings(obligatoryX, optionalY)
|
128 | {
|
129 | let rv = obligatoryX.trim()
|
130 |
|
131 | if (optionalY)
|
132 | {
|
133 | optionalY = optionalY.trim()
|
134 |
|
135 | if (optionalY != '')
|
136 | {
|
137 | rv = obligatoryX + ' ' + optionalY
|
138 | }
|
139 | }
|
140 |
|
141 | return rv
|
142 | },
|
143 |
|
144 | init(parent, namespace)
|
145 | {
|
146 |
|
147 | K.NateHtmlProto.init.call(this, parent, namespace);
|
148 |
|
149 | if (K.Object.isNotNull(parent))
|
150 | {
|
151 | parent.push(this);
|
152 | }
|
153 | else
|
154 | {
|
155 | this.tagOpenCloseMode = K.NateXmlAbstractProto.JUST_CHILDREN;
|
156 | }
|
157 |
|
158 | this.tagParamsList = {style: ''};
|
159 | },
|
160 |
|
161 | newTxt(theTxt)
|
162 | {
|
163 | const rv = this.getNamespace().createNate(this);
|
164 |
|
165 | rv.setTag(null, null, theTxt, K.NateXmlAbstractProto.JUST_THE_INNER);
|
166 |
|
167 | return rv;
|
168 | },
|
169 |
|
170 | br()
|
171 | {
|
172 | return this.newBr();
|
173 | },
|
174 |
|
175 | newScriptUrl(params)
|
176 | {
|
177 | return this.tagOpenClose('script', params);
|
178 | },
|
179 |
|
180 | newScriptJsonLd(dataObj = null)
|
181 | {
|
182 | const newTag = this.tagOpenClose('script', {
|
183 | type: 'application/ld+json',
|
184 | innerJSON: dataObj
|
185 | })
|
186 |
|
187 | return newTag;
|
188 | },
|
189 |
|
190 | newSvgNate(params)
|
191 | {
|
192 | const rv = K.NateSvgDocument(this);
|
193 | rv.setFromMap(params)
|
194 |
|
195 | return rv;
|
196 | },
|
197 |
|
198 | setOnClickEventBlocker()
|
199 | {
|
200 | this.set('onclick', K.NateHtmlElemProto.ON_CLICK_EVENT_BLOCKER);
|
201 |
|
202 | return this;
|
203 | },
|
204 |
|
205 | updateStyle(param, value)
|
206 | {
|
207 | value = value.trim();
|
208 |
|
209 | if (K.Object.isNotNull(value) && (value != ''))
|
210 | {
|
211 | const paramsMap = {
|
212 | 'overflowX' : 'overflow-x',
|
213 | 'overflowY' : 'overflow-y',
|
214 | 'zIndex' : 'z-index',
|
215 | 'textAlign' : 'text-align',
|
216 | 'verticalAlign' : 'vertical-align',
|
217 | 'backgroundColor': 'background-color',
|
218 | 'borderCollapse' : 'border-collapse',
|
219 | 'fontFamily' : 'font-family',
|
220 | 'fontSize' : 'font-size',
|
221 | 'fontStyle' : 'font-style',
|
222 | 'fontWeight' : 'font-weight',
|
223 | };
|
224 |
|
225 | let styleKey = paramsMap[param];
|
226 |
|
227 | if (K.Object.isUndefinedOrNull(styleKey))
|
228 | {
|
229 | styleKey = param;
|
230 | }
|
231 |
|
232 | this.tagParamsList.style += styleKey + ': ' + value + ';';
|
233 | }
|
234 | },
|
235 |
|
236 | _renderParams()
|
237 | {
|
238 | let rv = '';
|
239 |
|
240 | for (let paramKey in this.tagParamsList)
|
241 | {
|
242 | const value = this.tagParamsList[paramKey]
|
243 | if ((paramKey === 'style') && (value === ''))
|
244 | {
|
245 |
|
246 | }
|
247 | else
|
248 | {
|
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 | if (typeof value === 'boolean') {
|
255 | if (value) {
|
256 | rv += ' ' + paramKey
|
257 | }
|
258 | } else if (paramKey === 'innerHTML') {
|
259 | this.tagInnerContent = value
|
260 | } else {
|
261 | rv += ' ' + paramKey + '="' + value + '"'
|
262 | }
|
263 | }
|
264 | }
|
265 |
|
266 | return rv;
|
267 | },
|
268 |
|
269 | setData(key, value)
|
270 | {
|
271 | this._validateDataKey(key)
|
272 | this.tagParamsList['data-' + key.toLowerCase()] = value;
|
273 |
|
274 | return this;
|
275 | }
|
276 | });
|
277 |
|
278 | K.NateHtmlHeadProto = Object.assign({}, K.NateHtmlElemProto,
|
279 | {
|
280 | init(parent, namespace)
|
281 | {
|
282 | K.NateHtmlElemProto.init.call(this, parent, namespace)
|
283 |
|
284 | this.htmlDocument = parent;
|
285 | this.css = '';
|
286 | this.meta = {};
|
287 | this.alternateLinksForLanguages = null;
|
288 | this.linkCanonical = null;
|
289 | this.iconTxt = null;
|
290 | },
|
291 |
|
292 | setTitle(title)
|
293 | {
|
294 | this.meta.title = title;
|
295 | },
|
296 |
|
297 | setMetaAuthor(author)
|
298 | {
|
299 | this.meta.author = author;
|
300 | },
|
301 |
|
302 | setMetaDescription(description)
|
303 | {
|
304 | this.meta.description = description;
|
305 | },
|
306 |
|
307 | setMetaKeywords(keywords)
|
308 | {
|
309 | this.meta.keywords = keywords;
|
310 | },
|
311 |
|
312 | setLinksForAlternateLanguages(links)
|
313 | {
|
314 | this.alternateLinksForLanguages = links;
|
315 | },
|
316 |
|
317 | setLinkCanonical(link)
|
318 | {
|
319 | this.linkCanonical = link;
|
320 | },
|
321 |
|
322 | setIcon(href, type = "image/png")
|
323 | {
|
324 | this.iconTxt = '<link rel="icon" type="' + type + '" href="' + href + '" />';
|
325 | },
|
326 |
|
327 | render()
|
328 | {
|
329 | let txt = '<head>' + LB;
|
330 |
|
331 |
|
332 | txt += '<meta charset="UTF-8">' + LB;
|
333 |
|
334 | if (K.Object.isNotNull(this.meta.title))
|
335 | {
|
336 | txt += '<title>' + this.meta.title + '</title>' + LB;
|
337 | }
|
338 |
|
339 | if (K.Object.isNotNull(this.meta.description))
|
340 | {
|
341 | txt += '<meta name="description" content="' + K.NateHtmlProto.escapeHtmlAttribute(this.meta.description) + '">' + LB;
|
342 | }
|
343 |
|
344 | if (K.Object.isNotNull(this.meta.keywords))
|
345 | {
|
346 | txt += '<meta name="keywords" content="' + K.NateHtmlProto.escapeHtmlAttribute(this.meta.keywords) + '">' + LB;
|
347 | }
|
348 |
|
349 | if (K.Object.isNotNull(this.meta.author))
|
350 | {
|
351 | txt += '<meta name="author" content="' + K.NateHtmlProto.escapeHtmlAttribute(this.meta.author) + '">' + LB;
|
352 | }
|
353 |
|
354 | txt += '<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>' + LB;
|
355 | txt += '<meta name="viewport" content="width=device-width, initial-scale=1">' + LB;
|
356 |
|
357 | if (K.Object.isNotNull(this.iconTxt))
|
358 | {
|
359 | txt += this.iconTxt;
|
360 | }
|
361 |
|
362 |
|
363 | if (K.Object.isNotNull(this.alternateLinksForLanguages))
|
364 | {
|
365 | for (let langId in this.alternateLinksForLanguages)
|
366 | {
|
367 | const url = this.alternateLinksForLanguages[langId];
|
368 |
|
369 | txt += '<link rel="alternate" hreflang="' + langId + '" href="' + url + '" />' + LB;
|
370 | }
|
371 | }
|
372 |
|
373 |
|
374 | if (K.Object.isNotNull(this.linkCanonical))
|
375 | {
|
376 | txt += '<link rel="canonical" href="' + this.linkCanonical + '" />';
|
377 | }
|
378 |
|
379 |
|
380 | if (K.Object.isNotNull(this.css != ''))
|
381 | {
|
382 | if (EMIT_STATS_TO_CONSOLE_LOG)
|
383 | {
|
384 | console.log('head/css :', (this.css.length / 1024).toFixed(2), 'KB');
|
385 | }
|
386 |
|
387 | txt += '<style>' + LB + this.css + LB + '</style>' + LB;
|
388 | }
|
389 |
|
390 |
|
391 | const headJsCode = this.htmlDocument.jsGetCollector('head').getAllAsTag();
|
392 |
|
393 | if (EMIT_STATS_TO_CONSOLE_LOG)
|
394 | {
|
395 | console.log('head/js :', (headJsCode.length / 1024).toFixed(2), 'KB');
|
396 | }
|
397 |
|
398 | txt += headJsCode;
|
399 |
|
400 |
|
401 | txt += '</head>' + LB;
|
402 |
|
403 | return txt;
|
404 | },
|
405 |
|
406 | pushCss(css)
|
407 | {
|
408 | this.css += css.trim() + LB;
|
409 | }
|
410 | });
|
411 |
|
412 | K.NateHtmlDocumentProto = Object.assign({}, K.NateHtmlProto,
|
413 | {
|
414 | init(parent, namespace)
|
415 | {
|
416 | K.NateHtmlProto.init.call(this, parent, namespace);
|
417 |
|
418 | this.theHead = K.NateHtmlHead(this);
|
419 | this.theBody = K.NateHtmlBody(this);
|
420 |
|
421 | this.jsPipes = {
|
422 | head: K.JsCollector(),
|
423 | bodyEnd: K.JsCollector(),
|
424 | onLoad: K.JsCollector(),
|
425 | };
|
426 |
|
427 | this.resourcesToLoad = {
|
428 | css: [],
|
429 | js_head: [],
|
430 | js_bodyEnd: [],
|
431 | js_onLoad: [],
|
432 | };
|
433 |
|
434 | this.resourcesToLoadCnt = 0;
|
435 | this.resourcesLoadDoneCb = null;
|
436 | this.waitingToEmit = false;
|
437 | this.langId = null;
|
438 |
|
439 |
|
440 | if (EMIT_RESOURCE_LOADING_TO_CONSOLE_LOG) {
|
441 | this.LOG_resourceLoading = console.log;
|
442 | } else {
|
443 | this.LOG_resourceLoading = () => {};
|
444 | }
|
445 | },
|
446 |
|
447 | head()
|
448 | {
|
449 | return this.theHead;
|
450 | },
|
451 |
|
452 | body()
|
453 | {
|
454 | return this.theBody;
|
455 | },
|
456 |
|
457 | render()
|
458 | {
|
459 | let langMeta = '';
|
460 |
|
461 | if (K.Object.isNotNull(this.langId))
|
462 | {
|
463 | langMeta = ' lang="' + this.langId + '"';
|
464 | }
|
465 |
|
466 | if (EMIT_STATS_TO_CONSOLE_LOG)
|
467 | {
|
468 | console.log('------------------------------------------------------');
|
469 | }
|
470 |
|
471 | const bodyHtml = this.theBody.render();
|
472 | const headHtml = this.theHead.render();
|
473 |
|
474 | if (EMIT_STATS_TO_CONSOLE_LOG)
|
475 | {
|
476 | console.log('body :', (bodyHtml.length / 1024).toFixed(2), 'KB');
|
477 | console.log('head :', (headHtml.length / 1024).toFixed(2), 'KB');
|
478 | }
|
479 |
|
480 | let txt = '';
|
481 |
|
482 | txt += '<!DOCTYPE html>' + LB;
|
483 | txt += '<html' + langMeta + '>' + LB;
|
484 | txt += headHtml;
|
485 | txt += bodyHtml;
|
486 | txt += '</html>' + LB;
|
487 |
|
488 | return txt;
|
489 | },
|
490 |
|
491 | setLang(langId)
|
492 | {
|
493 | this.langId = langId;
|
494 | },
|
495 |
|
496 | loadResource(resourceData, done) {
|
497 | const cacheEntry = RESOURCE_CACHE[resourceData.path];
|
498 |
|
499 | if (RESOURCE_CACHE_ON && cacheEntry) {
|
500 | if (cacheEntry === RESOURCE_CACHE_STATE_PENDING) {
|
501 |
|
502 |
|
503 |
|
504 | this.LOG_resourceLoading('[Nate] RESOURCE_CACHE already pending !');
|
505 |
|
506 | resourceData.loadResourceCalled = false;
|
507 |
|
508 | if (this.resourcesToLoadCnt === 1) {
|
509 |
|
510 |
|
511 |
|
512 |
|
513 | if (RESOURCES_WAITING_OBJECTS.indexOf(this) === -1) {
|
514 | RESOURCES_WAITING_OBJECTS.push(this);
|
515 | }
|
516 | }
|
517 |
|
518 | } else {
|
519 |
|
520 | this.LOG_resourceLoading('[Nate] RESOURCE_CACHE hit !');
|
521 | done(cacheEntry)
|
522 | }
|
523 |
|
524 | } else {
|
525 |
|
526 |
|
527 | this.LOG_resourceLoading('[Nate] RESOURCE_CACHE miss !');
|
528 |
|
529 |
|
530 | RESOURCE_CACHE[resourceData.path] = RESOURCE_CACHE_STATE_PENDING;
|
531 |
|
532 |
|
533 | fs.readFile(resourceData.path, 'utf8', (readError, data) => {
|
534 | if (readError) {
|
535 |
|
536 | console.log(readError);
|
537 | throw new Error('File loading failed from path:' + resourceData.path);
|
538 |
|
539 | } else {
|
540 |
|
541 |
|
542 | RESOURCE_CACHE[resourceData.path] = data;
|
543 | done(data);
|
544 | }
|
545 | });
|
546 | }
|
547 | },
|
548 |
|
549 |
|
550 |
|
551 |
|
552 |
|
553 | _onResourceLoaded(resourceData, data)
|
554 | {
|
555 | resourceData.loaded = true;
|
556 | this.resourcesToLoadCnt--;
|
557 | this.LOG_resourceLoading('[Nate] loaded resource:', resourceData.path, '(', 'total pending:', RESOURCES_LOAD_PENDING_CNT, ')');
|
558 | resourceData.loadedContent = data;
|
559 | this._resourcesCheckForPendingResources()
|
560 | this._resourcesCheckForResourcesReady();
|
561 | },
|
562 |
|
563 | _triggerResourceLoad(resourceData)
|
564 | {
|
565 | if (RESOURCES_LOAD_PENDING_CNT < RESOURCES_LOAD_PENDING_LIMIT)
|
566 | {
|
567 | RESOURCES_LOAD_PENDING_CNT++;
|
568 |
|
569 |
|
570 | this.LOG_resourceLoading('[Nate] trigger resource load:', resourceData.path, '(', 'total pending:', RESOURCES_LOAD_PENDING_CNT, ')');
|
571 |
|
572 | resourceData.loadResourceCalled = true;
|
573 |
|
574 | this.loadResource(resourceData, (data) => {
|
575 | RESOURCES_LOAD_PENDING_CNT--;
|
576 | this._onResourceLoaded(resourceData, data);
|
577 | });
|
578 |
|
579 | } else {
|
580 | console.log('[Nate] WARNING! Too many open resource files. Pending resources limit reached (' + RESOURCES_LOAD_PENDING_CNT + '). Performance may be decreased.');
|
581 |
|
582 | if (this.resourcesToLoadCnt === 1) {
|
583 |
|
584 |
|
585 |
|
586 | if (RESOURCES_WAITING_OBJECTS.indexOf(this) === -1) {
|
587 | RESOURCES_WAITING_OBJECTS.push(this);
|
588 | }
|
589 | }
|
590 | }
|
591 | },
|
592 |
|
593 | addResourceToLoad(pipe, resourceData, done)
|
594 | {
|
595 |
|
596 | this.resourcesToLoadCnt++;
|
597 | resourceData.loaded = false;
|
598 | resourceData.done = done;
|
599 | this.resourcesToLoad[pipe].push(resourceData);
|
600 | this.LOG_resourceLoading('[Nate] add resource:', resourceData.path);
|
601 | this._triggerResourceLoad(resourceData);
|
602 | },
|
603 |
|
604 | _resourcesCheckForPendingResources()
|
605 | {
|
606 | if (!this._resourcesCheckForPendingResourcesCalled)
|
607 | {
|
608 | this._resourcesCheckForPendingResourcesCalled = true;
|
609 |
|
610 | this.LOG_resourceLoading('[Nate] check for pending resources');
|
611 |
|
612 |
|
613 |
|
614 | for (let pipe in this.resourcesToLoad)
|
615 | {
|
616 | this.resourcesToLoad[pipe].forEach((resourceData) => {
|
617 | if (!resourceData.loadResourceCalled)
|
618 | {
|
619 | this._triggerResourceLoad(resourceData);
|
620 | }
|
621 | })
|
622 | }
|
623 |
|
624 | this._resourcesCheckForPendingResourcesCalled = false;
|
625 | }
|
626 | },
|
627 |
|
628 | _resourcesCheckForResourcesReady()
|
629 | {
|
630 | if ((this.waitingToEmit) && (this.resourcesToLoadCnt == 0))
|
631 | {
|
632 | if (K.Object.isNotNull(this.resourcesLoadDoneCb))
|
633 | {
|
634 | this._resourcesLoadDoneCb();
|
635 | this.resourcesLoadDoneCb = null;
|
636 | }
|
637 | }
|
638 | },
|
639 |
|
640 | _resourcesLoadDoneCb()
|
641 | {
|
642 | this.LOG_resourceLoading('[Nate] All resources loaded...');
|
643 |
|
644 | for (let resourceType in this.resourcesToLoad)
|
645 | {
|
646 | this.resourcesToLoadItem = this.resourcesToLoad[resourceType];
|
647 |
|
648 | for (let resourceItemIdx in this.resourcesToLoadItem)
|
649 | {
|
650 | const resourceItem = this.resourcesToLoadItem[resourceItemIdx];
|
651 |
|
652 | resourceItem.done(resourceItem.loadedContent)
|
653 | }
|
654 | }
|
655 |
|
656 | if (K.Object.isNotNull(this.resourcesLoadDoneCb))
|
657 | {
|
658 | this.resourcesLoadDoneCb();
|
659 | }
|
660 |
|
661 |
|
662 |
|
663 | if (RESOURCES_WAITING_OBJECTS.length > 0) {
|
664 | RESOURCES_WAITING_OBJECTS.shift()._resourcesCheckForPendingResources();
|
665 | }
|
666 | },
|
667 |
|
668 | cssFromStaticFile(path, done)
|
669 | {
|
670 | this.addResourceToLoad('css', {path: path}, (data) =>
|
671 | {
|
672 | this.theHead.pushCss(data);
|
673 |
|
674 | if (K.Object.isNotNull(done))
|
675 | {
|
676 | done();
|
677 | }
|
678 | });
|
679 | },
|
680 |
|
681 |
|
682 |
|
683 |
|
684 |
|
685 | jsGetCollector(pipeName)
|
686 | {
|
687 | return this.jsPipes[pipeName];
|
688 | },
|
689 |
|
690 | jsFromStaticFile(path, pipeName, done)
|
691 | {
|
692 | K.Error.reportIfParameterNotSet(path, 'javascript path');
|
693 |
|
694 | this.addResourceToLoad('js_' + pipeName, {path: path}, (data) =>
|
695 | {
|
696 | this.jsGetCollector(pipeName).pushFromTxt(data);
|
697 |
|
698 | if (EMIT_STATS_TO_CONSOLE_LOG)
|
699 | {
|
700 | console.log('[', path, ']', (data.length / 1024).toFixed(2), 'KB');
|
701 | }
|
702 |
|
703 | if (K.Object.isNotNull(done))
|
704 | {
|
705 | done();
|
706 | }
|
707 | });
|
708 | },
|
709 |
|
710 | coffeeFromStaticFile(path, pipeName, done)
|
711 | {
|
712 | K.Error.reportIfParameterNotSet(path, 'coffeeScript path');
|
713 |
|
714 | this.addResourceToLoad('js_' + pipeName, {path: path}, (data) =>
|
715 | {
|
716 | let compiled = null;
|
717 |
|
718 |
|
719 | if (RESOURCE_CACHE_ON && K.Object.isNotNull(RESOURCE_COMPILED_CACHE[path]))
|
720 | {
|
721 | compiled = RESOURCE_COMPILED_CACHE[path];
|
722 | }
|
723 | else
|
724 | {
|
725 | compiled = coffee.compile(data);
|
726 |
|
727 | RESOURCE_COMPILED_CACHE[path] = compiled;
|
728 | }
|
729 |
|
730 |
|
731 | this.jsGetCollector(pipeName).pushFromTxt(compiled);
|
732 |
|
733 | if (EMIT_STATS_TO_CONSOLE_LOG)
|
734 | {
|
735 | console.log('[', path, ']', (compiled.length / 1024).toFixed(2), 'KB');
|
736 | }
|
737 |
|
738 | if (K.Object.isNotNull(done))
|
739 | {
|
740 | done();
|
741 | }
|
742 | });
|
743 | },
|
744 |
|
745 | _onReadyToEmit(done)
|
746 | {
|
747 | this.waitingToEmit = true;
|
748 | this.resourcesLoadDoneCb = done;
|
749 | this._resourcesCheckForResourcesReady();
|
750 | },
|
751 |
|
752 | emitAsResponse(httpResponse, cb)
|
753 | {
|
754 | this._onReadyToEmit(() =>
|
755 | {
|
756 | const responseBody = this.render();
|
757 |
|
758 | httpResponse.send(responseBody);
|
759 |
|
760 | if (K.Object.isNotNull(cb))
|
761 | {
|
762 | const responseStats = {htmlSize: responseBody.length};
|
763 |
|
764 | cb(responseStats);
|
765 | }
|
766 | });
|
767 | },
|
768 |
|
769 | emitToString(cb)
|
770 | {
|
771 | this._onReadyToEmit(() =>
|
772 | {
|
773 | if (K.Object.isNotNull(cb))
|
774 | {
|
775 | cb(this.render());
|
776 | }
|
777 | });
|
778 | }
|
779 | });
|
780 |
|
781 | K.NateHtmlBodyProto = Object.assign({}, K.NateHtmlElemProto,
|
782 | {
|
783 | init(parent, namespace)
|
784 | {
|
785 | K.NateHtmlElemProto.init.call(this, parent, namespace);
|
786 |
|
787 | this.htmlDocument = parent;
|
788 | this.tagOpenCloseMode = K.NateXmlAbstractProto.JUST_CHILDREN;
|
789 | },
|
790 |
|
791 | render()
|
792 | {
|
793 | const jsBodyEnd = this.htmlDocument.jsGetCollector('bodyEnd').getAllAsTag();
|
794 | const jsOnLoad = this.htmlDocument.jsGetCollector('onLoad').getAllAsOnLoadCode();
|
795 |
|
796 | if (EMIT_STATS_TO_CONSOLE_LOG)
|
797 | {
|
798 | console.log('js/bodyEnd :', (jsBodyEnd.length / 1024).toFixed(2), 'KB');
|
799 | console.log('js/onLoad :', (jsOnLoad.length / 1024).toFixed(2), 'KB');
|
800 | }
|
801 |
|
802 | let txt = '<body';
|
803 |
|
804 | txt += '>';
|
805 | txt += K.NateXmlAbstractProto.render.call(this);
|
806 | txt += jsBodyEnd;
|
807 | txt += jsOnLoad;
|
808 | txt += '</body>';
|
809 |
|
810 | return txt;
|
811 | }
|
812 | });
|
813 |
|
814 |
|
815 |
|
816 |
|
817 |
|
818 | K.nateHtmlStaticNamespace =
|
819 | K.nateXmlStaticNamespace.createExtension('HtmlStatic', {nateProto: K.NateHtmlElemProto})
|
820 |
|
821 |
|
822 |
|
823 |
|
824 |
|
825 | const commonStyleAttributeSetter = (nNode, key, value) => {
|
826 | nNode.updateStyle(key, value);
|
827 | }
|
828 |
|
829 | const commonJsHandlerAttributeSetter = (nNode, key, value) => {
|
830 | switch (typeof(value)) {
|
831 | case 'function': {
|
832 |
|
833 | value = value.toString()
|
834 |
|
835 |
|
836 | const idxFunction = value.indexOf('function')
|
837 |
|
838 | if (idxFunction !== -1) {
|
839 | const idxBegin = value.indexOf('{', idxFunction) + 1
|
840 | const idxEnd = value.lastIndexOf('}')
|
841 |
|
842 | value = value.substr(idxBegin, idxEnd - idxBegin)
|
843 | }
|
844 |
|
845 | nNode.tagParamsList[key] = value
|
846 |
|
847 | break
|
848 | }
|
849 |
|
850 | case 'string': {
|
851 |
|
852 | nNode.tagParamsList[key] = value
|
853 |
|
854 | break
|
855 | }
|
856 |
|
857 | default: {
|
858 |
|
859 |
|
860 | console.log('PANIC! Unexpected ', key, 'handler', typeof(value), value);
|
861 |
|
862 | break
|
863 | }
|
864 | }
|
865 | }
|
866 |
|
867 | K.BSC.nateCommonHtmlAttributes.forEach((key) => {
|
868 | K.nateHtmlStaticNamespace.addSetter(key, K.NateXmlAbstractProto.commonAttributeSetter)
|
869 | })
|
870 |
|
871 | K.BSC.nateCommonHtmlProperties.forEach((key) => {
|
872 | K.nateHtmlStaticNamespace.addSetter(key, K.NateXmlAbstractProto.commonAttributeSetter)
|
873 | })
|
874 |
|
875 | K.BSC.nateCommonStyleAttributes.forEach((key) => {
|
876 | K.nateHtmlStaticNamespace.addSetter(key, commonStyleAttributeSetter)
|
877 | })
|
878 |
|
879 | K.nateHtmlStaticNamespace.addSetter('style', K.NateXmlAbstractProto.commonAttributeSetter)
|
880 |
|
881 | K.nateHtmlStaticNamespace.addSetter('cssText', (nNode, key, value) => {
|
882 | nNode.set('style', value)
|
883 | })
|
884 |
|
885 | K.nateHtmlStaticNamespace.addSetter('on', (nNode, key, value) => {
|
886 |
|
887 | if (value) {
|
888 | nNode.set('display', 'block')
|
889 | } else {
|
890 | nNode.set('display', 'none')
|
891 | }
|
892 | })
|
893 |
|
894 | K.nateHtmlStaticNamespace.addSetter('innerHTML',
|
895 | K.NateXmlAbstractProto.commonInnerContentSetter)
|
896 |
|
897 |
|
898 | K.nateHtmlStaticNamespace.addSetter('innerJSON',
|
899 | K.NateXmlAbstractProto.commonInnerContentSetter)
|
900 |
|
901 | K.nateHtmlStaticNamespace.addSetter('rect', (nNode, key, value) => {
|
902 |
|
903 | })
|
904 |
|
905 | K.nateHtmlStaticNamespace.addSetter('textContent', (nNode, key, value) => {
|
906 | const escapedValue = K.NateHtmlProto.escapeHtmlContent(value)
|
907 | K.NateXmlAbstractProto.commonInnerContentSetter(nNode, 'textContent', escapedValue)
|
908 | })
|
909 |
|
910 | K.nateHtmlStaticNamespace.addSetter('innerText', (nNode, key, value) => {
|
911 |
|
912 | const escapedValue = K.NateHtmlProto.nl2br(K.NateHtmlProto.escapeHtmlContent(value))
|
913 | K.NateXmlAbstractProto.commonInnerContentSetter(nNode, 'innerText', escapedValue)
|
914 | })
|
915 |
|
916 | K.nateHtmlStaticNamespace.addSetter('readOnly', (nNode, key, value) => {
|
917 | if (value) {
|
918 |
|
919 | nNode.tagParamsList['readonly'] = ''
|
920 | } else if (nNode.tagParamsList['readonly']) {
|
921 | delete nNode.tagParamsList['readonly']
|
922 | }
|
923 | })
|
924 |
|
925 | K.nateHtmlStaticNamespace.addSetter('checked' , K.NateXmlAbstractProto.commonOnOffAttributeSetter)
|
926 | K.nateHtmlStaticNamespace.addSetter('selected', K.NateXmlAbstractProto.commonOnOffAttributeSetter)
|
927 |
|
928 | K.nateHtmlStaticNamespace.addSetter('onscroll', commonJsHandlerAttributeSetter)
|
929 | K.nateHtmlStaticNamespace.addSetter('onclick' , commonJsHandlerAttributeSetter)
|
930 |
|
931 |
|
932 |
|
933 |
|
934 |
|
935 | K.BSC.nateCommonHtmlOpenCloseTags.forEach((key) => {
|
936 | K.nateHtmlStaticNamespace.addCreateFunction(key, K.NateXmlAbstractProto.tagOpenClose);
|
937 | })
|
938 |
|
939 | K.BSC.nateCommonHtmlOpenCloseAtOnceTags.forEach((key) => {
|
940 | K.nateHtmlStaticNamespace.addCreateFunction(key, K.NateXmlAbstractProto.tagOpenCloseAtOnce);
|
941 | })
|
942 |
|
943 | K.BSC.nateCommonHtmlInputTypes.forEach((inputType) => {
|
944 | const key = 'input' + inputType.charAt(0).toUpperCase() + inputType.substr(1);
|
945 |
|
946 | K.nateHtmlStaticNamespace.addCreateFunction(key, function(tagName, params = {}) {
|
947 | params = K.Object.merge({type: inputType}, params);
|
948 |
|
949 | return this.tagOpenCloseAtOnce('input', params);
|
950 | });
|
951 | })
|
952 |
|
953 | K.nateHtmlStaticNamespace.addCreateFunction('svg', function() {
|
954 | return K.NateSvgDocument(this, K.BSC.NateAbstractProto.USE_DEFAULT_NAMESPACE);
|
955 | });
|
956 |
|
957 |
|
958 |
|
959 |
|
960 |
|
961 | K.NateHtmlElem = K.nateHtmlStaticNamespace.createFactoryFunction(K.NateHtmlElemProto);
|
962 | K.NateHtmlBody = K.nateHtmlStaticNamespace.createFactoryFunction(K.NateHtmlBodyProto);
|
963 | K.NateHtmlHead = K.nateHtmlStaticNamespace.createFactoryFunction(K.NateHtmlHeadProto);
|
964 | K.NateHtmlDocument = K.nateHtmlStaticNamespace.createFactoryFunction(K.NateHtmlDocumentProto);
|
965 |
|
966 | K.JsCollector = function() {
|
967 | const thiz = Object.create(K.JsCollectorProto);
|
968 |
|
969 | thiz.init();
|
970 |
|
971 | return thiz;
|
972 | }
|