UNPKG

53.8 kBJavaScriptView Raw
1(function (global, factory) {
2 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/common'), require('rxjs/operators')) :
3 typeof define === 'function' && define.amd ? define('preboot', ['exports', '@angular/core', '@angular/common', 'rxjs/operators'], factory) :
4 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.preboot = {}, global.ng.core, global.ng.common, global.rxjs.operators));
5}(this, (function (exports, core, common, operators) { 'use strict';
6
7 /**
8 * Attempt to generate key from node position in the DOM
9 */
10 function getNodeKeyForPreboot(nodeContext) {
11 var ancestors = [];
12 var root = nodeContext.root;
13 var node = nodeContext.node;
14 var temp = node;
15 // walk up the tree from the target node up to the root
16 while (temp && temp !== root.serverNode && temp !== root.clientNode) {
17 ancestors.push(temp);
18 temp = temp.parentNode;
19 }
20 // note: if temp doesn't exist here it means root node wasn't found
21 if (temp) {
22 ancestors.push(temp);
23 }
24 // now go backwards starting from the root, appending the appName to unique
25 // identify the node later..
26 var name = node.nodeName || 'unknown';
27 var key = name;
28 var len = ancestors.length;
29 for (var i = len - 1; i >= 0; i--) {
30 temp = ancestors[i];
31 if (temp.childNodes && i > 0) {
32 for (var j = 0; j < temp.childNodes.length; j++) {
33 if (temp.childNodes[j] === ancestors[i - 1]) {
34 key += '_s' + (j + 1);
35 break;
36 }
37 }
38 }
39 }
40 return key;
41 }
42
43 /**
44 * @license
45 * Copyright Google LLC All Rights Reserved.
46 *
47 * Use of this source code is governed by an MIT-style license that can be
48 * found in the LICENSE file at https://angular.io/license
49 */
50 var PREBOOT_NONCE = new core.InjectionToken('PrebootNonce');
51
52 /**
53 * @license
54 * Copyright Google LLC All Rights Reserved.
55 *
56 * Use of this source code is governed by an MIT-style license that can be
57 * found in the LICENSE file at https://angular.io/license
58 */
59
60 /*! *****************************************************************************
61 Copyright (c) Microsoft Corporation.
62
63 Permission to use, copy, modify, and/or distribute this software for any
64 purpose with or without fee is hereby granted.
65
66 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
67 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
68 AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
69 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
70 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
71 OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
72 PERFORMANCE OF THIS SOFTWARE.
73 ***************************************************************************** */
74 /* global Reflect, Promise */
75 var extendStatics = function (d, b) {
76 extendStatics = Object.setPrototypeOf ||
77 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
78 function (d, b) { for (var p in b)
79 if (Object.prototype.hasOwnProperty.call(b, p))
80 d[p] = b[p]; };
81 return extendStatics(d, b);
82 };
83 function __extends(d, b) {
84 if (typeof b !== "function" && b !== null)
85 throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
86 extendStatics(d, b);
87 function __() { this.constructor = d; }
88 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
89 }
90 var __assign = function () {
91 __assign = Object.assign || function __assign(t) {
92 for (var s, i = 1, n = arguments.length; i < n; i++) {
93 s = arguments[i];
94 for (var p in s)
95 if (Object.prototype.hasOwnProperty.call(s, p))
96 t[p] = s[p];
97 }
98 return t;
99 };
100 return __assign.apply(this, arguments);
101 };
102 function __rest(s, e) {
103 var t = {};
104 for (var p in s)
105 if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
106 t[p] = s[p];
107 if (s != null && typeof Object.getOwnPropertySymbols === "function")
108 for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
109 if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
110 t[p[i]] = s[p[i]];
111 }
112 return t;
113 }
114 function __decorate(decorators, target, key, desc) {
115 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
116 if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
117 r = Reflect.decorate(decorators, target, key, desc);
118 else
119 for (var i = decorators.length - 1; i >= 0; i--)
120 if (d = decorators[i])
121 r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
122 return c > 3 && r && Object.defineProperty(target, key, r), r;
123 }
124 function __param(paramIndex, decorator) {
125 return function (target, key) { decorator(target, key, paramIndex); };
126 }
127 function __metadata(metadataKey, metadataValue) {
128 if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
129 return Reflect.metadata(metadataKey, metadataValue);
130 }
131 function __awaiter(thisArg, _arguments, P, generator) {
132 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
133 return new (P || (P = Promise))(function (resolve, reject) {
134 function fulfilled(value) { try {
135 step(generator.next(value));
136 }
137 catch (e) {
138 reject(e);
139 } }
140 function rejected(value) { try {
141 step(generator["throw"](value));
142 }
143 catch (e) {
144 reject(e);
145 } }
146 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
147 step((generator = generator.apply(thisArg, _arguments || [])).next());
148 });
149 }
150 function __generator(thisArg, body) {
151 var _ = { label: 0, sent: function () { if (t[0] & 1)
152 throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
153 return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g;
154 function verb(n) { return function (v) { return step([n, v]); }; }
155 function step(op) {
156 if (f)
157 throw new TypeError("Generator is already executing.");
158 while (_)
159 try {
160 if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done)
161 return t;
162 if (y = 0, t)
163 op = [op[0] & 2, t.value];
164 switch (op[0]) {
165 case 0:
166 case 1:
167 t = op;
168 break;
169 case 4:
170 _.label++;
171 return { value: op[1], done: false };
172 case 5:
173 _.label++;
174 y = op[1];
175 op = [0];
176 continue;
177 case 7:
178 op = _.ops.pop();
179 _.trys.pop();
180 continue;
181 default:
182 if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
183 _ = 0;
184 continue;
185 }
186 if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) {
187 _.label = op[1];
188 break;
189 }
190 if (op[0] === 6 && _.label < t[1]) {
191 _.label = t[1];
192 t = op;
193 break;
194 }
195 if (t && _.label < t[2]) {
196 _.label = t[2];
197 _.ops.push(op);
198 break;
199 }
200 if (t[2])
201 _.ops.pop();
202 _.trys.pop();
203 continue;
204 }
205 op = body.call(thisArg, _);
206 }
207 catch (e) {
208 op = [6, e];
209 y = 0;
210 }
211 finally {
212 f = t = 0;
213 }
214 if (op[0] & 5)
215 throw op[1];
216 return { value: op[0] ? op[1] : void 0, done: true };
217 }
218 }
219 var __createBinding = Object.create ? (function (o, m, k, k2) {
220 if (k2 === undefined)
221 k2 = k;
222 Object.defineProperty(o, k2, { enumerable: true, get: function () { return m[k]; } });
223 }) : (function (o, m, k, k2) {
224 if (k2 === undefined)
225 k2 = k;
226 o[k2] = m[k];
227 });
228 function __exportStar(m, o) {
229 for (var p in m)
230 if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p))
231 __createBinding(o, m, p);
232 }
233 function __values(o) {
234 var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
235 if (m)
236 return m.call(o);
237 if (o && typeof o.length === "number")
238 return {
239 next: function () {
240 if (o && i >= o.length)
241 o = void 0;
242 return { value: o && o[i++], done: !o };
243 }
244 };
245 throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
246 }
247 function __read(o, n) {
248 var m = typeof Symbol === "function" && o[Symbol.iterator];
249 if (!m)
250 return o;
251 var i = m.call(o), r, ar = [], e;
252 try {
253 while ((n === void 0 || n-- > 0) && !(r = i.next()).done)
254 ar.push(r.value);
255 }
256 catch (error) {
257 e = { error: error };
258 }
259 finally {
260 try {
261 if (r && !r.done && (m = i["return"]))
262 m.call(i);
263 }
264 finally {
265 if (e)
266 throw e.error;
267 }
268 }
269 return ar;
270 }
271 /** @deprecated */
272 function __spread() {
273 for (var ar = [], i = 0; i < arguments.length; i++)
274 ar = ar.concat(__read(arguments[i]));
275 return ar;
276 }
277 /** @deprecated */
278 function __spreadArrays() {
279 for (var s = 0, i = 0, il = arguments.length; i < il; i++)
280 s += arguments[i].length;
281 for (var r = Array(s), k = 0, i = 0; i < il; i++)
282 for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
283 r[k] = a[j];
284 return r;
285 }
286 function __spreadArray(to, from) {
287 for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
288 to[j] = from[i];
289 return to;
290 }
291 function __await(v) {
292 return this instanceof __await ? (this.v = v, this) : new __await(v);
293 }
294 function __asyncGenerator(thisArg, _arguments, generator) {
295 if (!Symbol.asyncIterator)
296 throw new TypeError("Symbol.asyncIterator is not defined.");
297 var g = generator.apply(thisArg, _arguments || []), i, q = [];
298 return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
299 function verb(n) { if (g[n])
300 i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
301 function resume(n, v) { try {
302 step(g[n](v));
303 }
304 catch (e) {
305 settle(q[0][3], e);
306 } }
307 function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
308 function fulfill(value) { resume("next", value); }
309 function reject(value) { resume("throw", value); }
310 function settle(f, v) { if (f(v), q.shift(), q.length)
311 resume(q[0][0], q[0][1]); }
312 }
313 function __asyncDelegator(o) {
314 var i, p;
315 return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
316 function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; }
317 }
318 function __asyncValues(o) {
319 if (!Symbol.asyncIterator)
320 throw new TypeError("Symbol.asyncIterator is not defined.");
321 var m = o[Symbol.asyncIterator], i;
322 return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
323 function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
324 function settle(resolve, reject, d, v) { Promise.resolve(v).then(function (v) { resolve({ value: v, done: d }); }, reject); }
325 }
326 function __makeTemplateObject(cooked, raw) {
327 if (Object.defineProperty) {
328 Object.defineProperty(cooked, "raw", { value: raw });
329 }
330 else {
331 cooked.raw = raw;
332 }
333 return cooked;
334 }
335 ;
336 var __setModuleDefault = Object.create ? (function (o, v) {
337 Object.defineProperty(o, "default", { enumerable: true, value: v });
338 }) : function (o, v) {
339 o["default"] = v;
340 };
341 function __importStar(mod) {
342 if (mod && mod.__esModule)
343 return mod;
344 var result = {};
345 if (mod != null)
346 for (var k in mod)
347 if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
348 __createBinding(result, mod, k);
349 __setModuleDefault(result, mod);
350 return result;
351 }
352 function __importDefault(mod) {
353 return (mod && mod.__esModule) ? mod : { default: mod };
354 }
355 function __classPrivateFieldGet(receiver, privateMap) {
356 if (!privateMap.has(receiver)) {
357 throw new TypeError("attempted to get private field on non-instance");
358 }
359 return privateMap.get(receiver);
360 }
361 function __classPrivateFieldSet(receiver, privateMap, value) {
362 if (!privateMap.has(receiver)) {
363 throw new TypeError("attempted to set private field on non-instance");
364 }
365 privateMap.set(receiver, value);
366 return value;
367 }
368
369 function _window() {
370 return {
371 prebootData: window['prebootData'],
372 getComputedStyle: window.getComputedStyle,
373 document: document
374 };
375 }
376 var EventReplayer = /** @class */ (function () {
377 function EventReplayer() {
378 this.clientNodeCache = {};
379 this.replayStarted = false;
380 }
381 /**
382 * Window setting and getter to facilitate testing of window
383 * in non-browser environments
384 */
385 EventReplayer.prototype.setWindow = function (win) {
386 this.win = win;
387 };
388 /**
389 * Window setting and getter to facilitate testing of window
390 * in non-browser environments
391 */
392 EventReplayer.prototype.getWindow = function () {
393 if (!this.win) {
394 this.win = _window();
395 }
396 return this.win;
397 };
398 /**
399 * Replay all events for all apps. this can only be run once.
400 * if called multiple times, will only do something once
401 */
402 EventReplayer.prototype.replayAll = function () {
403 var _this = this;
404 if (this.replayStarted) {
405 return;
406 }
407 else {
408 this.replayStarted = true;
409 }
410 // loop through each of the preboot apps
411 var prebootData = this.getWindow().prebootData || {};
412 var apps = prebootData.apps || [];
413 apps.forEach(function (appData) { return _this.replayForApp(appData); });
414 // once all events have been replayed and buffers switched, then we cleanup preboot
415 this.cleanup(prebootData);
416 };
417 /**
418 * Replay all events for one app (most of the time there is just one app)
419 * @param appData
420 */
421 EventReplayer.prototype.replayForApp = function (appData) {
422 var _this = this;
423 appData = (appData || {});
424 // try catch around events b/c even if error occurs, we still move forward
425 try {
426 var events = appData.events || [];
427 // replay all the events from the server view onto the client view
428 events.forEach(function (event) { return _this.replayEvent(appData, event); });
429 }
430 catch (ex) {
431 console.error(ex);
432 }
433 // if we are buffering, switch the buffers
434 this.switchBuffer(appData);
435 };
436 /**
437 * Replay one particular event
438 * @param appData
439 * @param prebootEvent
440 */
441 EventReplayer.prototype.replayEvent = function (appData, prebootEvent) {
442 appData = (appData || {});
443 prebootEvent = (prebootEvent || {});
444 var event = prebootEvent.event;
445 var serverNode = prebootEvent.node || {};
446 var nodeKey = prebootEvent.nodeKey;
447 var clientNode = this.findClientNode({
448 root: appData.root,
449 node: serverNode,
450 nodeKey: nodeKey
451 });
452 // if client node can't be found, log a warning
453 if (!clientNode) {
454 console.warn("Trying to dispatch event " + event.type + " to node " + nodeKey + "\n but could not find client node. Server node is: " + serverNode);
455 return;
456 }
457 // now dispatch events and whatnot to the client node
458 clientNode.checked = serverNode.checked;
459 clientNode.selected = serverNode.selected;
460 clientNode.value = serverNode.value;
461 clientNode.dispatchEvent(event);
462 };
463 /**
464 * Switch the buffer for one particular app (i.e. display the client
465 * view and destroy the server view)
466 * @param appData
467 */
468 EventReplayer.prototype.switchBuffer = function (appData) {
469 appData = (appData || {});
470 var root = (appData.root || {});
471 var serverView = root.serverNode;
472 var clientView = root.clientNode;
473 // if no client view or the server view is the body or client
474 // and server view are the same, then don't do anything and return
475 if (!clientView || !serverView || serverView === clientView || serverView.nodeName === 'BODY') {
476 return;
477 }
478 // do a try-catch just in case something messed up
479 try {
480 // get the server view display mode
481 var gcs = this.getWindow().getComputedStyle;
482 var display = gcs(serverView).getPropertyValue('display') || 'block';
483 // first remove the server view
484 serverView.remove ? serverView.remove() : (serverView.style.display = 'none');
485 // now add the client view
486 clientView.style.display = display;
487 }
488 catch (ex) {
489 console.error(ex);
490 }
491 };
492 /**
493 * Finally, set focus, remove all the event listeners and remove
494 * any freeze screen that may be there
495 * @param prebootData
496 */
497 EventReplayer.prototype.cleanup = function (prebootData) {
498 var e_1, _a;
499 var _this = this;
500 prebootData = prebootData || {};
501 var listeners = prebootData.listeners || [];
502 // set focus on the active node AFTER a small delay to ensure buffer
503 // switched
504 var activeNode = prebootData.activeNode;
505 if (activeNode != null) {
506 setTimeout(function () { return _this.setFocus(activeNode); }, 1);
507 }
508 try {
509 // remove all event listeners
510 for (var listeners_1 = __values(listeners), listeners_1_1 = listeners_1.next(); !listeners_1_1.done; listeners_1_1 = listeners_1.next()) {
511 var listener = listeners_1_1.value;
512 listener.node.removeEventListener(listener.eventName, listener.handler);
513 }
514 }
515 catch (e_1_1) { e_1 = { error: e_1_1 }; }
516 finally {
517 try {
518 if (listeners_1_1 && !listeners_1_1.done && (_a = listeners_1.return)) _a.call(listeners_1);
519 }
520 finally { if (e_1) throw e_1.error; }
521 }
522 // remove the freeze overlay if it exists
523 var doc = this.getWindow().document;
524 var prebootOverlay = doc.getElementById('prebootOverlay');
525 if (prebootOverlay) {
526 prebootOverlay.remove ?
527 prebootOverlay.remove() : prebootOverlay.parentNode !== null ?
528 prebootOverlay.parentNode.removeChild(prebootOverlay) :
529 prebootOverlay.style.display = 'none';
530 }
531 // clear out the data stored for each app
532 prebootData.apps = [];
533 this.clientNodeCache = {};
534 // send event to document that signals preboot complete
535 // constructor is not supported by older browsers ( i.e. IE9-11 )
536 // in these browsers, the type of CustomEvent will be "object"
537 if (typeof CustomEvent === 'function') {
538 var completeEvent = new CustomEvent('PrebootComplete');
539 doc.dispatchEvent(completeEvent);
540 }
541 else {
542 console.warn("Could not dispatch PrebootComplete event.\n You can fix this by including a polyfill for CustomEvent.");
543 }
544 };
545 EventReplayer.prototype.setFocus = function (activeNode) {
546 // only do something if there is an active node
547 if (!activeNode || !activeNode.node || !activeNode.nodeKey) {
548 return;
549 }
550 // find the client node in the new client view
551 var clientNode = this.findClientNode(activeNode);
552 if (clientNode) {
553 // set focus on the client node
554 clientNode.focus();
555 // set selection if a modern browser (i.e. IE9+, etc.)
556 var selection = activeNode.selection;
557 if (clientNode.setSelectionRange && selection) {
558 try {
559 clientNode
560 .setSelectionRange(selection.start, selection.end, selection.direction);
561 }
562 catch (ex) { }
563 }
564 }
565 };
566 /**
567 * Given a node from the server rendered view, find the equivalent
568 * node in the client rendered view. We do this by the following approach:
569 * 1. take the name of the server node tag (ex. div or h1 or input)
570 * 2. add either id (ex. div#myid) or class names (ex. div.class1.class2)
571 * 3. use that value as a selector to get all the matching client nodes
572 * 4. loop through all client nodes found and for each generate a key value
573 * 5. compare the client key to the server key; once there is a match,
574 * we have our client node
575 *
576 * NOTE: this only works when the client view is almost exactly the same as
577 * the server view. we will need an improvement here in the future to account
578 * for situations where the client view is different in structure from the
579 * server view
580 */
581 EventReplayer.prototype.findClientNode = function (serverNodeContext) {
582 serverNodeContext = (serverNodeContext || {});
583 var serverNode = serverNodeContext.node;
584 var root = serverNodeContext.root;
585 // if no server or client root, don't do anything
586 if (!root || !root.serverNode || !root.clientNode) {
587 return null;
588 }
589 // we use the string of the node to compare to the client node & as key in
590 // cache
591 var serverNodeKey = serverNodeContext.nodeKey || getNodeKeyForPreboot(serverNodeContext);
592 // if client node already in cache, return it
593 if (this.clientNodeCache[serverNodeKey]) {
594 return this.clientNodeCache[serverNodeKey];
595 }
596 // get the selector for client nodes
597 var className = (serverNode.className || '').replace('ng-binding', '').trim();
598 var selector = serverNode.tagName;
599 if (serverNode.id) {
600 selector += "#" + serverNode.id;
601 }
602 else if (className) {
603 selector += "." + className.replace(/ /g, '.');
604 }
605 // select all possible client nodes and look through them to try and find a
606 // match
607 var rootClientNode = root.clientNode;
608 var clientNodes = rootClientNode.querySelectorAll(selector);
609 // if nothing found, then just try the tag name as a final option
610 if (!clientNodes.length) {
611 console.log("nothing found for " + selector + " so using " + serverNode.tagName);
612 clientNodes = rootClientNode.querySelectorAll(serverNode.tagName);
613 }
614 var length = clientNodes.length;
615 for (var i = 0; i < length; i++) {
616 var clientNode = clientNodes.item(i);
617 // get the key for the client node
618 var clientNodeKey = getNodeKeyForPreboot({
619 root: root,
620 node: clientNode
621 });
622 // if the client node key is exact match for the server node key, then we
623 // found the client node
624 if (clientNodeKey === serverNodeKey) {
625 this.clientNodeCache[serverNodeKey] = clientNode;
626 return clientNode;
627 }
628 }
629 // if we get here and there is one clientNode, use it as a fallback
630 if (clientNodes.length === 1) {
631 this.clientNodeCache[serverNodeKey] = clientNodes[0];
632 return clientNodes[0];
633 }
634 // if we get here it means we couldn't find the client node so give the user
635 // a warning
636 console.warn("No matching client node found for " + serverNodeKey + ".\n You can fix this by assigning this element a unique id attribute.");
637 return null;
638 };
639 return EventReplayer;
640 }());
641
642 /**
643 * Called right away to initialize preboot
644 *
645 * @param opts All the preboot options
646 * @param win
647 */
648 function initAll(opts, win) {
649 var theWindow = (win || window);
650 // Add the preboot options to the preboot data and then add the data to
651 // the window so it can be used later by the client.
652 // Only set new options if they're not already set - we may have multiple app roots
653 // and each of them invokes the init function separately.
654 var data = (theWindow.prebootData = {
655 opts: opts,
656 apps: [],
657 listeners: []
658 });
659 return function () { return start(data, theWindow); };
660 }
661 /**
662 * Start up preboot by going through each app and assigning the appropriate
663 * handlers. Normally this wouldn't be called directly, but we have set it up so
664 * that it can for older versions of Universal.
665 *
666 * @param prebootData Global preboot data object that contains options and will
667 * have events
668 * @param win Optional param to pass in mock window for testing purposes
669 */
670 function start(prebootData, win) {
671 var theWindow = (win || window);
672 var _document = (theWindow.document || {});
673 // Remove the current script from the DOM so that child indexes match
674 // between the client & the server. The script is already running so it
675 // doesn't affect it.
676 var currentScript = _document.currentScript ||
677 // Support: IE 9-11 only
678 // IE doesn't support document.currentScript. Since the script is invoked
679 // synchronously, though, the current running script is just the last one
680 // currently in the document.
681 [].slice.call(_document.getElementsByTagName('script'), -1)[0];
682 if (!currentScript) {
683 console.error('Preboot initialization failed, no currentScript has been detected.');
684 return;
685 }
686 var serverNode = currentScript.parentNode;
687 if (!serverNode) {
688 console.error('Preboot initialization failed, the script is detached');
689 return;
690 }
691 serverNode.removeChild(currentScript);
692 var opts = prebootData.opts || {};
693 var eventSelectors = opts.eventSelectors || [];
694 // get the root info
695 var appRoot = prebootData.opts ? getAppRoot(_document, prebootData.opts, serverNode) : null;
696 // we track all events for each app in the prebootData object which is on
697 // the global scope; each `start` invocation adds data for one app only.
698 var appData = { root: appRoot, events: [] };
699 if (prebootData.apps) {
700 prebootData.apps.push(appData);
701 }
702 eventSelectors = eventSelectors.map(function (eventSelector) {
703 if (!eventSelector.hasOwnProperty('replay')) {
704 eventSelector.replay = true;
705 }
706 return eventSelector;
707 });
708 // loop through all the eventSelectors and create event handlers
709 eventSelectors.forEach(function (eventSelector) { return handleEvents(_document, prebootData, appData, eventSelector); });
710 }
711 /**
712 * Create an overlay div and add it to the DOM so it can be used
713 * if a freeze event occurs
714 *
715 * @param _document The global document object (passed in for testing purposes)
716 * @returns Element The overlay node is returned
717 */
718 function createOverlay(_document) {
719 var overlay = _document.createElement('div');
720 overlay.setAttribute('id', 'prebootOverlay');
721 overlay.setAttribute('style', 'display:none;position:absolute;left:0;' +
722 'top:0;width:100%;height:100%;z-index:999999;background:black;opacity:.3');
723 _document.documentElement.appendChild(overlay);
724 return overlay;
725 }
726 /**
727 * Get references to the current app root node based on input options. Users can
728 * initialize preboot either by specifying appRoot which is just one or more
729 * selectors for apps. This section option is useful for people that are doing their own
730 * buffering (i.e. they have their own client and server view)
731 *
732 * @param _document The global document object used to attach the overlay
733 * @param opts Options passed in by the user to init()
734 * @param serverNode The server node serving as application root
735 * @returns ServerClientRoot An array of root info for the current app
736 */
737 function getAppRoot(_document, opts, serverNode) {
738 var root = { serverNode: serverNode };
739 // if we are doing buffering, we need to create the buffer for the client
740 // else the client root is the same as the server
741 root.clientNode = opts.buffer ? createBuffer(root) : root.serverNode;
742 // create an overlay if not disabled ,that can be used later if a freeze event occurs
743 if (!opts.disableOverlay) {
744 root.overlay = createOverlay(_document);
745 }
746 return root;
747 }
748 /**
749 * Under given server root, for given selector, record events
750 *
751 * @param _document
752 * @param prebootData
753 * @param appData
754 * @param eventSelector
755 */
756 function handleEvents(_document, prebootData, appData, eventSelector) {
757 var serverRoot = appData.root.serverNode;
758 // don't do anything if no server root
759 if (!serverRoot) {
760 return;
761 }
762 // Attach delegated event listeners for each event selector.
763 // We need to use delegated events as only the top level server node
764 // exists at this point.
765 eventSelector.events.forEach(function (eventName) {
766 // get the appropriate handler and add it as an event listener
767 var handler = createListenHandler(_document, prebootData, eventSelector, appData);
768 // attach the handler in the capture phase so that it fires even if
769 // one of the handlers below calls stopPropagation()
770 serverRoot.addEventListener(eventName, handler, true);
771 // need to keep track of listeners so we can do node.removeEventListener()
772 // when preboot done
773 if (prebootData.listeners) {
774 prebootData.listeners.push({
775 node: serverRoot,
776 eventName: eventName,
777 handler: handler
778 });
779 }
780 });
781 }
782 /**
783 * Create handler for events that we will record
784 */
785 function createListenHandler(_document, prebootData, eventSelector, appData) {
786 var CARET_EVENTS = ['keyup', 'keydown', 'focusin', 'mouseup', 'mousedown'];
787 var CARET_NODES = ['INPUT', 'TEXTAREA'];
788 // Support: IE 9-11 only
789 // IE uses a prefixed `matches` version
790 var matches = _document.documentElement.matches ||
791 _document.documentElement.msMatchesSelector;
792 var opts = prebootData.opts;
793 return function (event) {
794 var node = event.target;
795 // a delegated handlers on document is used so we need to check if
796 // event target matches a desired selector
797 if (!matches.call(node, eventSelector.selector)) {
798 return;
799 }
800 var root = appData.root;
801 var eventName = event.type;
802 // if no node or no event name, just return
803 if (!node || !eventName) {
804 return;
805 }
806 // if key codes set for eventSelector, then don't do anything if event
807 // doesn't include key
808 var keyCodes = eventSelector.keyCodes;
809 if (keyCodes && keyCodes.length) {
810 var matchingKeyCodes = keyCodes.filter(function (keyCode) { return event.which === keyCode; });
811 // if there are not matches (i.e. key entered NOT one of the key codes)
812 // then don't do anything
813 if (!matchingKeyCodes.length) {
814 return;
815 }
816 }
817 // if for a given set of events we are preventing default, do that
818 if (eventSelector.preventDefault) {
819 event.preventDefault();
820 }
821 // if an action handler passed in, use that
822 if (eventSelector.action) {
823 eventSelector.action(node, event);
824 }
825 // get the node key for a given node
826 var nodeKey = getNodeKeyForPreboot({ root: root, node: node });
827 // record active node
828 if (CARET_EVENTS.indexOf(eventName) >= 0) {
829 // if it's an caret node, get the selection for the active node
830 var isCaretNode = CARET_NODES.indexOf(node.tagName ? node.tagName : '') >= 0;
831 prebootData.activeNode = {
832 root: root,
833 node: node,
834 nodeKey: nodeKey,
835 selection: isCaretNode ? getSelection(node) : undefined
836 };
837 }
838 else if (eventName !== 'change' && eventName !== 'focusout') {
839 prebootData.activeNode = undefined;
840 }
841 // if overlay is not disabled and we are freezing the UI
842 if (opts && !opts.disableOverlay && eventSelector.freeze) {
843 var overlay_1 = root.overlay;
844 // show the overlay
845 overlay_1.style.display = 'block';
846 // hide the overlay after 10 seconds just in case preboot.complete() never
847 // called
848 setTimeout(function () {
849 overlay_1.style.display = 'none';
850 }, 10000);
851 }
852 // we will record events for later replay unless explicitly marked as
853 // doNotReplay
854 if (eventSelector.replay) {
855 appData.events.push({
856 node: node,
857 nodeKey: nodeKey,
858 event: event,
859 name: eventName
860 });
861 }
862 };
863 }
864 /**
865 * Get the selection data that is later used to set the cursor after client view
866 * is active
867 */
868 function getSelection(node) {
869 node = node || {};
870 var nodeValue = node.value || '';
871 var selection = {
872 start: nodeValue.length,
873 end: nodeValue.length,
874 direction: 'forward'
875 };
876 // if browser support selectionStart on node (Chrome, FireFox, IE9+)
877 try {
878 if (node.selectionStart || node.selectionStart === 0) {
879 selection.start = node.selectionStart;
880 selection.end = node.selectionEnd ? node.selectionEnd : 0;
881 selection.direction = node.selectionDirection ?
882 node.selectionDirection : 'none';
883 }
884 }
885 catch (ex) { }
886 return selection;
887 }
888 /**
889 * Create buffer for a given node
890 *
891 * @param root All the data related to a particular app
892 * @returns Returns the root client node.
893 */
894 function createBuffer(root) {
895 var serverNode = root.serverNode;
896 // if no rootServerNode OR the selector is on the entire html doc or the body
897 // OR no parentNode, don't buffer
898 if (!serverNode || !serverNode.parentNode ||
899 serverNode === document.documentElement || serverNode === document.body) {
900 return serverNode;
901 }
902 // create shallow clone of server root
903 var rootClientNode = serverNode.cloneNode(false);
904 // we want the client to write to a hidden div until the time for switching
905 // the buffers
906 rootClientNode.style.display = 'none';
907 // insert the client node before the server and return it
908 serverNode.parentNode.insertBefore(rootClientNode, serverNode);
909 // mark server node as not to be touched by AngularJS - needed for ngUpgrade
910 serverNode.setAttribute('ng-non-bindable', '');
911 // return the rootClientNode
912 return rootClientNode;
913 }
914
915 var eventRecorder = {
916 start: start,
917 createOverlay: createOverlay,
918 getAppRoot: getAppRoot,
919 handleEvents: handleEvents,
920 createListenHandler: createListenHandler,
921 getSelection: getSelection,
922 createBuffer: createBuffer
923 };
924 var initFunctionName = 'prebootInitFn';
925 // exporting default options in case developer wants to use these + custom on
926 // top
927 var defaultOptions = {
928 buffer: true,
929 replay: true,
930 disableOverlay: false,
931 // these are the default events are are listening for an transferring from
932 // server view to client view
933 eventSelectors: [
934 // for recording changes in form elements
935 {
936 selector: 'input,textarea',
937 events: ['keypress', 'keyup', 'keydown', 'input', 'change']
938 },
939 { selector: 'select,option', events: ['change'] },
940 // when user hits return button in an input box
941 {
942 selector: 'input',
943 events: ['keyup'],
944 preventDefault: true,
945 keyCodes: [13],
946 freeze: true
947 },
948 // when user submit form (press enter, click on button/input[type="submit"])
949 {
950 selector: 'form',
951 events: ['submit'],
952 preventDefault: true,
953 freeze: true
954 },
955 // for tracking focus (no need to replay)
956 {
957 selector: 'input,textarea',
958 events: ['focusin', 'focusout', 'mousedown', 'mouseup'],
959 replay: false
960 },
961 // user clicks on a button
962 {
963 selector: 'button',
964 events: ['click'],
965 preventDefault: true,
966 freeze: true
967 }
968 ]
969 };
970 /**
971 * Get the event recorder code based on all functions in event.recorder.ts
972 * and the getNodeKeyForPreboot function.
973 */
974 function getEventRecorderCode() {
975 var eventRecorderFunctions = [];
976 for (var funcName in eventRecorder) {
977 if (eventRecorder.hasOwnProperty(funcName)) {
978 var fn = eventRecorder[funcName].toString();
979 var fnCleaned = fn.replace('common_1.', '');
980 eventRecorderFunctions.push(fnCleaned);
981 }
982 }
983 // this is common function used to get the node key
984 eventRecorderFunctions.push(getNodeKeyForPreboot.toString());
985 // add new line characters for readability
986 return '\n\n' + eventRecorderFunctions.join('\n\n') + '\n\n';
987 }
988 /**
989 * Used by the server side version of preboot. The main purpose is to get the
990 * inline code that can be inserted into the server view.
991 * Returns the definitions of the prebootInit function called in code returned by
992 * getInlineInvocation for each server node separately.
993 *
994 * @param customOptions PrebootRecordOptions that override the defaults
995 * @returns Generated inline preboot code with just functions definitions
996 * to be used separately
997 */
998 function getInlineDefinition(customOptions) {
999 var opts = assign({}, defaultOptions, customOptions);
1000 // safety check to make sure options passed in are valid
1001 validateOptions(opts);
1002 var scriptCode = getEventRecorderCode();
1003 var optsStr = stringifyWithFunctions(opts);
1004 // wrap inline preboot code with a self executing function in order to create scope
1005 var initAllStr = initAll.toString();
1006 return "var " + initFunctionName + " = (function() {\n " + scriptCode + "\n return (" + initAllStr.replace('common_1.', '') + ")(" + optsStr + ");\n })();";
1007 }
1008 /**
1009 * Used by the server side version of preboot. The main purpose is to get the
1010 * inline code that can be inserted into the server view.
1011 * Invokes the prebootInit function defined in getInlineDefinition with proper
1012 * parameters. Each appRoot should get a separate inlined code from a separate
1013 * call to getInlineInvocation but only one inlined code from getInlineDefinition.
1014 *
1015 * @returns Generated inline preboot code with just invocations of functions from
1016 * getInlineDefinition
1017 */
1018 function getInlineInvocation() {
1019 return initFunctionName + "();";
1020 }
1021 /**
1022 * Throw an error if issues with any options
1023 * @param opts
1024 */
1025 function validateOptions(opts) {
1026 if (!opts.appRoot || !opts.appRoot.length) {
1027 throw new Error('The appRoot is missing from preboot options. ' +
1028 'This is needed to find the root of your application. ' +
1029 'Set this value in the preboot options to be a selector for the root element of your app.');
1030 }
1031 }
1032 /**
1033 * Object.assign() is not fully supporting in TypeScript, so
1034 * this is just a simple implementation of it
1035 *
1036 * @param target The target object
1037 * @param optionSets Any number of addition objects that are added on top of the
1038 * target
1039 * @returns A new object that contains all the merged values
1040 */
1041 function assign(target) {
1042 var optionSets = [];
1043 for (var _i = 1; _i < arguments.length; _i++) {
1044 optionSets[_i - 1] = arguments[_i];
1045 }
1046 if (target === undefined || target === null) {
1047 throw new TypeError('Cannot convert undefined or null to object');
1048 }
1049 var output = Object(target);
1050 for (var index = 0; index < optionSets.length; index++) {
1051 var source = optionSets[index];
1052 if (source !== undefined && source !== null) {
1053 for (var nextKey in source) {
1054 if (source.hasOwnProperty && source.hasOwnProperty(nextKey)) {
1055 output[nextKey] = source[nextKey];
1056 }
1057 }
1058 }
1059 }
1060 return output;
1061 }
1062 /**
1063 * Stringify an object and include functions. This is needed since we are
1064 * letting users pass in options that include custom functions for things like
1065 * the freeze handler or action when an event occurs
1066 *
1067 * @param obj This is the object you want to stringify that includes some
1068 * functions
1069 * @returns The stringified version of an object
1070 */
1071 function stringifyWithFunctions(obj) {
1072 var FUNC_START = 'START_FUNCTION_HERE';
1073 var FUNC_STOP = 'STOP_FUNCTION_HERE';
1074 // first stringify except mark off functions with markers
1075 var str = JSON.stringify(obj, function (_key, value) {
1076 // if the value is a function, we want to wrap it with markers
1077 if (!!(value && value.constructor && value.call && value.apply)) {
1078 return FUNC_START + value.toString() + FUNC_STOP;
1079 }
1080 else {
1081 return value;
1082 }
1083 });
1084 // now we use the markers to replace function strings with actual functions
1085 var startFuncIdx = str.indexOf(FUNC_START);
1086 var stopFuncIdx;
1087 var fn;
1088 while (startFuncIdx >= 0) {
1089 stopFuncIdx = str.indexOf(FUNC_STOP);
1090 // pull string out
1091 fn = str.substring(startFuncIdx + FUNC_START.length, stopFuncIdx);
1092 fn = fn.replace(/\\n/g, '\n');
1093 str = str.substring(0, startFuncIdx - 1) + fn +
1094 str.substring(stopFuncIdx + FUNC_STOP.length + 1);
1095 startFuncIdx = str.indexOf(FUNC_START);
1096 }
1097 return str;
1098 }
1099
1100 /**
1101 * @license
1102 * Copyright Google LLC All Rights Reserved.
1103 *
1104 * Use of this source code is governed by an MIT-style license that can be
1105 * found in the LICENSE file at https://angular.io/license
1106 */
1107
1108 /**
1109 * @license
1110 * Copyright Google LLC All Rights Reserved.
1111 *
1112 * Use of this source code is governed by an MIT-style license that can be
1113 * found in the LICENSE file at https://angular.io/license
1114 */
1115 var PREBOOT_SCRIPT_CLASS = 'preboot-inline-script';
1116 var PREBOOT_OPTIONS = new core.InjectionToken('PrebootOptions');
1117 function createScriptFromCode(doc, nonce, inlineCode) {
1118 var script = doc.createElement('script');
1119 if (nonce) {
1120 script.nonce = nonce;
1121 }
1122 script.className = PREBOOT_SCRIPT_CLASS;
1123 script.textContent = inlineCode;
1124 return script;
1125 }
1126 function PREBOOT_FACTORY(doc, prebootOpts, nonce, platformId, appRef, eventReplayer) {
1127 return function () {
1128 validateOptions(prebootOpts);
1129 if (common.isPlatformServer(platformId)) {
1130 var inlineCodeDefinition = getInlineDefinition(prebootOpts);
1131 var scriptWithDefinition = createScriptFromCode(doc, nonce, inlineCodeDefinition);
1132 var inlineCodeInvocation_1 = getInlineInvocation();
1133 var existingScripts = doc.getElementsByClassName(PREBOOT_SCRIPT_CLASS);
1134 // Check to see if preboot scripts are already inlined before adding them
1135 // to the DOM. If they are, update the nonce to be current.
1136 if (existingScripts.length === 0) {
1137 var baseList = [];
1138 var appRootSelectors = baseList.concat(prebootOpts.appRoot);
1139 doc.head.appendChild(scriptWithDefinition);
1140 appRootSelectors
1141 .map(function (selector) { return ({
1142 selector: selector,
1143 appRootElem: doc.querySelector(selector)
1144 }); })
1145 .forEach(function (_a) {
1146 var selector = _a.selector, appRootElem = _a.appRootElem;
1147 if (!appRootElem) {
1148 console.log("No server node found for selector: " + selector);
1149 return;
1150 }
1151 var scriptWithInvocation = createScriptFromCode(doc, nonce, inlineCodeInvocation_1);
1152 appRootElem.insertBefore(scriptWithInvocation, appRootElem.firstChild);
1153 });
1154 }
1155 else if (existingScripts.length > 0 && nonce) {
1156 existingScripts[0].nonce = nonce;
1157 }
1158 }
1159 if (common.isPlatformBrowser(platformId)) {
1160 var replay = prebootOpts.replay != null ? prebootOpts.replay : true;
1161 if (replay) {
1162 appRef.isStable
1163 .pipe(operators.filter(function (stable) { return stable; }), operators.take(1)).subscribe(function () {
1164 eventReplayer.replayAll();
1165 });
1166 }
1167 }
1168 };
1169 }
1170 var PREBOOT_PROVIDER = {
1171 provide: core.APP_BOOTSTRAP_LISTENER,
1172 useFactory: PREBOOT_FACTORY,
1173 deps: [
1174 common.DOCUMENT,
1175 PREBOOT_OPTIONS,
1176 [new core.Optional(), new core.Inject(PREBOOT_NONCE)],
1177 core.PLATFORM_ID,
1178 core.ApplicationRef,
1179 EventReplayer,
1180 ],
1181 multi: true
1182 };
1183
1184 /**
1185 * @license
1186 * Copyright Google LLC All Rights Reserved.
1187 *
1188 * Use of this source code is governed by an MIT-style license that can be
1189 * found in the LICENSE file at https://angular.io/license
1190 */
1191 var PrebootModule = /** @class */ (function () {
1192 function PrebootModule() {
1193 }
1194 PrebootModule.withConfig = function (opts) {
1195 return {
1196 ngModule: PrebootModule,
1197 providers: [{ provide: PREBOOT_OPTIONS, useValue: opts }]
1198 };
1199 };
1200 return PrebootModule;
1201 }());
1202 PrebootModule.decorators = [
1203 { type: core.NgModule, args: [{
1204 providers: [EventReplayer, PREBOOT_PROVIDER]
1205 },] }
1206 ];
1207
1208 /**
1209 * @license
1210 * Copyright Google LLC All Rights Reserved.
1211 *
1212 * Use of this source code is governed by an MIT-style license that can be
1213 * found in the LICENSE file at https://angular.io/license
1214 */
1215
1216 /**
1217 * Generated bundle index. Do not edit.
1218 */
1219
1220 exports.EventReplayer = EventReplayer;
1221 exports.PREBOOT_NONCE = PREBOOT_NONCE;
1222 exports.PrebootModule = PrebootModule;
1223 exports._window = _window;
1224 exports.assign = assign;
1225 exports.createBuffer = createBuffer;
1226 exports.createListenHandler = createListenHandler;
1227 exports.createOverlay = createOverlay;
1228 exports.defaultOptions = defaultOptions;
1229 exports.getAppRoot = getAppRoot;
1230 exports.getEventRecorderCode = getEventRecorderCode;
1231 exports.getInlineDefinition = getInlineDefinition;
1232 exports.getInlineInvocation = getInlineInvocation;
1233 exports.getNodeKeyForPreboot = getNodeKeyForPreboot;
1234 exports.getSelection = getSelection;
1235 exports.handleEvents = handleEvents;
1236 exports.initAll = initAll;
1237 exports.initFunctionName = initFunctionName;
1238 exports.start = start;
1239 exports.stringifyWithFunctions = stringifyWithFunctions;
1240 exports.validateOptions = validateOptions;
1241 exports.ɵa = PREBOOT_OPTIONS;
1242 exports.ɵb = PREBOOT_FACTORY;
1243 exports.ɵc = PREBOOT_PROVIDER;
1244
1245 Object.defineProperty(exports, '__esModule', { value: true });
1246
1247})));
1248//# sourceMappingURL=preboot.umd.js.map