UNPKG

3.29 kBJavaScriptView Raw
1var Page = require('./Page');
2var util = require('racer/lib/util');
3var contexts = require('derby-templates').contexts;
4
5Page.prototype.render = function(status, ns) {
6 if (typeof status !== 'number') {
7 ns = status;
8 status = null;
9 }
10 this.app.emit('render', this);
11
12 if (status) this.res.statusCode = status;
13 // Prevent the browser from storing the HTML response in its back cache, since
14 // that will cause it to render with the data from the initial load first
15 this.res.setHeader('Cache-Control', 'no-store');
16 // Set HTML utf-8 content type unless already set
17 if (!this.res.getHeader('Content-Type')) {
18 this.res.setHeader('Content-Type', 'text/html; charset=utf-8');
19 }
20
21 this._setRenderParams(ns);
22 var pageHtml = this.get('Page', ns);
23 this.res.write(pageHtml);
24
25 var bundleScriptTag = '<script async data-derby-app src="' + this.app.scriptUrl + '"';
26 if (this.app.scriptCrossOrigin) {
27 // Scripts loaded from a different origin (such as a CDN) won't report
28 // much information to the host page's window.onerror. Adding the
29 // "crossorigin" attribute to the script tag allows reporting of detailed
30 // error info to the host page.
31 // HOWEVER - if the "crossorigin" attribute is present for a script tag
32 // with a cross-origin "src", then the script's HTTP response MUST have
33 // an appropriate "Access-Control-Allow-Origin" header set. Otherwise,
34 // the browser will refuse to load the script.
35 bundleScriptTag += ' crossorigin';
36 }
37 bundleScriptTag += '></script>';
38 this.res.write(bundleScriptTag);
39
40 this.res.write('<script type="application/json">');
41 var tailHtml = this.get('Tail', ns);
42
43 this.model.destroy('$components');
44
45 var page = this;
46 this.model.bundle(function(err, bundle) {
47 if (page.model.hasErrored) return;
48 if (err) return page.emit('error', err);
49 var json = stringifyBundle(bundle);
50 page.res.write(json);
51 page.res.end('</script>' + tailHtml);
52 page.app.emit('routeDone', page, 'render');
53 });
54};
55
56Page.prototype.renderStatic = function(status, ns) {
57 if (typeof status !== 'number') {
58 ns = status;
59 status = null;
60 }
61 this.app.emit('renderStatic', this);
62
63 if (status) this.res.statusCode = status;
64 this.params = pageParams(this.req);
65 this._setRenderParams(ns);
66 var pageHtml = this.get('Page', ns);
67 var tailHtml = this.get('Tail', ns);
68 this.res.send(pageHtml + tailHtml);
69 this.app.emit('routeDone', this, 'renderStatic');
70};
71
72// Don't register any listeners on the server
73Page.prototype._addListeners = function() {};
74
75function stringifyBundle(bundle) {
76 var json = JSON.stringify(bundle);
77 return json.replace(/<[\/!]/g, function(match) {
78 // Replace the end tag sequence with an equivalent JSON string to make
79 // sure the script is not prematurely closed
80 if (match === '</') return '<\\/';
81 // Replace the start of an HTML comment tag sequence with an equivalent
82 // JSON string
83 if (match === '<!') return '<\\u0021';
84 throw new Error('Unexpected match when escaping JSON');
85 });
86}
87
88// TODO: Cleanup; copied from tracks
89function pageParams(req) {
90 var params = {
91 url: req.url
92 , body: req.body
93 , query: req.query
94 };
95 for (var key in req.params) {
96 params[key] = req.params[key];
97 }
98 return params;
99}