Contents
About this report
Report parameters
Contexts
No contexts were selected, so all contexts were included by default.
Sites
The following sites were included:
- http://localhost:4200
(If no sites were selected, all sites were included by default.)
An included site must also be within one of the included contexts for its data to be included in the report.
Risk levels
Included: Hoch, Mittel, Gering, Informational
Excluded: None
Confidence levels
Included: User Confirmed, Hoch, Mittel, Gering
Excluded: User Confirmed, Hoch, Mittel, Gering, Falsch Positives Ergebnis
Summaries
Alert counts by risk and confidence
Confidence | ||||||
---|---|---|---|---|---|---|
User Confirmed | Hoch | Mittel | Gering | Total | ||
Risk | Hoch | 0 (0,0 %) |
0 (0,0 %) |
0 (0,0 %) |
1 (10,0 %) |
1 (10,0 %) |
Mittel | 0 (0,0 %) |
2 (20,0 %) |
3 (30,0 %) |
0 (0,0 %) |
5 (50,0 %) |
|
Gering | 0 (0,0 %) |
0 (0,0 %) |
2 (20,0 %) |
0 (0,0 %) |
2 (20,0 %) |
|
Informational | 0 (0,0 %) |
0 (0,0 %) |
1 (10,0 %) |
1 (10,0 %) |
2 (20,0 %) |
|
Total | 0 (0,0 %) |
2 (20,0 %) |
6 (60,0 %) |
2 (20,0 %) |
10 (100%) |
Alert counts by site and risk
Risk | |||||
---|---|---|---|---|---|
Hoch (= Hoch) |
Mittel (>= Mittel) |
Gering (>= Gering) |
Informational (>= Informational) |
||
Site | http://localhost:4200 | 1 (1) |
5 (6) |
2 (8) |
2 (10) |
Alert counts by alert type
Alert type | Risk | Count |
---|---|---|
Cloud Metadata Potentially Exposed | Hoch | 1 (10,0 %) |
CSP: Wildcard Directive | Mittel | 2 (20,0 %) |
Content Security Policy (CSP) Header Not Set | Mittel | 2 (20,0 %) |
Cross-Domain Misconfiguration | Mittel | 12 (120,0 %) |
Directory Browsing | Mittel | 1 (10,0 %) |
Missing Anti-clickjacking Header | Mittel | 2 (20,0 %) |
Server Leaks Information via "X-Powered-By" HTTP Response Header Field(s) | Gering | 12 (120,0 %) |
X-Content-Type-Options Header Missing | Gering | 10 (100,0 %) |
Information Disclosure - Suspicious Comments | Informational | 31 (310,0 %) |
Modern Web Application | Informational | 5 (50,0 %) |
Total | 10 |
Alerts
-
Risk=Hoch, Confidence=Gering (1)
-
http://localhost:4200 (1)
-
Cloud Metadata Potentially Exposed (1)
GET http://localhost:4200/latest/meta-data/
Alert tags Alert description The Cloud Metadata Attack attempts to abuse a misconfigured NGINX server in order to access the instance metadata maintained by cloud service providers such as AWS, GCP and Azure.
All of these providers provide metadata via an internal unroutable IP address '169.254.169.254' - this can be exposed by incorrectly configured NGINX servers and accessed by using this IP address in the Host header field.
Other info Based on the successful response status code cloud metadata may have been returned in the response. Check the response data to see if any cloud metadata has been returned.
The meta data returned can include information that would allow an attacker to completely compromise the system.
Request Request line and header section (216 bytes)
GET http://localhost:4200/latest/meta-data/ HTTP/1.1 Host: aws.zaproxy.org User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0 Pragma: no-cache Cache-Control: no-cache
Request body (0 bytes)
Response Status line and header section (228 bytes)
HTTP/1.1 200 OK X-Powered-By: Express Content-Type: text/html; charset=utf-8 Content-Length: 19 ETag: W/"13-OxsTL6IB85fkJxv9HO8uum0slCI" Date: Wed, 12 Jul 2023 12:19:35 GMT Connection: keep-alive Keep-Alive: timeout=5
Response body (19 bytes)
Invalid Host header
Attack aws.zaproxy.org
Solution Do not trust any user data in NGINX configs. In this case it is probably the use of the $host variable which is set from the 'Host' header and can be controlled by an attacker.
-
-
-
Risk=Mittel, Confidence=Hoch (2)
-
http://localhost:4200 (2)
-
CSP: Wildcard Directive (1)
GET http://localhost:4200/sitemap.xml
Alert tags Alert description Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks. Including (but not limited to) Cross Site Scripting (XSS), and data injection attacks. These attacks are used for everything from data theft to site defacement or distribution of malware. CSP provides a set of standard HTTP headers that allow website owners to declare approved sources of content that browsers should be allowed to load on that page — covered types are JavaScript, CSS, HTML frames, fonts, images and embeddable objects such as Java applets, ActiveX, audio and video files.
Other info The following directives either allow wildcard sources (or ancestors), are not defined, or are overly broadly defined:
frame-ancestors, form-action
The directive(s): frame-ancestors, form-action are among the directives that do not fallback to default-src, missing/excluding them is the same as allowing anything.
Request Request line and header section (209 bytes)
GET http://localhost:4200/sitemap.xml HTTP/1.1 Host: localhost:4200 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0 Pragma: no-cache Cache-Control: no-cache
Request body (0 bytes)
Response Status line and header section (304 bytes)
HTTP/1.1 404 Not Found X-Powered-By: Express Access-Control-Allow-Origin: * Content-Security-Policy: default-src 'none' X-Content-Type-Options: nosniff Content-Type: text/html; charset=utf-8 Content-Length: 150 Date: Wed, 12 Jul 2023 12:17:59 GMT Connection: keep-alive Keep-Alive: timeout=5
Response body (150 bytes)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>Cannot GET /sitemap.xml</pre> </body> </html>
Parameter Content-Security-Policy
Evidence default-src 'none'
Solution Ensure that your web server, application server, load balancer, etc. is properly configured to set the Content-Security-Policy header.
-
Content Security Policy (CSP) Header Not Set (1)
GET http://localhost:4200
Alert tags Alert description Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement or distribution of malware. CSP provides a set of standard HTTP headers that allow website owners to declare approved sources of content that browsers should be allowed to load on that page — covered types are JavaScript, CSS, HTML frames, fonts, images and embeddable objects such as Java applets, ActiveX, audio and video files.
Request Request line and header section (197 bytes)
GET http://localhost:4200 HTTP/1.1 Host: localhost:4200 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0 Pragma: no-cache Cache-Control: no-cache
Request body (0 bytes)
Response Status line and header section (284 bytes)
HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: text/html; charset=utf-8 Accept-Ranges: bytes Content-Length: 605 ETag: W/"25d-UpBapEnLJL784/b7WXkIvMJx+u0" Date: Wed, 12 Jul 2023 12:17:59 GMT Connection: keep-alive Keep-Alive: timeout=5
Response body (605 bytes)
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Mindmap3d</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="stylesheet" href="styles.css"></head> <body> <app-root></app-root> <script src="runtime.js" type="module"></script><script src="polyfills.js" type="module"></script><script src="styles.js" defer></script><script src="scripts.js" defer></script><script src="vendor.js" type="module"></script><script src="main.js" type="module"></script></body> </html>
Solution Ensure that your web server, application server, load balancer, etc. is configured to set the Content-Security-Policy header, to achieve optimal browser support: "Content-Security-Policy" for Chrome 25+, Firefox 23+ and Safari 7+, "X-Content-Security-Policy" for Firefox 4.0+ and Internet Explorer 10+, and "X-WebKit-CSP" for Chrome 14+ and Safari 6+.
-
-
-
Risk=Mittel, Confidence=Mittel (3)
-
http://localhost:4200 (3)
-
Cross-Domain Misconfiguration (1)
GET http://localhost:4200/sitemap.xml
Alert tags Alert description Web browser data loading may be possible, due to a Cross Origin Resource Sharing (CORS) misconfiguration on the web server
Other info The CORS misconfiguration on the web server permits cross-domain read requests from arbitrary third party domains, using unauthenticated APIs on this domain. Web browser implementations do not permit arbitrary third parties to read the response from authenticated APIs, however. This reduces the risk somewhat. This misconfiguration could be used by an attacker to access data that is available in an unauthenticated manner, but which uses some other form of security, such as IP address white-listing.
Request Request line and header section (209 bytes)
GET http://localhost:4200/sitemap.xml HTTP/1.1 Host: localhost:4200 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0 Pragma: no-cache Cache-Control: no-cache
Request body (0 bytes)
Response Status line and header section (304 bytes)
HTTP/1.1 404 Not Found X-Powered-By: Express Access-Control-Allow-Origin: * Content-Security-Policy: default-src 'none' X-Content-Type-Options: nosniff Content-Type: text/html; charset=utf-8 Content-Length: 150 Date: Wed, 12 Jul 2023 12:17:59 GMT Connection: keep-alive Keep-Alive: timeout=5
Response body (150 bytes)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>Cannot GET /sitemap.xml</pre> </body> </html>
Evidence Access-Control-Allow-Origin: *
Solution Ensure that sensitive data is not available in an unauthenticated manner (using IP address white-listing, for instance).
Configure the "Access-Control-Allow-Origin" HTTP header to a more restrictive set of domains, or remove all CORS headers entirely, to allow the web browser to enforce the Same Origin Policy (SOP) in a more restrictive manner.
-
Directory Browsing (1)
GET http://localhost:4200/vendor.js/
Alert tags Alert description It is possible to view the directory listing. Directory listing may reveal hidden scripts, include files, backup source files, etc. which can be accessed to read sensitive information.
Request Request line and header section (240 bytes)
GET http://localhost:4200/vendor.js/ HTTP/1.1 Host: localhost:4200 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0 Pragma: no-cache Cache-Control: no-cache Referer: http://localhost:4200
Request body (0 bytes)
Response Status line and header section (304 bytes)
HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: application/javascript; charset=utf-8 Accept-Ranges: bytes Content-Length: 5437734 ETag: W/"52f926-wh63A1AjC5xJx4A7np7NIbcg+Ds" Date: Wed, 12 Jul 2023 12:19:36 GMT Connection: keep-alive Keep-Alive: timeout=5
Response body (5437734 bytes)
(self["webpackChunkmindmap3d"] = self["webpackChunkmindmap3d"] || []).push([["vendor"],{ /***/ 7716: /*!***************************************************!*\ !*** ./node_modules/ansi-html-community/index.js ***! \***************************************************/ /***/ ((module) => { "use strict"; module.exports = ansiHTML; // Reference to https://github.com/sindresorhus/ansi-regex var _regANSI = /(?:(?:\u001b\[)|\u009b)(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])|\u001b[A-M]/; var _defColors = { reset: ['fff', '000'], // [FOREGROUD_COLOR, BACKGROUND_COLOR] black: '000', red: 'ff0000', green: '209805', yellow: 'e8bf03', blue: '0000ff', magenta: 'ff00ff', cyan: '00ffee', lightgrey: 'f0f0f0', darkgrey: '888' }; var _styles = { 30: 'black', 31: 'red', 32: 'green', 33: 'yellow', 34: 'blue', 35: 'magenta', 36: 'cyan', 37: 'lightgrey' }; var _openTags = { '1': 'font-weight:bold', // bold '2': 'opacity:0.5', // dim '3': '<i>', // italic '4': '<u>', // underscore '8': 'display:none', // hidden '9': '<del>' // delete }; var _closeTags = { '23': '</i>', // reset italic '24': '</u>', // reset underscore '29': '</del>' // reset delete }; [0, 21, 22, 27, 28, 39, 49].forEach(function (n) { _closeTags[n] = '</span>'; }); /** * Converts text with ANSI color codes to HTML markup. * @param {String} text * @returns {*} */ function ansiHTML(text) { // Returns the text if the string has no ANSI escape code. if (!_regANSI.test(text)) { return text; } // Cache opened sequence. var ansiCodes = []; // Replace with markup. var ret = text.replace(/\033\[(\d+)m/g, function (match, seq) { var ot = _openTags[seq]; if (ot) { // If current sequence has been opened, close it. if (!!~ansiCodes.indexOf(seq)) { // eslint-disable-line no-extra-boolean-cast ansiCodes.pop(); return '</span>'; } // Open tag. ansiCodes.push(seq); return ot[0] === '<' ? ot : '<span style="' + ot + ';">'; } var ct = _closeTags[seq]; if (ct) { // Pop sequence ansiCodes.pop(); return ct; } return ''; }); // Make sure tags are closed. var l = ansiCodes.length; l > 0 && (ret += Array(l + 1).join('</span>')); return ret; } /** * Customize colors. * @param {Object} colors reference to _defColors */ ansiHTML.setColors = function (colors) { if (typeof colors !== 'object') { throw new Error('`colors` parameter must be an Object.'); } var _finalColors = {}; for (var key in _defColors) { var hex = colors.hasOwnProperty(key) ? colors[key] : null; if (!hex) { _finalColors[key] = _defColors[key]; continue; } if ('reset' === key) { if (typeof hex === 'string') { hex = [hex]; } if (!Array.isArray(hex) || hex.length === 0 || hex.some(function (h) { return typeof h !== 'string'; })) { throw new Error('The value of `' + key + '` property must be an Array and each item could only be a hex string, e.g.: FF0000'); } var defHexColor = _defColors[key]; if (!hex[0]) { hex[0] = defHexColor[0]; } if (hex.length === 1 || !hex[1]) { hex = [hex[0]]; hex.push(defHexColor[1]); } hex = hex.slice(0, 2); } else if (typeof hex !== 'string') { throw new Error('The value of `' + key + '` property must be a hex string, e.g.: FF0000'); } _finalColors[key] = hex; } _setTags(_finalColors); }; /** * Reset colors. */ ansiHTML.reset = function () { _setTags(_defColors); }; /** * Expose tags, including open and close. * @type {Object} */ ansiHTML.tags = {}; if (Object.defineProperty) { Object.defineProperty(ansiHTML.tags, 'open', { get: function () { return _openTags; } }); Object.defineProperty(ansiHTML.tags, 'close', { get: function () { return _closeTags; } }); } else { ansiHTML.tags.open = _openTags; ansiHTML.tags.close = _closeTags; } function _setTags(colors) { // reset all _openTags['0'] = 'font-weight:normal;opacity:1;color:#' + colors.reset[0] + ';background:#' + colors.reset[1]; // inverse _openTags['7'] = 'color:#' + colors.reset[1] + ';background:#' + colors.reset[0]; // dark grey _openTags['90'] = 'color:#' + colors.darkgrey; for (var code in _styles) { var color = _styles[code]; var oriColor = colors[color] || '000'; _openTags[code] = 'color:#' + oriColor; code = parseInt(code); _openTags[(code + 10).toString()] = 'background:#' + oriColor; } } ansiHTML.reset(); /***/ }), /***/ 3358: /*!***************************************!*\ !*** ./node_modules/events/events.js ***! \***************************************/ /***/ ((module) => { "use strict"; // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. var R = typeof Reflect === 'object' ? Reflect : null; var ReflectApply = R && typeof R.apply === 'function' ? R.apply : function ReflectApply(target, receiver, args) { return Function.prototype.apply.call(target, receiver, args); }; var ReflectOwnKeys; if (R && typeof R.ownKeys === 'function') { ReflectOwnKeys = R.ownKeys; } else if (Object.getOwnPropertySymbols) { ReflectOwnKeys = function ReflectOwnKeys(target) { return Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)); }; } else { ReflectOwnKeys = function ReflectOwnKeys(target) { return Object.getOwnPropertyNames(target); }; } function ProcessEmitWarning(warning) { if (console && console.warn) console.warn(warning); } var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { return value !== value; }; function EventEmitter() { EventEmitter.init.call(this); } module.exports = EventEmitter; module.exports.once = once; // Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter; EventEmitter.prototype._events = undefined; EventEmitter.prototype._eventsCount = 0; EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. var defaultMaxListeners = 10; function checkListener(listener) { if (typeof listener !== 'function') { throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); } } Object.defineProperty(EventEmitter, 'defaultMaxListeners', { enumerable: true, get: function () { return defaultMaxListeners; }, set: function (arg) { if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); } defaultMaxListeners = arg; } }); EventEmitter.init = function () { if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events) { this._events = Object.create(null); this._eventsCount = 0; } this._maxListeners = this._maxListeners || undefined; }; // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); } this._maxListeners = n; return this; }; function _getMaxListeners(that) { if (that._maxListeners === undefined) return EventEmitter.defaultMaxListeners; return that._maxListeners; } EventEmitter.prototype.getMaxListeners = function getMaxListeners() { return _getMaxListeners(this); }; EventEmitter.prototype.emit = function emit(type) { var args = []; for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); var doError = type === 'error'; var events = this._events; if (events !== undefined) doError = doError && events.error === undefined;else if (!doError) return false; // If there is no 'error' event listener then throw. if (doError) { var er; if (args.length > 0) er = args[0]; if (er instanceof Error) { // Note: The comments on the `throw` lines are intentional, they show // up in Node's output if this results in an unhandled exception. throw er; // Unhandled 'error' event } // At least give some kind of context to the user var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); err.context = er; throw err; // Unhandled 'error' event } var handler = events[type]; if (handler === undefined) return false; if (typeof handler === 'function') { ReflectApply(handler, this, args); } else { var len = handler.length; var listeners = arrayClone(handler, len); for (var i = 0; i < len; ++i) ReflectApply(listeners[i], this, args); } return true; }; function _addListener(target, type, listener, prepend) { var m; var events; var existing; checkListener(listener); events = target._events; if (events === undefined) { events = target._events = Object.create(null); target._eventsCount = 0; } else { // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (events.newListener !== undefined) { target.emit('newListener', type, listener.listener ? listener.listener : listener); // Re-assign `events` because a newListener handler could have caused the // this._events to be assigned to a new object events = target._events; } existing = events[type]; } if (existing === undefined) { // Optimize the case of one listener. Don't need the extra array object. existing = events[type] = listener; ++target._eventsCount; } else { if (typeof existing === 'function') { // Adding the second element, need to change to array. existing = events[type] = prepend ? [listener, existing] : [existing, listener]; // If we've already got an array, just append. } else if (prepend) { existing.unshift(listener); } else { existing.push(listener); } // Check for listener leak m = _getMaxListeners(target); if (m > 0 && existing.length > m && !existing.warned) { existing.warned = true; // No error code for this since it is a Warning // eslint-disable-next-line no-restricted-syntax var w = new Error('Possible EventEmitter memory leak detected. ' + existing.length + ' ' + String(type) + ' listeners ' + 'added. Use emitter.setMaxListeners() to ' + 'increase limit'); w.name = 'MaxListenersExceededWarning'; w.emitter = target; w.type = type; w.count = existing.length; ProcessEmitWarning(w); } } return target; } EventEmitter.prototype.addListener = function addListener(type, listener) { return _addListener(this, type, listener, false); }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.prependListener = function prependListener(type, listener) { return _addListener(this, type, listener, true); }; function onceWrapper() { if (!this.fired) { this.target.removeListener(this.type, this.wrapFn); this.fired = true; if (arguments.length === 0) return this.listener.call(this.target); return this.listener.apply(this.target, arguments); } } function _onceWrap(target, type, listener) { var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; var wrapped = onceWrapper.bind(state); wrapped.listener = listener; state.wrapFn = wrapped; return wrapped; } EventEmitter.prototype.once = function once(type, listener) { checkListener(listener); this.on(type, _onceWrap(this, type, listener)); return this; }; EventEmitter.prototype.prependOnceListener = function prependOnceListener(type, listener) { checkListener(listener); this.prependListener(type, _onceWrap(this, type, listener)); return this; }; // Emits a 'removeListener' event if and only if the listener was removed. EventEmitter.prototype.removeListener = function removeListener(type, listener) { var list, events, position, i, originalListener; checkListener(listener); events = this._events; if (events === undefined) return this; list = events[type]; if (list === undefined) return this; if (list === listener || list.listener === listener) { if (--this._eventsCount === 0) this._events = Object.create(null);else { delete events[type]; if (events.removeListener) this.emit('removeListener', type, list.listener || listener); } } else if (typeof list !== 'function') { position = -1; for (i = list.length - 1; i >= 0; i--) { if (list[i] === listener || list[i].listener === listener) { originalListener = list[i].listener; position = i; break; } } if (position < 0) return this; if (position === 0) list.shift();else { spliceOne(list, position); } if (list.length === 1) events[type] = list[0]; if (events.removeListener !== undefined) this.emit('removeListener', type, originalListener || listener); } return this; }; EventEmitter.prototype.off = EventEmitter.prototype.removeListener; EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) { var listeners, events, i; events = this._events; if (events === undefined) return this; // not listening for removeListener, no need to emit if (events.removeListener === undefined) { if (arguments.length === 0) { this._events = Object.create(null); this._eventsCount = 0; } else if (events[type] !== undefined) { if (--this._eventsCount === 0) this._events = Object.create(null);else delete events[type]; } return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { var keys = Object.keys(events); var key; for (i = 0; i < keys.length; ++i) { key = keys[i]; if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); this._events = Object.create(null); this._eventsCount = 0; return this; } listeners = events[type]; if (typeof listeners === 'function') { this.removeListener(type, listeners); } else if (listeners !== undefined) { // LIFO order for (i = listeners.length - 1; i >= 0; i--) { this.removeListener(type, listeners[i]); } } return this; }; function _listeners(target, type, unwrap) { var events = target._events; if (events === undefined) return []; var evlistener = events[type]; if (evlistener === undefined) return []; if (typeof evlistener === 'function') return unwrap ? [evlistener.listener || evlistener] : [evlistener]; return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); } EventEmitter.prototype.listeners = function listeners(type) { return _listeners(this, type, true); }; EventEmitter.prototype.rawListeners = function rawListeners(type) { return _listeners(this, type, false); }; EventEmitter.listenerCount = function (emitter, type) { if (typeof emitter.listenerCount === 'function') { return emitter.listenerCount(type); } else { return listenerCount.call(emitter, type); } }; EventEmitter.prototype.listenerCount = listenerCount; function listenerCount(type) { var events = this._events; if (events !== undefined) { var evlistener = events[type]; if (typeof evlistener === 'function') { return 1; } else if (evlistener !== undefined) { return evlistener.length; } } return 0; } EventEmitter.prototype.eventNames = function eventNames() { return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; }; function arrayClone(arr, n) { var copy = new Array(n); for (var i = 0; i < n; ++i) copy[i] = arr[i]; return copy; } function spliceOne(list, index) { for (; index + 1 < list.length; index++) list[index] = list[index + 1]; list.pop(); } function unwrapListeners(arr) { var ret = new Array(arr.length); for (var i = 0; i < ret.length; ++i) { ret[i] = arr[i].listener || arr[i]; } return ret; } function once(emitter, name) { return new Promise(function (resolve, reject) { function errorListener(err) { emitter.removeListener(name, resolver); reject(err); } function resolver() { if (typeof emitter.removeListener === 'function') { emitter.removeListener('error', errorListener); } resolve([].slice.call(arguments)); } ; eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); if (name !== 'error') { addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); } }); } function addErrorHandlerIfEventEmitter(emitter, handler, flags) { if (typeof emitter.on === 'function') { eventTargetAgnosticAddListener(emitter, 'error', handler, flags); } } function eventTargetAgnosticAddListener(emitter, name, listener, flags) { if (typeof emitter.on === 'function') { if (flags.once) { emitter.once(name, listener); } else { emitter.on(name, listener); } } else if (typeof emitter.addEventListener === 'function') { // EventTarget does not have `error` event semantics like Node // EventEmitters, we do not listen for `error` events here. emitter.addEventListener(name, function wrapListener(arg) { // IE does not have builtin `{ once: true }` support so we // have to do it manually. if (flags.once) { emitter.removeEventListener(name, wrapListener); } listener(arg); }); } else { throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter); } } /***/ }), /***/ 4948: /*!*************************************************!*\ !*** ./node_modules/html-entities/lib/index.js ***! \*************************************************/ /***/ (function(__unused_webpack_module, exports, __webpack_require__) { "use strict"; var __assign = this && this.__assign || function () { __assign = Object.assign || function (t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", ({ value: true })); var named_references_1 = __webpack_require__(/*! ./named-references */ 1873); var numeric_unicode_map_1 = __webpack_require__(/*! ./numeric-unicode-map */ 9613); var surrogate_pairs_1 = __webpack_require__(/*! ./surrogate-pairs */ 8305); var allNamedReferences = __assign(__assign({}, named_references_1.namedReferences), { all: named_references_1.namedReferences.html5 }); var encodeRegExps = { specialChars: /[<>'"&]/g, nonAscii: /[<>'"&\u0080-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/g, nonAsciiPrintable: /[<>'"&\x01-\x08\x11-\x15\x17-\x1F\x7f-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/g, nonAsciiPrintableOnly: /[\x01-\x08\x11-\x15\x17-\x1F\x7f-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/g, extensive: /[\x01-\x0c\x0e-\x1f\x21-\x2c\x2e-\x2f\x3a-\x40\x5b-\x60\x7b-\x7d\x7f-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/g }; var defaultEncodeOptions = { mode: 'specialChars', level: 'all', numeric: 'decimal' }; /** Encodes all the necessary (specified by `level`) characters in the text */ function encode(text, _a) { var _b = _a === void 0 ? defaultEncodeOptions : _a, _c = _b.mode, mode = _c === void 0 ? 'specialChars' : _c, _d = _b.numeric, numeric = _d === void 0 ? 'decimal' : _d, _e = _b.level, level = _e === void 0 ? 'all' : _e; if (!text) { return ''; } var encodeRegExp = encodeRegExps[mode]; var references = allNamedReferences[level].characters; var isHex = numeric === 'hexadecimal'; encodeRegExp.lastIndex = 0; var _b = encodeRegExp.exec(text); var _c; if (_b) { _c = ''; var _d = 0; do { if (_d !== _b.index) { _c += text.substring(_d, _b.index); } var _e = _b[0]; var result_1 = references[_e]; if (!result_1) { var code_1 = _e.length > 1 ? surrogate_pairs_1.getCodePoint(_e, 0) : _e.charCodeAt(0); result_1 = (isHex ? '&#x' + code_1.toString(16) : '&#' + code_1) + ';'; } _c += result_1; _d = _b.index + _e.length; } while (_b = encodeRegExp.exec(text)); if (_d !== text.length) { _c += text.substring(_d); } } else { _c = text; } return _c; } exports.encode = encode; var defaultDecodeOptions = { scope: 'body', level: 'all' }; var strict = /&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);/g; var attribute = /&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+)[;=]?/g; var baseDecodeRegExps = { xml: { strict: strict, attribute: attribute, body: named_references_1.bodyRegExps.xml }, html4: { strict: strict, attribute: attribute, body: named_references_1.bodyRegExps.html4 }, html5: { strict: strict, attribute: attribute, body: named_references_1.bodyRegExps.html5 } }; var decodeRegExps = __assign(__assign({}, baseDecodeRegExps), { all: baseDecodeRegExps.html5 }); var fromCharCode = String.fromCharCode; var outOfBoundsChar = fromCharCode(65533); var defaultDecodeEntityOptions = { level: 'all' }; /** Decodes a single entity */ function decodeEntity(entity, _a) { var _b = (_a === void 0 ? defaultDecodeEntityOptions : _a).level, level = _b === void 0 ? 'all' : _b; if (!entity) { return ''; } var _b = entity; var decodeEntityLastChar_1 = entity[entity.length - 1]; if (false) {} else if (false) {} else { var decodeResultByReference_1 = allNamedReferences[level].entities[entity]; if (decodeResultByReference_1) { _b = decodeResultByReference_1; } else if (entity[0] === '&' && entity[1] === '#') { var decodeSecondChar_1 = entity[2]; var decodeCode_1 = decodeSecondChar_1 == 'x' || decodeSecondChar_1 == 'X' ? parseInt(entity.substr(3), 16) : parseInt(entity.substr(2)); _b = decodeCode_1 >= 0x10ffff ? outOfBoundsChar : decodeCode_1 > 65535 ? surrogate_pairs_1.fromCodePoint(decodeCode_1) : fromCharCode(numeric_unicode_map_1.numericUnicodeMap[decodeCode_1] || decodeCode_1); } } return _b; } exports.decodeEntity = decodeEntity; /** Decodes all entities in the text */ function decode(text, _a) { var decodeSecondChar_1 = _a === void 0 ? defaultDecodeOptions : _a, decodeCode_1 = decodeSecondChar_1.level, level = decodeCode_1 === void 0 ? 'all' : decodeCode_1, _b = decodeSecondChar_1.scope, scope = _b === void 0 ? level === 'xml' ? 'strict' : 'body' : _b; if (!text) { return ''; } var decodeRegExp = decodeRegExps[level][scope]; var references = allNamedReferences[level].entities; var isAttribute = scope === 'attribute'; var isStrict = scope === 'strict'; decodeRegExp.lastIndex = 0; var replaceMatch_1 = decodeRegExp.exec(text); var replaceResult_1; if (replaceMatch_1) { replaceResult_1 = ''; var replaceLastIndex_1 = 0; do { if (replaceLastIndex_1 !== replaceMatch_1.index) { replaceResult_1 += text.substring(replaceLastIndex_1, replaceMatch_1.index); } var replaceInput_1 = replaceMatch_1[0]; var decodeResult_1 = replaceInput_1; var decodeEntityLastChar_2 = replaceInput_1[replaceInput_1.length - 1]; if (isAttribute && decodeEntityLastChar_2 === '=') { decodeResult_1 = replaceInput_1; } else if (isStrict && decodeEntityLastChar_2 !== ';') { decodeResult_1 = replaceInput_1; } else { var decodeResultByReference_2 = references[replaceInput_1]; if (decodeResultByReference_2) { decodeResult_1 = decodeResultByReference_2; } else if (replaceInput_1[0] === '&' && replaceInput_1[1] === '#') { var decodeSecondChar_2 = replaceInput_1[2]; var decodeCode_2 = decodeSecondChar_2 == 'x' || decodeSecondChar_2 == 'X' ? parseInt(replaceInput_1.substr(3), 16) : parseInt(replaceInput_1.substr(2)); decodeResult_1 = decodeCode_2 >= 0x10ffff ? outOfBoundsChar : decodeCode_2 > 65535 ? surrogate_pairs_1.fromCodePoint(decodeCode_2) : fromCharCode(numeric_unicode_map_1.numericUnicodeMap[decodeCode_2] || decodeCode_2); } } replaceResult_1 += decodeResult_1; replaceLastIndex_1 = replaceMatch_1.index + replaceInput_1.length; } while (replaceMatch_1 = decodeRegExp.exec(text)); if (replaceLastIndex_1 !== text.length) { replaceResult_1 += text.substring(replaceLastIndex_1); } } else { replaceResult_1 = text; } return replaceResult_1; } exports.decode = decode; /***/ }), /***/ 1873: /*!************************************************************!*\ !*** ./node_modules/html-entities/lib/named-references.js ***! \************************************************************/ /***/ ((__unused_webpack_module, exports) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.bodyRegExps = { xml: /&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);?/g, html4: /∉|&(?:nbsp|iexcl|cent|pound|curren|yen|brvbar|sect|uml|copy|ordf|laquo|not|shy|reg|macr|deg|plusmn|sup2|sup3|acute|micro|para|middot|cedil|sup1|ordm|raquo|frac14|frac12|frac34|iquest|Agrave|Aacute|Acirc|Atilde|Auml|Aring|AElig|Ccedil|Egrave|Eacute|Ecirc|Euml|Igrave|Iacute|Icirc|Iuml|ETH|Ntilde|Ograve|Oacute|Ocirc|Otilde|Ouml|times|Oslash|Ugrave|Uacute|Ucirc|Uuml|Yacute|THORN|szlig|agrave|aacute|acirc|atilde|auml|aring|aelig|ccedil|egrave|eacute|ecirc|euml|igrave|iacute|icirc|iuml|eth|ntilde|ograve|oacute|ocirc|otilde|ouml|divide|oslash|ugrave|uacute|ucirc|uuml|yacute|thorn|yuml|quot|amp|lt|gt|#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);?/g, html5: /·|℗|⋇|⪧|⩺|⋗|⦕|⩼|⪆|⥸|⋗|⋛|⪌|≷|≳|⪦|⩹|⋖|⋋|⋉|⥶|⩻|⦖|◃|⊴|◂|∉|⋹̸|⋵̸|∉|⋷|⋶|∌|∌|⋾|⋽|∥|⊠|⨱|⨰|&(?:AElig|AMP|Aacute|Acirc|Agrave|Aring|Atilde|Auml|COPY|Ccedil|ETH|Eacute|Ecirc|Egrave|Euml|GT|Iacute|Icirc|Igrave|Iuml|LT|Ntilde|Oacute|Ocirc|Ograve|Oslash|Otilde|Ouml|QUOT|REG|THORN|Uacute|Ucirc|Ugrave|Uuml|Yacute|aacute|acirc|acute|aelig|agrave|amp|aring|atilde|auml|brvbar|ccedil|cedil|cent|copy|curren|deg|divide|eacute|ecirc|egrave|eth|euml|frac12|frac14|frac34|gt|iacute|icirc|iexcl|igrave|iquest|iuml|laquo|lt|macr|micro|middot|nbsp|not|ntilde|oacute|ocirc|ograve|ordf|ordm|oslash|otilde|ouml|para|plusmn|pound|quot|raquo|reg|sect|shy|sup1|sup2|sup3|szlig|thorn|times|uacute|ucirc|ugrave|uml|uuml|yacute|yen|yuml|#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);?/g }; exports.namedReferences = { xml: { entities: { "<": "<", ">": ">", """: '"', "'": "'", "&": "&" }, characters: { "<": "<", ">": ">", '"': """, "'": "'", "&": "&" } }, html4: { entities: { "'": "'", " ": " ", " ": " ", "¡": "¡", "¡": "¡", "¢": "¢", "¢": "¢", "£": "£", "£": "£", "¤": "¤", "¤": "¤", "¥": "¥", "¥": "¥", "¦": "¦", "¦": "¦", "§": "§", "§": "§", "¨": "¨", "¨": "¨", "©": "©", "©": "©", "ª": "ª", "ª": "ª", "«": "«", "«": "«", "¬": "¬", "¬": "¬", "­": "", "­": "", "®": "®", "®": "®", "¯": "¯", "¯": "¯", "°": "°", "°": "°", "±": "±", "±": "±", "²": "²", "²": "²", "³": "³", "³": "³", "´": "´", "´": "´", "µ": "µ", "µ": "µ", "¶": "¶", "¶": "¶", "·": "·", "·": "·", "¸": "¸", "¸": "¸", "¹": "¹", "¹": "¹", "º": "º", "º": "º", "»": "»", "»": "»", "¼": "¼", "¼": "¼", "½": "½", "½": "½", "¾": "¾", "¾": "¾", "¿": "¿", "¿": "¿", "À": "À", "À": "À", "Á": "Á", "Á": "Á", "Â": "Â", "Â": "Â", "Ã": "Ã", "Ã": "Ã", "Ä": "Ä", "Ä": "Ä", "Å": "Å", "Å": "Å", "Æ": "Æ", "Æ": "Æ", "Ç": "Ç", "Ç": "Ç", "È": "È", "È": "È", "É": "É", "É": "É", "Ê": "Ê", "Ê": "Ê", "Ë": "Ë", "Ë": "Ë", "Ì": "Ì", "Ì": "Ì", "Í": "Í", "Í": "Í", "Î": "Î", "Î": "Î", "Ï": "Ï", "Ï": "Ï", "Ð": "Ð", "Ð": "Ð", "Ñ": "Ñ", "Ñ": "Ñ", "Ò": "Ò", "Ò": "Ò", "Ó": "Ó", "Ó": "Ó", "Ô": "Ô", "Ô": "Ô", "Õ": "Õ", "Õ": "Õ", "Ö": "Ö", "Ö": "Ö", "×": "×", "×": "×", "Ø": "Ø", "Ø": "Ø", "Ù": "Ù", "Ù": "Ù", "Ú": "Ú", "Ú": "Ú", "Û": "Û", "Û": "Û", "Ü": "Ü", "Ü": "Ü", "Ý": "Ý", "Ý": "Ý", "Þ": "Þ", "Þ": "Þ", "ß": "ß", "ß": "ß", "à": "à", "à": "à", "á": "á", "á": "á", "â": "â", "â": "â", "ã": "ã", "ã": "ã", "ä": "ä", "ä": "ä", "å": "å", "å": "å", "æ": "æ", "æ": "æ", "ç": "ç", "ç": "ç", "è": "è", "è": "è", "é": "é", "é": "é", "ê": "ê", "ê": "ê", "ë": "ë", "ë": "ë", "ì": "ì", "ì": "ì", "í": "í", "í": "í", "î": "î", "î": "î", "ï": "ï", "ï": "ï", "ð": "ð", "ð": "ð", "ñ": "ñ", "ñ": "ñ", "ò": "ò", "ò": "ò", "ó": "ó", "ó": "ó", "ô": "ô", "ô": "ô", "õ": "õ", "õ": "õ", "ö": "ö", "ö": "ö", "÷": "÷", "÷": "÷", "ø": "ø", "ø": "ø", "ù": "ù", "ù": "ù", "ú": "ú", "ú": "ú", "û": "û", "û": "û", "ü": "ü", "ü": "ü", "ý": "ý", "ý": "ý", "þ": "þ", "þ": "þ", "ÿ": "ÿ", "ÿ": "ÿ", """: '"', """: '"', "&": "&", "&": "&", "<": "<", "<": "<", ">": ">", ">": ">", "Œ": "Œ", "œ": "œ", "Š": "Š", "š": "š", "Ÿ": "Ÿ", "ˆ": "ˆ", "˜": "˜", " ": " ", " ": " ", " ": " ", "‌": "", "‍": "", "‎": "", "‏": "", "–": "–", "—": "—", "‘": "‘", "’": "’", "‚": "‚", "“": "“", "”": "”", "„": "„", "†": "†", "‡": "‡", "‰": "‰", "‹": "‹", "›": "›", "€": "€", "ƒ": "ƒ", "Α": "Α", "Β": "Β", "Γ": "Γ", "Δ": "Δ", "Ε": "Ε", "Ζ": "Ζ", "Η": "Η", "Θ": "Θ", "Ι": "Ι", "Κ": "Κ", "Λ": "Λ", "Μ": "Μ", "Ν": "Ν", "Ξ": "Ξ", "Ο": "Ο", "Π": "Π", "Ρ": "Ρ", "Σ": "Σ", "Τ": "Τ", "Υ": "Υ", "Φ": "Φ", "Χ": "Χ", "Ψ": "Ψ", "Ω": "Ω", "α": "α", "β": "β", "γ": "γ", "δ": "δ", "ε": "ε", "ζ": "ζ", "η": "η", "θ": "θ", "ι": "ι", "κ": "κ", "λ": "λ", "μ": "μ", "ν": "ν", "ξ": "ξ", "ο": "ο", "π": "π", "ρ": "ρ", "ς": "ς", "σ": "σ", "τ": "τ", "υ": "υ", "φ": "φ", "χ": "χ", "ψ": "ψ", "ω": "ω", "ϑ": "ϑ", "ϒ": "ϒ", "ϖ": "ϖ", "•": "•", "…": "…", "′": "′", "″": "″", "‾": "‾", "⁄": "⁄", "℘": "℘", "ℑ": "ℑ", "ℜ": "ℜ", "™": "™", "ℵ": "ℵ", "←": "←", "↑": "↑", "→": "→", "↓": "↓", "↔": "↔", "↵": "↵", "⇐": "⇐", "⇑": "⇑", "⇒": "⇒", "⇓": "⇓", "⇔": "⇔", "∀": "∀", "∂": "∂", "∃": "∃", "∅": "∅", "∇": "∇", "∈": "∈", "∉": "∉", "∋": "∋", "∏": "∏", "∑": "∑", "−": "−", "∗": "∗", "√": "√", "∝": "∝", "∞": "∞", "∠": "∠", "∧": "∧", "∨": "∨", "∩": "∩", "∪": "∪", "∫": "∫", "∴": "∴", "∼": "∼", "≅": "≅", "≈": "≈", "≠": "≠", "≡": "≡", "≤": "≤", "≥": "≥", "⊂": "⊂", "⊃": "⊃", "⊄": "⊄", "⊆": "⊆", "⊇": "⊇", "⊕": "⊕", "⊗": "⊗", "⊥": "⊥", "⋅": "⋅", "⌈": "⌈", "⌉": "⌉", "⌊": "⌊", "⌋": "⌋", "⟨": "〈", "⟩": "〉", "◊": "◊", "♠": "♠", "♣": "♣", "♥": "♥", "♦": "♦" }, characters: { "'": "'", " ": " ", "¡": "¡", "¢": "¢", "£": "£", "¤": "¤", "¥": "¥", "¦": "¦", "§": "§", "¨": "¨", "©": "©", "ª": "ª", "«": "«", "¬": "¬", "": "­", "®": "®", "¯": "¯", "°": "°", "±": "±", "²": "²", "³": "³", "´": "´", "µ": "µ", "¶": "¶", "·": "·", "¸": "¸", "¹": "¹", "º": "º", "»": "»", "¼": "¼", "½": "½", "¾": "¾", "¿": "¿", "À": "À", "Á": "Á", "Â": "Â", "Ã": "Ã", "Ä": "Ä", "Å": "Å", "Æ": "Æ", "Ç": "Ç", "È": "È", "É": "É", "Ê": "Ê", "Ë": "Ë", "Ì": "Ì", "Í": "Í", "Î": "Î", "Ï": "Ï", "Ð": "Ð", "Ñ": "Ñ", "Ò": "Ò", "Ó": "Ó", "Ô": "Ô", "Õ": "Õ", "Ö": "Ö", "×": "×", "Ø": "Ø", "Ù": "Ù", "Ú": "Ú", "Û": "Û", "Ü": "Ü", "Ý": "Ý", "Þ": "Þ", "ß": "ß", "à": "à", "á": "á", "â": "â", "ã": "ã", "ä": "ä", "å": "å", "æ": "æ", "ç": "ç", "è": "è", "é": "é", "ê": "ê", "ë": "ë", "ì": "ì", "í": "í", "î": "î", "ï": "ï", "ð": "ð", "ñ": "ñ", "ò": "ò", "ó": "ó", "ô": "ô", "õ": "õ", "ö": "ö", "÷": "÷", "ø": "ø", "ù": "ù", "ú": "ú", "û": "û", "ü": "ü", "ý": "ý", "þ": "þ", "ÿ": "ÿ", '"': """, "&": "&", "<": "<", ">": ">", "Œ": "Œ", "œ": "œ", "Š": "Š", "š": "š", "Ÿ": "Ÿ", "ˆ": "ˆ", "˜": "˜", " ": " ", " ": " ", " ": " ", "": "‌", "": "‍", "": "‎", "": "‏", "–": "–", "—": "—", "‘": "‘", "’": "’", "‚": "‚", "“": "“", "”": "”", "„": "„", "†": "†", "‡": "‡", "‰": "‰", "‹": "‹", "›": "›", "€": "€", "ƒ": "ƒ", "Α": "Α", "Β": "Β", "Γ": "Γ", "Δ": "Δ", "Ε": "Ε", "Ζ": "Ζ", "Η": "Η", "Θ": "Θ", "Ι": "Ι", "Κ": "Κ", "Λ": "Λ", "Μ": "Μ", "Ν": "Ν", "Ξ": "Ξ", "Ο": "Ο", "Π": "Π", "Ρ": "Ρ", "Σ": "Σ", "Τ": "Τ", "Υ": "Υ", "Φ": "Φ", "Χ": "Χ", "Ψ": "Ψ", "Ω": "Ω", "α": "α", "β": "β", "γ": "γ", "δ": "δ", "ε": "ε", "ζ": "ζ", "η": "η", "θ": "θ", "ι": "ι", "κ": "κ", "λ": "λ", "μ": "μ", "ν": "ν", "ξ": "ξ", "ο": "ο", "π": "π", "ρ": "ρ", "ς": "ς", "σ": "σ", "τ": "τ", "υ": "υ", "φ": "φ", "χ": "χ", "ψ": "ψ", "ω": "ω", "ϑ": "ϑ", "ϒ": "ϒ", "ϖ": "ϖ", "•": "•", "…": "…", "′": "′", "″": "″", "‾": "‾", "⁄": "⁄", "℘": "℘", "ℑ": "ℑ", "ℜ": "ℜ", "™": "™", "ℵ": "ℵ", "←": "←", "↑": "↑", "→": "→", "↓": "↓", "↔": "↔", "↵": "↵", "⇐": "⇐", "⇑": "⇑", "⇒": "⇒", "⇓": "⇓", "⇔": "⇔", "∀": "∀", "∂": "∂", "∃": "∃", "∅": "∅", "∇": "∇", "∈": "∈", "∉": "∉", "∋": "∋", "∏": "∏", "∑": "∑", "−": "−", "∗": "∗", "√": "√", "∝": "∝", "∞": "∞", "∠": "∠", "∧": "∧", "∨": "∨", "∩": "∩", "∪": "∪", "∫": "∫", "∴": "∴", "∼": "∼", "≅": "≅", "≈": "≈", "≠": "≠", "≡": "≡", "≤": "≤", "≥": "≥", "⊂": "⊂", "⊃": "⊃", "⊄": "⊄", "⊆": "⊆", "⊇": "⊇", "⊕": "⊕", "⊗": "⊗", "⊥": "⊥", "⋅": "⋅", "⌈": "⌈", "⌉": "⌉", "⌊": "⌊", "⌋": "⌋", "〈": "⟨", "〉": "⟩", "◊": "◊", "♠": "♠", "♣": "♣", "♥": "♥", "♦": "♦" } }, html5: { entities: { "Æ": "Æ", "Æ": "Æ", "&": "&", "&": "&", "Á": "Á", "Á": "Á", "Ă": "Ă", "Â": "Â", "Â": "Â", "А": "А", "𝔄": "𝔄", "À": "À", "À": "À", "Α": "Α", "Ā": "Ā", "⩓": "⩓", "Ą": "Ą", "𝔸": "𝔸", "⁡": "", "Å": "Å", "Å": "Å", "𝒜": "𝒜", "≔": "≔", "Ã": "Ã", "Ã": "Ã", "Ä": "Ä", "Ä": "Ä", "∖": "∖", "⫧": "⫧", "⌆": "⌆", "Б": "Б", "∵": "∵", "ℬ": "ℬ", "Β": "Β", "𝔅": "𝔅", "𝔹": "𝔹", "˘": "˘", "ℬ": "ℬ", "≎": "≎", "Ч": "Ч", "©": "©", "©": "©", "Ć": "Ć", "⋒": "⋒", "ⅅ": "ⅅ", "ℭ": "ℭ", "Č": "Č", "Ç": "Ç", "Ç": "Ç", "Ĉ": "Ĉ", "∰": "∰", "Ċ": "Ċ", "¸": "¸", "·": "·", "ℭ": "ℭ", "Χ": "Χ", "⊙": "⊙", "⊖": "⊖", "⊕": "⊕", "⊗": "⊗", "∲": "∲", "”": "”", "’": "’", "∷": "∷", "⩴": "⩴", "≡": "≡", "∯": "∯", "∮": "∮", "ℂ": "ℂ", "∐": "∐", "∳": "∳", "⨯": "⨯", "𝒞": "𝒞", "⋓": "⋓", "≍": "≍", "ⅅ": "ⅅ", "⤑": "⤑", "Ђ": "Ђ", "Ѕ": "Ѕ", "Џ": "Џ", "‡": "‡", "↡": "↡", "⫤": "⫤", "Ď": "Ď", "Д": "Д", "∇": "∇", "Δ": "Δ", "𝔇": "𝔇", "´": "´", "˙": "˙", "˝": "˝", "`": "`", "˜": "˜", "⋄": "⋄", "ⅆ": "ⅆ", "𝔻": "𝔻", "¨": "¨", "⃜": "⃜", "≐": "≐", "∯": "∯", "¨": "¨", "⇓": "⇓", "⇐": "⇐", "⇔": "⇔", "⫤": "⫤", "⟸": "⟸", "⟺": "⟺", "⟹": "⟹", "⇒": "⇒", "⊨": "⊨", "⇑": "⇑", "⇕": "⇕", "∥": "∥", "↓": "↓", "⤓": "⤓", "⇵": "⇵", "̑": "̑", "⥐": "⥐", "⥞": "⥞", "↽": "↽", "⥖": "⥖", "⥟": "⥟", "⇁": "⇁", "⥗": "⥗", "⊤": "⊤", "↧": "↧", "⇓": "⇓", "𝒟": "𝒟", "Đ": "Đ", "Ŋ": "Ŋ", "Ð": "Ð", "Ð": "Ð", "É": "É", "É": "É", "Ě": "Ě", "Ê": "Ê", "Ê": "Ê", "Э": "Э", "Ė": "Ė", "𝔈": "𝔈", "È": "È", "È": "È", "∈": "∈", "Ē": "Ē", "◻": "◻", "▫": "▫", "Ę": "Ę", "𝔼": "𝔼", "Ε": "Ε", "⩵": "⩵", "≂": "≂", "⇌": "⇌", "ℰ": "ℰ", "⩳": "⩳", "Η": "Η", "Ë": "Ë", "Ë": "Ë", "∃": "∃", "ⅇ": "ⅇ", "Ф": "Ф", "𝔉": "𝔉", "◼": "◼", "▪": "▪", "𝔽": "𝔽", "∀": "∀", "ℱ": "ℱ", "ℱ": "ℱ", "Ѓ": "Ѓ", ">": ">", ">": ">", "Γ": "Γ", "Ϝ": "Ϝ", "Ğ": "Ğ", "Ģ": "Ģ", "Ĝ": "Ĝ", "Г": "Г", "Ġ": "Ġ", "𝔊": "𝔊", "⋙": "⋙", "𝔾": "𝔾", "≥": "≥", "⋛": "⋛", "≧": "≧", "⪢": "⪢", "≷": "≷", "⩾": "⩾", "≳": "≳", "𝒢": "𝒢", "≫": "≫", "Ъ": "Ъ", "ˇ": "ˇ", "^": "^", "Ĥ": "Ĥ", "ℌ": "ℌ", "ℋ": "ℋ", "ℍ": "ℍ", "─": "─", "ℋ": "ℋ", "Ħ": "Ħ", "≎": "≎", "≏": "≏", "Е": "Е", "IJ": "IJ", "Ё": "Ё", "Í": "Í", "Í": "Í", "Î": "Î", "Î": "Î", "И": "И", "İ": "İ", "ℑ": "ℑ", "Ì": "Ì", "Ì": "Ì", "ℑ": "ℑ", "Ī": "Ī", "ⅈ": "ⅈ", "⇒": "⇒", "∬": "∬", "∫": "∫", "⋂": "⋂", "⁣": "", "⁢": "", "Į": "Į", "𝕀": "𝕀", "Ι": "Ι", "ℐ": "ℐ", "Ĩ": "Ĩ", "І": "І", "Ï": "Ï", "Ï": "Ï", "Ĵ": "Ĵ", "Й": "Й", "𝔍": "𝔍", "𝕁": "𝕁", "𝒥": "𝒥", "Ј": "Ј", "Є": "Є", "Х": "Х", "Ќ": "Ќ", "Κ": "Κ", "Ķ": "Ķ", "К": "К", "𝔎": "𝔎", "𝕂": "𝕂", "𝒦": "𝒦", "Љ": "Љ", "<": "<", "<": "<", "Ĺ": "Ĺ", "Λ": "Λ", "⟪": "⟪", "ℒ": "ℒ", "↞": "↞", "Ľ": "Ľ", "Ļ": "Ļ", "Л": "Л", "⟨": "⟨", "←": "←", "⇤": "⇤", "⇆": "⇆", "⌈": "⌈", "⟦": "⟦", "⥡": "⥡", "⇃": "⇃", "⥙": "⥙", "⌊": "⌊", "↔": "↔", "⥎": "⥎", "⊣": "⊣", "↤": "↤", "⥚": "⥚", "⊲": "⊲", "⧏": "⧏", "⊴": "⊴", "⥑": "⥑", "⥠": "⥠", "↿": "↿", "⥘": "⥘", "↼": "↼", "⥒": "⥒", "⇐": "⇐", "⇔": "⇔", "⋚": "⋚", "≦": "≦", "≶": "≶", "⪡": "⪡", "⩽": "⩽", "≲": "≲", "𝔏": "𝔏", "⋘": "⋘", "⇚": "⇚", "Ŀ": "Ŀ", "⟵": "⟵", "⟷": "⟷", "⟶": "⟶", "⟸": "⟸", "⟺": "⟺", "⟹": "⟹", "𝕃": "𝕃", "↙": "↙", "↘": "↘", "ℒ": "ℒ", "↰": "↰", "Ł": "Ł", "≪": "≪", "⤅": "⤅", "М": "М", " ": " ", "ℳ": "ℳ", "𝔐": "𝔐", "∓": "∓", "𝕄": "𝕄", "ℳ": "ℳ", "Μ": "Μ", "Њ": "Њ", "Ń": "Ń", "Ň": "Ň", "Ņ": "Ņ", "Н": "Н", "​": "", "​": "", "​": "", "​": "", "≫": "≫", "≪": "≪", "
": "\n", "𝔑": "𝔑", "⁠": "", " ": " ", "ℕ": "ℕ", "⫬": "⫬", "≢": "≢", "≭": "≭", "∦": "∦", "∉": "∉", "≠": "≠", "≂̸": "≂̸", "∄": "∄", "≯": "≯", "≱": "≱", "≧̸": "≧̸", "≫̸": "≫̸", "≹": "≹", "⩾̸": "⩾̸", "≵": "≵", "≎̸": "≎̸", "≏̸": "≏̸", "⋪": "⋪", "⧏̸": "⧏̸", "⋬": "⋬", "≮": "≮", "≰": "≰", "≸": "≸", "≪̸": "≪̸", "⩽̸": "⩽̸", "≴": "≴", "⪢̸": "⪢̸", "⪡̸": "⪡̸", "⊀": "⊀", "⪯̸": "⪯̸", "⋠": "⋠", "∌": "∌", "⋫": "⋫", "⧐̸": "⧐̸", "⋭": "⋭", "⊏̸": "⊏̸", "⋢": "⋢", "⊐̸": "⊐̸", "⋣": "⋣", "⊂⃒": "⊂⃒", "⊈": "⊈", "⊁": "⊁", "⪰̸": "⪰̸", "⋡": "⋡", "≿̸": "≿̸", "⊃⃒": "⊃⃒", "⊉": "⊉", "≁": "≁", "≄": "≄", "≇": "≇", "≉": "≉", "∤": "∤", "𝒩": "𝒩", "Ñ": "Ñ", "Ñ": "Ñ", "Ν": "Ν", "Œ": "Œ", "Ó": "Ó", "Ó": "Ó", "Ô": "Ô", "Ô": "Ô", "О": "О", "Ő": "Ő", "𝔒": "𝔒", "Ò": "Ò", "Ò": "Ò", "Ō": "Ō", "Ω": "Ω", "Ο": "Ο", "𝕆": "𝕆", "“": "“", "‘": "‘", "⩔": "⩔", "𝒪": "𝒪", "Ø": "Ø", "Ø": "Ø", "Õ": "Õ", "Õ": "Õ", "⨷": "⨷", "Ö": "Ö", "Ö": "Ö", "‾": "‾", "⏞": "⏞", "⎴": "⎴", "⏜": "⏜", "∂": "∂", "П": "П", "𝔓": "𝔓", "Φ": "Φ", "Π": "Π", "±": "±", "ℌ": "ℌ", "ℙ": "ℙ", "⪻": "⪻", "≺": "≺", "⪯": "⪯", "≼": "≼", "≾": "≾", "″": "″", "∏": "∏", "∷": "∷", "∝": "∝", "𝒫": "𝒫", "Ψ": "Ψ", """: '"', """: '"', "𝔔": "𝔔", "ℚ": "ℚ", "𝒬": "𝒬", "⤐": "⤐", "®": "®", "®": "®", "Ŕ": "Ŕ", "⟫": "⟫", "↠": "↠", "⤖": "⤖", "Ř": "Ř", "Ŗ": "Ŗ", "Р": "Р", "ℜ": "ℜ", "∋": "∋", "⇋": "⇋", "⥯": "⥯", "ℜ": "ℜ", "Ρ": "Ρ", "⟩": "⟩", "→": "→", "⇥": "⇥", "⇄": "⇄", "⌉": "⌉", "⟧": "⟧", "⥝": "⥝", "⇂": "⇂", "⥕": "⥕", "⌋": "⌋", "⊢": "⊢", "↦": "↦", "⥛": "⥛", "⊳": "⊳", "⧐": "⧐", "⊵": "⊵", "⥏": "⥏", "⥜": "⥜", "↾": "↾", "⥔": "⥔", "⇀": "⇀", "⥓": "⥓", "⇒": "⇒", "ℝ": "ℝ", "⥰": "⥰", "⇛": "⇛", "ℛ": "ℛ", "↱": "↱", "⧴": "⧴", "Щ": "Щ", "Ш": "Ш", "Ь": "Ь", "Ś": "Ś", "⪼": "⪼", "Š": "Š", "Ş": "Ş", "Ŝ": "Ŝ", "С": "С", "𝔖": "𝔖", "↓": "↓", "←": "←", "→": "→", "↑": "↑", "Σ": "Σ", "∘": "∘", "𝕊": "𝕊", "√": "√", "□": "□", "⊓": "⊓", "⊏": "⊏", "⊑": "⊑", "⊐": "⊐", "⊒": "⊒", "⊔": "⊔", "𝒮": "𝒮", "⋆": "⋆", "⋐": "⋐", "⋐": "⋐", "⊆": "⊆", "≻": "≻", "⪰": "⪰", "≽": "≽", "≿": "≿", "∋": "∋", "∑": "∑", "⋑": "⋑", "⊃": "⊃", "⊇": "⊇", "⋑": "⋑", "Þ": "Þ", "Þ": "Þ", "™": "™", "Ћ": "Ћ", "Ц": "Ц", "	": "\t", "Τ": "Τ", "Ť": "Ť", "Ţ": "Ţ", "Т": "Т", "𝔗": "𝔗", "∴": "∴", "Θ": "Θ", "  ": " ", " ": " ", "∼": "∼", "≃": "≃", "≅": "≅", "≈": "≈", "𝕋": "𝕋", "⃛": "⃛", "𝒯": "𝒯", "Ŧ": "Ŧ", "Ú": "Ú", "Ú": "Ú", "↟": "↟", "⥉": "⥉", "Ў": "Ў", "Ŭ": "Ŭ", "Û": "Û", "Û": "Û", "У": "У", "Ű": "Ű", "𝔘": "𝔘", "Ù": "Ù", "Ù": "Ù", "Ū": "Ū", "_": "_", "⏟": "⏟", "⎵": "⎵", "⏝": "⏝", "⋃": "⋃", "⊎": "⊎", "Ų": "Ų", "𝕌": "𝕌", "↑": "↑", "⤒": "⤒", "⇅": "⇅", "↕": "↕", "⥮": "⥮", "⊥": "⊥", "↥": "↥", "⇑": "⇑", "⇕": "⇕", "↖": "↖", "↗": "↗", "ϒ": "ϒ", "Υ": "Υ", "Ů": "Ů", "𝒰": "𝒰", "Ũ": "Ũ", "Ü": "Ü", "Ü": "Ü", "⊫": "⊫", "⫫": "⫫", "В": "В", "⊩": "⊩", "⫦": "⫦", "⋁": "⋁", "‖": "‖", "‖": "‖", "∣": "∣", "|": "|", "❘": "❘", "≀": "≀", " ": " ", "𝔙": "𝔙", "𝕍": "𝕍", "𝒱": "𝒱", "⊪": "⊪", "Ŵ": "Ŵ", "⋀": "⋀", "𝔚": "𝔚", "𝕎": "𝕎", "𝒲": "𝒲", "𝔛": "𝔛", "Ξ": "Ξ", "𝕏": "𝕏", "𝒳": "𝒳", "Я": "Я", "Ї": "Ї", "Ю": "Ю", "Ý": "Ý", "Ý": "Ý", "Ŷ": "Ŷ", "Ы": "Ы", "𝔜": "𝔜", "𝕐": "𝕐", "𝒴": "𝒴", "Ÿ": "Ÿ", "Ж": "Ж", "Ź": "Ź", "Ž": "Ž", "З": "З", "Ż": "Ż", "​": "", "Ζ": "Ζ", "ℨ": "ℨ", "ℤ": "ℤ", "𝒵": "𝒵", "á": "á", "á": "á", "ă": "ă", "∾": "∾", "∾̳": "∾̳", "∿": "∿", "â": "â", "â": "â", "´": "´", "´": "´", "а": "а", "æ": "æ", "æ": "æ", "⁡": "", "𝔞": "𝔞", "à": "à", "à": "à", "ℵ": "ℵ", "ℵ": "ℵ", "α": "α", "ā": "ā", "⨿": "⨿", "&": "&", "&": "&", "∧": "∧", "⩕": "⩕", "⩜": "⩜", "⩘": "⩘", "⩚": "⩚", "∠": "∠", "⦤": "⦤", "∠": "∠", "∡": "∡", "⦨": "⦨", "⦩": "⦩", "⦪": "⦪", "⦫": "⦫", "⦬": "⦬", "⦭": "⦭", "⦮": "⦮", "⦯": "⦯", "∟": "∟", "⊾": "⊾", "⦝": "⦝", "∢": "∢", "Å": "Å", "⍼": "⍼", "ą": "ą", "𝕒": "𝕒", "≈": "≈", "⩰": "⩰", "⩯": "⩯", "≊": "≊", "≋": "≋", "'": "'", "≈": "≈", "≊": "≊", "å": "å", "å": "å", "𝒶": "𝒶", "*": "*", "≈": "≈", "≍": "≍", "ã": "ã", "ã": "ã", "ä": "ä", "ä": "ä", "∳": "∳", "⨑": "⨑", "⫭": "⫭", "≌": "≌", "϶": "϶", "‵": "‵", "∽": "∽", "⋍": "⋍", "⊽": "⊽", "⌅": "⌅", "⌅": "⌅", "⎵": "⎵", "⎶": "⎶", "≌": "≌", "б": "б", "„": "„", "∵": "∵", "∵": "∵", "⦰": "⦰", "϶": "϶", "ℬ": "ℬ", "β": "β", "ℶ": "ℶ", "≬": "≬", "𝔟": "𝔟", "⋂": "⋂", "◯": "◯", "⋃": "⋃", "⨀": "⨀", "⨁": "⨁", "⨂": "⨂", "⨆": "⨆", "★": "★", "▽": "▽", "△": "△", "⨄": "⨄", "⋁": "⋁", "⋀": "⋀", "⤍": "⤍", "⧫": "⧫", "▪": "▪", "▴": "▴", "▾": "▾", "◂": "◂", "▸": "▸", "␣": "␣", "▒": "▒", "░": "░", "▓": "▓", "█": "█", "=⃥": "=⃥", "≡⃥": "≡⃥", "⌐": "⌐", "𝕓": "𝕓", "⊥": "⊥", "⊥": "⊥", "⋈": "⋈", "╗": "╗", "╔": "╔", "╖": "╖", "╓": "╓", "═": "═", "╦": "╦", "╩": "╩", "╤": "╤", "╧": "╧", "╝": "╝", "╚": "╚", "╜": "╜", "╙": "╙", "║": "║", "╬": "╬", "╣": "╣", "╠": "╠", "╫": "╫", "╢": "╢", "╟": "╟", "⧉": "⧉", "╕": "╕", "╒": "╒", "┐": "┐", "┌": "┌", "─": "─", "╥": "╥", "╨": "╨", "┬": "┬", "┴": "┴", "⊟": "⊟", "⊞": "⊞", "⊠": "⊠", "╛": "╛", "╘": "╘", "┘": "┘", "└": "└", "│": "│", "╪": "╪", "╡": "╡", "╞": "╞", "┼": "┼", "┤": "┤", "├": "├", "‵": "‵", "˘": "˘", "¦": "¦", "¦": "¦", "𝒷": "𝒷", "⁏": "⁏", "∽": "∽", "⋍": "⋍", "\": "\\", "⧅": "⧅", "⟈": "⟈", "•": "•", "•": "•", "≎": "≎", "⪮": "⪮", "≏": "≏", "≏": "≏", "ć": "ć", "∩": "∩", "⩄": "⩄", "⩉": "⩉", "⩋": "⩋", "⩇": "⩇", "⩀": "⩀", "∩︀": "∩︀", "⁁": "⁁", "ˇ": "ˇ", "⩍": "⩍", "č": "č", "ç": "ç", "ç": "ç", "ĉ": "ĉ", "⩌": "⩌", "⩐": "⩐", "ċ": "ċ", "¸": "¸", "¸": "¸", "⦲": "⦲", "¢": "¢", "¢": "¢", "·": "·", "𝔠": "𝔠", "ч": "ч", "✓": "✓", "✓": "✓", "χ": "χ", "○": "○", "⧃": "⧃", "ˆ": "ˆ", "≗": "≗", "↺": "↺", "↻": "↻", "®": "®", "Ⓢ": "Ⓢ", "⊛": "⊛", "⊚": "⊚", "⊝": "⊝", "≗": "≗", "⨐": "⨐", "⫯": "⫯", "⧂": "⧂", "♣": "♣", "♣": "♣", ":": ":", "≔": "≔", "≔": "≔", ",": ",", "@": "@", "∁": "∁", "∘": "∘", "∁": "∁", "ℂ": "ℂ", "≅": "≅", "⩭": "⩭", "∮": "∮", "𝕔": "𝕔", "∐": "∐", "©": "©", "©": "©", "℗": "℗", "↵": "↵", "✗": "✗", "𝒸": "𝒸", "⫏": "⫏", "⫑": "⫑", "⫐": "⫐", "⫒": "⫒", "⋯": "⋯", "⤸": "⤸", "⤵": "⤵", "⋞": "⋞", "⋟": "⋟", "↶": "↶", "⤽": "⤽", "∪": "∪", "⩈": "⩈", "⩆": "⩆", "⩊": "⩊", "⊍": "⊍", "⩅": "⩅", "∪︀": "∪︀", "↷": "↷", "⤼": "⤼", "⋞": "⋞", "⋟": "⋟", "⋎": "⋎", "⋏": "⋏", "¤": "¤", "¤": "¤", "↶": "↶", "↷": "↷", "⋎": "⋎", "⋏": "⋏", "∲": "∲", "∱": "∱", "⌭": "⌭", "⇓": "⇓", "⥥": "⥥", "†": "†", "ℸ": "ℸ", "↓": "↓", "‐": "‐", "⊣": "⊣", "⤏": "⤏", "˝": "˝", "ď": "ď", "д": "д", "ⅆ": "ⅆ", "‡": "‡", "⇊": "⇊", "⩷": "⩷", "°": "°", "°": "°", "δ": "δ", "⦱": "⦱", "⥿": "⥿", "𝔡": "𝔡", "⇃": "⇃", "⇂": "⇂", "⋄": "⋄", "⋄": "⋄", "♦": "♦", "♦": "♦", "¨": "¨", "ϝ": "ϝ", "⋲": "⋲", "÷": "÷", "÷": "÷", "÷": "÷", "⋇": "⋇", "⋇": "⋇", "ђ": "ђ", "⌞": "⌞", "⌍": "⌍", "$": "$", "𝕕": "𝕕", "˙": "˙", "≐": "≐", "≑": "≑", "∸": "∸", "∔": "∔", "⊡": "⊡", "⌆": "⌆", "↓": "↓", "⇊": "⇊", "⇃": "⇃", "⇂": "⇂", "⤐": "⤐", "⌟": "⌟", "⌌": "⌌", "𝒹": "𝒹", "ѕ": "ѕ", "⧶": "⧶", "đ": "đ", "⋱": "⋱", "▿": "▿", "▾": "▾", "⇵": "⇵", "⥯": "⥯", "⦦": "⦦", "џ": "џ", "⟿": "⟿", "⩷": "⩷", "≑": "≑", "é": "é", "é": "é", "⩮": "⩮", "ě": "ě", "≖": "≖", "ê": "ê", "ê": "ê", "≕": "≕", "э": "э", "ė": "ė", "ⅇ": "ⅇ", "≒": "≒", "𝔢": "𝔢", "⪚": "⪚", "è": "è", "è": "è", "⪖": "⪖", "⪘": "⪘", "⪙": "⪙", "⏧": "⏧", "ℓ": "ℓ", "⪕": "⪕", "⪗": "⪗", "ē": "ē", "∅": "∅", "∅": "∅", "∅": "∅", " ": " ", " ": " ", " ": " ", "ŋ": "ŋ", " ": " ", "ę": "ę", "𝕖": "𝕖", "⋕": "⋕", "⧣": "⧣", "⩱": "⩱", "ε": "ε", "ε": "ε", "ϵ": "ϵ", "≖": "≖", "≕": "≕", "≂": "≂", "⪖": "⪖", "⪕": "⪕", "=": "=", "≟": "≟", "≡": "≡", "⩸": "⩸", "⧥": "⧥", "≓": "≓", "⥱": "⥱", "ℯ": "ℯ", "≐": "≐", "≂": "≂", "η": "η", "ð": "ð", "ð": "ð", "ë": "ë", "ë": "ë", "€": "€", "!": "!", "∃": "∃", "ℰ": "ℰ", "ⅇ": "ⅇ", "≒": "≒", "ф": "ф", "♀": "♀", "ffi": "ffi", "ff": "ff", "ffl": "ffl", "𝔣": "𝔣", "fi": "fi", "fj": "fj", "♭": "♭", "fl": "fl", "▱": "▱", "ƒ": "ƒ", "𝕗": "𝕗", "∀": "∀", "⋔": "⋔", "⫙": "⫙", "⨍": "⨍", "½": "½", "½": "½", "⅓": "⅓", "¼": "¼", "¼": "¼", "⅕": "⅕", "⅙": "⅙", "⅛": "⅛", "⅔": "⅔", "⅖": "⅖", "¾": "¾", "¾": "¾", "⅗": "⅗", "⅜": "⅜", "⅘": "⅘", "⅚": "⅚", "⅝": "⅝", "⅞": "⅞", "⁄": "⁄", "⌢": "⌢", "𝒻": "𝒻", "≧": "≧", "⪌": "⪌", "ǵ": "ǵ", "γ": "γ", "ϝ": "ϝ", "⪆": "⪆", "ğ": "ğ", "ĝ": "ĝ", "г": "г", "ġ": "ġ", "≥": "≥", "⋛": "⋛", "≥": "≥", "≧": "≧", "⩾": "⩾", "⩾": "⩾", "⪩": "⪩", "⪀": "⪀", "⪂": "⪂", "⪄": "⪄", "⋛︀": "⋛︀", "⪔": "⪔", "𝔤": "𝔤", "≫": "≫", "⋙": "⋙", "ℷ": "ℷ", "ѓ": "ѓ", "≷": "≷", "⪒": "⪒", "⪥": "⪥", "⪤": "⪤", "≩": "≩", "⪊": "⪊", "⪊": "⪊", "⪈": "⪈", "⪈": "⪈", "≩": "≩", "⋧": "⋧", "𝕘": "𝕘", "`": "`", "ℊ": "ℊ", "≳": "≳", "⪎": "⪎", "⪐": "⪐", ">": ">", ">": ">", "⪧": "⪧", "⩺": "⩺", "⋗": "⋗", "⦕": "⦕", "⩼": "⩼", "⪆": "⪆", "⥸": "⥸", "⋗": "⋗", "⋛": "⋛", "⪌": "⪌", "≷": "≷", "≳": "≳", "≩︀": "≩︀", "≩︀": "≩︀", "⇔": "⇔", " ": " ", "½": "½", "ℋ": "ℋ", "ъ": "ъ", "↔": "↔", "⥈": "⥈", "↭": "↭", "ℏ": "ℏ", "ĥ": "ĥ", "♥": "♥", "♥": "♥", "…": "…", "⊹": "⊹", "𝔥": "𝔥", "⤥": "⤥", "⤦": "⤦", "⇿": "⇿", "∻": "∻", "↩": "↩", "↪": "↪", "𝕙": "𝕙", "―": "―", "𝒽": "𝒽", "ℏ": "ℏ", "ħ": "ħ", "⁃": "⁃", "‐": "‐", "í": "í", "í": "í", "⁣": "", "î": "î", "î": "î", "и": "и", "е": "е", "¡": "¡", "¡": "¡", "⇔": "⇔", "𝔦": "𝔦", "ì": "ì", "ì": "ì", "ⅈ": "ⅈ", "⨌": "⨌", "∭": "∭", "⧜": "⧜", "℩": "℩", "ij": "ij", "ī": "ī", "ℑ": "ℑ", "ℐ": "ℐ", "ℑ": "ℑ", "ı": "ı", "⊷": "⊷", "Ƶ": "Ƶ", "∈": "∈", "℅": "℅", "∞": "∞", "⧝": "⧝", "ı": "ı", "∫": "∫", "⊺": "⊺", "ℤ": "ℤ", "⊺": "⊺", "⨗": "⨗", "⨼": "⨼", "ё": "ё", "į": "į", "𝕚": "𝕚", "ι": "ι", "⨼": "⨼", "¿": "¿", "¿": "¿", "𝒾": "𝒾", "∈": "∈", "⋹": "⋹", "⋵": "⋵", "⋴": "⋴", "⋳": "⋳", "∈": "∈", "⁢": "", "ĩ": "ĩ", "і": "і", "ï": "ï", "ï": "ï", "ĵ": "ĵ", "й": "й", "𝔧": "𝔧", "ȷ": "ȷ", "𝕛": "𝕛", "𝒿": "𝒿", "ј": "ј", "є": "є", "κ": "κ", "ϰ": "ϰ", "ķ": "ķ", "к": "к", "𝔨": "𝔨", "ĸ": "ĸ", "х": "х", "ќ": "ќ", "𝕜": "𝕜", "𝓀": "𝓀", "⇚": "⇚", "⇐": "⇐", "⤛": "⤛", "⤎": "⤎", "≦": "≦", "⪋": "⪋", "⥢": "⥢", "ĺ": "ĺ", "⦴": "⦴", "ℒ": "ℒ", "λ": "λ", "⟨": "⟨", "⦑": "⦑", "⟨": "⟨", "⪅": "⪅", "«": "«", "«": "«", "←": "←", "⇤": "⇤", "⤟": "⤟", "⤝": "⤝", "↩": "↩", "↫": "↫", "⤹": "⤹", "⥳": "⥳", "↢": "↢", "⪫": "⪫", "⤙": "⤙", "⪭": "⪭", "⪭︀": "⪭︀", "⤌": "⤌", "❲": "❲", "{": "{", "[": "[", "⦋": "⦋", "⦏": "⦏", "⦍": "⦍", "ľ": "ľ", "ļ": "ļ", "⌈": "⌈", "{": "{", "л": "л", "⤶": "⤶", "“": "“", "„": "„", "⥧": "⥧", "⥋": "⥋", "↲": "↲", "≤": "≤", "←": "←", "↢": "↢", "↽": "↽", "↼": "↼", "⇇": "⇇", "↔": "↔", "⇆": "⇆", "⇋": "⇋", "↭": "↭", "⋋": "⋋", "⋚": "⋚", "≤": "≤", "≦": "≦", "⩽": "⩽", "⩽": "⩽", "⪨": "⪨", "⩿": "⩿", "⪁": "⪁", "⪃": "⪃", "⋚︀": "⋚︀", "⪓": "⪓", "⪅": "⪅", "⋖": "⋖", "⋚": "⋚", "⪋": "⪋", "≶": "≶", "≲": "≲", "⥼": "⥼", "⌊": "⌊", "𝔩": "𝔩", "≶": "≶", "⪑": "⪑", "↽": "↽", "↼": "↼", "⥪": "⥪", "▄": "▄", "љ": "љ", "≪": "≪", "⇇": "⇇", "⌞": "⌞", "⥫": "⥫", "◺": "◺", "ŀ": "ŀ", "⎰": "⎰", "⎰": "⎰", "≨": "≨", "⪉": "⪉", "⪉": "⪉", "⪇": "⪇", "⪇": "⪇", "≨": "≨", "⋦": "⋦", "⟬": "⟬", "⇽": "⇽", "⟦": "⟦", "⟵": "⟵", "⟷": "⟷", "⟼": "⟼", "⟶": "⟶", "↫": "↫", "↬": "↬", "⦅": "⦅", "𝕝": "𝕝", "⨭": "⨭", "⨴": "⨴", "∗": "∗", "_": "_", "◊": "◊", "◊": "◊", "⧫": "⧫", "(": "(", "⦓": "⦓", "⇆": "⇆", "⌟": "⌟", "⇋": "⇋", "⥭": "⥭", "‎": "", "⊿": "⊿", "‹": "‹", "𝓁": "𝓁", "↰": "↰", "≲": "≲", "⪍": "⪍", "⪏": "⪏", "[": "[", "‘": "‘", "‚": "‚", "ł": "ł", "<": "<", "<": "<", "⪦": "⪦", "⩹": "⩹", "⋖": "⋖", "⋋": "⋋", "⋉": "⋉", "⥶": "⥶", "⩻": "⩻", "⦖": "⦖", "◃": "◃", "⊴": "⊴", "◂": "◂", "⥊": "⥊", "⥦": "⥦", "≨︀": "≨︀", "≨︀": "≨︀", "∺": "∺", "¯": "¯", "¯": "¯", "♂": "♂", "✠": "✠", "✠": "✠", "↦": "↦", "↦": "↦", "↧": "↧", "↤": "↤", "↥": "↥", "▮": "▮", "⨩": "⨩", "м": "м", "—": "—", "∡": "∡", "𝔪": "𝔪", "℧": "℧", "µ": "µ", "µ": "µ", "∣": "∣", "*": "*", "⫰": "⫰", "·": "·", "·": "·", "−": "−", "⊟": "⊟", "∸": "∸", "⨪": "⨪", "⫛": "⫛", "…": "…", "∓": "∓", "⊧": "⊧", "𝕞": "𝕞", "∓": "∓", "𝓂": "𝓂", "∾": "∾", "μ": "μ", "⊸": "⊸", "⊸": "⊸", "⋙̸": "⋙̸", "≫⃒": "≫⃒", "≫̸": "≫̸", "⇍": "⇍", "⇎": "⇎", "⋘̸": "⋘̸", "≪⃒": "≪⃒", "≪̸": "≪̸", "⇏": "⇏", "⊯": "⊯", "⊮": "⊮", "∇": "∇", "ń": "ń", "∠⃒": "∠⃒", "≉": "≉", "⩰̸": "⩰̸", "≋̸": "≋̸", "ʼn": "ʼn", "≉": "≉", "♮": "♮", "♮": "♮", "ℕ": "ℕ", " ": " ", " ": " ", "≎̸": "≎̸", "≏̸": "≏̸", "⩃": "⩃", "ň": "ň", "ņ": "ņ", "≇": "≇", "⩭̸": "⩭̸", "⩂": "⩂", "н": "н", "–": "–", "≠": "≠", "⇗": "⇗", "⤤": "⤤", "↗": "↗", "↗": "↗", "≐̸": "≐̸", "≢": "≢", "⤨": "⤨", "≂̸": "≂̸", "∄": "∄", "∄": "∄", "𝔫": "𝔫", "≧̸": "≧̸", "≱": "≱", "≱": "≱", "≧̸": "≧̸", "⩾̸": "⩾̸", "⩾̸": "⩾̸", "≵": "≵", "≯": "≯", "≯": "≯", "⇎": "⇎", "↮": "↮", "⫲": "⫲", "∋": "∋", "⋼": "⋼", "⋺": "⋺", "∋": "∋", "њ": "њ", "⇍": "⇍", "≦̸": "≦̸", "↚": "↚", "‥": "‥", "≰": "≰", "↚": "↚", "↮": "↮", "≰": "≰", "≦̸": "≦̸", "⩽̸": "⩽̸", "⩽̸": "⩽̸", "≮": "≮", "≴": "≴", "≮": "≮", "⋪": "⋪", "⋬": "⋬", "∤": "∤", "𝕟": "𝕟", "¬": "¬", "¬": "¬", "∉": "∉", "⋹̸": "⋹̸", "⋵̸": "⋵̸", "∉": "∉", "⋷": "⋷", "⋶": "⋶", "∌": "∌", "∌": "∌", "⋾": "⋾", "⋽": "⋽", "∦": "∦", "∦": "∦", "⫽⃥": "⫽⃥", "∂̸": "∂̸", "⨔": "⨔", "⊀": "⊀", "⋠": "⋠", "⪯̸": "⪯̸", "⊀": "⊀", "⪯̸": "⪯̸", "⇏": "⇏", "↛": "↛", "⤳̸": "⤳̸", "↝̸": "↝̸", "↛": "↛", "⋫": "⋫", "⋭": "⋭", "⊁": "⊁", "⋡": "⋡", "⪰̸": "⪰̸", "𝓃": "𝓃", "∤": "∤", "∦": "∦", "≁": "≁", "≄": "≄", "≄": "≄", "∤": "∤", "∦": "∦", "⋢": "⋢", "⋣": "⋣", "⊄": "⊄", "⫅̸": "⫅̸", "⊈": "⊈", "⊂⃒": "⊂⃒", "⊈": "⊈", "⫅̸": "⫅̸", "⊁": "⊁", "⪰̸": "⪰̸", "⊅": "⊅", "⫆̸": "⫆̸", "⊉": "⊉", "⊃⃒": "⊃⃒", "⊉": "⊉", "⫆̸": "⫆̸", "≹": "≹", "ñ": "ñ", "ñ": "ñ", "≸": "≸", "⋪": "⋪", "⋬": "⋬", "⋫": "⋫", "⋭": "⋭", "ν": "ν", "#": "#", "№": "№", " ": " ", "⊭": "⊭", "⤄": "⤄", "≍⃒": "≍⃒", "⊬": "⊬", "≥⃒": "≥⃒", ">⃒": ">⃒", "⧞": "⧞", "⤂": "⤂", "≤⃒": "≤⃒", "<⃒": "<⃒", "⊴⃒": "⊴⃒", "⤃": "⤃", "⊵⃒": "⊵⃒", "∼⃒": "∼⃒", "⇖": "⇖", "⤣": "⤣", "↖": "↖", "↖": "↖", "⤧": "⤧", "Ⓢ": "Ⓢ", "ó": "ó", "ó": "ó", "⊛": "⊛", "⊚": "⊚", "ô": "ô", "ô": "ô", "о": "о", "⊝": "⊝", "ő": "ő", "⨸": "⨸", "⊙": "⊙", "⦼": "⦼", "œ": "œ", "⦿": "⦿", "𝔬": "𝔬", "˛": "˛", "ò": "ò", "ò": "ò", "⧁": "⧁", "⦵": "⦵", "Ω": "Ω", "∮": "∮", "↺": "↺", "⦾": "⦾", "⦻": "⦻", "‾": "‾", "⧀": "⧀", "ō": "ō", "ω": "ω", "ο": "ο", "⦶": "⦶", "⊖": "⊖", "𝕠": "𝕠", "⦷": "⦷", "⦹": "⦹", "⊕": "⊕", "∨": "∨", "↻": "↻", "⩝": "⩝", "ℴ": "ℴ", "ℴ": "ℴ", "ª": "ª", "ª": "ª", "º": "º", "º": "º", "⊶": "⊶", "⩖": "⩖", "⩗": "⩗", "⩛": "⩛", "ℴ": "ℴ", "ø": "ø", "ø": "ø", "⊘": "⊘", "õ": "õ", "õ": "õ", "⊗": "⊗", "⨶": "⨶", "ö": "ö", "ö": "ö", "⌽": "⌽", "∥": "∥", "¶": "¶", "¶": "¶", "∥": "∥", "⫳": "⫳", "⫽": "⫽", "∂": "∂", "п": "п", "%": "%", ".": ".", "‰": "‰", "⊥": "⊥", "‱": "‱", "𝔭": "𝔭", "φ": "φ", "ϕ": "ϕ", "ℳ": "ℳ", "☎": "☎", "π": "π", "⋔": "⋔", "ϖ": "ϖ", "ℏ": "ℏ", "ℎ": "ℎ", "ℏ": "ℏ", "+": "+", "⨣": "⨣", "⊞": "⊞", "⨢": "⨢", "∔": "∔", "⨥": "⨥", "⩲": "⩲", "±": "±", "±": "±", "⨦": "⨦", "⨧": "⨧", "±": "±", "⨕": "⨕", "𝕡": "𝕡", "£": "£", "£": "£", "≺": "≺", "⪳": "⪳", "⪷": "⪷", "≼": "≼", "⪯": "⪯", "≺": "≺", "⪷": "⪷", "≼": "≼", "⪯": "⪯", "⪹": "⪹", "⪵": "⪵", "⋨": "⋨", "≾": "≾", "′": "′", "ℙ": "ℙ", "⪵": "⪵", "⪹": "⪹", "⋨": "⋨", "∏": "∏", "⌮": "⌮", "⌒": "⌒", "⌓": "⌓", "∝": "∝", "∝": "∝", "≾": "≾", "⊰": "⊰", "𝓅": "𝓅", "ψ": "ψ", " ": " ", "𝔮": "𝔮", "⨌": "⨌", "𝕢": "𝕢", "⁗": "⁗", "𝓆": "𝓆", "ℍ": "ℍ", "⨖": "⨖", "?": "?", "≟": "≟", """: '"', """: '"', "⇛": "⇛", "⇒": "⇒", "⤜": "⤜", "⤏": "⤏", "⥤": "⥤", "∽̱": "∽̱", "ŕ": "ŕ", "√": "√", "⦳": "⦳", "⟩": "⟩", "⦒": "⦒", "⦥": "⦥", "⟩": "⟩", "»": "»", "»": "»", "→": "→", "⥵": "⥵", "⇥": "⇥", "⤠": "⤠", "⤳": "⤳", "⤞": "⤞", "↪": "↪", "↬": "↬", "⥅": "⥅", "⥴": "⥴", "↣": "↣", "↝": "↝", "⤚": "⤚", "∶": "∶", "ℚ": "ℚ", "⤍": "⤍", "❳": "❳", "}": "}", "]": "]", "⦌": "⦌", "⦎": "⦎", "⦐": "⦐", "ř": "ř", "ŗ": "ŗ", "⌉": "⌉", "}": "}", "р": "р", "⤷": "⤷", "⥩": "⥩", "”": "”", "”": "”", "↳": "↳", "ℜ": "ℜ", "ℛ": "ℛ", "ℜ": "ℜ", "ℝ": "ℝ", "▭": "▭", "®": "®", "®": "®", "⥽": "⥽", "⌋": "⌋", "𝔯": "𝔯", "⇁": "⇁", "⇀": "⇀", "⥬": "⥬", "ρ": "ρ", "ϱ": "ϱ", "→": "→", "↣": "↣", "⇁": "⇁", "⇀": "⇀", "⇄": "⇄", "⇌": "⇌", "⇉": "⇉", "↝": "↝", "⋌": "⋌", "˚": "˚", "≓": "≓", "⇄": "⇄", "⇌": "⇌", "‏": "", "⎱": "⎱", "⎱": "⎱", "⫮": "⫮", "⟭": "⟭", "⇾": "⇾", "⟧": "⟧", "⦆": "⦆", "𝕣": "𝕣", "⨮": "⨮", "⨵": "⨵", ")": ")", "⦔": "⦔", "⨒": "⨒", "⇉": "⇉", "›": "›", "𝓇": "𝓇", "↱": "↱", "]": "]", "’": "’", "’": "’", "⋌": "⋌", "⋊": "⋊", "▹": "▹", "⊵": "⊵", "▸": "▸", "⧎": "⧎", "⥨": "⥨", "℞": "℞", "ś": "ś", "‚": "‚", "≻": "≻", "⪴": "⪴", "⪸": "⪸", "š": "š", "≽": "≽", "⪰": "⪰", "ş": "ş", "ŝ": "ŝ", "⪶": "⪶", "⪺": "⪺", "⋩": "⋩", "⨓": "⨓", "≿": "≿", "с": "с", "⋅": "⋅", "⊡": "⊡", "⩦": "⩦", "⇘": "⇘", "⤥": "⤥", "↘": "↘", "↘": "↘", "§": "§", "§": "§", ";": ";", "⤩": "⤩", "∖": "∖", "∖": "∖", "✶": "✶", "𝔰": "𝔰", "⌢": "⌢", "♯": "♯", "щ": "щ", "ш": "ш", "∣": "∣", "∥": "∥", "­": "", "­": "", "σ": "σ", "ς": "ς", "ς": "ς", "∼": "∼", "⩪": "⩪", "≃": "≃", "≃": "≃", "⪞": "⪞", "⪠": "⪠", "⪝": "⪝", "⪟": "⪟", "≆": "≆", "⨤": "⨤", "⥲": "⥲", "←": "←", "∖": "∖", "⨳": "⨳", "⧤": "⧤", "∣": "∣", "⌣": "⌣", "⪪": "⪪", "⪬": "⪬", "⪬︀": "⪬︀", "ь": "ь", "/": "/", "⧄": "⧄", "⌿": "⌿", "𝕤": "𝕤", "♠": "♠", "♠": "♠", "∥": "∥", "⊓": "⊓", "⊓︀": "⊓︀", "⊔": "⊔", "⊔︀": "⊔︀", "⊏": "⊏", "⊑": "⊑", "⊏": "⊏", "⊑": "⊑", "⊐": "⊐", "⊒": "⊒", "⊐": "⊐", "⊒": "⊒", "□": "□", "□": "□", "▪": "▪", "▪": "▪", "→": "→", "𝓈": "𝓈", "∖": "∖", "⌣": "⌣", "⋆": "⋆", "☆": "☆", "★": "★", "ϵ": "ϵ", "ϕ": "ϕ", "¯": "¯", "⊂": "⊂", "⫅": "⫅", "⪽": "⪽", "⊆": "⊆", "⫃": "⫃", "⫁": "⫁", "⫋": "⫋", "⊊": "⊊", "⪿": "⪿", "⥹": "⥹", "⊂": "⊂", "⊆": "⊆", "⫅": "⫅", "⊊": "⊊", "⫋": "⫋", "⫇": "⫇", "⫕": "⫕", "⫓": "⫓", "≻": "≻", "⪸": "⪸", "≽": "≽", "⪰": "⪰", "⪺": "⪺", "⪶": "⪶", "⋩": "⋩", "≿": "≿", "∑": "∑", "♪": "♪", "¹": "¹", "¹": "¹", "²": "²", "²": "²", "³": "³", "³": "³", "⊃": "⊃", "⫆": "⫆", "⪾": "⪾", "⫘": "⫘", "⊇": "⊇", "⫄": "⫄", "⟉": "⟉", "⫗": "⫗", "⥻": "⥻", "⫂": "⫂", "⫌": "⫌", "⊋": "⊋", "⫀": "⫀", "⊃": "⊃", "⊇": "⊇", "⫆": "⫆", "⊋": "⊋", "⫌": "⫌", "⫈": "⫈", "⫔": "⫔", "⫖": "⫖", "⇙": "⇙", "⤦": "⤦", "↙": "↙", "↙": "↙", "⤪": "⤪", "ß": "ß", "ß": "ß", "⌖": "⌖", "τ": "τ", "⎴": "⎴", "ť": "ť", "ţ": "ţ", "т": "т", "⃛": "⃛", "⌕": "⌕", "𝔱": "𝔱", "∴": "∴", "∴": "∴", "θ": "θ", "ϑ": "ϑ", "ϑ": "ϑ", "≈": "≈", "∼": "∼", " ": " ", "≈": "≈", "∼": "∼", "þ": "þ", "þ": "þ", "˜": "˜", "×": "×", "×": "×", "⊠": "⊠", "⨱": "⨱", "⨰": "⨰", "∭": "∭", "⤨": "⤨", "⊤": "⊤", "⌶": "⌶", "⫱": "⫱", "𝕥": "𝕥", "⫚": "⫚", "⤩": "⤩", "‴": "‴", "™": "™", "▵": "▵", "▿": "▿", "◃": "◃", "⊴": "⊴", "≜": "≜", "▹": "▹", "⊵": "⊵", "◬": "◬", "≜": "≜", "⨺": "⨺", "⨹": "⨹", "⧍": "⧍", "⨻": "⨻", "⏢": "⏢", "𝓉": "𝓉", "ц": "ц", "ћ": "ћ", "ŧ": "ŧ", "≬": "≬", "↞": "↞", "↠": "↠", "⇑": "⇑", "⥣": "⥣", "ú": "ú", "ú": "ú", "↑": "↑", "ў": "ў", "ŭ": "ŭ", "û": "û", "û": "û", "у": "у", "⇅": "⇅", "ű": "ű", "⥮": "⥮", "⥾": "⥾", "𝔲": "𝔲", "ù": "ù", "ù": "ù", "↿": "↿", "↾": "↾", "▀": "▀", "⌜": "⌜", "⌜": "⌜", "⌏": "⌏", "◸": "◸", "ū": "ū", "¨": "¨", "¨": "¨", "ų": "ų", "𝕦": "𝕦", "↑": "↑", "↕": "↕", "↿": "↿", "↾": "↾", "⊎": "⊎", "υ": "υ", "ϒ": "ϒ", "υ": "υ", "⇈": "⇈", "⌝": "⌝", "⌝": "⌝", "⌎": "⌎", "ů": "ů", "◹": "◹", "𝓊": "𝓊", "⋰": "⋰", "ũ": "ũ", "▵": "▵", "▴": "▴", "⇈": "⇈", "ü": "ü", "ü": "ü", "⦧": "⦧", "⇕": "⇕", "⫨": "⫨", "⫩": "⫩", "⊨": "⊨", "⦜": "⦜", "ϵ": "ϵ", "ϰ": "ϰ", "∅": "∅", "ϕ": "ϕ", "ϖ": "ϖ", "∝": "∝", "↕": "↕", "ϱ": "ϱ", "ς": "ς", "⊊︀": "⊊︀", "⫋︀": "⫋︀", "⊋︀": "⊋︀", "⫌︀": "⫌︀", "ϑ": "ϑ", "⊲": "⊲", "⊳": "⊳", "в": "в", "⊢": "⊢", "∨": "∨", "⊻": "⊻", "≚": "≚", "⋮": "⋮", "|": "|", "|": "|", "𝔳": "𝔳", "⊲": "⊲", "⊂⃒": "⊂⃒", "⊃⃒": "⊃⃒", "𝕧": "𝕧", "∝": "∝", "⊳": "⊳", "𝓋": "𝓋", "⫋︀": "⫋︀", "⊊︀": "⊊︀", "⫌︀": "⫌︀", "⊋︀": "⊋︀", "⦚": "⦚", "ŵ": "ŵ", "⩟": "⩟", "∧": "∧", "≙": "≙", "℘": "℘", "𝔴": "𝔴", "𝕨": "𝕨", "℘": "℘", "≀": "≀", "≀": "≀", "𝓌": "𝓌", "⋂": "⋂", "◯": "◯", "⋃": "⋃", "▽": "▽", "𝔵": "𝔵", "⟺": "⟺", "⟷": "⟷", "ξ": "ξ", "⟸": "⟸", "⟵": "⟵", "⟼": "⟼", "⋻": "⋻", "⨀": "⨀", "𝕩": "𝕩", "⨁": "⨁", "⨂": "⨂", "⟹": "⟹", "⟶": "⟶", "𝓍": "𝓍", "⨆": "⨆", "⨄": "⨄", "△": "△", "⋁": "⋁", "⋀": "⋀", "ý": "ý", "ý": "ý", "я": "я", "ŷ": "ŷ", "ы": "ы", "¥": "¥", "¥": "¥", "𝔶": "𝔶", "ї": "ї", "𝕪": "𝕪", "𝓎": "𝓎", "ю": "ю", "ÿ": "ÿ", "ÿ": "ÿ", "ź": "ź", "ž": "ž", "з": "з", "ż": "ż", "ℨ": "ℨ", "ζ": "ζ", "𝔷": "𝔷", "ж": "ж", "⇝": "⇝", "𝕫": "𝕫", "𝓏": "𝓏", "‍": "", "‌": "" }, characters: { "Æ": "Æ", "&": "&", "Á": "Á", "Ă": "Ă", "Â": "Â", "А": "А", "𝔄": "𝔄", "À": "À", "Α": "Α", "Ā": "Ā", "⩓": "⩓", "Ą": "Ą", "𝔸": "𝔸", "": "⁡", "Å": "Å", "𝒜": "𝒜", "≔": "≔", "Ã": "Ã", "Ä": "Ä", "∖": "∖", "⫧": "⫧", "⌆": "⌆", "Б": "Б", "∵": "∵", "ℬ": "ℬ", "Β": "Β", "𝔅": "𝔅", "𝔹": "𝔹", "˘": "˘", "≎": "≎", "Ч": "Ч", "©": "©", "Ć": "Ć", "⋒": "⋒", "ⅅ": "ⅅ", "ℭ": "ℭ", "Č": "Č", "Ç": "Ç", "Ĉ": "Ĉ", "∰": "∰", "Ċ": "Ċ", "¸": "¸", "·": "·", "Χ": "Χ", "⊙": "⊙", "⊖": "⊖", "⊕": "⊕", "⊗": "⊗", "∲": "∲", "”": "”", "’": "’", "∷": "∷", "⩴": "⩴", "≡": "≡", "∯": "∯", "∮": "∮", "ℂ": "ℂ", "∐": "∐", "∳": "∳", "⨯": "⨯", "𝒞": "𝒞", "⋓": "⋓", "≍": "≍", "⤑": "⤑", "Ђ": "Ђ", "Ѕ": "Ѕ", "Џ": "Џ", "‡": "‡", "↡": "↡", "⫤": "⫤", "Ď": "Ď", "Д": "Д", "∇": "∇", "Δ": "Δ", "𝔇": "𝔇", "´": "´", "˙": "˙", "˝": "˝", "`": "`", "˜": "˜", "⋄": "⋄", "ⅆ": "ⅆ", "𝔻": "𝔻", "¨": "¨", "⃜": "⃜", "≐": "≐", "⇓": "⇓", "⇐": "⇐", "⇔": "⇔", "⟸": "⟸", "⟺": "⟺", "⟹": "⟹", "⇒": "⇒", "⊨": "⊨", "⇑": "⇑", "⇕": "⇕", "∥": "∥", "↓": "↓", "⤓": "⤓", "⇵": "⇵", "̑": "̑", "⥐": "⥐", "⥞": "⥞", "↽": "↽", "⥖": "⥖", "⥟": "⥟", "⇁": "⇁", "⥗": "⥗", "⊤": "⊤", "↧": "↧", "𝒟": "𝒟", "Đ": "Đ", "Ŋ": "Ŋ", "Ð": "Ð", "É": "É", "Ě": "Ě", "Ê": "Ê", "Э": "Э", "Ė": "Ė", "𝔈": "𝔈", "È": "È", "∈": "∈", "Ē": "Ē", "◻": "◻", "▫": "▫", "Ę": "Ę", "𝔼": "𝔼", "Ε": "Ε", "⩵": "⩵", "≂": "≂", "⇌": "⇌", "ℰ": "ℰ", "⩳": "⩳", "Η": "Η", "Ë": "Ë", "∃": "∃", "ⅇ": "ⅇ", "Ф": "Ф", "𝔉": "𝔉", "◼": "◼", "▪": "▪", "𝔽": "𝔽", "∀": "∀", "ℱ": "ℱ", "Ѓ": "Ѓ", ">": ">", "Γ": "Γ", "Ϝ": "Ϝ", "Ğ": "Ğ", "Ģ": "Ģ", "Ĝ": "Ĝ", "Г": "Г", "Ġ": "Ġ", "𝔊": "𝔊", "⋙": "⋙", "𝔾": "𝔾", "≥": "≥", "⋛": "⋛", "≧": "≧", "⪢": "⪢", "≷": "≷", "⩾": "⩾", "≳": "≳", "𝒢": "𝒢", "≫": "≫", "Ъ": "Ъ", "ˇ": "ˇ", "^": "^", "Ĥ": "Ĥ", "ℌ": "ℌ", "ℋ": "ℋ", "ℍ": "ℍ", "─": "─", "Ħ": "Ħ", "≏": "≏", "Е": "Е", "IJ": "IJ", "Ё": "Ё", "Í": "Í", "Î": "Î", "И": "И", "İ": "İ", "ℑ": "ℑ", "Ì": "Ì", "Ī": "Ī", "ⅈ": "ⅈ", "∬": "∬", "∫": "∫", "⋂": "⋂", "": "⁣", "": "⁢", "Į": "Į", "𝕀": "𝕀", "Ι": "Ι", "ℐ": "ℐ", "Ĩ": "Ĩ", "І": "І", "Ï": "Ï", "Ĵ": "Ĵ", "Й": "Й", "𝔍": "𝔍", "𝕁": "𝕁", "𝒥": "𝒥", "Ј": "Ј", "Є": "Є", "Х": "Х", "Ќ": "Ќ", "Κ": "Κ", "Ķ": "Ķ", "К": "К", "𝔎": "𝔎", "𝕂": "𝕂", "𝒦": "𝒦", "Љ": "Љ", "<": "<", "Ĺ": "Ĺ", "Λ": "Λ", "⟪": "⟪", "ℒ": "ℒ", "↞": "↞", "Ľ": "Ľ", "Ļ": "Ļ", "Л": "Л", "⟨": "⟨", "←": "←", "⇤": "⇤", "⇆": "⇆", "⌈": "⌈", "⟦": "⟦", "⥡": "⥡", "⇃": "⇃", "⥙": "⥙", "⌊": "⌊", "↔": "↔", "⥎": "⥎", "⊣": "⊣", "↤": "↤", "⥚": "⥚", "⊲": "⊲", "⧏": "⧏", "⊴": "⊴", "⥑": "⥑", "⥠": "⥠", "↿": "↿", "⥘": "⥘", "↼": "↼", "⥒": "⥒", "⋚": "⋚", "≦": "≦", "≶": "≶", "⪡": "⪡", "⩽": "⩽", "≲": "≲", "𝔏": "𝔏", "⋘": "⋘", "⇚": "⇚", "Ŀ": "Ŀ", "⟵": "⟵", "⟷": "⟷", "⟶": "⟶", "𝕃": "𝕃", "↙": "↙", "↘": "↘", "↰": "↰", "Ł": "Ł", "≪": "≪", "⤅": "⤅", "М": "М", " ": " ", "ℳ": "ℳ", "𝔐": "𝔐", "∓": "∓", "𝕄": "𝕄", "Μ": "Μ", "Њ": "Њ", "Ń": "Ń", "Ň": "Ň", "Ņ": "Ņ", "Н": "Н", "": "​", "\n": "
", "𝔑": "𝔑", "": "⁠", " ": " ", "ℕ": "ℕ", "⫬": "⫬", "≢": "≢", "≭": "≭", "∦": "∦", "∉": "∉", "≠": "≠", "≂̸": "≂̸", "∄": "∄", "≯": "≯", "≱": "≱", "≧̸": "≧̸", "≫̸": "≫̸", "≹": "≹", "⩾̸": "⩾̸", "≵": "≵", "≎̸": "≎̸", "≏̸": "≏̸", "⋪": "⋪", "⧏̸": "⧏̸", "⋬": "⋬", "≮": "≮", "≰": "≰", "≸": "≸", "≪̸": "≪̸", "⩽̸": "⩽̸", "≴": "≴", "⪢̸": "⪢̸", "⪡̸": "⪡̸", "⊀": "⊀", "⪯̸": "⪯̸", "⋠": "⋠", "∌": "∌", "⋫": "⋫", "⧐̸": "⧐̸", "⋭": "⋭", "⊏̸": "⊏̸", "⋢": "⋢", "⊐̸": "⊐̸", "⋣": "⋣", "⊂⃒": "⊂⃒", "⊈": "⊈", "⊁": "⊁", "⪰̸": "⪰̸", "⋡": "⋡", "≿̸": "≿̸", "⊃⃒": "⊃⃒", "⊉": "⊉", "≁": "≁", "≄": "≄", "≇": "≇", "≉": "≉", "∤": "∤", "𝒩": "𝒩", "Ñ": "Ñ", "Ν": "Ν", "Œ": "Œ", "Ó": "Ó", "Ô": "Ô", "О": "О", "Ő": "Ő", "𝔒": "𝔒", "Ò": "Ò", "Ō": "Ō", "Ω": "Ω", "Ο": "Ο", "𝕆": "𝕆", "“": "“", "‘": "‘", "⩔": "⩔", "𝒪": "𝒪", "Ø": "Ø", "Õ": "Õ", "⨷": "⨷", "Ö": "Ö", "‾": "‾", "⏞": "⏞", "⎴": "⎴", "⏜": "⏜", "∂": "∂", "П": "П", "𝔓": "𝔓", "Φ": "Φ", "Π": "Π", "±": "±", "ℙ": "ℙ", "⪻": "⪻", "≺": "≺", "⪯": "⪯", "≼": "≼", "≾": "≾", "″": "″", "∏": "∏", "∝": "∝", "𝒫": "𝒫", "Ψ": "Ψ", '"': """, "𝔔": "𝔔", "ℚ": "ℚ", "𝒬": "𝒬", "⤐": "⤐", "®": "®", "Ŕ": "Ŕ", "⟫": "⟫", "↠": "↠", "⤖": "⤖", "Ř": "Ř", "Ŗ": "Ŗ", "Р": "Р", "ℜ": "ℜ", "∋": "∋", "⇋": "⇋", "⥯": "⥯", "Ρ": "Ρ", "⟩": "⟩", "→": "→", "⇥": "⇥", "⇄": "⇄", "⌉": "⌉", "⟧": "⟧", "⥝": "⥝", "⇂": "⇂", "⥕": "⥕", "⌋": "⌋", "⊢": "⊢", "↦": "↦", "⥛": "⥛", "⊳": "⊳", "⧐": "⧐", "⊵": "⊵", "⥏": "⥏", "⥜": "⥜", "↾": "↾", "⥔": "⥔", "⇀": "⇀", "⥓": "⥓", "ℝ": "ℝ", "⥰": "⥰", "⇛": "⇛", "ℛ": "ℛ", "↱": "↱", "⧴": "⧴", "Щ": "Щ", "Ш": "Ш", "Ь": "Ь", "Ś": "Ś", "⪼": "⪼", "Š": "Š", "Ş": "Ş", "Ŝ": "Ŝ", "С": "С", "𝔖": "𝔖", "↑": "↑", "Σ": "Σ", "∘": "∘", "𝕊": "𝕊", "√": "√", "□": "□", "⊓": "⊓", "⊏": "⊏", "⊑": "⊑", "⊐": "⊐", "⊒": "⊒", "⊔": "⊔", "𝒮": "𝒮", "⋆": "⋆", "⋐": "⋐", "⊆": "⊆", "≻": "≻", "⪰": "⪰", "≽": "≽", "≿": "≿", "∑": "∑", "⋑": "⋑", "⊃": "⊃", "⊇": "⊇", "Þ": "Þ", "™": "™", "Ћ": "Ћ", "Ц": "Ц", "\t": "	", "Τ": "Τ", "Ť": "Ť", "Ţ": "Ţ", "Т": "Т", "𝔗": "𝔗", "∴": "∴", "Θ": "Θ", " ": "  ", " ": " ", "∼": "∼", "≃": "≃", "≅": "≅", "≈": "≈", "𝕋": "𝕋", "⃛": "⃛", "𝒯": "𝒯", "Ŧ": "Ŧ", "Ú": "Ú", "↟": "↟", "⥉": "⥉", "Ў": "Ў", "Ŭ": "Ŭ", "Û": "Û", "У": "У", "Ű": "Ű", "𝔘": "𝔘", "Ù": "Ù", "Ū": "Ū", _: "_", "⏟": "⏟", "⎵": "⎵", "⏝": "⏝", "⋃": "⋃", "⊎": "⊎", "Ų": "Ų", "𝕌": "𝕌", "⤒": "⤒", "⇅": "⇅", "↕": "↕", "⥮": "⥮", "⊥": "⊥", "↥": "↥", "↖": "↖", "↗": "↗", "ϒ": "ϒ", "Υ": "Υ", "Ů": "Ů", "𝒰": "𝒰", "Ũ": "Ũ", "Ü": "Ü", "⊫": "⊫", "⫫": "⫫", "В": "В", "⊩": "⊩", "⫦": "⫦", "⋁": "⋁", "‖": "‖", "∣": "∣", "|": "|", "❘": "❘", "≀": "≀", " ": " ", "𝔙": "𝔙", "𝕍": "𝕍", "𝒱": "𝒱", "⊪": "⊪", "Ŵ": "Ŵ", "⋀": "⋀", "𝔚": "𝔚", "𝕎": "𝕎", "𝒲": "𝒲", "𝔛": "𝔛", "Ξ": "Ξ", "𝕏": "𝕏", "𝒳": "𝒳", "Я": "Я", "Ї": "Ї", "Ю": "Ю", "Ý": "Ý", "Ŷ": "Ŷ", "Ы": "Ы", "𝔜": "𝔜", "𝕐": "𝕐", "𝒴": "𝒴", "Ÿ": "Ÿ", "Ж": "Ж", "Ź": "Ź", "Ž": "Ž", "З": "З", "Ż": "Ż", "Ζ": "Ζ", "ℨ": "ℨ", "ℤ": "ℤ", "𝒵": "𝒵", "á": "á", "ă": "ă", "∾": "∾", "∾̳": "∾̳", "∿": "∿", "â": "â", "а": "а", "æ": "æ", "𝔞": "𝔞", "à": "à", "ℵ": "ℵ", "α": "α", "ā": "ā", "⨿": "⨿", "∧": "∧", "⩕": "⩕", "⩜": "⩜", "⩘": "⩘", "⩚": "⩚", "∠": "∠", "⦤": "⦤", "∡": "∡", "⦨": "⦨", "⦩": "⦩", "⦪": "⦪", "⦫": "⦫", "⦬": "⦬", "⦭": "⦭", "⦮": "⦮", "⦯": "⦯", "∟": "∟", "⊾": "⊾", "⦝": "⦝", "∢": "∢", "⍼": "⍼", "ą": "ą", "𝕒": "𝕒", "⩰": "⩰", "⩯": "⩯", "≊": "≊", "≋": "≋", "'": "'", "å": "å", "𝒶": "𝒶", "*": "*", "ã": "ã", "ä": "ä", "⨑": "⨑", "⫭": "⫭", "≌": "≌", "϶": "϶", "‵": "‵", "∽": "∽", "⋍": "⋍", "⊽": "⊽", "⌅": "⌅", "⎶": "⎶", "б": "б", "„": "„", "⦰": "⦰", "β": "β", "ℶ": "ℶ", "≬": "≬", "𝔟": "𝔟", "◯": "◯", "⨀": "⨀", "⨁": "⨁", "⨂": "⨂", "⨆": "⨆", "★": "★", "▽": "▽", "△": "△", "⨄": "⨄", "⤍": "⤍", "⧫": "⧫", "▴": "▴", "▾": "▾", "◂": "◂", "▸": "▸", "␣": "␣", "▒": "▒", "░": "░", "▓": "▓", "█": "█", "=⃥": "=⃥", "≡⃥": "≡⃥", "⌐": "⌐", "𝕓": "𝕓", "⋈": "⋈", "╗": "╗", "╔": "╔", "╖": "╖", "╓": "╓", "═": "═", "╦": "╦", "╩": "╩", "╤": "╤", "╧": "╧", "╝": "╝", "╚": "╚", "╜": "╜", "╙": "╙", "║": "║", "╬": "╬", "╣": "╣", "╠": "╠", "╫": "╫", "╢": "╢", "╟": "╟", "⧉": "⧉", "╕": "╕", "╒": "╒", "┐": "┐", "┌": "┌", "╥": "╥", "╨": "╨", "┬": "┬", "┴": "┴", "⊟": "⊟", "⊞": "⊞", "⊠": "⊠", "╛": "╛", "╘": "╘", "┘": "┘", "└": "└", "│": "│", "╪": "╪", "╡": "╡", "╞": "╞", "┼": "┼", "┤": "┤", "├": "├", "¦": "¦", "𝒷": "𝒷", "⁏": "⁏", "\\": "\", "⧅": "⧅", "⟈": "⟈", "•": "•", "⪮": "⪮", "ć": "ć", "∩": "∩", "⩄": "⩄", "⩉": "⩉", "⩋": "⩋", "⩇": "⩇", "⩀": "⩀", "∩︀": "∩︀", "⁁": "⁁", "⩍": "⩍", "č": "č", "ç": "ç", "ĉ": "ĉ", "⩌": "⩌", "⩐": "⩐", "ċ": "ċ", "⦲": "⦲", "¢": "¢", "𝔠": "𝔠", "ч": "ч", "✓": "✓", "χ": "χ", "○": "○", "⧃": "⧃", "ˆ": "ˆ", "≗": "≗", "↺": "↺", "↻": "↻", "Ⓢ": "Ⓢ", "⊛": "⊛", "⊚": "⊚", "⊝": "⊝", "⨐": "⨐", "⫯": "⫯", "⧂": "⧂", "♣": "♣", ":": ":", ",": ",", "@": "@", "∁": "∁", "⩭": "⩭", "𝕔": "𝕔", "℗": "℗", "↵": "↵", "✗": "✗", "𝒸": "𝒸", "⫏": "⫏", "⫑": "⫑", "⫐": "⫐", "⫒": "⫒", "⋯": "⋯", "⤸": "⤸", "⤵": "⤵", "⋞": "⋞", "⋟": "⋟", "↶": "↶", "⤽": "⤽", "∪": "∪", "⩈": "⩈", "⩆": "⩆", "⩊": "⩊", "⊍": "⊍", "⩅": "⩅", "∪︀": "∪︀", "↷": "↷", "⤼": "⤼", "⋎": "⋎", "⋏": "⋏", "¤": "¤", "∱": "∱", "⌭": "⌭", "⥥": "⥥", "†": "†", "ℸ": "ℸ", "‐": "‐", "⤏": "⤏", "ď": "ď", "д": "д", "⇊": "⇊", "⩷": "⩷", "°": "°", "δ": "δ", "⦱": "⦱", "⥿": "⥿", "𝔡": "𝔡", "♦": "♦", "ϝ": "ϝ", "⋲": "⋲", "÷": "÷", "⋇": "⋇", "ђ": "ђ", "⌞": "⌞", "⌍": "⌍", $: "$", "𝕕": "𝕕", "≑": "≑", "∸": "∸", "∔": "∔", "⊡": "⊡", "⌟": "⌟", "⌌": "⌌", "𝒹": "𝒹", "ѕ": "ѕ", "⧶": "⧶", "đ": "đ", "⋱": "⋱", "▿": "▿", "⦦": "⦦", "џ": "џ", "⟿": "⟿", "é": "é", "⩮": "⩮", "ě": "ě", "≖": "≖", "ê": "ê", "≕": "≕", "э": "э", "ė": "ė", "≒": "≒", "𝔢": "𝔢", "⪚": "⪚", "è": "è", "⪖": "⪖", "⪘": "⪘", "⪙": "⪙", "⏧": "⏧", "ℓ": "ℓ", "⪕": "⪕", "⪗": "⪗", "ē": "ē", "∅": "∅", " ": " ", " ": " ", " ": " ", "ŋ": "ŋ", " ": " ", "ę": "ę", "𝕖": "𝕖", "⋕": "⋕", "⧣": "⧣", "⩱": "⩱", "ε": "ε", "ϵ": "ϵ", "=": "=", "≟": "≟", "⩸": "⩸", "⧥": "⧥", "≓": "≓", "⥱": "⥱", "ℯ": "ℯ", "η": "η", "ð": "ð", "ë": "ë", "€": "€", "!": "!", "ф": "ф", "♀": "♀", "ffi": "ffi", "ff": "ff", "ffl": "ffl", "𝔣": "𝔣", "fi": "fi", fj: "fj", "♭": "♭", "fl": "fl", "▱": "▱", "ƒ": "ƒ", "𝕗": "𝕗", "⋔": "⋔", "⫙": "⫙", "⨍": "⨍", "½": "½", "⅓": "⅓", "¼": "¼", "⅕": "⅕", "⅙": "⅙", "⅛": "⅛", "⅔": "⅔", "⅖": "⅖", "¾": "¾", "⅗": "⅗", "⅜": "⅜", "⅘": "⅘", "⅚": "⅚", "⅝": "⅝", "⅞": "⅞", "⁄": "⁄", "⌢": "⌢", "𝒻": "𝒻", "⪌": "⪌", "ǵ": "ǵ", "γ": "γ", "⪆": "⪆", "ğ": "ğ", "ĝ": "ĝ", "г": "г", "ġ": "ġ", "⪩": "⪩", "⪀": "⪀", "⪂": "⪂", "⪄": "⪄", "⋛︀": "⋛︀", "⪔": "⪔", "𝔤": "𝔤", "ℷ": "ℷ", "ѓ": "ѓ", "⪒": "⪒", "⪥": "⪥", "⪤": "⪤", "≩": "≩", "⪊": "⪊", "⪈": "⪈", "⋧": "⋧", "𝕘": "𝕘", "ℊ": "ℊ", "⪎": "⪎", "⪐": "⪐", "⪧": "⪧", "⩺": "⩺", "⋗": "⋗", "⦕": "⦕", "⩼": "⩼", "⥸": "⥸", "≩︀": "≩︀", "ъ": "ъ", "⥈": "⥈", "↭": "↭", "ℏ": "ℏ", "ĥ": "ĥ", "♥": "♥", "…": "…", "⊹": "⊹", "𝔥": "𝔥", "⤥": "⤥", "⤦": "⤦", "⇿": "⇿", "∻": "∻", "↩": "↩", "↪": "↪", "𝕙": "𝕙", "―": "―", "𝒽": "𝒽", "ħ": "ħ", "⁃": "⁃", "í": "í", "î": "î", "и": "и", "е": "е", "¡": "¡", "𝔦": "𝔦", "ì": "ì", "⨌": "⨌", "∭": "∭", "⧜": "⧜", "℩": "℩", "ij": "ij", "ī": "ī", "ı": "ı", "⊷": "⊷", "Ƶ": "Ƶ", "℅": "℅", "∞": "∞", "⧝": "⧝", "⊺": "⊺", "⨗": "⨗", "⨼": "⨼", "ё": "ё", "į": "į", "𝕚": "𝕚", "ι": "ι", "¿": "¿", "𝒾": "𝒾", "⋹": "⋹", "⋵": "⋵", "⋴": "⋴", "⋳": "⋳", "ĩ": "ĩ", "і": "і", "ï": "ï", "ĵ": "ĵ", "й": "й", "𝔧": "𝔧", "ȷ": "ȷ", "𝕛": "𝕛", "𝒿": "𝒿", "ј": "ј", "є": "є", "κ": "κ", "ϰ": "ϰ", "ķ": "ķ", "к": "к", "𝔨": "𝔨", "ĸ": "ĸ", "х": "х", "ќ": "ќ", "𝕜": "𝕜", "𝓀": "𝓀", "⤛": "⤛", "⤎": "⤎", "⪋": "⪋", "⥢": "⥢", "ĺ": "ĺ", "⦴": "⦴", "λ": "λ", "⦑": "⦑", "⪅": "⪅", "«": "«", "⤟": "⤟", "⤝": "⤝", "↫": "↫", "⤹": "⤹", "⥳": "⥳", "↢": "↢", "⪫": "⪫", "⤙": "⤙", "⪭": "⪭", "⪭︀": "⪭︀", "⤌": "⤌", "❲": "❲", "{": "{", "[": "[", "⦋": "⦋", "⦏": "⦏", "⦍": "⦍", "ľ": "ľ", "ļ": "ļ", "л": "л", "⤶": "⤶", "⥧": "⥧", "⥋": "⥋", "↲": "↲", "≤": "≤", "⇇": "⇇", "⋋": "⋋", "⪨": "⪨", "⩿": "⩿", "⪁": "⪁", "⪃": "⪃", "⋚︀": "⋚︀", "⪓": "⪓", "⋖": "⋖", "⥼": "⥼", "𝔩": "𝔩", "⪑": "⪑", "⥪": "⥪", "▄": "▄", "љ": "љ", "⥫": "⥫", "◺": "◺", "ŀ": "ŀ", "⎰": "⎰", "≨": "≨", "⪉": "⪉", "⪇": "⪇", "⋦": "⋦", "⟬": "⟬", "⇽": "⇽", "⟼": "⟼", "↬": "↬", "⦅": "⦅", "𝕝": "𝕝", "⨭": "⨭", "⨴": "⨴", "∗": "∗", "◊": "◊", "(": "(", "⦓": "⦓", "⥭": "⥭", "": "‎", "⊿": "⊿", "‹": "‹", "𝓁": "𝓁", "⪍": "⪍", "⪏": "⪏", "‚": "‚", "ł": "ł", "⪦": "⪦", "⩹": "⩹", "⋉": "⋉", "⥶": "⥶", "⩻": "⩻", "⦖": "⦖", "◃": "◃", "⥊": "⥊", "⥦": "⥦", "≨︀": "≨︀", "∺": "∺", "¯": "¯", "♂": "♂", "✠": "✠", "▮": "▮", "⨩": "⨩", "м": "м", "—": "—", "𝔪": "𝔪", "℧": "℧", "µ": "µ", "⫰": "⫰", "−": "−", "⨪": "⨪", "⫛": "⫛", "⊧": "⊧", "𝕞": "𝕞", "𝓂": "𝓂", "μ": "μ", "⊸": "⊸", "⋙̸": "⋙̸", "≫⃒": "≫⃒", "⇍": "⇍", "⇎": "⇎", "⋘̸": "⋘̸", "≪⃒": "≪⃒", "⇏": "⇏", "⊯": "⊯", "⊮": "⊮", "ń": "ń", "∠⃒": "∠⃒", "⩰̸": "⩰̸", "≋̸": "≋̸", "ʼn": "ʼn", "♮": "♮", "⩃": "⩃", "ň": "ň", "ņ": "ņ", "⩭̸": "⩭̸", "⩂": "⩂", "н": "н", "–": "–", "⇗": "⇗", "⤤": "⤤", "≐̸": "≐̸", "⤨": "⤨", "𝔫": "𝔫", "↮": "↮", "⫲": "⫲", "⋼": "⋼", "⋺": "⋺", "њ": "њ", "≦̸": "≦̸", "↚": "↚", "‥": "‥", "𝕟": "𝕟", "¬": "¬", "⋹̸": "⋹̸", "⋵̸": "⋵̸", "⋷": "⋷", "⋶": "⋶", "⋾": "⋾", "⋽": "⋽", "⫽⃥": "⫽⃥", "∂̸": "∂̸", "⨔": "⨔", "↛": "↛", "⤳̸": "⤳̸", "↝̸": "↝̸", "𝓃": "𝓃", "⊄": "⊄", "⫅̸": "⫅̸", "⊅": "⊅", "⫆̸": "⫆̸", "ñ": "ñ", "ν": "ν", "#": "#", "№": "№", " ": " ", "⊭": "⊭", "⤄": "⤄", "≍⃒": "≍⃒", "⊬": "⊬", "≥⃒": "≥⃒", ">⃒": ">⃒", "⧞": "⧞", "⤂": "⤂", "≤⃒": "≤⃒", "<⃒": "<⃒", "⊴⃒": "⊴⃒", "⤃": "⤃", "⊵⃒": "⊵⃒", "∼⃒": "∼⃒", "⇖": "⇖", "⤣": "⤣", "⤧": "⤧", "ó": "ó", "ô": "ô", "о": "о", "ő": "ő", "⨸": "⨸", "⦼": "⦼", "œ": "œ", "⦿": "⦿", "𝔬": "𝔬", "˛": "˛", "ò": "ò", "⧁": "⧁", "⦵": "⦵", "⦾": "⦾", "⦻": "⦻", "⧀": "⧀", "ō": "ō", "ω": "ω", "ο": "ο", "⦶": "⦶", "𝕠": "𝕠", "⦷": "⦷", "⦹": "⦹", "∨": "∨", "⩝": "⩝", "ℴ": "ℴ", "ª": "ª", "º": "º", "⊶": "⊶", "⩖": "⩖", "⩗": "⩗", "⩛": "⩛", "ø": "ø", "⊘": "⊘", "õ": "õ", "⨶": "⨶", "ö": "ö", "⌽": "⌽", "¶": "¶", "⫳": "⫳", "⫽": "⫽", "п": "п", "%": "%", ".": ".", "‰": "‰", "‱": "‱", "𝔭": "𝔭", "φ": "φ", "ϕ": "ϕ", "☎": "☎", "π": "π", "ϖ": "ϖ", "ℎ": "ℎ", "+": "+", "⨣": "⨣", "⨢": "⨢", "⨥": "⨥", "⩲": "⩲", "⨦": "⨦", "⨧": "⨧", "⨕": "⨕", "𝕡": "𝕡", "£": "£", "⪳": "⪳", "⪷": "⪷", "⪹": "⪹", "⪵": "⪵", "⋨": "⋨", "′": "′", "⌮": "⌮", "⌒": "⌒", "⌓": "⌓", "⊰": "⊰", "𝓅": "𝓅", "ψ": "ψ", " ": " ", "𝔮": "𝔮", "𝕢": "𝕢", "⁗": "⁗", "𝓆": "𝓆", "⨖": "⨖", "?": "?", "⤜": "⤜", "⥤": "⥤", "∽̱": "∽̱", "ŕ": "ŕ", "⦳": "⦳", "⦒": "⦒", "⦥": "⦥", "»": "»", "⥵": "⥵", "⤠": "⤠", "⤳": "⤳", "⤞": "⤞", "⥅": "⥅", "⥴": "⥴", "↣": "↣", "↝": "↝", "⤚": "⤚", "∶": "∶", "❳": "❳", "}": "}", "]": "]", "⦌": "⦌", "⦎": "⦎", "⦐": "⦐", "ř": "ř", "ŗ": "ŗ", "р": "р", "⤷": "⤷", "⥩": "⥩", "↳": "↳", "▭": "▭", "⥽": "⥽", "𝔯": "𝔯", "⥬": "⥬", "ρ": "ρ", "ϱ": "ϱ", "⇉": "⇉", "⋌": "⋌", "˚": "˚", "": "‏", "⎱": "⎱", "⫮": "⫮", "⟭": "⟭", "⇾": "⇾", "⦆": "⦆", "𝕣": "𝕣", "⨮": "⨮", "⨵": "⨵", ")": ")", "⦔": "⦔", "⨒": "⨒", "›": "›", "𝓇": "𝓇", "⋊": "⋊", "▹": "▹", "⧎": "⧎", "⥨": "⥨", "℞": "℞", "ś": "ś", "⪴": "⪴", "⪸": "⪸", "š": "š", "ş": "ş", "ŝ": "ŝ", "⪶": "⪶", "⪺": "⪺", "⋩": "⋩", "⨓": "⨓", "с": "с", "⋅": "⋅", "⩦": "⩦", "⇘": "⇘", "§": "§", ";": ";", "⤩": "⤩", "✶": "✶", "𝔰": "𝔰", "♯": "♯", "щ": "щ", "ш": "ш", "": "­", "σ": "σ", "ς": "ς", "⩪": "⩪", "⪞": "⪞", "⪠": "⪠", "⪝": "⪝", "⪟": "⪟", "≆": "≆", "⨤": "⨤", "⥲": "⥲", "⨳": "⨳", "⧤": "⧤", "⌣": "⌣", "⪪": "⪪", "⪬": "⪬", "⪬︀": "⪬︀", "ь": "ь", "/": "/", "⧄": "⧄", "⌿": "⌿", "𝕤": "𝕤", "♠": "♠", "⊓︀": "⊓︀", "⊔︀": "⊔︀", "𝓈": "𝓈", "☆": "☆", "⊂": "⊂", "⫅": "⫅", "⪽": "⪽", "⫃": "⫃", "⫁": "⫁", "⫋": "⫋", "⊊": "⊊", "⪿": "⪿", "⥹": "⥹", "⫇": "⫇", "⫕": "⫕", "⫓": "⫓", "♪": "♪", "¹": "¹", "²": "²", "³": "³", "⫆": "⫆", "⪾": "⪾", "⫘": "⫘", "⫄": "⫄", "⟉": "⟉", "⫗": "⫗", "⥻": "⥻", "⫂": "⫂", "⫌": "⫌", "⊋": "⊋", "⫀": "⫀", "⫈": "⫈", "⫔": "⫔", "⫖": "⫖", "⇙": "⇙", "⤪": "⤪", "ß": "ß", "⌖": "⌖", "τ": "τ", "ť": "ť", "ţ": "ţ", "т": "т", "⌕": "⌕", "𝔱": "𝔱", "θ": "θ", "ϑ": "ϑ", "þ": "þ", "×": "×", "⨱": "⨱", "⨰": "⨰", "⌶": "⌶", "⫱": "⫱", "𝕥": "𝕥", "⫚": "⫚", "‴": "‴", "▵": "▵", "≜": "≜", "◬": "◬", "⨺": "⨺", "⨹": "⨹", "⧍": "⧍", "⨻": "⨻", "⏢": "⏢", "𝓉": "𝓉", "ц": "ц", "ћ": "ћ", "ŧ": "ŧ", "⥣": "⥣", "ú": "ú", "ў": "ў", "ŭ": "ŭ", "û": "û", "у": "у", "ű": "ű", "⥾": "⥾", "𝔲": "𝔲", "ù": "ù", "▀": "▀", "⌜": "⌜", "⌏": "⌏", "◸": "◸", "ū": "ū", "ų": "ų", "𝕦": "𝕦", "υ": "υ", "⇈": "⇈", "⌝": "⌝", "⌎": "⌎", "ů": "ů", "◹": "◹", "𝓊": "𝓊", "⋰": "⋰", "ũ": "ũ", "ü": "ü", "⦧": "⦧", "⫨": "⫨", "⫩": "⫩", "⦜": "⦜", "⊊︀": "⊊︀", "⫋︀": "⫋︀", "⊋︀": "⊋︀", "⫌︀": "⫌︀", "в": "в", "⊻": "⊻", "≚": "≚", "⋮": "⋮", "𝔳": "𝔳", "𝕧": "𝕧", "𝓋": "𝓋", "⦚": "⦚", "ŵ": "ŵ", "⩟": "⩟", "≙": "≙", "℘": "℘", "𝔴": "𝔴", "𝕨": "𝕨", "𝓌": "𝓌", "𝔵": "𝔵", "ξ": "ξ", "⋻": "⋻", "𝕩": "𝕩", "𝓍": "𝓍", "ý": "ý", "я": "я", "ŷ": "ŷ", "ы": "ы", "¥": "¥", "𝔶": "𝔶", "ї": "ї", "𝕪": "𝕪", "𝓎": "𝓎", "ю": "ю", "ÿ": "ÿ", "ź": "ź", "ž": "ž", "з": "з", "ż": "ż", "ζ": "ζ", "𝔷": "𝔷", "ж": "ж", "⇝": "⇝", "𝕫": "𝕫", "𝓏": "𝓏", "": "‍", "": "‌" } } }; /***/ }), /***/ 9613: /*!***************************************************************!*\ !*** ./node_modules/html-entities/lib/numeric-unicode-map.js ***! \***************************************************************/ /***/ ((__unused_webpack_module, exports) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.numericUnicodeMap = { 0: 65533, 128: 8364, 130: 8218, 131: 402, 132: 8222, 133: 8230, 134: 8224, 135: 8225, 136: 710, 137: 8240, 138: 352, 139: 8249, 140: 338, 142: 381, 145: 8216, 146: 8217, 147: 8220, 148: 8221, 149: 8226, 150: 8211, 151: 8212, 152: 732, 153: 8482, 154: 353, 155: 8250, 156: 339, 158: 382, 159: 376 }; /***/ }), /***/ 8305: /*!***********************************************************!*\ !*** ./node_modules/html-entities/lib/surrogate-pairs.js ***! \***********************************************************/ /***/ ((__unused_webpack_module, exports) => { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.fromCodePoint = String.fromCodePoint || function (astralCodePoint) { return String.fromCharCode(Math.floor((astralCodePoint - 65536) / 1024) + 55296, (astralCodePoint - 65536) % 1024 + 56320); }; exports.getCodePoint = String.prototype.codePointAt ? function (input, position) { return input.codePointAt(position); } : function (input, position) { return (input.charCodeAt(position) - 55296) * 1024 + input.charCodeAt(position + 1) - 56320 + 65536; }; exports.highSurrogateFrom = 55296; exports.highSurrogateTo = 56319; /***/ }), /***/ 6317: /*!****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/BehaviorSubject.js ***! \****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "BehaviorSubject": () => (/* binding */ BehaviorSubject) /* harmony export */ }); /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Subject */ 228); class BehaviorSubject extends _Subject__WEBPACK_IMPORTED_MODULE_0__.Subject { constructor(_value) { super(); this._value = _value; } get value() { return this.getValue(); } _subscribe(subscriber) { const subscription = super._subscribe(subscriber); !subscription.closed && subscriber.next(this._value); return subscription; } getValue() { const { hasError, thrownError, _value } = this; if (hasError) { throw thrownError; } this._throwIfClosed(); return _value; } next(value) { super.next(this._value = value); } } /***/ }), /***/ 3279: /*!**********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/NotificationFactories.js ***! \**********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "COMPLETE_NOTIFICATION": () => (/* binding */ COMPLETE_NOTIFICATION), /* harmony export */ "createNotification": () => (/* binding */ createNotification), /* harmony export */ "errorNotification": () => (/* binding */ errorNotification), /* harmony export */ "nextNotification": () => (/* binding */ nextNotification) /* harmony export */ }); const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined))(); function errorNotification(error) { return createNotification('E', undefined, error); } function nextNotification(value) { return createNotification('N', value, undefined); } function createNotification(kind, value, error) { return { kind, value, error }; } /***/ }), /***/ 833: /*!***********************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/Observable.js ***! \***********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "Observable": () => (/* binding */ Observable) /* harmony export */ }); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Subscriber */ 9904); /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./Subscription */ 6078); /* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./symbol/observable */ 4585); /* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./util/pipe */ 629); /* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./config */ 9057); /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./util/isFunction */ 2971); /* harmony import */ var _util_errorContext__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./util/errorContext */ 2309); class Observable { constructor(subscribe) { if (subscribe) { this._subscribe = subscribe; } } lift(operator) { const observable = new Observable(); observable.source = this; observable.operator = operator; return observable; } subscribe(observerOrNext, error, complete) { const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new _Subscriber__WEBPACK_IMPORTED_MODULE_0__.SafeSubscriber(observerOrNext, error, complete); (0,_util_errorContext__WEBPACK_IMPORTED_MODULE_1__.errorContext)(() => { const { operator, source } = this; subscriber.add(operator ? operator.call(subscriber, source) : source ? this._subscribe(subscriber) : this._trySubscribe(subscriber)); }); return subscriber; } _trySubscribe(sink) { try { return this._subscribe(sink); } catch (err) { sink.error(err); } } forEach(next, promiseCtor) { promiseCtor = getPromiseCtor(promiseCtor); return new promiseCtor((resolve, reject) => { const subscriber = new _Subscriber__WEBPACK_IMPORTED_MODULE_0__.SafeSubscriber({ next: value => { try { next(value); } catch (err) { reject(err); subscriber.unsubscribe(); } }, error: reject, complete: resolve }); this.subscribe(subscriber); }); } _subscribe(subscriber) { var _a; return (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber); } [_symbol_observable__WEBPACK_IMPORTED_MODULE_2__.observable]() { return this; } pipe(...operations) { return (0,_util_pipe__WEBPACK_IMPORTED_MODULE_3__.pipeFromArray)(operations)(this); } toPromise(promiseCtor) { promiseCtor = getPromiseCtor(promiseCtor); return new promiseCtor((resolve, reject) => { let value; this.subscribe(x => value = x, err => reject(err), () => resolve(value)); }); } } Observable.create = subscribe => { return new Observable(subscribe); }; function getPromiseCtor(promiseCtor) { var _a; return (_a = promiseCtor !== null && promiseCtor !== void 0 ? promiseCtor : _config__WEBPACK_IMPORTED_MODULE_4__.config.Promise) !== null && _a !== void 0 ? _a : Promise; } function isObserver(value) { return value && (0,_util_isFunction__WEBPACK_IMPORTED_MODULE_5__.isFunction)(value.next) && (0,_util_isFunction__WEBPACK_IMPORTED_MODULE_5__.isFunction)(value.error) && (0,_util_isFunction__WEBPACK_IMPORTED_MODULE_5__.isFunction)(value.complete); } function isSubscriber(value) { return value && value instanceof _Subscriber__WEBPACK_IMPORTED_MODULE_0__.Subscriber || isObserver(value) && (0,_Subscription__WEBPACK_IMPORTED_MODULE_6__.isSubscription)(value); } /***/ }), /***/ 228: /*!********************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/Subject.js ***! \********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "AnonymousSubject": () => (/* binding */ AnonymousSubject), /* harmony export */ "Subject": () => (/* binding */ Subject) /* harmony export */ }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Observable */ 833); /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Subscription */ 6078); /* harmony import */ var _util_ObjectUnsubscribedError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./util/ObjectUnsubscribedError */ 9872); /* harmony import */ var _util_arrRemove__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./util/arrRemove */ 9663); /* harmony import */ var _util_errorContext__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./util/errorContext */ 2309); class Subject extends _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable { constructor() { super(); this.closed = false; this.currentObservers = null; this.observers = []; this.isStopped = false; this.hasError = false; this.thrownError = null; } lift(operator) { const subject = new AnonymousSubject(this, this); subject.operator = operator; return subject; } _throwIfClosed() { if (this.closed) { throw new _util_ObjectUnsubscribedError__WEBPACK_IMPORTED_MODULE_1__.ObjectUnsubscribedError(); } } next(value) { (0,_util_errorContext__WEBPACK_IMPORTED_MODULE_2__.errorContext)(() => { this._throwIfClosed(); if (!this.isStopped) { if (!this.currentObservers) { this.currentObservers = Array.from(this.observers); } for (const observer of this.currentObservers) { observer.next(value); } } }); } error(err) { (0,_util_errorContext__WEBPACK_IMPORTED_MODULE_2__.errorContext)(() => { this._throwIfClosed(); if (!this.isStopped) { this.hasError = this.isStopped = true; this.thrownError = err; const { observers } = this; while (observers.length) { observers.shift().error(err); } } }); } complete() { (0,_util_errorContext__WEBPACK_IMPORTED_MODULE_2__.errorContext)(() => { this._throwIfClosed(); if (!this.isStopped) { this.isStopped = true; const { observers } = this; while (observers.length) { observers.shift().complete(); } } }); } unsubscribe() { this.isStopped = this.closed = true; this.observers = this.currentObservers = null; } get observed() { var _a; return ((_a = this.observers) === null || _a === void 0 ? void 0 : _a.length) > 0; } _trySubscribe(subscriber) { this._throwIfClosed(); return super._trySubscribe(subscriber); } _subscribe(subscriber) { this._throwIfClosed(); this._checkFinalizedStatuses(subscriber); return this._innerSubscribe(subscriber); } _innerSubscribe(subscriber) { const { hasError, isStopped, observers } = this; if (hasError || isStopped) { return _Subscription__WEBPACK_IMPORTED_MODULE_3__.EMPTY_SUBSCRIPTION; } this.currentObservers = null; observers.push(subscriber); return new _Subscription__WEBPACK_IMPORTED_MODULE_3__.Subscription(() => { this.currentObservers = null; (0,_util_arrRemove__WEBPACK_IMPORTED_MODULE_4__.arrRemove)(observers, subscriber); }); } _checkFinalizedStatuses(subscriber) { const { hasError, thrownError, isStopped } = this; if (hasError) { subscriber.error(thrownError); } else if (isStopped) { subscriber.complete(); } } asObservable() { const observable = new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(); observable.source = this; return observable; } } Subject.create = (destination, source) => { return new AnonymousSubject(destination, source); }; class AnonymousSubject extends Subject { constructor(destination, source) { super(); this.destination = destination; this.source = source; } next(value) { var _a, _b; (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.next) === null || _b === void 0 ? void 0 : _b.call(_a, value); } error(err) { var _a, _b; (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.call(_a, err); } complete() { var _a, _b; (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.complete) === null || _b === void 0 ? void 0 : _b.call(_a); } _subscribe(subscriber) { var _a, _b; return (_b = (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber)) !== null && _b !== void 0 ? _b : _Subscription__WEBPACK_IMPORTED_MODULE_3__.EMPTY_SUBSCRIPTION; } } /***/ }), /***/ 9904: /*!***********************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/Subscriber.js ***! \***********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "EMPTY_OBSERVER": () => (/* binding */ EMPTY_OBSERVER), /* harmony export */ "SafeSubscriber": () => (/* binding */ SafeSubscriber), /* harmony export */ "Subscriber": () => (/* binding */ Subscriber) /* harmony export */ }); /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./util/isFunction */ 2971); /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Subscription */ 6078); /* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./config */ 9057); /* harmony import */ var _util_reportUnhandledError__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./util/reportUnhandledError */ 4709); /* harmony import */ var _util_noop__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./util/noop */ 9635); /* harmony import */ var _NotificationFactories__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./NotificationFactories */ 3279); /* harmony import */ var _scheduler_timeoutProvider__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./scheduler/timeoutProvider */ 3542); /* harmony import */ var _util_errorContext__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./util/errorContext */ 2309); class Subscriber extends _Subscription__WEBPACK_IMPORTED_MODULE_0__.Subscription { constructor(destination) { super(); this.isStopped = false; if (destination) { this.destination = destination; if ((0,_Subscription__WEBPACK_IMPORTED_MODULE_0__.isSubscription)(destination)) { destination.add(this); } } else { this.destination = EMPTY_OBSERVER; } } static create(next, error, complete) { return new SafeSubscriber(next, error, complete); } next(value) { if (this.isStopped) { handleStoppedNotification((0,_NotificationFactories__WEBPACK_IMPORTED_MODULE_1__.nextNotification)(value), this); } else { this._next(value); } } error(err) { if (this.isStopped) { handleStoppedNotification((0,_NotificationFactories__WEBPACK_IMPORTED_MODULE_1__.errorNotification)(err), this); } else { this.isStopped = true; this._error(err); } } complete() { if (this.isStopped) { handleStoppedNotification(_NotificationFactories__WEBPACK_IMPORTED_MODULE_1__.COMPLETE_NOTIFICATION, this); } else { this.isStopped = true; this._complete(); } } unsubscribe() { if (!this.closed) { this.isStopped = true; super.unsubscribe(); this.destination = null; } } _next(value) { this.destination.next(value); } _error(err) { try { this.destination.error(err); } finally { this.unsubscribe(); } } _complete() { try { this.destination.complete(); } finally { this.unsubscribe(); } } } const _bind = Function.prototype.bind; function bind(fn, thisArg) { return _bind.call(fn, thisArg); } class ConsumerObserver { constructor(partialObserver) { this.partialObserver = partialObserver; } next(value) { const { partialObserver } = this; if (partialObserver.next) { try { partialObserver.next(value); } catch (error) { handleUnhandledError(error); } } } error(err) { const { partialObserver } = this; if (partialObserver.error) { try { partialObserver.error(err); } catch (error) { handleUnhandledError(error); } } else { handleUnhandledError(err); } } complete() { const { partialObserver } = this; if (partialObserver.complete) { try { partialObserver.complete(); } catch (error) { handleUnhandledError(error); } } } } class SafeSubscriber extends Subscriber { constructor(observerOrNext, error, complete) { super(); let partialObserver; if ((0,_util_isFunction__WEBPACK_IMPORTED_MODULE_2__.isFunction)(observerOrNext) || !observerOrNext) { partialObserver = { next: observerOrNext !== null && observerOrNext !== void 0 ? observerOrNext : undefined, error: error !== null && error !== void 0 ? error : undefined, complete: complete !== null && complete !== void 0 ? complete : undefined }; } else { let context; if (this && _config__WEBPACK_IMPORTED_MODULE_3__.config.useDeprecatedNextContext) { context = Object.create(observerOrNext); context.unsubscribe = () => this.unsubscribe(); partialObserver = { next: observerOrNext.next && bind(observerOrNext.next, context), error: observerOrNext.error && bind(observerOrNext.error, context), complete: observerOrNext.complete && bind(observerOrNext.complete, context) }; } else { partialObserver = observerOrNext; } } this.destination = new ConsumerObserver(partialObserver); } } function handleUnhandledError(error) { if (_config__WEBPACK_IMPORTED_MODULE_3__.config.useDeprecatedSynchronousErrorHandling) { (0,_util_errorContext__WEBPACK_IMPORTED_MODULE_4__.captureError)(error); } else { (0,_util_reportUnhandledError__WEBPACK_IMPORTED_MODULE_5__.reportUnhandledError)(error); } } function defaultErrorHandler(err) { throw err; } function handleStoppedNotification(notification, subscriber) { const { onStoppedNotification } = _config__WEBPACK_IMPORTED_MODULE_3__.config; onStoppedNotification && _scheduler_timeoutProvider__WEBPACK_IMPORTED_MODULE_6__.timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber)); } const EMPTY_OBSERVER = { closed: true, next: _util_noop__WEBPACK_IMPORTED_MODULE_7__.noop, error: defaultErrorHandler, complete: _util_noop__WEBPACK_IMPORTED_MODULE_7__.noop }; /***/ }), /***/ 6078: /*!*************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/Subscription.js ***! \*************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "EMPTY_SUBSCRIPTION": () => (/* binding */ EMPTY_SUBSCRIPTION), /* harmony export */ "Subscription": () => (/* binding */ Subscription), /* harmony export */ "isSubscription": () => (/* binding */ isSubscription) /* harmony export */ }); /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util/isFunction */ 2971); /* harmony import */ var _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./util/UnsubscriptionError */ 2524); /* harmony import */ var _util_arrRemove__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./util/arrRemove */ 9663); class Subscription { constructor(initialTeardown) { this.initialTeardown = initialTeardown; this.closed = false; this._parentage = null; this._finalizers = null; } unsubscribe() { let errors; if (!this.closed) { this.closed = true; const { _parentage } = this; if (_parentage) { this._parentage = null; if (Array.isArray(_parentage)) { for (const parent of _parentage) { parent.remove(this); } } else { _parentage.remove(this); } } const { initialTeardown: initialFinalizer } = this; if ((0,_util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(initialFinalizer)) { try { initialFinalizer(); } catch (e) { errors = e instanceof _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_1__.UnsubscriptionError ? e.errors : [e]; } } const { _finalizers } = this; if (_finalizers) { this._finalizers = null; for (const finalizer of _finalizers) { try { execFinalizer(finalizer); } catch (err) { errors = errors !== null && errors !== void 0 ? errors : []; if (err instanceof _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_1__.UnsubscriptionError) { errors = [...errors, ...err.errors]; } else { errors.push(err); } } } } if (errors) { throw new _util_UnsubscriptionError__WEBPACK_IMPORTED_MODULE_1__.UnsubscriptionError(errors); } } } add(teardown) { var _a; if (teardown && teardown !== this) { if (this.closed) { execFinalizer(teardown); } else { if (teardown instanceof Subscription) { if (teardown.closed || teardown._hasParent(this)) { return; } teardown._addParent(this); } (this._finalizers = (_a = this._finalizers) !== null && _a !== void 0 ? _a : []).push(teardown); } } } _hasParent(parent) { const { _parentage } = this; return _parentage === parent || Array.isArray(_parentage) && _parentage.includes(parent); } _addParent(parent) { const { _parentage } = this; this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent; } _removeParent(parent) { const { _parentage } = this; if (_parentage === parent) { this._parentage = null; } else if (Array.isArray(_parentage)) { (0,_util_arrRemove__WEBPACK_IMPORTED_MODULE_2__.arrRemove)(_parentage, parent); } } remove(teardown) { const { _finalizers } = this; _finalizers && (0,_util_arrRemove__WEBPACK_IMPORTED_MODULE_2__.arrRemove)(_finalizers, teardown); if (teardown instanceof Subscription) { teardown._removeParent(this); } } } Subscription.EMPTY = (() => { const empty = new Subscription(); empty.closed = true; return empty; })(); const EMPTY_SUBSCRIPTION = Subscription.EMPTY; function isSubscription(value) { return value instanceof Subscription || value && 'closed' in value && (0,_util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(value.remove) && (0,_util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(value.add) && (0,_util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(value.unsubscribe); } function execFinalizer(finalizer) { if ((0,_util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(finalizer)) { finalizer(); } else { finalizer.unsubscribe(); } } /***/ }), /***/ 9057: /*!*******************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/config.js ***! \*******************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "config": () => (/* binding */ config) /* harmony export */ }); const config = { onUnhandledError: null, onStoppedNotification: null, Promise: undefined, useDeprecatedSynchronousErrorHandling: false, useDeprecatedNextContext: false }; /***/ }), /***/ 3932: /*!*********************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/observable/ConnectableObservable.js ***! \*********************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "ConnectableObservable": () => (/* binding */ ConnectableObservable) /* harmony export */ }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Observable */ 833); /* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../Subscription */ 6078); /* harmony import */ var _operators_refCount__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../operators/refCount */ 6074); /* harmony import */ var _operators_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../operators/OperatorSubscriber */ 3945); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/lift */ 1944); class ConnectableObservable extends _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable { constructor(source, subjectFactory) { super(); this.source = source; this.subjectFactory = subjectFactory; this._subject = null; this._refCount = 0; this._connection = null; if ((0,_util_lift__WEBPACK_IMPORTED_MODULE_1__.hasLift)(source)) { this.lift = source.lift; } } _subscribe(subscriber) { return this.getSubject().subscribe(subscriber); } getSubject() { const subject = this._subject; if (!subject || subject.isStopped) { this._subject = this.subjectFactory(); } return this._subject; } _teardown() { this._refCount = 0; const { _connection } = this; this._subject = this._connection = null; _connection === null || _connection === void 0 ? void 0 : _connection.unsubscribe(); } connect() { let connection = this._connection; if (!connection) { connection = this._connection = new _Subscription__WEBPACK_IMPORTED_MODULE_2__.Subscription(); const subject = this.getSubject(); connection.add(this.source.subscribe((0,_operators_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_3__.createOperatorSubscriber)(subject, undefined, () => { this._teardown(); subject.complete(); }, err => { this._teardown(); subject.error(err); }, () => this._teardown()))); if (connection.closed) { this._connection = null; connection = _Subscription__WEBPACK_IMPORTED_MODULE_2__.Subscription.EMPTY; } } return connection; } refCount() { return (0,_operators_refCount__WEBPACK_IMPORTED_MODULE_4__.refCount)()(this); } } /***/ }), /***/ 6562: /*!*************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/observable/combineLatest.js ***! \*************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "combineLatest": () => (/* binding */ combineLatest), /* harmony export */ "combineLatestInit": () => (/* binding */ combineLatestInit) /* harmony export */ }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../Observable */ 833); /* harmony import */ var _util_argsArgArrayOrObject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/argsArgArrayOrObject */ 5756); /* harmony import */ var _from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./from */ 9346); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../util/identity */ 9173); /* harmony import */ var _util_mapOneOrManyArgs__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../util/mapOneOrManyArgs */ 8385); /* harmony import */ var _util_args__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/args */ 420); /* harmony import */ var _util_createObject__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../util/createObject */ 1054); /* harmony import */ var _operators_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../operators/OperatorSubscriber */ 3945); /* harmony import */ var _util_executeSchedule__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../util/executeSchedule */ 1817); function combineLatest(...args) { const scheduler = (0,_util_args__WEBPACK_IMPORTED_MODULE_0__.popScheduler)(args); const resultSelector = (0,_util_args__WEBPACK_IMPORTED_MODULE_0__.popResultSelector)(args); const { args: observables, keys } = (0,_util_argsArgArrayOrObject__WEBPACK_IMPORTED_MODULE_1__.argsArgArrayOrObject)(args); if (observables.length === 0) { return (0,_from__WEBPACK_IMPORTED_MODULE_2__.from)([], scheduler); } const result = new _Observable__WEBPACK_IMPORTED_MODULE_3__.Observable(combineLatestInit(observables, scheduler, keys ? values => (0,_util_createObject__WEBPACK_IMPORTED_MODULE_4__.createObject)(keys, values) : _util_identity__WEBPACK_IMPORTED_MODULE_5__.identity)); return resultSelector ? result.pipe((0,_util_mapOneOrManyArgs__WEBPACK_IMPORTED_MODULE_6__.mapOneOrManyArgs)(resultSelector)) : result; } function combineLatestInit(observables, scheduler, valueTransform = _util_identity__WEBPACK_IMPORTED_MODULE_5__.identity) { return subscriber => { maybeSchedule(scheduler, () => { const { length } = observables; const values = new Array(length); let active = length; let remainingFirstValues = length; for (let i = 0; i < length; i++) { maybeSchedule(scheduler, () => { const source = (0,_from__WEBPACK_IMPORTED_MODULE_2__.from)(observables[i], scheduler); let hasFirstValue = false; source.subscribe((0,_operators_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_7__.createOperatorSubscriber)(subscriber, value => { values[i] = value; if (!hasFirstValue) { hasFirstValue = true; remainingFirstValues--; } if (!remainingFirstValues) { subscriber.next(valueTransform(values.slice())); } }, () => { if (! --active) { subscriber.complete(); } })); }, subscriber); } }, subscriber); }; } function maybeSchedule(scheduler, execute, subscription) { if (scheduler) { (0,_util_executeSchedule__WEBPACK_IMPORTED_MODULE_8__.executeSchedule)(subscription, scheduler, execute); } else { execute(); } } /***/ }), /***/ 4240: /*!******************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/observable/concat.js ***! \******************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "concat": () => (/* binding */ concat) /* harmony export */ }); /* harmony import */ var _operators_concatAll__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../operators/concatAll */ 4770); /* harmony import */ var _util_args__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../util/args */ 420); /* harmony import */ var _from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./from */ 9346); function concat(...args) { return (0,_operators_concatAll__WEBPACK_IMPORTED_MODULE_0__.concatAll)()((0,_from__WEBPACK_IMPORTED_MODULE_1__.from)(args, (0,_util_args__WEBPACK_IMPORTED_MODULE_2__.popScheduler)(args))); } /***/ }), /***/ 1954: /*!*****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/observable/defer.js ***! \*****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "defer": () => (/* binding */ defer) /* harmony export */ }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Observable */ 833); /* harmony import */ var _innerFrom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./innerFrom */ 4987); function defer(observableFactory) { return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { (0,_innerFrom__WEBPACK_IMPORTED_MODULE_1__.innerFrom)(observableFactory()).subscribe(subscriber); }); } /***/ }), /***/ 591: /*!*****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/observable/empty.js ***! \*****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "EMPTY": () => (/* binding */ EMPTY), /* harmony export */ "empty": () => (/* binding */ empty) /* harmony export */ }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Observable */ 833); const EMPTY = new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => subscriber.complete()); function empty(scheduler) { return scheduler ? emptyScheduled(scheduler) : EMPTY; } function emptyScheduled(scheduler) { return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => scheduler.schedule(() => subscriber.complete())); } /***/ }), /***/ 1640: /*!********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/observable/forkJoin.js ***! \********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "forkJoin": () => (/* binding */ forkJoin) /* harmony export */ }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../Observable */ 833); /* harmony import */ var _util_argsArgArrayOrObject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/argsArgArrayOrObject */ 5756); /* harmony import */ var _innerFrom__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./innerFrom */ 4987); /* harmony import */ var _util_args__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/args */ 420); /* harmony import */ var _operators_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../operators/OperatorSubscriber */ 3945); /* harmony import */ var _util_mapOneOrManyArgs__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../util/mapOneOrManyArgs */ 8385); /* harmony import */ var _util_createObject__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../util/createObject */ 1054); function forkJoin(...args) { const resultSelector = (0,_util_args__WEBPACK_IMPORTED_MODULE_0__.popResultSelector)(args); const { args: sources, keys } = (0,_util_argsArgArrayOrObject__WEBPACK_IMPORTED_MODULE_1__.argsArgArrayOrObject)(args); const result = new _Observable__WEBPACK_IMPORTED_MODULE_2__.Observable(subscriber => { const { length } = sources; if (!length) { subscriber.complete(); return; } const values = new Array(length); let remainingCompletions = length; let remainingEmissions = length; for (let sourceIndex = 0; sourceIndex < length; sourceIndex++) { let hasValue = false; (0,_innerFrom__WEBPACK_IMPORTED_MODULE_3__.innerFrom)(sources[sourceIndex]).subscribe((0,_operators_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_4__.createOperatorSubscriber)(subscriber, value => { if (!hasValue) { hasValue = true; remainingEmissions--; } values[sourceIndex] = value; }, () => remainingCompletions--, undefined, () => { if (!remainingCompletions || !hasValue) { if (!remainingEmissions) { subscriber.next(keys ? (0,_util_createObject__WEBPACK_IMPORTED_MODULE_5__.createObject)(keys, values) : values); } subscriber.complete(); } })); } }); return resultSelector ? result.pipe((0,_util_mapOneOrManyArgs__WEBPACK_IMPORTED_MODULE_6__.mapOneOrManyArgs)(resultSelector)) : result; } /***/ }), /***/ 9346: /*!****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/observable/from.js ***! \****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "from": () => (/* binding */ from) /* harmony export */ }); /* harmony import */ var _scheduled_scheduled__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../scheduled/scheduled */ 9517); /* harmony import */ var _innerFrom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./innerFrom */ 4987); function from(input, scheduler) { return scheduler ? (0,_scheduled_scheduled__WEBPACK_IMPORTED_MODULE_0__.scheduled)(input, scheduler) : (0,_innerFrom__WEBPACK_IMPORTED_MODULE_1__.innerFrom)(input); } /***/ }), /***/ 4987: /*!*********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/observable/innerFrom.js ***! \*********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "fromArrayLike": () => (/* binding */ fromArrayLike), /* harmony export */ "fromAsyncIterable": () => (/* binding */ fromAsyncIterable), /* harmony export */ "fromInteropObservable": () => (/* binding */ fromInteropObservable), /* harmony export */ "fromIterable": () => (/* binding */ fromIterable), /* harmony export */ "fromPromise": () => (/* binding */ fromPromise), /* harmony export */ "fromReadableStreamLike": () => (/* binding */ fromReadableStreamLike), /* harmony export */ "innerFrom": () => (/* binding */ innerFrom) /* harmony export */ }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! tslib */ 2321); /* harmony import */ var _util_isArrayLike__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../util/isArrayLike */ 9806); /* harmony import */ var _util_isPromise__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../util/isPromise */ 9548); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Observable */ 833); /* harmony import */ var _util_isInteropObservable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/isInteropObservable */ 1331); /* harmony import */ var _util_isAsyncIterable__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../util/isAsyncIterable */ 470); /* harmony import */ var _util_throwUnobservableError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../util/throwUnobservableError */ 7785); /* harmony import */ var _util_isIterable__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../util/isIterable */ 3433); /* harmony import */ var _util_isReadableStreamLike__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../util/isReadableStreamLike */ 181); /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../util/isFunction */ 2971); /* harmony import */ var _util_reportUnhandledError__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../util/reportUnhandledError */ 4709); /* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../symbol/observable */ 4585); function innerFrom(input) { if (input instanceof _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable) { return input; } if (input != null) { if ((0,_util_isInteropObservable__WEBPACK_IMPORTED_MODULE_1__.isInteropObservable)(input)) { return fromInteropObservable(input); } if ((0,_util_isArrayLike__WEBPACK_IMPORTED_MODULE_2__.isArrayLike)(input)) { return fromArrayLike(input); } if ((0,_util_isPromise__WEBPACK_IMPORTED_MODULE_3__.isPromise)(input)) { return fromPromise(input); } if ((0,_util_isAsyncIterable__WEBPACK_IMPORTED_MODULE_4__.isAsyncIterable)(input)) { return fromAsyncIterable(input); } if ((0,_util_isIterable__WEBPACK_IMPORTED_MODULE_5__.isIterable)(input)) { return fromIterable(input); } if ((0,_util_isReadableStreamLike__WEBPACK_IMPORTED_MODULE_6__.isReadableStreamLike)(input)) { return fromReadableStreamLike(input); } } throw (0,_util_throwUnobservableError__WEBPACK_IMPORTED_MODULE_7__.createInvalidObservableTypeError)(input); } function fromInteropObservable(obj) { return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { const obs = obj[_symbol_observable__WEBPACK_IMPORTED_MODULE_8__.observable](); if ((0,_util_isFunction__WEBPACK_IMPORTED_MODULE_9__.isFunction)(obs.subscribe)) { return obs.subscribe(subscriber); } throw new TypeError('Provided object does not correctly implement Symbol.observable'); }); } function fromArrayLike(array) { return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { for (let i = 0; i < array.length && !subscriber.closed; i++) { subscriber.next(array[i]); } subscriber.complete(); }); } function fromPromise(promise) { return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { promise.then(value => { if (!subscriber.closed) { subscriber.next(value); subscriber.complete(); } }, err => subscriber.error(err)).then(null, _util_reportUnhandledError__WEBPACK_IMPORTED_MODULE_10__.reportUnhandledError); }); } function fromIterable(iterable) { return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { for (const value of iterable) { subscriber.next(value); if (subscriber.closed) { return; } } subscriber.complete(); }); } function fromAsyncIterable(asyncIterable) { return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { process(asyncIterable, subscriber).catch(err => subscriber.error(err)); }); } function fromReadableStreamLike(readableStream) { return fromAsyncIterable((0,_util_isReadableStreamLike__WEBPACK_IMPORTED_MODULE_6__.readableStreamLikeToAsyncGenerator)(readableStream)); } function process(asyncIterable, subscriber) { var asyncIterable_1, asyncIterable_1_1; var e_1, _a; return (0,tslib__WEBPACK_IMPORTED_MODULE_11__.__awaiter)(this, void 0, void 0, function* () { try { for (asyncIterable_1 = (0,tslib__WEBPACK_IMPORTED_MODULE_11__.__asyncValues)(asyncIterable); asyncIterable_1_1 = yield asyncIterable_1.next(), !asyncIterable_1_1.done;) { const value = asyncIterable_1_1.value; subscriber.next(value); if (subscriber.closed) { return; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (asyncIterable_1_1 && !asyncIterable_1_1.done && (_a = asyncIterable_1.return)) yield _a.call(asyncIterable_1); } finally { if (e_1) throw e_1.error; } } subscriber.complete(); }); } /***/ }), /***/ 6646: /*!*****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/observable/merge.js ***! \*****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "merge": () => (/* binding */ merge) /* harmony export */ }); /* harmony import */ var _operators_mergeAll__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../operators/mergeAll */ 1308); /* harmony import */ var _innerFrom__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./innerFrom */ 4987); /* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./empty */ 591); /* harmony import */ var _util_args__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/args */ 420); /* harmony import */ var _from__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./from */ 9346); function merge(...args) { const scheduler = (0,_util_args__WEBPACK_IMPORTED_MODULE_0__.popScheduler)(args); const concurrent = (0,_util_args__WEBPACK_IMPORTED_MODULE_0__.popNumber)(args, Infinity); const sources = args; return !sources.length ? _empty__WEBPACK_IMPORTED_MODULE_1__.EMPTY : sources.length === 1 ? (0,_innerFrom__WEBPACK_IMPORTED_MODULE_2__.innerFrom)(sources[0]) : (0,_operators_mergeAll__WEBPACK_IMPORTED_MODULE_3__.mergeAll)(concurrent)((0,_from__WEBPACK_IMPORTED_MODULE_4__.from)(sources, scheduler)); } /***/ }), /***/ 745: /*!**************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/observable/of.js ***! \**************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "of": () => (/* binding */ of) /* harmony export */ }); /* harmony import */ var _util_args__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/args */ 420); /* harmony import */ var _from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./from */ 9346); function of(...args) { const scheduler = (0,_util_args__WEBPACK_IMPORTED_MODULE_0__.popScheduler)(args); return (0,_from__WEBPACK_IMPORTED_MODULE_1__.from)(args, scheduler); } /***/ }), /***/ 5474: /*!**********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/observable/throwError.js ***! \**********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "throwError": () => (/* binding */ throwError) /* harmony export */ }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../Observable */ 833); /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/isFunction */ 2971); function throwError(errorOrErrorFactory, scheduler) { const errorFactory = (0,_util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(errorOrErrorFactory) ? errorOrErrorFactory : () => errorOrErrorFactory; const init = subscriber => subscriber.error(errorFactory()); return new _Observable__WEBPACK_IMPORTED_MODULE_1__.Observable(scheduler ? subscriber => scheduler.schedule(init, 0, subscriber) : init); } /***/ }), /***/ 3945: /*!*****************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/OperatorSubscriber.js ***! \*****************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "OperatorSubscriber": () => (/* binding */ OperatorSubscriber), /* harmony export */ "createOperatorSubscriber": () => (/* binding */ createOperatorSubscriber) /* harmony export */ }); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Subscriber */ 9904); function createOperatorSubscriber(destination, onNext, onComplete, onError, onFinalize) { return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize); } class OperatorSubscriber extends _Subscriber__WEBPACK_IMPORTED_MODULE_0__.Subscriber { constructor(destination, onNext, onComplete, onError, onFinalize, shouldUnsubscribe) { super(destination); this.onFinalize = onFinalize; this.shouldUnsubscribe = shouldUnsubscribe; this._next = onNext ? function (value) { try { onNext(value); } catch (err) { destination.error(err); } } : super._next; this._error = onError ? function (err) { try { onError(err); } catch (err) { destination.error(err); } finally { this.unsubscribe(); } } : super._error; this._complete = onComplete ? function () { try { onComplete(); } catch (err) { destination.error(err); } finally { this.unsubscribe(); } } : super._complete; } unsubscribe() { var _a; if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) { const { closed } = this; super.unsubscribe(); !closed && ((_a = this.onFinalize) === null || _a === void 0 ? void 0 : _a.call(this)); } } } /***/ }), /***/ 3158: /*!*********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/catchError.js ***! \*********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "catchError": () => (/* binding */ catchError) /* harmony export */ }); /* harmony import */ var _observable_innerFrom__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../observable/innerFrom */ 4987); /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./OperatorSubscriber */ 3945); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/lift */ 1944); function catchError(selector) { return (0,_util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { let innerSub = null; let syncUnsub = false; let handledResult; innerSub = source.subscribe((0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)(subscriber, undefined, undefined, err => { handledResult = (0,_observable_innerFrom__WEBPACK_IMPORTED_MODULE_2__.innerFrom)(selector(err, catchError(selector)(source))); if (innerSub) { innerSub.unsubscribe(); innerSub = null; handledResult.subscribe(subscriber); } else { syncUnsub = true; } })); if (syncUnsub) { innerSub.unsubscribe(); innerSub = null; handledResult.subscribe(subscriber); } }); } /***/ }), /***/ 4770: /*!********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/concatAll.js ***! \********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "concatAll": () => (/* binding */ concatAll) /* harmony export */ }); /* harmony import */ var _mergeAll__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./mergeAll */ 1308); function concatAll() { return (0,_mergeAll__WEBPACK_IMPORTED_MODULE_0__.mergeAll)(1); } /***/ }), /***/ 3853: /*!********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/concatMap.js ***! \********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "concatMap": () => (/* binding */ concatMap) /* harmony export */ }); /* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./mergeMap */ 1353); /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/isFunction */ 2971); function concatMap(project, resultSelector) { return (0,_util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(resultSelector) ? (0,_mergeMap__WEBPACK_IMPORTED_MODULE_1__.mergeMap)(project, resultSelector, 1) : (0,_mergeMap__WEBPACK_IMPORTED_MODULE_1__.mergeMap)(project, 1); } /***/ }), /***/ 4744: /*!*************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/defaultIfEmpty.js ***! \*************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "defaultIfEmpty": () => (/* binding */ defaultIfEmpty) /* harmony export */ }); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/lift */ 1944); /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./OperatorSubscriber */ 3945); function defaultIfEmpty(defaultValue) { return (0,_util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { let hasValue = false; source.subscribe((0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)(subscriber, value => { hasValue = true; subscriber.next(value); }, () => { if (!hasValue) { subscriber.next(defaultValue); } subscriber.complete(); })); }); } /***/ }), /***/ 116: /*!*****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/filter.js ***! \*****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "filter": () => (/* binding */ filter) /* harmony export */ }); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/lift */ 1944); /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./OperatorSubscriber */ 3945); function filter(predicate, thisArg) { return (0,_util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { let index = 0; source.subscribe((0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)(subscriber, value => predicate.call(thisArg, value, index++) && subscriber.next(value))); }); } /***/ }), /***/ 2313: /*!*******************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/finalize.js ***! \*******************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "finalize": () => (/* binding */ finalize) /* harmony export */ }); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/lift */ 1944); function finalize(callback) { return (0,_util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { try { source.subscribe(subscriber); } finally { subscriber.add(callback); } }); } /***/ }), /***/ 155: /*!****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/first.js ***! \****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "first": () => (/* binding */ first) /* harmony export */ }); /* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../util/EmptyError */ 4423); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./filter */ 116); /* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./take */ 9295); /* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./defaultIfEmpty */ 4744); /* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./throwIfEmpty */ 7354); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/identity */ 9173); function first(predicate, defaultValue) { const hasDefaultValue = arguments.length >= 2; return source => source.pipe(predicate ? (0,_filter__WEBPACK_IMPORTED_MODULE_0__.filter)((v, i) => predicate(v, i, source)) : _util_identity__WEBPACK_IMPORTED_MODULE_1__.identity, (0,_take__WEBPACK_IMPORTED_MODULE_2__.take)(1), hasDefaultValue ? (0,_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__.defaultIfEmpty)(defaultValue) : (0,_throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__.throwIfEmpty)(() => new _util_EmptyError__WEBPACK_IMPORTED_MODULE_5__.EmptyError())); } /***/ }), /***/ 1955: /*!***************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/last.js ***! \***************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "last": () => (/* binding */ last) /* harmony export */ }); /* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../util/EmptyError */ 4423); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./filter */ 116); /* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./takeLast */ 9601); /* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./throwIfEmpty */ 7354); /* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./defaultIfEmpty */ 4744); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/identity */ 9173); function last(predicate, defaultValue) { const hasDefaultValue = arguments.length >= 2; return source => source.pipe(predicate ? (0,_filter__WEBPACK_IMPORTED_MODULE_0__.filter)((v, i) => predicate(v, i, source)) : _util_identity__WEBPACK_IMPORTED_MODULE_1__.identity, (0,_takeLast__WEBPACK_IMPORTED_MODULE_2__.takeLast)(1), hasDefaultValue ? (0,_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__.defaultIfEmpty)(defaultValue) : (0,_throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__.throwIfEmpty)(() => new _util_EmptyError__WEBPACK_IMPORTED_MODULE_5__.EmptyError())); } /***/ }), /***/ 635: /*!**************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/map.js ***! \**************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "map": () => (/* binding */ map) /* harmony export */ }); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/lift */ 1944); /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./OperatorSubscriber */ 3945); function map(project, thisArg) { return (0,_util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { let index = 0; source.subscribe((0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)(subscriber, value => { subscriber.next(project.call(thisArg, value, index++)); })); }); } /***/ }), /***/ 73: /*!****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/mapTo.js ***! \****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "mapTo": () => (/* binding */ mapTo) /* harmony export */ }); /* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./map */ 635); function mapTo(value) { return (0,_map__WEBPACK_IMPORTED_MODULE_0__.map)(() => value); } /***/ }), /***/ 1308: /*!*******************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/mergeAll.js ***! \*******************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "mergeAll": () => (/* binding */ mergeAll) /* harmony export */ }); /* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./mergeMap */ 1353); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/identity */ 9173); function mergeAll(concurrent = Infinity) { return (0,_mergeMap__WEBPACK_IMPORTED_MODULE_0__.mergeMap)(_util_identity__WEBPACK_IMPORTED_MODULE_1__.identity, concurrent); } /***/ }), /***/ 9280: /*!*************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/mergeInternals.js ***! \*************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "mergeInternals": () => (/* binding */ mergeInternals) /* harmony export */ }); /* harmony import */ var _observable_innerFrom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../observable/innerFrom */ 4987); /* harmony import */ var _util_executeSchedule__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../util/executeSchedule */ 1817); /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./OperatorSubscriber */ 3945); function mergeInternals(source, subscriber, project, concurrent, onBeforeNext, expand, innerSubScheduler, additionalFinalizer) { const buffer = []; let active = 0; let index = 0; let isComplete = false; const checkComplete = () => { if (isComplete && !buffer.length && !active) { subscriber.complete(); } }; const outerNext = value => active < concurrent ? doInnerSub(value) : buffer.push(value); const doInnerSub = value => { expand && subscriber.next(value); active++; let innerComplete = false; (0,_observable_innerFrom__WEBPACK_IMPORTED_MODULE_0__.innerFrom)(project(value, index++)).subscribe((0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)(subscriber, innerValue => { onBeforeNext === null || onBeforeNext === void 0 ? void 0 : onBeforeNext(innerValue); if (expand) { outerNext(innerValue); } else { subscriber.next(innerValue); } }, () => { innerComplete = true; }, undefined, () => { if (innerComplete) { try { active--; while (buffer.length && active < concurrent) { const bufferedValue = buffer.shift(); if (innerSubScheduler) { (0,_util_executeSchedule__WEBPACK_IMPORTED_MODULE_2__.executeSchedule)(subscriber, innerSubScheduler, () => doInnerSub(bufferedValue)); } else { doInnerSub(bufferedValue); } } checkComplete(); } catch (err) { subscriber.error(err); } } })); }; source.subscribe((0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)(subscriber, outerNext, () => { isComplete = true; checkComplete(); })); return () => { additionalFinalizer === null || additionalFinalizer === void 0 ? void 0 : additionalFinalizer(); }; } /***/ }), /***/ 1353: /*!*******************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/mergeMap.js ***! \*******************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "mergeMap": () => (/* binding */ mergeMap) /* harmony export */ }); /* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./map */ 635); /* harmony import */ var _observable_innerFrom__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../observable/innerFrom */ 4987); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../util/lift */ 1944); /* harmony import */ var _mergeInternals__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./mergeInternals */ 9280); /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/isFunction */ 2971); function mergeMap(project, resultSelector, concurrent = Infinity) { if ((0,_util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(resultSelector)) { return mergeMap((a, i) => (0,_map__WEBPACK_IMPORTED_MODULE_1__.map)((b, ii) => resultSelector(a, b, i, ii))((0,_observable_innerFrom__WEBPACK_IMPORTED_MODULE_2__.innerFrom)(project(a, i))), concurrent); } else if (typeof resultSelector === 'number') { concurrent = resultSelector; } return (0,_util_lift__WEBPACK_IMPORTED_MODULE_3__.operate)((source, subscriber) => (0,_mergeInternals__WEBPACK_IMPORTED_MODULE_4__.mergeInternals)(source, subscriber, project, concurrent)); } /***/ }), /***/ 8728: /*!********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/observeOn.js ***! \********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "observeOn": () => (/* binding */ observeOn) /* harmony export */ }); /* harmony import */ var _util_executeSchedule__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../util/executeSchedule */ 1817); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/lift */ 1944); /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./OperatorSubscriber */ 3945); function observeOn(scheduler, delay = 0) { return (0,_util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { source.subscribe((0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)(subscriber, value => (0,_util_executeSchedule__WEBPACK_IMPORTED_MODULE_2__.executeSchedule)(subscriber, scheduler, () => subscriber.next(value), delay), () => (0,_util_executeSchedule__WEBPACK_IMPORTED_MODULE_2__.executeSchedule)(subscriber, scheduler, () => subscriber.complete(), delay), err => (0,_util_executeSchedule__WEBPACK_IMPORTED_MODULE_2__.executeSchedule)(subscriber, scheduler, () => subscriber.error(err), delay))); }); } /***/ }), /***/ 6074: /*!*******************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/refCount.js ***! \*******************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "refCount": () => (/* binding */ refCount) /* harmony export */ }); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/lift */ 1944); /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./OperatorSubscriber */ 3945); function refCount() { return (0,_util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { let connection = null; source._refCount++; const refCounter = (0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)(subscriber, undefined, undefined, undefined, () => { if (!source || source._refCount <= 0 || 0 < --source._refCount) { connection = null; return; } const sharedConnection = source._connection; const conn = connection; connection = null; if (sharedConnection && (!conn || sharedConnection === conn)) { sharedConnection.unsubscribe(); } subscriber.unsubscribe(); }); source.subscribe(refCounter); if (!refCounter.closed) { connection = source.connect(); } }); } /***/ }), /***/ 4503: /*!***************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/scan.js ***! \***************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "scan": () => (/* binding */ scan) /* harmony export */ }); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/lift */ 1944); /* harmony import */ var _scanInternals__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./scanInternals */ 4382); function scan(accumulator, seed) { return (0,_util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((0,_scanInternals__WEBPACK_IMPORTED_MODULE_1__.scanInternals)(accumulator, seed, arguments.length >= 2, true)); } /***/ }), /***/ 4382: /*!************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/scanInternals.js ***! \************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "scanInternals": () => (/* binding */ scanInternals) /* harmony export */ }); /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./OperatorSubscriber */ 3945); function scanInternals(accumulator, seed, hasSeed, emitOnNext, emitBeforeComplete) { return (source, subscriber) => { let hasState = hasSeed; let state = seed; let index = 0; source.subscribe((0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_0__.createOperatorSubscriber)(subscriber, value => { const i = index++; state = hasState ? accumulator(state, value, i) : (hasState = true, value); emitOnNext && subscriber.next(state); }, emitBeforeComplete && (() => { hasState && subscriber.next(state); subscriber.complete(); }))); }; } /***/ }), /***/ 1203: /*!****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/share.js ***! \****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "share": () => (/* binding */ share) /* harmony export */ }); /* harmony import */ var _observable_innerFrom__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../observable/innerFrom */ 4987); /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Subject */ 228); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../Subscriber */ 9904); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/lift */ 1944); function share(options = {}) { const { connector = () => new _Subject__WEBPACK_IMPORTED_MODULE_0__.Subject(), resetOnError = true, resetOnComplete = true, resetOnRefCountZero = true } = options; return wrapperSource => { let connection; let resetConnection; let subject; let refCount = 0; let hasCompleted = false; let hasErrored = false; const cancelReset = () => { resetConnection === null || resetConnection === void 0 ? void 0 : resetConnection.unsubscribe(); resetConnection = undefined; }; const reset = () => { cancelReset(); connection = subject = undefined; hasCompleted = hasErrored = false; }; const resetAndUnsubscribe = () => { const conn = connection; reset(); conn === null || conn === void 0 ? void 0 : conn.unsubscribe(); }; return (0,_util_lift__WEBPACK_IMPORTED_MODULE_1__.operate)((source, subscriber) => { refCount++; if (!hasErrored && !hasCompleted) { cancelReset(); } const dest = subject = subject !== null && subject !== void 0 ? subject : connector(); subscriber.add(() => { refCount--; if (refCount === 0 && !hasErrored && !hasCompleted) { resetConnection = handleReset(resetAndUnsubscribe, resetOnRefCountZero); } }); dest.subscribe(subscriber); if (!connection && refCount > 0) { connection = new _Subscriber__WEBPACK_IMPORTED_MODULE_2__.SafeSubscriber({ next: value => dest.next(value), error: err => { hasErrored = true; cancelReset(); resetConnection = handleReset(reset, resetOnError, err); dest.error(err); }, complete: () => { hasCompleted = true; cancelReset(); resetConnection = handleReset(reset, resetOnComplete); dest.complete(); } }); (0,_observable_innerFrom__WEBPACK_IMPORTED_MODULE_3__.innerFrom)(source).subscribe(connection); } })(wrapperSource); }; } function handleReset(reset, on, ...args) { if (on === true) { reset(); return; } if (on === false) { return; } const onSubscriber = new _Subscriber__WEBPACK_IMPORTED_MODULE_2__.SafeSubscriber({ next: () => { onSubscriber.unsubscribe(); reset(); } }); return (0,_observable_innerFrom__WEBPACK_IMPORTED_MODULE_3__.innerFrom)(on(...args)).subscribe(onSubscriber); } /***/ }), /***/ 4874: /*!********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/startWith.js ***! \********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "startWith": () => (/* binding */ startWith) /* harmony export */ }); /* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../observable/concat */ 4240); /* harmony import */ var _util_args__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/args */ 420); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/lift */ 1944); function startWith(...values) { const scheduler = (0,_util_args__WEBPACK_IMPORTED_MODULE_0__.popScheduler)(values); return (0,_util_lift__WEBPACK_IMPORTED_MODULE_1__.operate)((source, subscriber) => { (scheduler ? (0,_observable_concat__WEBPACK_IMPORTED_MODULE_2__.concat)(values, source, scheduler) : (0,_observable_concat__WEBPACK_IMPORTED_MODULE_2__.concat)(values, source)).subscribe(subscriber); }); } /***/ }), /***/ 4317: /*!**********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/subscribeOn.js ***! \**********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "subscribeOn": () => (/* binding */ subscribeOn) /* harmony export */ }); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/lift */ 1944); function subscribeOn(scheduler, delay = 0) { return (0,_util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { subscriber.add(scheduler.schedule(() => source.subscribe(subscriber), delay)); }); } /***/ }), /***/ 2673: /*!********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/switchMap.js ***! \********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "switchMap": () => (/* binding */ switchMap) /* harmony export */ }); /* harmony import */ var _observable_innerFrom__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../observable/innerFrom */ 4987); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/lift */ 1944); /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./OperatorSubscriber */ 3945); function switchMap(project, resultSelector) { return (0,_util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { let innerSubscriber = null; let index = 0; let isComplete = false; const checkComplete = () => isComplete && !innerSubscriber && subscriber.complete(); source.subscribe((0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)(subscriber, value => { innerSubscriber === null || innerSubscriber === void 0 ? void 0 : innerSubscriber.unsubscribe(); let innerIndex = 0; const outerIndex = index++; (0,_observable_innerFrom__WEBPACK_IMPORTED_MODULE_2__.innerFrom)(project(value, outerIndex)).subscribe(innerSubscriber = (0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)(subscriber, innerValue => subscriber.next(resultSelector ? resultSelector(value, innerValue, outerIndex, innerIndex++) : innerValue), () => { innerSubscriber = null; checkComplete(); })); }, () => { isComplete = true; checkComplete(); })); }); } /***/ }), /***/ 9295: /*!***************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/take.js ***! \***************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "take": () => (/* binding */ take) /* harmony export */ }); /* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../observable/empty */ 591); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/lift */ 1944); /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./OperatorSubscriber */ 3945); function take(count) { return count <= 0 ? () => _observable_empty__WEBPACK_IMPORTED_MODULE_0__.EMPTY : (0,_util_lift__WEBPACK_IMPORTED_MODULE_1__.operate)((source, subscriber) => { let seen = 0; source.subscribe((0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_2__.createOperatorSubscriber)(subscriber, value => { if (++seen <= count) { subscriber.next(value); if (count <= seen) { subscriber.complete(); } } })); }); } /***/ }), /***/ 9601: /*!*******************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/takeLast.js ***! \*******************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "takeLast": () => (/* binding */ takeLast) /* harmony export */ }); /* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../observable/empty */ 591); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/lift */ 1944); /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./OperatorSubscriber */ 3945); function takeLast(count) { return count <= 0 ? () => _observable_empty__WEBPACK_IMPORTED_MODULE_0__.EMPTY : (0,_util_lift__WEBPACK_IMPORTED_MODULE_1__.operate)((source, subscriber) => { let buffer = []; source.subscribe((0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_2__.createOperatorSubscriber)(subscriber, value => { buffer.push(value); count < buffer.length && buffer.shift(); }, () => { for (const value of buffer) { subscriber.next(value); } subscriber.complete(); }, undefined, () => { buffer = null; })); }); } /***/ }), /***/ 2566: /*!********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/takeWhile.js ***! \********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "takeWhile": () => (/* binding */ takeWhile) /* harmony export */ }); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/lift */ 1944); /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./OperatorSubscriber */ 3945); function takeWhile(predicate, inclusive = false) { return (0,_util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { let index = 0; source.subscribe((0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)(subscriber, value => { const result = predicate(value, index++); (result || inclusive) && subscriber.next(value); !result && subscriber.complete(); })); }); } /***/ }), /***/ 9337: /*!**************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/tap.js ***! \**************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "tap": () => (/* binding */ tap) /* harmony export */ }); /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/isFunction */ 2971); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/lift */ 1944); /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./OperatorSubscriber */ 3945); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../util/identity */ 9173); function tap(observerOrNext, error, complete) { const tapObserver = (0,_util_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(observerOrNext) || error || complete ? { next: observerOrNext, error, complete } : observerOrNext; return tapObserver ? (0,_util_lift__WEBPACK_IMPORTED_MODULE_1__.operate)((source, subscriber) => { var _a; (_a = tapObserver.subscribe) === null || _a === void 0 ? void 0 : _a.call(tapObserver); let isUnsub = true; source.subscribe((0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_2__.createOperatorSubscriber)(subscriber, value => { var _a; (_a = tapObserver.next) === null || _a === void 0 ? void 0 : _a.call(tapObserver, value); subscriber.next(value); }, () => { var _a; isUnsub = false; (_a = tapObserver.complete) === null || _a === void 0 ? void 0 : _a.call(tapObserver); subscriber.complete(); }, err => { var _a; isUnsub = false; (_a = tapObserver.error) === null || _a === void 0 ? void 0 : _a.call(tapObserver, err); subscriber.error(err); }, () => { var _a, _b; if (isUnsub) { (_a = tapObserver.unsubscribe) === null || _a === void 0 ? void 0 : _a.call(tapObserver); } (_b = tapObserver.finalize) === null || _b === void 0 ? void 0 : _b.call(tapObserver); })); }) : _util_identity__WEBPACK_IMPORTED_MODULE_3__.identity; } /***/ }), /***/ 7354: /*!***********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/operators/throwIfEmpty.js ***! \***********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "throwIfEmpty": () => (/* binding */ throwIfEmpty) /* harmony export */ }); /* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../util/EmptyError */ 4423); /* harmony import */ var _util_lift__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/lift */ 1944); /* harmony import */ var _OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./OperatorSubscriber */ 3945); function throwIfEmpty(errorFactory = defaultErrorFactory) { return (0,_util_lift__WEBPACK_IMPORTED_MODULE_0__.operate)((source, subscriber) => { let hasValue = false; source.subscribe((0,_OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.createOperatorSubscriber)(subscriber, value => { hasValue = true; subscriber.next(value); }, () => hasValue ? subscriber.complete() : subscriber.error(errorFactory()))); }); } function defaultErrorFactory() { return new _util_EmptyError__WEBPACK_IMPORTED_MODULE_2__.EmptyError(); } /***/ }), /***/ 3417: /*!************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/scheduled/scheduleArray.js ***! \************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "scheduleArray": () => (/* binding */ scheduleArray) /* harmony export */ }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Observable */ 833); function scheduleArray(input, scheduler) { return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { let i = 0; return scheduler.schedule(function () { if (i === input.length) { subscriber.complete(); } else { subscriber.next(input[i++]); if (!subscriber.closed) { this.schedule(); } } }); }); } /***/ }), /***/ 5646: /*!********************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/scheduled/scheduleAsyncIterable.js ***! \********************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "scheduleAsyncIterable": () => (/* binding */ scheduleAsyncIterable) /* harmony export */ }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Observable */ 833); /* harmony import */ var _util_executeSchedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/executeSchedule */ 1817); function scheduleAsyncIterable(input, scheduler) { if (!input) { throw new Error('Iterable cannot be null'); } return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { (0,_util_executeSchedule__WEBPACK_IMPORTED_MODULE_1__.executeSchedule)(subscriber, scheduler, () => { const iterator = input[Symbol.asyncIterator](); (0,_util_executeSchedule__WEBPACK_IMPORTED_MODULE_1__.executeSchedule)(subscriber, scheduler, () => { iterator.next().then(result => { if (result.done) { subscriber.complete(); } else { subscriber.next(result.value); } }); }, 0, true); }); }); } /***/ }), /***/ 4924: /*!***************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/scheduled/scheduleIterable.js ***! \***************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "scheduleIterable": () => (/* binding */ scheduleIterable) /* harmony export */ }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Observable */ 833); /* harmony import */ var _symbol_iterator__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../symbol/iterator */ 7321); /* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../util/isFunction */ 2971); /* harmony import */ var _util_executeSchedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/executeSchedule */ 1817); function scheduleIterable(input, scheduler) { return new _Observable__WEBPACK_IMPORTED_MODULE_0__.Observable(subscriber => { let iterator; (0,_util_executeSchedule__WEBPACK_IMPORTED_MODULE_1__.executeSchedule)(subscriber, scheduler, () => { iterator = input[_symbol_iterator__WEBPACK_IMPORTED_MODULE_2__.iterator](); (0,_util_executeSchedule__WEBPACK_IMPORTED_MODULE_1__.executeSchedule)(subscriber, scheduler, () => { let value; let done; try { ({ value, done } = iterator.next()); } catch (err) { subscriber.error(err); return; } if (done) { subscriber.complete(); } else { subscriber.next(value); } }, 0, true); }); return () => (0,_util_isFunction__WEBPACK_IMPORTED_MODULE_3__.isFunction)(iterator === null || iterator === void 0 ? void 0 : iterator.return) && iterator.return(); }); } /***/ }), /***/ 4349: /*!*****************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/scheduled/scheduleObservable.js ***! \*****************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "scheduleObservable": () => (/* binding */ scheduleObservable) /* harmony export */ }); /* harmony import */ var _observable_innerFrom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../observable/innerFrom */ 4987); /* harmony import */ var _operators_observeOn__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../operators/observeOn */ 8728); /* harmony import */ var _operators_subscribeOn__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../operators/subscribeOn */ 4317); function scheduleObservable(input, scheduler) { return (0,_observable_innerFrom__WEBPACK_IMPORTED_MODULE_0__.innerFrom)(input).pipe((0,_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_1__.subscribeOn)(scheduler), (0,_operators_observeOn__WEBPACK_IMPORTED_MODULE_2__.observeOn)(scheduler)); } /***/ }), /***/ 6642: /*!**************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/scheduled/schedulePromise.js ***! \**************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "schedulePromise": () => (/* binding */ schedulePromise) /* harmony export */ }); /* harmony import */ var _observable_innerFrom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../observable/innerFrom */ 4987); /* harmony import */ var _operators_observeOn__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../operators/observeOn */ 8728); /* harmony import */ var _operators_subscribeOn__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../operators/subscribeOn */ 4317); function schedulePromise(input, scheduler) { return (0,_observable_innerFrom__WEBPACK_IMPORTED_MODULE_0__.innerFrom)(input).pipe((0,_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_1__.subscribeOn)(scheduler), (0,_operators_observeOn__WEBPACK_IMPORTED_MODULE_2__.observeOn)(scheduler)); } /***/ }), /***/ 316: /*!*************************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/scheduled/scheduleReadableStreamLike.js ***! \*************************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "scheduleReadableStreamLike": () => (/* binding */ scheduleReadableStreamLike) /* harmony export */ }); /* harmony import */ var _scheduleAsyncIterable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./scheduleAsyncIterable */ 5646); /* harmony import */ var _util_isReadableStreamLike__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/isReadableStreamLike */ 181); function scheduleReadableStreamLike(input, scheduler) { return (0,_scheduleAsyncIterable__WEBPACK_IMPORTED_MODULE_0__.scheduleAsyncIterable)((0,_util_isReadableStreamLike__WEBPACK_IMPORTED_MODULE_1__.readableStreamLikeToAsyncGenerator)(input), scheduler); } /***/ }), /***/ 9517: /*!********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/scheduled/scheduled.js ***! \********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "scheduled": () => (/* binding */ scheduled) /* harmony export */ }); /* harmony import */ var _scheduleObservable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./scheduleObservable */ 4349); /* harmony import */ var _schedulePromise__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./schedulePromise */ 6642); /* harmony import */ var _scheduleArray__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./scheduleArray */ 3417); /* harmony import */ var _scheduleIterable__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./scheduleIterable */ 4924); /* harmony import */ var _scheduleAsyncIterable__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./scheduleAsyncIterable */ 5646); /* harmony import */ var _util_isInteropObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/isInteropObservable */ 1331); /* harmony import */ var _util_isPromise__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../util/isPromise */ 9548); /* harmony import */ var _util_isArrayLike__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../util/isArrayLike */ 9806); /* harmony import */ var _util_isIterable__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../util/isIterable */ 3433); /* harmony import */ var _util_isAsyncIterable__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../util/isAsyncIterable */ 470); /* harmony import */ var _util_throwUnobservableError__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../util/throwUnobservableError */ 7785); /* harmony import */ var _util_isReadableStreamLike__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../util/isReadableStreamLike */ 181); /* harmony import */ var _scheduleReadableStreamLike__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./scheduleReadableStreamLike */ 316); function scheduled(input, scheduler) { if (input != null) { if ((0,_util_isInteropObservable__WEBPACK_IMPORTED_MODULE_0__.isInteropObservable)(input)) { return (0,_scheduleObservable__WEBPACK_IMPORTED_MODULE_1__.scheduleObservable)(input, scheduler); } if ((0,_util_isArrayLike__WEBPACK_IMPORTED_MODULE_2__.isArrayLike)(input)) { return (0,_scheduleArray__WEBPACK_IMPORTED_MODULE_3__.scheduleArray)(input, scheduler); } if ((0,_util_isPromise__WEBPACK_IMPORTED_MODULE_4__.isPromise)(input)) { return (0,_schedulePromise__WEBPACK_IMPORTED_MODULE_5__.schedulePromise)(input, scheduler); } if ((0,_util_isAsyncIterable__WEBPACK_IMPORTED_MODULE_6__.isAsyncIterable)(input)) { return (0,_scheduleAsyncIterable__WEBPACK_IMPORTED_MODULE_7__.scheduleAsyncIterable)(input, scheduler); } if ((0,_util_isIterable__WEBPACK_IMPORTED_MODULE_8__.isIterable)(input)) { return (0,_scheduleIterable__WEBPACK_IMPORTED_MODULE_9__.scheduleIterable)(input, scheduler); } if ((0,_util_isReadableStreamLike__WEBPACK_IMPORTED_MODULE_10__.isReadableStreamLike)(input)) { return (0,_scheduleReadableStreamLike__WEBPACK_IMPORTED_MODULE_11__.scheduleReadableStreamLike)(input, scheduler); } } throw (0,_util_throwUnobservableError__WEBPACK_IMPORTED_MODULE_12__.createInvalidObservableTypeError)(input); } /***/ }), /***/ 3542: /*!**************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/scheduler/timeoutProvider.js ***! \**************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "timeoutProvider": () => (/* binding */ timeoutProvider) /* harmony export */ }); const timeoutProvider = { setTimeout(handler, timeout, ...args) { const { delegate } = timeoutProvider; if (delegate === null || delegate === void 0 ? void 0 : delegate.setTimeout) { return delegate.setTimeout(handler, timeout, ...args); } return setTimeout(handler, timeout, ...args); }, clearTimeout(handle) { const { delegate } = timeoutProvider; return ((delegate === null || delegate === void 0 ? void 0 : delegate.clearTimeout) || clearTimeout)(handle); }, delegate: undefined }; /***/ }), /***/ 7321: /*!****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/symbol/iterator.js ***! \****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "getSymbolIterator": () => (/* binding */ getSymbolIterator), /* harmony export */ "iterator": () => (/* binding */ iterator) /* harmony export */ }); function getSymbolIterator() { if (typeof Symbol !== 'function' || !Symbol.iterator) { return '@@iterator'; } return Symbol.iterator; } const iterator = getSymbolIterator(); /***/ }), /***/ 4585: /*!******************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/symbol/observable.js ***! \******************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "observable": () => (/* binding */ observable) /* harmony export */ }); const observable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')(); /***/ }), /***/ 4423: /*!****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/EmptyError.js ***! \****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "EmptyError": () => (/* binding */ EmptyError) /* harmony export */ }); /* harmony import */ var _createErrorClass__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./createErrorClass */ 7543); const EmptyError = (0,_createErrorClass__WEBPACK_IMPORTED_MODULE_0__.createErrorClass)(_super => function EmptyErrorImpl() { _super(this); this.name = 'EmptyError'; this.message = 'no elements in sequence'; }); /***/ }), /***/ 9872: /*!*****************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/ObjectUnsubscribedError.js ***! \*****************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "ObjectUnsubscribedError": () => (/* binding */ ObjectUnsubscribedError) /* harmony export */ }); /* harmony import */ var _createErrorClass__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./createErrorClass */ 7543); const ObjectUnsubscribedError = (0,_createErrorClass__WEBPACK_IMPORTED_MODULE_0__.createErrorClass)(_super => function ObjectUnsubscribedErrorImpl() { _super(this); this.name = 'ObjectUnsubscribedError'; this.message = 'object unsubscribed'; }); /***/ }), /***/ 2524: /*!*************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/UnsubscriptionError.js ***! \*************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "UnsubscriptionError": () => (/* binding */ UnsubscriptionError) /* harmony export */ }); /* harmony import */ var _createErrorClass__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./createErrorClass */ 7543); const UnsubscriptionError = (0,_createErrorClass__WEBPACK_IMPORTED_MODULE_0__.createErrorClass)(_super => function UnsubscriptionErrorImpl(errors) { _super(this); this.message = errors ? `${errors.length} errors occurred during unsubscription: ${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\n ')}` : ''; this.name = 'UnsubscriptionError'; this.errors = errors; }); /***/ }), /***/ 420: /*!**********************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/args.js ***! \**********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "popNumber": () => (/* binding */ popNumber), /* harmony export */ "popResultSelector": () => (/* binding */ popResultSelector), /* harmony export */ "popScheduler": () => (/* binding */ popScheduler) /* harmony export */ }); /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./isFunction */ 2971); /* harmony import */ var _isScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./isScheduler */ 9867); function last(arr) { return arr[arr.length - 1]; } function popResultSelector(args) { return (0,_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(last(args)) ? args.pop() : undefined; } function popScheduler(args) { return (0,_isScheduler__WEBPACK_IMPORTED_MODULE_1__.isScheduler)(last(args)) ? args.pop() : undefined; } function popNumber(args, defaultValue) { return typeof last(args) === 'number' ? args.pop() : defaultValue; } /***/ }), /***/ 5756: /*!**************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/argsArgArrayOrObject.js ***! \**************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "argsArgArrayOrObject": () => (/* binding */ argsArgArrayOrObject) /* harmony export */ }); const { isArray } = Array; const { getPrototypeOf, prototype: objectProto, keys: getKeys } = Object; function argsArgArrayOrObject(args) { if (args.length === 1) { const first = args[0]; if (isArray(first)) { return { args: first, keys: null }; } if (isPOJO(first)) { const keys = getKeys(first); return { args: keys.map(key => first[key]), keys }; } } return { args: args, keys: null }; } function isPOJO(obj) { return obj && typeof obj === 'object' && getPrototypeOf(obj) === objectProto; } /***/ }), /***/ 9663: /*!***************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/arrRemove.js ***! \***************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "arrRemove": () => (/* binding */ arrRemove) /* harmony export */ }); function arrRemove(arr, item) { if (arr) { const index = arr.indexOf(item); 0 <= index && arr.splice(index, 1); } } /***/ }), /***/ 7543: /*!**********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/createErrorClass.js ***! \**********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "createErrorClass": () => (/* binding */ createErrorClass) /* harmony export */ }); function createErrorClass(createImpl) { const _super = instance => { Error.call(instance); instance.stack = new Error().stack; }; const ctorFunc = createImpl(_super); ctorFunc.prototype = Object.create(Error.prototype); ctorFunc.prototype.constructor = ctorFunc; return ctorFunc; } /***/ }), /***/ 1054: /*!******************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/createObject.js ***! \******************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "createObject": () => (/* binding */ createObject) /* harmony export */ }); function createObject(keys, values) { return keys.reduce((result, key, i) => (result[key] = values[i], result), {}); } /***/ }), /***/ 2309: /*!******************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/errorContext.js ***! \******************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "captureError": () => (/* binding */ captureError), /* harmony export */ "errorContext": () => (/* binding */ errorContext) /* harmony export */ }); /* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../config */ 9057); let context = null; function errorContext(cb) { if (_config__WEBPACK_IMPORTED_MODULE_0__.config.useDeprecatedSynchronousErrorHandling) { const isRoot = !context; if (isRoot) { context = { errorThrown: false, error: null }; } cb(); if (isRoot) { const { errorThrown, error } = context; context = null; if (errorThrown) { throw error; } } } else { cb(); } } function captureError(err) { if (_config__WEBPACK_IMPORTED_MODULE_0__.config.useDeprecatedSynchronousErrorHandling && context) { context.errorThrown = true; context.error = err; } } /***/ }), /***/ 1817: /*!*********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/executeSchedule.js ***! \*********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "executeSchedule": () => (/* binding */ executeSchedule) /* harmony export */ }); function executeSchedule(parentSubscription, scheduler, work, delay = 0, repeat = false) { const scheduleSubscription = scheduler.schedule(function () { work(); if (repeat) { parentSubscription.add(this.schedule(null, delay)); } else { this.unsubscribe(); } }, delay); parentSubscription.add(scheduleSubscription); if (!repeat) { return scheduleSubscription; } } /***/ }), /***/ 9173: /*!**************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/identity.js ***! \**************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "identity": () => (/* binding */ identity) /* harmony export */ }); function identity(x) { return x; } /***/ }), /***/ 9806: /*!*****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/isArrayLike.js ***! \*****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "isArrayLike": () => (/* binding */ isArrayLike) /* harmony export */ }); const isArrayLike = x => x && typeof x.length === 'number' && typeof x !== 'function'; /***/ }), /***/ 470: /*!*********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/isAsyncIterable.js ***! \*********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "isAsyncIterable": () => (/* binding */ isAsyncIterable) /* harmony export */ }); /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./isFunction */ 2971); function isAsyncIterable(obj) { return Symbol.asyncIterator && (0,_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(obj === null || obj === void 0 ? void 0 : obj[Symbol.asyncIterator]); } /***/ }), /***/ 2971: /*!****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/isFunction.js ***! \****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "isFunction": () => (/* binding */ isFunction) /* harmony export */ }); function isFunction(value) { return typeof value === 'function'; } /***/ }), /***/ 1331: /*!*************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/isInteropObservable.js ***! \*************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "isInteropObservable": () => (/* binding */ isInteropObservable) /* harmony export */ }); /* harmony import */ var _symbol_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../symbol/observable */ 4585); /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./isFunction */ 2971); function isInteropObservable(input) { return (0,_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(input[_symbol_observable__WEBPACK_IMPORTED_MODULE_1__.observable]); } /***/ }), /***/ 3433: /*!****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/isIterable.js ***! \****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "isIterable": () => (/* binding */ isIterable) /* harmony export */ }); /* harmony import */ var _symbol_iterator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../symbol/iterator */ 7321); /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./isFunction */ 2971); function isIterable(input) { return (0,_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(input === null || input === void 0 ? void 0 : input[_symbol_iterator__WEBPACK_IMPORTED_MODULE_1__.iterator]); } /***/ }), /***/ 9548: /*!***************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/isPromise.js ***! \***************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "isPromise": () => (/* binding */ isPromise) /* harmony export */ }); /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./isFunction */ 2971); function isPromise(value) { return (0,_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(value === null || value === void 0 ? void 0 : value.then); } /***/ }), /***/ 181: /*!**************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/isReadableStreamLike.js ***! \**************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "isReadableStreamLike": () => (/* binding */ isReadableStreamLike), /* harmony export */ "readableStreamLikeToAsyncGenerator": () => (/* binding */ readableStreamLikeToAsyncGenerator) /* harmony export */ }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ 2321); /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./isFunction */ 2971); function readableStreamLikeToAsyncGenerator(readableStream) { return (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__asyncGenerator)(this, arguments, function* readableStreamLikeToAsyncGenerator_1() { const reader = readableStream.getReader(); try { while (true) { const { value, done } = yield (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__await)(reader.read()); if (done) { return yield (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__await)(void 0); } yield yield (0,tslib__WEBPACK_IMPORTED_MODULE_0__.__await)(value); } } finally { reader.releaseLock(); } }); } function isReadableStreamLike(obj) { return (0,_isFunction__WEBPACK_IMPORTED_MODULE_1__.isFunction)(obj === null || obj === void 0 ? void 0 : obj.getReader); } /***/ }), /***/ 9867: /*!*****************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/isScheduler.js ***! \*****************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "isScheduler": () => (/* binding */ isScheduler) /* harmony export */ }); /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./isFunction */ 2971); function isScheduler(value) { return value && (0,_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(value.schedule); } /***/ }), /***/ 1944: /*!**********************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/lift.js ***! \**********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "hasLift": () => (/* binding */ hasLift), /* harmony export */ "operate": () => (/* binding */ operate) /* harmony export */ }); /* harmony import */ var _isFunction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./isFunction */ 2971); function hasLift(source) { return (0,_isFunction__WEBPACK_IMPORTED_MODULE_0__.isFunction)(source === null || source === void 0 ? void 0 : source.lift); } function operate(init) { return source => { if (hasLift(source)) { return source.lift(function (liftedSource) { try { return init(liftedSource, this); } catch (err) { this.error(err); } }); } throw new TypeError('Unable to lift unknown Observable type'); }; } /***/ }), /***/ 8385: /*!**********************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/mapOneOrManyArgs.js ***! \**********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "mapOneOrManyArgs": () => (/* binding */ mapOneOrManyArgs) /* harmony export */ }); /* harmony import */ var _operators_map__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../operators/map */ 635); const { isArray } = Array; function callOrApply(fn, args) { return isArray(args) ? fn(...args) : fn(args); } function mapOneOrManyArgs(fn) { return (0,_operators_map__WEBPACK_IMPORTED_MODULE_0__.map)(args => callOrApply(fn, args)); } /***/ }), /***/ 9635: /*!**********************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/noop.js ***! \**********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "noop": () => (/* binding */ noop) /* harmony export */ }); function noop() {} /***/ }), /***/ 629: /*!**********************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/pipe.js ***! \**********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "pipe": () => (/* binding */ pipe), /* harmony export */ "pipeFromArray": () => (/* binding */ pipeFromArray) /* harmony export */ }); /* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./identity */ 9173); function pipe(...fns) { return pipeFromArray(fns); } function pipeFromArray(fns) { if (fns.length === 0) { return _identity__WEBPACK_IMPORTED_MODULE_0__.identity; } if (fns.length === 1) { return fns[0]; } return function piped(input) { return fns.reduce((prev, fn) => fn(prev), input); }; } /***/ }), /***/ 4709: /*!**************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/reportUnhandledError.js ***! \**************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "reportUnhandledError": () => (/* binding */ reportUnhandledError) /* harmony export */ }); /* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../config */ 9057); /* harmony import */ var _scheduler_timeoutProvider__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../scheduler/timeoutProvider */ 3542); function reportUnhandledError(err) { _scheduler_timeoutProvider__WEBPACK_IMPORTED_MODULE_0__.timeoutProvider.setTimeout(() => { const { onUnhandledError } = _config__WEBPACK_IMPORTED_MODULE_1__.config; if (onUnhandledError) { onUnhandledError(err); } else { throw err; } }); } /***/ }), /***/ 7785: /*!****************************************************************************!*\ !*** ./node_modules/rxjs/dist/esm/internal/util/throwUnobservableError.js ***! \****************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "createInvalidObservableTypeError": () => (/* binding */ createInvalidObservableTypeError) /* harmony export */ }); function createInvalidObservableTypeError(input) { return new TypeError(`You provided ${input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`} where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`); } /***/ }), /***/ 280: /*!*************************************************!*\ !*** ./node_modules/vis-data/peer/esm/index.js ***! \*************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "DELETE": () => (/* reexport safe */ _vis_data__WEBPACK_IMPORTED_MODULE_0__.DELETE), /* harmony export */ "DataSet": () => (/* reexport safe */ _vis_data__WEBPACK_IMPORTED_MODULE_0__.DataSet), /* harmony export */ "DataStream": () => (/* reexport safe */ _vis_data__WEBPACK_IMPORTED_MODULE_0__.DataStream), /* harmony export */ "DataView": () => (/* reexport safe */ _vis_data__WEBPACK_IMPORTED_MODULE_0__.DataView), /* harmony export */ "Queue": () => (/* reexport safe */ _vis_data__WEBPACK_IMPORTED_MODULE_0__.Queue), /* harmony export */ "createNewDataPipeFrom": () => (/* reexport safe */ _vis_data__WEBPACK_IMPORTED_MODULE_0__.createNewDataPipeFrom), /* harmony export */ "isDataSetLike": () => (/* reexport safe */ _vis_data__WEBPACK_IMPORTED_MODULE_0__.isDataSetLike), /* harmony export */ "isDataViewLike": () => (/* reexport safe */ _vis_data__WEBPACK_IMPORTED_MODULE_0__.isDataViewLike) /* harmony export */ }); /* harmony import */ var _vis_data__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./vis-data */ 6115); /***/ }), /***/ 6115: /*!****************************************************!*\ !*** ./node_modules/vis-data/peer/esm/vis-data.js ***! \****************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "DELETE": () => (/* binding */ DELETE), /* harmony export */ "DataSet": () => (/* binding */ DataSet), /* harmony export */ "DataStream": () => (/* binding */ DataStream), /* harmony export */ "DataView": () => (/* binding */ DataView), /* harmony export */ "Queue": () => (/* binding */ Queue), /* harmony export */ "createNewDataPipeFrom": () => (/* binding */ createNewDataPipeFrom), /* harmony export */ "isDataSetLike": () => (/* binding */ isDataSetLike), /* harmony export */ "isDataViewLike": () => (/* binding */ isDataViewLike) /* harmony export */ }); /** * vis-data * http://visjs.org/ * * Manage unstructured data using DataSet. Add, update, and remove data, and listen for changes in the data. * * @version 7.1.6 * @date 2023-03-22T11:39:37.256Z * * @copyright (c) 2011-2017 Almende B.V, http://almende.com * @copyright (c) 2017-2019 visjs contributors, https://github.com/visjs * * @license * vis.js is dual licensed under both * * 1. The Apache 2.0 License * http://www.apache.org/licenses/LICENSE-2.0 * * and * * 2. The MIT License * http://opensource.org/licenses/MIT * * vis.js may be distributed under either license. */ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function getDefaultExportFromCjs(x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var definePropertyExports$3 = {}; var defineProperty$f = { get exports() { return definePropertyExports$3; }, set exports(v) { definePropertyExports$3 = v; } }; var definePropertyExports$2 = {}; var defineProperty$e = { get exports() { return definePropertyExports$2; }, set exports(v) { definePropertyExports$2 = v; } }; var definePropertyExports$1 = {}; var defineProperty$d = { get exports() { return definePropertyExports$1; }, set exports(v) { definePropertyExports$1 = v; } }; var check = function (it) { return it && it.Math == Math && it; }; // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 var global$n = // eslint-disable-next-line es/no-global-this -- safe check(typeof globalThis == 'object' && globalThis) || check(typeof window == 'object' && window) || // eslint-disable-next-line no-restricted-globals -- safe check(typeof self == 'object' && self) || check(typeof commonjsGlobal == 'object' && commonjsGlobal) || // eslint-disable-next-line no-new-func -- fallback function () { return this; }() || Function('return this')(); var fails$w = function (exec) { try { return !!exec(); } catch (error) { return true; } }; var fails$v = fails$w; var functionBindNative = !fails$v(function () { // eslint-disable-next-line es/no-function-prototype-bind -- safe var test = function () {/* empty */}.bind(); // eslint-disable-next-line no-prototype-builtins -- safe return typeof test != 'function' || test.hasOwnProperty('prototype'); }); var NATIVE_BIND$4 = functionBindNative; var FunctionPrototype$3 = Function.prototype; var apply$6 = FunctionPrototype$3.apply; var call$k = FunctionPrototype$3.call; // eslint-disable-next-line es/no-reflect -- safe var functionApply = typeof Reflect == 'object' && Reflect.apply || (NATIVE_BIND$4 ? call$k.bind(apply$6) : function () { return call$k.apply(apply$6, arguments); }); var NATIVE_BIND$3 = functionBindNative; var FunctionPrototype$2 = Function.prototype; var call$j = FunctionPrototype$2.call; var uncurryThisWithBind = NATIVE_BIND$3 && FunctionPrototype$2.bind.bind(call$j, call$j); var functionUncurryThis = NATIVE_BIND$3 ? uncurryThisWithBind : function (fn) { return function () { return call$j.apply(fn, arguments); }; }; var uncurryThis$w = functionUncurryThis; var toString$c = uncurryThis$w({}.toString); var stringSlice$1 = uncurryThis$w(''.slice); var classofRaw$2 = function (it) { return stringSlice$1(toString$c(it), 8, -1); }; var classofRaw$1 = classofRaw$2; var uncurryThis$v = functionUncurryThis; var functionUncurryThisClause = function (fn) { // Nashorn bug: // https://github.com/zloirock/core-js/issues/1128 // https://github.com/zloirock/core-js/issues/1130 if (classofRaw$1(fn) === 'Function') return uncurryThis$v(fn); }; var documentAll$2 = typeof document == 'object' && document.all; // https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot // eslint-disable-next-line unicorn/no-typeof-undefined -- required for testing var IS_HTMLDDA = typeof documentAll$2 == 'undefined' && documentAll$2 !== undefined; var documentAll_1 = { all: documentAll$2, IS_HTMLDDA: IS_HTMLDDA }; var $documentAll$1 = documentAll_1; var documentAll$1 = $documentAll$1.all; // `IsCallable` abstract operation // https://tc39.es/ecma262/#sec-iscallable var isCallable$m = $documentAll$1.IS_HTMLDDA ? function (argument) { return typeof argument == 'function' || argument === documentAll$1; } : function (argument) { return typeof argument == 'function'; }; var objectGetOwnPropertyDescriptor = {}; var fails$u = fails$w; // Detect IE8's incomplete defineProperty implementation var descriptors = !fails$u(function () { // eslint-disable-next-line es/no-object-defineproperty -- required for testing return Object.defineProperty({}, 1, { get: function () { return 7; } })[1] != 7; }); var NATIVE_BIND$2 = functionBindNative; var call$i = Function.prototype.call; var functionCall = NATIVE_BIND$2 ? call$i.bind(call$i) : function () { return call$i.apply(call$i, arguments); }; var objectPropertyIsEnumerable = {}; var $propertyIsEnumerable$2 = {}.propertyIsEnumerable; // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe var getOwnPropertyDescriptor$7 = Object.getOwnPropertyDescriptor; // Nashorn ~ JDK8 bug var NASHORN_BUG = getOwnPropertyDescriptor$7 && !$propertyIsEnumerable$2.call({ 1: 2 }, 1); // `Object.prototype.propertyIsEnumerable` method implementation // https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable objectPropertyIsEnumerable.f = NASHORN_BUG ? function propertyIsEnumerable(V) { var descriptor = getOwnPropertyDescriptor$7(this, V); return !!descriptor && descriptor.enumerable; } : $propertyIsEnumerable$2; var createPropertyDescriptor$7 = function (bitmap, value) { return { enumerable: !(bitmap & 1), configurable: !(bitmap & 2), writable: !(bitmap & 4), value: value }; }; var uncurryThis$u = functionUncurryThis; var fails$t = fails$w; var classof$g = classofRaw$2; var $Object$4 = Object; var split = uncurryThis$u(''.split); // fallback for non-array-like ES3 and non-enumerable old V8 strings var indexedObject = fails$t(function () { // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346 // eslint-disable-next-line no-prototype-builtins -- safe return !$Object$4('z').propertyIsEnumerable(0); }) ? function (it) { return classof$g(it) == 'String' ? split(it, '') : $Object$4(it); } : $Object$4; // we can't use just `it == null` since of `document.all` special case // https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot-aec var isNullOrUndefined$5 = function (it) { return it === null || it === undefined; }; var isNullOrUndefined$4 = isNullOrUndefined$5; var $TypeError$h = TypeError; // `RequireObjectCoercible` abstract operation // https://tc39.es/ecma262/#sec-requireobjectcoercible var requireObjectCoercible$5 = function (it) { if (isNullOrUndefined$4(it)) throw $TypeError$h("Can't call method on " + it); return it; }; // toObject with fallback for non-array-like ES3 strings var IndexedObject$3 = indexedObject; var requireObjectCoercible$4 = requireObjectCoercible$5; var toIndexedObject$b = function (it) { return IndexedObject$3(requireObjectCoercible$4(it)); }; var isCallable$l = isCallable$m; var $documentAll = documentAll_1; var documentAll = $documentAll.all; var isObject$i = $documentAll.IS_HTMLDDA ? function (it) { return typeof it == 'object' ? it !== null : isCallable$l(it) || it === documentAll; } : function (it) { return typeof it == 'object' ? it !== null : isCallable$l(it); }; var path$r = {}; var path$q = path$r; var global$m = global$n; var isCallable$k = isCallable$m; var aFunction = function (variable) { return isCallable$k(variable) ? variable : undefined; }; var getBuiltIn$f = function (namespace, method) { return arguments.length < 2 ? aFunction(path$q[namespace]) || aFunction(global$m[namespace]) : path$q[namespace] && path$q[namespace][method] || global$m[namespace] && global$m[namespace][method]; }; var uncurryThis$t = functionUncurryThis; var objectIsPrototypeOf = uncurryThis$t({}.isPrototypeOf); var engineUserAgent = typeof navigator != 'undefined' && String(navigator.userAgent) || ''; var global$l = global$n; var userAgent$5 = engineUserAgent; var process$4 = global$l.process; var Deno$1 = global$l.Deno; var versions = process$4 && process$4.versions || Deno$1 && Deno$1.version; var v8 = versions && versions.v8; var match, version; if (v8) { match = v8.split('.'); // in old Chrome, versions of V8 isn't V8 = Chrome / 10 // but their correct versions are not interesting for us version = match[0] > 0 && match[0] < 4 ? 1 : +(match[0] + match[1]); } // BrowserFS NodeJS `process` polyfill incorrectly set `.v8` to `0.0` // so check `userAgent` even if `.v8` exists, but 0 if (!version && userAgent$5) { match = userAgent$5.match(/Edge\/(\d+)/); if (!match || match[1] >= 74) { match = userAgent$5.match(/Chrome\/(\d+)/); if (match) version = +match[1]; } } var engineV8Version = version; /* eslint-disable es/no-symbol -- required for testing */ var V8_VERSION$3 = engineV8Version; var fails$s = fails$w; // eslint-disable-next-line es/no-object-getownpropertysymbols -- required for testing var symbolConstructorDetection = !!Object.getOwnPropertySymbols && !fails$s(function () { var symbol = Symbol(); // Chrome 38 Symbol has incorrect toString conversion // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances return !String(symbol) || !(Object(symbol) instanceof Symbol) || // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances !Symbol.sham && V8_VERSION$3 && V8_VERSION$3 < 41; }); /* eslint-disable es/no-symbol -- required for testing */ var NATIVE_SYMBOL$5 = symbolConstructorDetection; var useSymbolAsUid = NATIVE_SYMBOL$5 && !Symbol.sham && typeof Symbol.iterator == 'symbol'; var getBuiltIn$e = getBuiltIn$f; var isCallable$j = isCallable$m; var isPrototypeOf$n = objectIsPrototypeOf; var USE_SYMBOL_AS_UID$1 = useSymbolAsUid; var $Object$3 = Object; var isSymbol$5 = USE_SYMBOL_AS_UID$1 ? function (it) { return typeof it == 'symbol'; } : function (it) { var $Symbol = getBuiltIn$e('Symbol'); return isCallable$j($Symbol) && isPrototypeOf$n($Symbol.prototype, $Object$3(it)); }; var $String$4 = String; var tryToString$6 = function (argument) { try { return $String$4(argument); } catch (error) { return 'Object'; } }; var isCallable$i = isCallable$m; var tryToString$5 = tryToString$6; var $TypeError$g = TypeError; // `Assert: IsCallable(argument) is true` var aCallable$e = function (argument) { if (isCallable$i(argument)) return argument; throw $TypeError$g(tryToString$5(argument) + ' is not a function'); }; var aCallable$d = aCallable$e; var isNullOrUndefined$3 = isNullOrUndefined$5; // `GetMethod` abstract operation // https://tc39.es/ecma262/#sec-getmethod var getMethod$3 = function (V, P) { var func = V[P]; return isNullOrUndefined$3(func) ? undefined : aCallable$d(func); }; var call$h = functionCall; var isCallable$h = isCallable$m; var isObject$h = isObject$i; var $TypeError$f = TypeError; // `OrdinaryToPrimitive` abstract operation // https://tc39.es/ecma262/#sec-ordinarytoprimitive var ordinaryToPrimitive$1 = function (input, pref) { var fn, val; if (pref === 'string' && isCallable$h(fn = input.toString) && !isObject$h(val = call$h(fn, input))) return val; if (isCallable$h(fn = input.valueOf) && !isObject$h(val = call$h(fn, input))) return val; if (pref !== 'string' && isCallable$h(fn = input.toString) && !isObject$h(val = call$h(fn, input))) return val; throw $TypeError$f("Can't convert object to primitive value"); }; var sharedExports = {}; var shared$7 = { get exports() { return sharedExports; }, set exports(v) { sharedExports = v; } }; var isPure = true; var global$k = global$n; // eslint-disable-next-line es/no-object-defineproperty -- safe var defineProperty$c = Object.defineProperty; var defineGlobalProperty$1 = function (key, value) { try { defineProperty$c(global$k, key, { value: value, configurable: true, writable: true }); } catch (error) { global$k[key] = value; } return value; }; var global$j = global$n; var defineGlobalProperty = defineGlobalProperty$1; var SHARED = '__core-js_shared__'; var store$3 = global$j[SHARED] || defineGlobalProperty(SHARED, {}); var sharedStore = store$3; var store$2 = sharedStore; (shared$7.exports = function (key, value) { return store$2[key] || (store$2[key] = value !== undefined ? value : {}); })('versions', []).push({ version: '3.29.0', mode: 'pure', copyright: '© 2014-2023 Denis Pushkarev (zloirock.ru)', license: 'https://github.com/zloirock/core-js/blob/v3.29.0/LICENSE', source: 'https://github.com/zloirock/core-js' }); var requireObjectCoercible$3 = requireObjectCoercible$5; var $Object$2 = Object; // `ToObject` abstract operation // https://tc39.es/ecma262/#sec-toobject var toObject$e = function (argument) { return $Object$2(requireObjectCoercible$3(argument)); }; var uncurryThis$s = functionUncurryThis; var toObject$d = toObject$e; var hasOwnProperty = uncurryThis$s({}.hasOwnProperty); // `HasOwnProperty` abstract operation // https://tc39.es/ecma262/#sec-hasownproperty // eslint-disable-next-line es/no-object-hasown -- safe var hasOwnProperty_1 = Object.hasOwn || function hasOwn(it, key) { return hasOwnProperty(toObject$d(it), key); }; var uncurryThis$r = functionUncurryThis; var id$1 = 0; var postfix = Math.random(); var toString$b = uncurryThis$r(1.0.toString); var uid$4 = function (key) { return 'Symbol(' + (key === undefined ? '' : key) + ')_' + toString$b(++id$1 + postfix, 36); }; var global$i = global$n; var shared$6 = sharedExports; var hasOwn$j = hasOwnProperty_1; var uid$3 = uid$4; var NATIVE_SYMBOL$4 = symbolConstructorDetection; var USE_SYMBOL_AS_UID = useSymbolAsUid; var Symbol$4 = global$i.Symbol; var WellKnownSymbolsStore$2 = shared$6('wks'); var createWellKnownSymbol = USE_SYMBOL_AS_UID ? Symbol$4['for'] || Symbol$4 : Symbol$4 && Symbol$4.withoutSetter || uid$3; var wellKnownSymbol$o = function (name) { if (!hasOwn$j(WellKnownSymbolsStore$2, name)) { WellKnownSymbolsStore$2[name] = NATIVE_SYMBOL$4 && hasOwn$j(Symbol$4, name) ? Symbol$4[name] : createWellKnownSymbol('Symbol.' + name); } return WellKnownSymbolsStore$2[name]; }; var call$g = functionCall; var isObject$g = isObject$i; var isSymbol$4 = isSymbol$5; var getMethod$2 = getMethod$3; var ordinaryToPrimitive = ordinaryToPrimitive$1; var wellKnownSymbol$n = wellKnownSymbol$o; var $TypeError$e = TypeError; var TO_PRIMITIVE = wellKnownSymbol$n('toPrimitive'); // `ToPrimitive` abstract operation // https://tc39.es/ecma262/#sec-toprimitive var toPrimitive$7 = function (input, pref) { if (!isObject$g(input) || isSymbol$4(input)) return input; var exoticToPrim = getMethod$2(input, TO_PRIMITIVE); var result; if (exoticToPrim) { if (pref === undefined) pref = 'default'; result = call$g(exoticToPrim, input, pref); if (!isObject$g(result) || isSymbol$4(result)) return result; throw $TypeError$e("Can't convert object to primitive value"); } if (pref === undefined) pref = 'number'; return ordinaryToPrimitive(input, pref); }; var toPrimitive$6 = toPrimitive$7; var isSymbol$3 = isSymbol$5; // `ToPropertyKey` abstract operation // https://tc39.es/ecma262/#sec-topropertykey var toPropertyKey$4 = function (argument) { var key = toPrimitive$6(argument, 'string'); return isSymbol$3(key) ? key : key + ''; }; var global$h = global$n; var isObject$f = isObject$i; var document$3 = global$h.document; // typeof document.createElement is 'object' in old IE var EXISTS$1 = isObject$f(document$3) && isObject$f(document$3.createElement); var documentCreateElement$1 = function (it) { return EXISTS$1 ? document$3.createElement(it) : {}; }; var DESCRIPTORS$i = descriptors; var fails$r = fails$w; var createElement$1 = documentCreateElement$1; // Thanks to IE8 for its funny defineProperty var ie8DomDefine = !DESCRIPTORS$i && !fails$r(function () { // eslint-disable-next-line es/no-object-defineproperty -- required for testing return Object.defineProperty(createElement$1('div'), 'a', { get: function () { return 7; } }).a != 7; }); var DESCRIPTORS$h = descriptors; var call$f = functionCall; var propertyIsEnumerableModule$2 = objectPropertyIsEnumerable; var createPropertyDescriptor$6 = createPropertyDescriptor$7; var toIndexedObject$a = toIndexedObject$b; var toPropertyKey$3 = toPropertyKey$4; var hasOwn$i = hasOwnProperty_1; var IE8_DOM_DEFINE$1 = ie8DomDefine; // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe var $getOwnPropertyDescriptor$2 = Object.getOwnPropertyDescriptor; // `Object.getOwnPropertyDescriptor` method // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor objectGetOwnPropertyDescriptor.f = DESCRIPTORS$h ? $getOwnPropertyDescriptor$2 : function getOwnPropertyDescriptor(O, P) { O = toIndexedObject$a(O); P = toPropertyKey$3(P); if (IE8_DOM_DEFINE$1) try { return $getOwnPropertyDescriptor$2(O, P); } catch (error) {/* empty */} if (hasOwn$i(O, P)) return createPropertyDescriptor$6(!call$f(propertyIsEnumerableModule$2.f, O, P), O[P]); }; var fails$q = fails$w; var isCallable$g = isCallable$m; var replacement = /#|\.prototype\./; var isForced$2 = function (feature, detection) { var value = data[normalize(feature)]; return value == POLYFILL ? true : value == NATIVE ? false : isCallable$g(detection) ? fails$q(detection) : !!detection; }; var normalize = isForced$2.normalize = function (string) { return String(string).replace(replacement, '.').toLowerCase(); }; var data = isForced$2.data = {}; var NATIVE = isForced$2.NATIVE = 'N'; var POLYFILL = isForced$2.POLYFILL = 'P'; var isForced_1 = isForced$2; var uncurryThis$q = functionUncurryThisClause; var aCallable$c = aCallable$e; var NATIVE_BIND$1 = functionBindNative; var bind$j = uncurryThis$q(uncurryThis$q.bind); // optional / simple context binding var functionBindContext = function (fn, that) { aCallable$c(fn); return that === undefined ? fn : NATIVE_BIND$1 ? bind$j(fn, that) : function /* ...args */ () { return fn.apply(that, arguments); }; }; var objectDefineProperty = {}; var DESCRIPTORS$g = descriptors; var fails$p = fails$w; // V8 ~ Chrome 36- // https://bugs.chromium.org/p/v8/issues/detail?id=3334 var v8PrototypeDefineBug = DESCRIPTORS$g && fails$p(function () { // eslint-disable-next-line es/no-object-defineproperty -- required for testing return Object.defineProperty(function () {/* empty */}, 'prototype', { value: 42, writable: false }).prototype != 42; }); var isObject$e = isObject$i; var $String$3 = String; var $TypeError$d = TypeError; // `Assert: Type(argument) is Object` var anObject$d = function (argument) { if (isObject$e(argument)) return argument; throw $TypeError$d($String$3(argument) + ' is not an object'); }; var DESCRIPTORS$f = descriptors; var IE8_DOM_DEFINE = ie8DomDefine; var V8_PROTOTYPE_DEFINE_BUG$1 = v8PrototypeDefineBug; var anObject$c = anObject$d; var toPropertyKey$2 = toPropertyKey$4; var $TypeError$c = TypeError; // eslint-disable-next-line es/no-object-defineproperty -- safe var $defineProperty$1 = Object.defineProperty; // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe var $getOwnPropertyDescriptor$1 = Object.getOwnPropertyDescriptor; var ENUMERABLE = 'enumerable'; var CONFIGURABLE$1 = 'configurable'; var WRITABLE = 'writable'; // `Object.defineProperty` method // https://tc39.es/ecma262/#sec-object.defineproperty objectDefineProperty.f = DESCRIPTORS$f ? V8_PROTOTYPE_DEFINE_BUG$1 ? function defineProperty(O, P, Attributes) { anObject$c(O); P = toPropertyKey$2(P); anObject$c(Attributes); if (typeof O === 'function' && P === 'prototype' && 'value' in Attributes && WRITABLE in Attributes && !Attributes[WRITABLE]) { var current = $getOwnPropertyDescriptor$1(O, P); if (current && current[WRITABLE]) { O[P] = Attributes.value; Attributes = { configurable: CONFIGURABLE$1 in Attributes ? Attributes[CONFIGURABLE$1] : current[CONFIGURABLE$1], enumerable: ENUMERABLE in Attributes ? Attributes[ENUMERABLE] : current[ENUMERABLE], writable: false }; } } return $defineProperty$1(O, P, Attributes); } : $defineProperty$1 : function defineProperty(O, P, Attributes) { anObject$c(O); P = toPropertyKey$2(P); anObject$c(Attributes); if (IE8_DOM_DEFINE) try { return $defineProperty$1(O, P, Attributes); } catch (error) {/* empty */} if ('get' in Attributes || 'set' in Attributes) throw $TypeError$c('Accessors not supported'); if ('value' in Attributes) O[P] = Attributes.value; return O; }; var DESCRIPTORS$e = descriptors; var definePropertyModule$4 = objectDefineProperty; var createPropertyDescriptor$5 = createPropertyDescriptor$7; var createNonEnumerableProperty$9 = DESCRIPTORS$e ? function (object, key, value) { return definePropertyModule$4.f(object, key, createPropertyDescriptor$5(1, value)); } : function (object, key, value) { object[key] = value; return object; }; var global$g = global$n; var apply$5 = functionApply; var uncurryThis$p = functionUncurryThisClause; var isCallable$f = isCallable$m; var getOwnPropertyDescriptor$6 = objectGetOwnPropertyDescriptor.f; var isForced$1 = isForced_1; var path$p = path$r; var bind$i = functionBindContext; var createNonEnumerableProperty$8 = createNonEnumerableProperty$9; var hasOwn$h = hasOwnProperty_1; var wrapConstructor = function (NativeConstructor) { var Wrapper = function (a, b, c) { if (this instanceof Wrapper) { switch (arguments.length) { case 0: return new NativeConstructor(); case 1: return new NativeConstructor(a); case 2: return new NativeConstructor(a, b); } return new NativeConstructor(a, b, c); } return apply$5(NativeConstructor, this, arguments); }; Wrapper.prototype = NativeConstructor.prototype; return Wrapper; }; /* options.target - name of the target object options.global - target is the global object options.stat - export as static methods of target options.proto - export as prototype methods of target options.real - real prototype method for the `pure` version options.forced - export even if the native feature is available options.bind - bind methods to the target, required for the `pure` version options.wrap - wrap constructors to preventing global pollution, required for the `pure` version options.unsafe - use the simple assignment of property instead of delete + defineProperty options.sham - add a flag to not completely full polyfills options.enumerable - export as enumerable property options.dontCallGetSet - prevent calling a getter on target options.name - the .name of the function if it does not match the key */ var _export = function (options, source) { var TARGET = options.target; var GLOBAL = options.global; var STATIC = options.stat; var PROTO = options.proto; var nativeSource = GLOBAL ? global$g : STATIC ? global$g[TARGET] : (global$g[TARGET] || {}).prototype; var target = GLOBAL ? path$p : path$p[TARGET] || createNonEnumerableProperty$8(path$p, TARGET, {})[TARGET]; var targetPrototype = target.prototype; var FORCED, USE_NATIVE, VIRTUAL_PROTOTYPE; var key, sourceProperty, targetProperty, nativeProperty, resultProperty, descriptor; for (key in source) { FORCED = isForced$1(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced); // contains in native USE_NATIVE = !FORCED && nativeSource && hasOwn$h(nativeSource, key); targetProperty = target[key]; if (USE_NATIVE) if (options.dontCallGetSet) { descriptor = getOwnPropertyDescriptor$6(nativeSource, key); nativeProperty = descriptor && descriptor.value; } else nativeProperty = nativeSource[key]; // export native or implementation sourceProperty = USE_NATIVE && nativeProperty ? nativeProperty : source[key]; if (USE_NATIVE && typeof targetProperty == typeof sourceProperty) continue; // bind methods to global for calling from export context if (options.bind && USE_NATIVE) resultProperty = bind$i(sourceProperty, global$g); // wrap global constructors for prevent changes in this version else if (options.wrap && USE_NATIVE) resultProperty = wrapConstructor(sourceProperty); // make static versions for prototype methods else if (PROTO && isCallable$f(sourceProperty)) resultProperty = uncurryThis$p(sourceProperty); // default case else resultProperty = sourceProperty; // add a flag to not completely full polyfills if (options.sham || sourceProperty && sourceProperty.sham || targetProperty && targetProperty.sham) { createNonEnumerableProperty$8(resultProperty, 'sham', true); } createNonEnumerableProperty$8(target, key, resultProperty); if (PROTO) { VIRTUAL_PROTOTYPE = TARGET + 'Prototype'; if (!hasOwn$h(path$p, VIRTUAL_PROTOTYPE)) { createNonEnumerableProperty$8(path$p, VIRTUAL_PROTOTYPE, {}); } // export virtual prototype methods createNonEnumerableProperty$8(path$p[VIRTUAL_PROTOTYPE], key, sourceProperty); // export real prototype methods if (options.real && targetPrototype && (FORCED || !targetPrototype[key])) { createNonEnumerableProperty$8(targetPrototype, key, sourceProperty); } } } }; var $$T = _export; var DESCRIPTORS$d = descriptors; var defineProperty$b = objectDefineProperty.f; // `Object.defineProperty` method // https://tc39.es/ecma262/#sec-object.defineproperty // eslint-disable-next-line es/no-object-defineproperty -- safe $$T({ target: 'Object', stat: true, forced: Object.defineProperty !== defineProperty$b, sham: !DESCRIPTORS$d }, { defineProperty: defineProperty$b }); var path$o = path$r; var Object$4 = path$o.Object; var defineProperty$a = defineProperty$d.exports = function defineProperty(it, key, desc) { return Object$4.defineProperty(it, key, desc); }; if (Object$4.defineProperty.sham) defineProperty$a.sham = true; var parent$1c = definePropertyExports$1; var defineProperty$9 = parent$1c; var parent$1b = defineProperty$9; var defineProperty$8 = parent$1b; var parent$1a = defineProperty$8; var defineProperty$7 = parent$1a; (function (module) { module.exports = defineProperty$7; })(defineProperty$e); (function (module) { module.exports = definePropertyExports$2; })(defineProperty$f); var _Object$defineProperty$1 = /*@__PURE__*/getDefaultExportFromCjs(definePropertyExports$3); var symbolExports$2 = {}; var symbol$6 = { get exports() { return symbolExports$2; }, set exports(v) { symbolExports$2 = v; } }; var symbolExports$1 = {}; var symbol$5 = { get exports() { return symbolExports$1; }, set exports(v) { symbolExports$1 = v; } }; var classof$f = classofRaw$2; // `IsArray` abstract operation // https://tc39.es/ecma262/#sec-isarray // eslint-disable-next-line es/no-array-isarray -- safe var isArray$f = Array.isArray || function isArray(argument) { return classof$f(argument) == 'Array'; }; var ceil = Math.ceil; var floor$1 = Math.floor; // `Math.trunc` method // https://tc39.es/ecma262/#sec-math.trunc // eslint-disable-next-line es/no-math-trunc -- safe var mathTrunc = Math.trunc || function trunc(x) { var n = +x; return (n > 0 ? floor$1 : ceil)(n); }; var trunc = mathTrunc; // `ToIntegerOrInfinity` abstract operation // https://tc39.es/ecma262/#sec-tointegerorinfinity var toIntegerOrInfinity$4 = function (argument) { var number = +argument; // eslint-disable-next-line no-self-compare -- NaN check return number !== number || number === 0 ? 0 : trunc(number); }; var toIntegerOrInfinity$3 = toIntegerOrInfinity$4; var min$2 = Math.min; // `ToLength` abstract operation // https://tc39.es/ecma262/#sec-tolength var toLength$1 = function (argument) { return argument > 0 ? min$2(toIntegerOrInfinity$3(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991 }; var toLength = toLength$1; // `LengthOfArrayLike` abstract operation // https://tc39.es/ecma262/#sec-lengthofarraylike var lengthOfArrayLike$d = function (obj) { return toLength(obj.length); }; var $TypeError$b = TypeError; var MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFF; // 2 ** 53 - 1 == 9007199254740991 var doesNotExceedSafeInteger$3 = function (it) { if (it > MAX_SAFE_INTEGER) throw $TypeError$b('Maximum allowed index exceeded'); return it; }; var toPropertyKey$1 = toPropertyKey$4; var definePropertyModule$3 = objectDefineProperty; var createPropertyDescriptor$4 = createPropertyDescriptor$7; var createProperty$6 = function (object, key, value) { var propertyKey = toPropertyKey$1(key); if (propertyKey in object) definePropertyModule$3.f(object, propertyKey, createPropertyDescriptor$4(0, value));else object[propertyKey] = value; }; var wellKnownSymbol$m = wellKnownSymbol$o; var TO_STRING_TAG$4 = wellKnownSymbol$m('toStringTag'); var test$2 = {}; test$2[TO_STRING_TAG$4] = 'z'; var toStringTagSupport = String(test$2) === '[object z]'; var TO_STRING_TAG_SUPPORT$2 = toStringTagSupport; var isCallable$e = isCallable$m; var classofRaw = classofRaw$2; var wellKnownSymbol$l = wellKnownSymbol$o; var TO_STRING_TAG$3 = wellKnownSymbol$l('toStringTag'); var $Object$1 = Object; // ES3 wrong here var CORRECT_ARGUMENTS = classofRaw(function () { return arguments; }()) == 'Arguments'; // fallback for IE11 Script Access Denied error var tryGet = function (it, key) { try { return it[key]; } catch (error) {/* empty */} }; // getting tag from ES6+ `Object.prototype.toString` var classof$e = TO_STRING_TAG_SUPPORT$2 ? classofRaw : function (it) { var O, tag, result; return it === undefined ? 'Undefined' : it === null ? 'Null' // @@toStringTag case : typeof (tag = tryGet(O = $Object$1(it), TO_STRING_TAG$3)) == 'string' ? tag // builtinTag case : CORRECT_ARGUMENTS ? classofRaw(O) // ES3 arguments fallback : (result = classofRaw(O)) == 'Object' && isCallable$e(O.callee) ? 'Arguments' : result; }; var uncurryThis$o = functionUncurryThis; var isCallable$d = isCallable$m; var store$1 = sharedStore; var functionToString = uncurryThis$o(Function.toString); // this helper broken in `core-js@3.4.1-3.4.4`, so we can't use `shared` helper if (!isCallable$d(store$1.inspectSource)) { store$1.inspectSource = function (it) { return functionToString(it); }; } var inspectSource$2 = store$1.inspectSource; var uncurryThis$n = functionUncurryThis; var fails$o = fails$w; var isCallable$c = isCallable$m; var classof$d = classof$e; var getBuiltIn$d = getBuiltIn$f; var inspectSource$1 = inspectSource$2; var noop = function () {/* empty */}; var empty = []; var construct$4 = getBuiltIn$d('Reflect', 'construct'); var constructorRegExp = /^\s*(?:class|function)\b/; var exec$2 = uncurryThis$n(constructorRegExp.exec); var INCORRECT_TO_STRING = !constructorRegExp.exec(noop); var isConstructorModern = function isConstructor(argument) { if (!isCallable$c(argument)) return false; try { construct$4(noop, empty, argument); return true; } catch (error) { return false; } }; var isConstructorLegacy = function isConstructor(argument) { if (!isCallable$c(argument)) return false; switch (classof$d(argument)) { case 'AsyncFunction': case 'GeneratorFunction': case 'AsyncGeneratorFunction': return false; } try { // we can't check .prototype since constructors produced by .bind haven't it // `Function#toString` throws on some built-it function in some legacy engines // (for example, `DOMQuad` and similar in FF41-) return INCORRECT_TO_STRING || !!exec$2(constructorRegExp, inspectSource$1(argument)); } catch (error) { return true; } }; isConstructorLegacy.sham = true; // `IsConstructor` abstract operation // https://tc39.es/ecma262/#sec-isconstructor var isConstructor$4 = !construct$4 || fails$o(function () { var called; return isConstructorModern(isConstructorModern.call) || !isConstructorModern(Object) || !isConstructorModern(function () { called = true; }) || called; }) ? isConstructorLegacy : isConstructorModern; var isArray$e = isArray$f; var isConstructor$3 = isConstructor$4; var isObject$d = isObject$i; var wellKnownSymbol$k = wellKnownSymbol$o; var SPECIES$5 = wellKnownSymbol$k('species'); var $Array$3 = Array; // a part of `ArraySpeciesCreate` abstract operation // https://tc39.es/ecma262/#sec-arrayspeciescreate var arraySpeciesConstructor$1 = function (originalArray) { var C; if (isArray$e(originalArray)) { C = originalArray.constructor; // cross-realm fallback if (isConstructor$3(C) && (C === $Array$3 || isArray$e(C.prototype))) C = undefined;else if (isObject$d(C)) { C = C[SPECIES$5]; if (C === null) C = undefined; } } return C === undefined ? $Array$3 : C; }; var arraySpeciesConstructor = arraySpeciesConstructor$1; // `ArraySpeciesCreate` abstract operation // https://tc39.es/ecma262/#sec-arrayspeciescreate var arraySpeciesCreate$4 = function (originalArray, length) { return new (arraySpeciesConstructor(originalArray))(length === 0 ? 0 : length); }; var fails$n = fails$w; var wellKnownSymbol$j = wellKnownSymbol$o; var V8_VERSION$2 = engineV8Version; var SPECIES$4 = wellKnownSymbol$j('species'); var arrayMethodHasSpeciesSupport$5 = function (METHOD_NAME) { // We can't use this feature detection in V8 since it causes // deoptimization and serious performance degradation // https://github.com/zloirock/core-js/issues/677 return V8_VERSION$2 >= 51 || !fails$n(function () { var array = []; var constructor = array.constructor = {}; constructor[SPECIES$4] = function () { return { foo: 1 }; }; return array[METHOD_NAME](Boolean).foo !== 1; }); }; var $$S = _export; var fails$m = fails$w; var isArray$d = isArray$f; var isObject$c = isObject$i; var toObject$c = toObject$e; var lengthOfArrayLike$c = lengthOfArrayLike$d; var doesNotExceedSafeInteger$2 = doesNotExceedSafeInteger$3; var createProperty$5 = createProperty$6; var arraySpeciesCreate$3 = arraySpeciesCreate$4; var arrayMethodHasSpeciesSupport$4 = arrayMethodHasSpeciesSupport$5; var wellKnownSymbol$i = wellKnownSymbol$o; var V8_VERSION$1 = engineV8Version; var IS_CONCAT_SPREADABLE = wellKnownSymbol$i('isConcatSpreadable'); // We can't use this feature detection in V8 since it causes // deoptimization and serious performance degradation // https://github.com/zloirock/core-js/issues/679 var IS_CONCAT_SPREADABLE_SUPPORT = V8_VERSION$1 >= 51 || !fails$m(function () { var array = []; array[IS_CONCAT_SPREADABLE] = false; return array.concat()[0] !== array; }); var isConcatSpreadable = function (O) { if (!isObject$c(O)) return false; var spreadable = O[IS_CONCAT_SPREADABLE]; return spreadable !== undefined ? !!spreadable : isArray$d(O); }; var FORCED$7 = !IS_CONCAT_SPREADABLE_SUPPORT || !arrayMethodHasSpeciesSupport$4('concat'); // `Array.prototype.concat` method // https://tc39.es/ecma262/#sec-array.prototype.concat // with adding support of @@isConcatSpreadable and @@species $$S({ target: 'Array', proto: true, arity: 1, forced: FORCED$7 }, { // eslint-disable-next-line no-unused-vars -- required for `.length` concat: function concat(arg) { var O = toObject$c(this); var A = arraySpeciesCreate$3(O, 0); var n = 0; var i, k, length, len, E; for (i = -1, length = arguments.length; i < length; i++) { E = i === -1 ? O : arguments[i]; if (isConcatSpreadable(E)) { len = lengthOfArrayLike$c(E); doesNotExceedSafeInteger$2(n + len); for (k = 0; k < len; k++, n++) if (k in E) createProperty$5(A, n, E[k]); } else { doesNotExceedSafeInteger$2(n + 1); createProperty$5(A, n++, E); } } A.length = n; return A; } }); var classof$c = classof$e; var $String$2 = String; var toString$a = function (argument) { if (classof$c(argument) === 'Symbol') throw TypeError('Cannot convert a Symbol value to a string'); return $String$2(argument); }; var objectDefineProperties = {}; var toIntegerOrInfinity$2 = toIntegerOrInfinity$4; var max$3 = Math.max; var min$1 = Math.min; // Helper for a popular repeating case of the spec: // Let integer be ? ToInteger(index). // If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length). var toAbsoluteIndex$5 = function (index, length) { var integer = toIntegerOrInfinity$2(index); return integer < 0 ? max$3(integer + length, 0) : min$1(integer, length); }; var toIndexedObject$9 = toIndexedObject$b; var toAbsoluteIndex$4 = toAbsoluteIndex$5; var lengthOfArrayLike$b = lengthOfArrayLike$d; // `Array.prototype.{ indexOf, includes }` methods implementation var createMethod$5 = function (IS_INCLUDES) { return function ($this, el, fromIndex) { var O = toIndexedObject$9($this); var length = lengthOfArrayLike$b(O); var index = toAbsoluteIndex$4(fromIndex, length); var value; // Array#includes uses SameValueZero equality algorithm // eslint-disable-next-line no-self-compare -- NaN check if (IS_INCLUDES && el != el) while (length > index) { value = O[index++]; // eslint-disable-next-line no-self-compare -- NaN check if (value != value) return true; // Array#indexOf ignores holes, Array#includes - not } else for (; length > index; index++) { if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0; } return !IS_INCLUDES && -1; }; }; var arrayIncludes = { // `Array.prototype.includes` method // https://tc39.es/ecma262/#sec-array.prototype.includes includes: createMethod$5(true), // `Array.prototype.indexOf` method // https://tc39.es/ecma262/#sec-array.prototype.indexof indexOf: createMethod$5(false) }; var hiddenKeys$6 = {}; var uncurryThis$m = functionUncurryThis; var hasOwn$g = hasOwnProperty_1; var toIndexedObject$8 = toIndexedObject$b; var indexOf$4 = arrayIncludes.indexOf; var hiddenKeys$5 = hiddenKeys$6; var push$7 = uncurryThis$m([].push); var objectKeysInternal = function (object, names) { var O = toIndexedObject$8(object); var i = 0; var result = []; var key; for (key in O) !hasOwn$g(hiddenKeys$5, key) && hasOwn$g(O, key) && push$7(result, key); // Don't enum bug & hidden keys while (names.length > i) if (hasOwn$g(O, key = names[i++])) { ~indexOf$4(result, key) || push$7(result, key); } return result; }; // IE8- don't enum bug keys var enumBugKeys$3 = ['constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf']; var internalObjectKeys$1 = objectKeysInternal; var enumBugKeys$2 = enumBugKeys$3; // `Object.keys` method // https://tc39.es/ecma262/#sec-object.keys // eslint-disable-next-line es/no-object-keys -- safe var objectKeys$4 = Object.keys || function keys(O) { return internalObjectKeys$1(O, enumBugKeys$2); }; var DESCRIPTORS$c = descriptors; var V8_PROTOTYPE_DEFINE_BUG = v8PrototypeDefineBug; var definePropertyModule$2 = objectDefineProperty; var anObject$b = anObject$d; var toIndexedObject$7 = toIndexedObject$b; var objectKeys$3 = objectKeys$4; // `Object.defineProperties` method // https://tc39.es/ecma262/#sec-object.defineproperties // eslint-disable-next-line es/no-object-defineproperties -- safe objectDefineProperties.f = DESCRIPTORS$c && !V8_PROTOTYPE_DEFINE_BUG ? Object.defineProperties : function defineProperties(O, Properties) { anObject$b(O); var props = toIndexedObject$7(Properties); var keys = objectKeys$3(Properties); var length = keys.length; var index = 0; var key; while (length > index) definePropertyModule$2.f(O, key = keys[index++], props[key]); return O; }; var getBuiltIn$c = getBuiltIn$f; var html$2 = getBuiltIn$c('document', 'documentElement'); var shared$5 = sharedExports; var uid$2 = uid$4; var keys$7 = shared$5('keys'); var sharedKey$4 = function (key) { return keys$7[key] || (keys$7[key] = uid$2(key)); }; /* global ActiveXObject -- old IE, WSH */ var anObject$a = anObject$d; var definePropertiesModule$1 = objectDefineProperties; var enumBugKeys$1 = enumBugKeys$3; var hiddenKeys$4 = hiddenKeys$6; var html$1 = html$2; var documentCreateElement = documentCreateElement$1; var sharedKey$3 = sharedKey$4; var GT = '>'; var LT = '<'; var PROTOTYPE$1 = 'prototype'; var SCRIPT = 'script'; var IE_PROTO$1 = sharedKey$3('IE_PROTO'); var EmptyConstructor = function () {/* empty */}; var scriptTag = function (content) { return LT + SCRIPT + GT + content + LT + '/' + SCRIPT + GT; }; // Create object with fake `null` prototype: use ActiveX Object with cleared prototype var NullProtoObjectViaActiveX = function (activeXDocument) { activeXDocument.write(scriptTag('')); activeXDocument.close(); var temp = activeXDocument.parentWindow.Object; activeXDocument = null; // avoid memory leak return temp; }; // Create object with fake `null` prototype: use iframe Object with cleared prototype var NullProtoObjectViaIFrame = function () { // Thrash, waste and sodomy: IE GC bug var iframe = documentCreateElement('iframe'); var JS = 'java' + SCRIPT + ':'; var iframeDocument; iframe.style.display = 'none'; html$1.appendChild(iframe); // https://github.com/zloirock/core-js/issues/475 iframe.src = String(JS); iframeDocument = iframe.contentWindow.document; iframeDocument.open(); iframeDocument.write(scriptTag('document.F=Object')); iframeDocument.close(); return iframeDocument.F; }; // Check for document.domain and active x support // No need to use active x approach when document.domain is not set // see https://github.com/es-shims/es5-shim/issues/150 // variation of https://github.com/kitcambridge/es5-shim/commit/4f738ac066346 // avoid IE GC bug var activeXDocument; var NullProtoObject = function () { try { activeXDocument = new ActiveXObject('htmlfile'); } catch (error) {/* ignore */} NullProtoObject = typeof document != 'undefined' ? document.domain && activeXDocument ? NullProtoObjectViaActiveX(activeXDocument) // old IE : NullProtoObjectViaIFrame() : NullProtoObjectViaActiveX(activeXDocument); // WSH var length = enumBugKeys$1.length; while (length--) delete NullProtoObject[PROTOTYPE$1][enumBugKeys$1[length]]; return NullProtoObject(); }; hiddenKeys$4[IE_PROTO$1] = true; // `Object.create` method // https://tc39.es/ecma262/#sec-object.create // eslint-disable-next-line es/no-object-create -- safe var objectCreate = Object.create || function create(O, Properties) { var result; if (O !== null) { EmptyConstructor[PROTOTYPE$1] = anObject$a(O); result = new EmptyConstructor(); EmptyConstructor[PROTOTYPE$1] = null; // add "__proto__" for Object.getPrototypeOf polyfill result[IE_PROTO$1] = O; } else result = NullProtoObject(); return Properties === undefined ? result : definePropertiesModule$1.f(result, Properties); }; var objectGetOwnPropertyNames = {}; var internalObjectKeys = objectKeysInternal; var enumBugKeys = enumBugKeys$3; var hiddenKeys$3 = enumBugKeys.concat('length', 'prototype'); // `Object.getOwnPropertyNames` method // https://tc39.es/ecma262/#sec-object.getownpropertynames // eslint-disable-next-line es/no-object-getownpropertynames -- safe objectGetOwnPropertyNames.f = Object.getOwnPropertyNames || function getOwnPropertyNames(O) { return internalObjectKeys(O, hiddenKeys$3); }; var objectGetOwnPropertyNamesExternal = {}; var toAbsoluteIndex$3 = toAbsoluteIndex$5; var lengthOfArrayLike$a = lengthOfArrayLike$d; var createProperty$4 = createProperty$6; var $Array$2 = Array; var max$2 = Math.max; var arraySliceSimple = function (O, start, end) { var length = lengthOfArrayLike$a(O); var k = toAbsoluteIndex$3(start, length); var fin = toAbsoluteIndex$3(end === undefined ? length : end, length); var result = $Array$2(max$2(fin - k, 0)); for (var n = 0; k < fin; k++, n++) createProperty$4(result, n, O[k]); result.length = n; return result; }; /* eslint-disable es/no-object-getownpropertynames -- safe */ var classof$b = classofRaw$2; var toIndexedObject$6 = toIndexedObject$b; var $getOwnPropertyNames$1 = objectGetOwnPropertyNames.f; var arraySlice$6 = arraySliceSimple; var windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames ? Object.getOwnPropertyNames(window) : []; var getWindowNames = function (it) { try { return $getOwnPropertyNames$1(it); } catch (error) { return arraySlice$6(windowNames); } }; // fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window objectGetOwnPropertyNamesExternal.f = function getOwnPropertyNames(it) { return windowNames && classof$b(it) == 'Window' ? getWindowNames(it) : $getOwnPropertyNames$1(toIndexedObject$6(it)); }; var objectGetOwnPropertySymbols = {}; // eslint-disable-next-line es/no-object-getownpropertysymbols -- safe objectGetOwnPropertySymbols.f = Object.getOwnPropertySymbols; var createNonEnumerableProperty$7 = createNonEnumerableProperty$9; var defineBuiltIn$6 = function (target, key, value, options) { if (options && options.enumerable) target[key] = value;else createNonEnumerableProperty$7(target, key, value); return target; }; var defineProperty$6 = objectDefineProperty; var defineBuiltInAccessor$3 = function (target, name, descriptor) { return defineProperty$6.f(target, name, descriptor); }; var wellKnownSymbolWrapped = {}; var wellKnownSymbol$h = wellKnownSymbol$o; wellKnownSymbolWrapped.f = wellKnownSymbol$h; var path$n = path$r; var hasOwn$f = hasOwnProperty_1; var wrappedWellKnownSymbolModule$1 = wellKnownSymbolWrapped; var defineProperty$5 = objectDefineProperty.f; var wellKnownSymbolDefine = function (NAME) { var Symbol = path$n.Symbol || (path$n.Symbol = {}); if (!hasOwn$f(Symbol, NAME)) defineProperty$5(Symbol, NAME, { value: wrappedWellKnownSymbolModule$1.f(NAME) }); }; var call$e = functionCall; var getBuiltIn$b = getBuiltIn$f; var wellKnownSymbol$g = wellKnownSymbol$o; var defineBuiltIn$5 = defineBuiltIn$6; var symbolDefineToPrimitive = function () { var Symbol = getBuiltIn$b('Symbol'); var SymbolPrototype = Symbol && Symbol.prototype; var valueOf = SymbolPrototype && SymbolPrototype.valueOf; var TO_PRIMITIVE = wellKnownSymbol$g('toPrimitive'); if (SymbolPrototype && !SymbolPrototype[TO_PRIMITIVE]) { // `Symbol.prototype[@@toPrimitive]` method // https://tc39.es/ecma262/#sec-symbol.prototype-@@toprimitive // eslint-disable-next-line no-unused-vars -- required for .length defineBuiltIn$5(SymbolPrototype, TO_PRIMITIVE, function (hint) { return call$e(valueOf, this); }, { arity: 1 }); } }; var TO_STRING_TAG_SUPPORT$1 = toStringTagSupport; var classof$a = classof$e; // `Object.prototype.toString` method implementation // https://tc39.es/ecma262/#sec-object.prototype.tostring var objectToString = TO_STRING_TAG_SUPPORT$1 ? {}.toString : function toString() { return '[object ' + classof$a(this) + ']'; }; var TO_STRING_TAG_SUPPORT = toStringTagSupport; var defineProperty$4 = objectDefineProperty.f; var createNonEnumerableProperty$6 = createNonEnumerableProperty$9; var hasOwn$e = hasOwnProperty_1; var toString$9 = objectToString; var wellKnownSymbol$f = wellKnownSymbol$o; var TO_STRING_TAG$2 = wellKnownSymbol$f('toStringTag'); var setToStringTag$7 = function (it, TAG, STATIC, SET_METHOD) { if (it) { var target = STATIC ? it : it.prototype; if (!hasOwn$e(target, TO_STRING_TAG$2)) { defineProperty$4(target, TO_STRING_TAG$2, { configurable: true, value: TAG }); } if (SET_METHOD && !TO_STRING_TAG_SUPPORT) { createNonEnumerableProperty$6(target, 'toString', toString$9); } } }; var global$f = global$n; var isCallable$b = isCallable$m; var WeakMap$1 = global$f.WeakMap; var weakMapBasicDetection = isCallable$b(WeakMap$1) && /native code/.test(String(WeakMap$1)); var NATIVE_WEAK_MAP = weakMapBasicDetection; var global$e = global$n; var isObject$b = isObject$i; var createNonEnumerableProperty$5 = createNonEnumerableProperty$9; var hasOwn$d = hasOwnProperty_1; var shared$4 = sharedStore; var sharedKey$2 = sharedKey$4; var hiddenKeys$2 = hiddenKeys$6; var OBJECT_ALREADY_INITIALIZED = 'Object already initialized'; var TypeError$3 = global$e.TypeError; var WeakMap = global$e.WeakMap; var set$4, get, has; var enforce = function (it) { return has(it) ? get(it) : set$4(it, {}); }; var getterFor = function (TYPE) { return function (it) { var state; if (!isObject$b(it) || (state = get(it)).type !== TYPE) { throw TypeError$3('Incompatible receiver, ' + TYPE + ' required'); } return state; }; }; if (NATIVE_WEAK_MAP || shared$4.state) { var store = shared$4.state || (shared$4.state = new WeakMap()); /* eslint-disable no-self-assign -- prototype methods protection */ store.get = store.get; store.has = store.has; store.set = store.set; /* eslint-enable no-self-assign -- prototype methods protection */ set$4 = function (it, metadata) { if (store.has(it)) throw TypeError$3(OBJECT_ALREADY_INITIALIZED); metadata.facade = it; store.set(it, metadata); return metadata; }; get = function (it) { return store.get(it) || {}; }; has = function (it) { return store.has(it); }; } else { var STATE = sharedKey$2('state'); hiddenKeys$2[STATE] = true; set$4 = function (it, metadata) { if (hasOwn$d(it, STATE)) throw TypeError$3(OBJECT_ALREADY_INITIALIZED); metadata.facade = it; createNonEnumerableProperty$5(it, STATE, metadata); return metadata; }; get = function (it) { return hasOwn$d(it, STATE) ? it[STATE] : {}; }; has = function (it) { return hasOwn$d(it, STATE); }; } var internalState = { set: set$4, get: get, has: has, enforce: enforce, getterFor: getterFor }; var bind$h = functionBindContext; var uncurryThis$l = functionUncurryThis; var IndexedObject$2 = indexedObject; var toObject$b = toObject$e; var lengthOfArrayLike$9 = lengthOfArrayLike$d; var arraySpeciesCreate$2 = arraySpeciesCreate$4; var push$6 = uncurryThis$l([].push); // `Array.prototype.{ forEach, map, filter, some, every, find, findIndex, filterReject }` methods implementation var createMethod$4 = function (TYPE) { var IS_MAP = TYPE == 1; var IS_FILTER = TYPE == 2; var IS_SOME = TYPE == 3; var IS_EVERY = TYPE == 4; var IS_FIND_INDEX = TYPE == 6; var IS_FILTER_REJECT = TYPE == 7; var NO_HOLES = TYPE == 5 || IS_FIND_INDEX; return function ($this, callbackfn, that, specificCreate) { var O = toObject$b($this); var self = IndexedObject$2(O); var boundFunction = bind$h(callbackfn, that); var length = lengthOfArrayLike$9(self); var index = 0; var create = specificCreate || arraySpeciesCreate$2; var target = IS_MAP ? create($this, length) : IS_FILTER || IS_FILTER_REJECT ? create($this, 0) : undefined; var value, result; for (; length > index; index++) if (NO_HOLES || index in self) { value = self[index]; result = boundFunction(value, index, O); if (TYPE) { if (IS_MAP) target[index] = result; // map else if (result) switch (TYPE) { case 3: return true; // some case 5: return value; // find case 6: return index; // findIndex case 2: push$6(target, value); // filter } else switch (TYPE) { case 4: return false; // every case 7: push$6(target, value); // filterReject } } } return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : target; }; }; var arrayIteration = { // `Array.prototype.forEach` method // https://tc39.es/ecma262/#sec-array.prototype.foreach forEach: createMethod$4(0), // `Array.prototype.map` method // https://tc39.es/ecma262/#sec-array.prototype.map map: createMethod$4(1), // `Array.prototype.filter` method // https://tc39.es/ecma262/#sec-array.prototype.filter filter: createMethod$4(2), // `Array.prototype.some` method // https://tc39.es/ecma262/#sec-array.prototype.some some: createMethod$4(3), // `Array.prototype.every` method // https://tc39.es/ecma262/#sec-array.prototype.every every: createMethod$4(4), // `Array.prototype.find` method // https://tc39.es/ecma262/#sec-array.prototype.find find: createMethod$4(5), // `Array.prototype.findIndex` method // https://tc39.es/ecma262/#sec-array.prototype.findIndex findIndex: createMethod$4(6), // `Array.prototype.filterReject` method // https://github.com/tc39/proposal-array-filtering filterReject: createMethod$4(7) }; var $$R = _export; var global$d = global$n; var call$d = functionCall; var uncurryThis$k = functionUncurryThis; var DESCRIPTORS$b = descriptors; var NATIVE_SYMBOL$3 = symbolConstructorDetection; var fails$l = fails$w; var hasOwn$c = hasOwnProperty_1; var isPrototypeOf$m = objectIsPrototypeOf; var anObject$9 = anObject$d; var toIndexedObject$5 = toIndexedObject$b; var toPropertyKey = toPropertyKey$4; var $toString = toString$a; var createPropertyDescriptor$3 = createPropertyDescriptor$7; var nativeObjectCreate = objectCreate; var objectKeys$2 = objectKeys$4; var getOwnPropertyNamesModule$2 = objectGetOwnPropertyNames; var getOwnPropertyNamesExternal = objectGetOwnPropertyNamesExternal; var getOwnPropertySymbolsModule$3 = objectGetOwnPropertySymbols; var getOwnPropertyDescriptorModule$2 = objectGetOwnPropertyDescriptor; var definePropertyModule$1 = objectDefineProperty; var definePropertiesModule = objectDefineProperties; var propertyIsEnumerableModule$1 = objectPropertyIsEnumerable; var defineBuiltIn$4 = defineBuiltIn$6; var defineBuiltInAccessor$2 = defineBuiltInAccessor$3; var shared$3 = sharedExports; var sharedKey$1 = sharedKey$4; var hiddenKeys$1 = hiddenKeys$6; var uid$1 = uid$4; var wellKnownSymbol$e = wellKnownSymbol$o; var wrappedWellKnownSymbolModule = wellKnownSymbolWrapped; var defineWellKnownSymbol$l = wellKnownSymbolDefine; var defineSymbolToPrimitive$1 = symbolDefineToPrimitive; var setToStringTag$6 = setToStringTag$7; var InternalStateModule$5 = internalState; var $forEach$1 = arrayIteration.forEach; var HIDDEN = sharedKey$1('hidden'); var SYMBOL = 'Symbol'; var PROTOTYPE = 'prototype'; var setInternalState$5 = InternalStateModule$5.set; var getInternalState$2 = InternalStateModule$5.getterFor(SYMBOL); var ObjectPrototype$2 = Object[PROTOTYPE]; var $Symbol = global$d.Symbol; var SymbolPrototype = $Symbol && $Symbol[PROTOTYPE]; var TypeError$2 = global$d.TypeError; var QObject = global$d.QObject; var nativeGetOwnPropertyDescriptor$1 = getOwnPropertyDescriptorModule$2.f; var nativeDefineProperty = definePropertyModule$1.f; var nativeGetOwnPropertyNames = getOwnPropertyNamesExternal.f; var nativePropertyIsEnumerable = propertyIsEnumerableModule$1.f; var push$5 = uncurryThis$k([].push); var AllSymbols = shared$3('symbols'); var ObjectPrototypeSymbols = shared$3('op-symbols'); var WellKnownSymbolsStore$1 = shared$3('wks'); // Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173 var USE_SETTER = !QObject || !QObject[PROTOTYPE] || !QObject[PROTOTYPE].findChild; // fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687 var setSymbolDescriptor = DESCRIPTORS$b && fails$l(function () { return nativeObjectCreate(nativeDefineProperty({}, 'a', { get: function () { return nativeDefineProperty(this, 'a', { value: 7 }).a; } })).a != 7; }) ? function (O, P, Attributes) { var ObjectPrototypeDescriptor = nativeGetOwnPropertyDescriptor$1(ObjectPrototype$2, P); if (ObjectPrototypeDescriptor) delete ObjectPrototype$2[P]; nativeDefineProperty(O, P, Attributes); if (ObjectPrototypeDescriptor && O !== ObjectPrototype$2) { nativeDefineProperty(ObjectPrototype$2, P, ObjectPrototypeDescriptor); } } : nativeDefineProperty; var wrap = function (tag, description) { var symbol = AllSymbols[tag] = nativeObjectCreate(SymbolPrototype); setInternalState$5(symbol, { type: SYMBOL, tag: tag, description: description }); if (!DESCRIPTORS$b) symbol.description = description; return symbol; }; var $defineProperty = function defineProperty(O, P, Attributes) { if (O === ObjectPrototype$2) $defineProperty(ObjectPrototypeSymbols, P, Attributes); anObject$9(O); var key = toPropertyKey(P); anObject$9(Attributes); if (hasOwn$c(AllSymbols, key)) { if (!Attributes.enumerable) { if (!hasOwn$c(O, HIDDEN)) nativeDefineProperty(O, HIDDEN, createPropertyDescriptor$3(1, {})); O[HIDDEN][key] = true; } else { if (hasOwn$c(O, HIDDEN) && O[HIDDEN][key]) O[HIDDEN][key] = false; Attributes = nativeObjectCreate(Attributes, { enumerable: createPropertyDescriptor$3(0, false) }); } return setSymbolDescriptor(O, key, Attributes); } return nativeDefineProperty(O, key, Attributes); }; var $defineProperties = function defineProperties(O, Properties) { anObject$9(O); var properties = toIndexedObject$5(Properties); var keys = objectKeys$2(properties).concat($getOwnPropertySymbols(properties)); $forEach$1(keys, function (key) { if (!DESCRIPTORS$b || call$d($propertyIsEnumerable$1, properties, key)) $defineProperty(O, key, properties[key]); }); return O; }; var $create = function create(O, Properties) { return Properties === undefined ? nativeObjectCreate(O) : $defineProperties(nativeObjectCreate(O), Properties); }; var $propertyIsEnumerable$1 = function propertyIsEnumerable(V) { var P = toPropertyKey(V); var enumerable = call$d(nativePropertyIsEnumerable, this, P); if (this === ObjectPrototype$2 && hasOwn$c(AllSymbols, P) && !hasOwn$c(ObjectPrototypeSymbols, P)) return false; return enumerable || !hasOwn$c(this, P) || !hasOwn$c(AllSymbols, P) || hasOwn$c(this, HIDDEN) && this[HIDDEN][P] ? enumerable : true; }; var $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(O, P) { var it = toIndexedObject$5(O); var key = toPropertyKey(P); if (it === ObjectPrototype$2 && hasOwn$c(AllSymbols, key) && !hasOwn$c(ObjectPrototypeSymbols, key)) return; var descriptor = nativeGetOwnPropertyDescriptor$1(it, key); if (descriptor && hasOwn$c(AllSymbols, key) && !(hasOwn$c(it, HIDDEN) && it[HIDDEN][key])) { descriptor.enumerable = true; } return descriptor; }; var $getOwnPropertyNames = function getOwnPropertyNames(O) { var names = nativeGetOwnPropertyNames(toIndexedObject$5(O)); var result = []; $forEach$1(names, function (key) { if (!hasOwn$c(AllSymbols, key) && !hasOwn$c(hiddenKeys$1, key)) push$5(result, key); }); return result; }; var $getOwnPropertySymbols = function (O) { var IS_OBJECT_PROTOTYPE = O === ObjectPrototype$2; var names = nativeGetOwnPropertyNames(IS_OBJECT_PROTOTYPE ? ObjectPrototypeSymbols : toIndexedObject$5(O)); var result = []; $forEach$1(names, function (key) { if (hasOwn$c(AllSymbols, key) && (!IS_OBJECT_PROTOTYPE || hasOwn$c(ObjectPrototype$2, key))) { push$5(result, AllSymbols[key]); } }); return result; }; // `Symbol` constructor // https://tc39.es/ecma262/#sec-symbol-constructor if (!NATIVE_SYMBOL$3) { $Symbol = function Symbol() { if (isPrototypeOf$m(SymbolPrototype, this)) throw TypeError$2('Symbol is not a constructor'); var description = !arguments.length || arguments[0] === undefined ? undefined : $toString(arguments[0]); var tag = uid$1(description); var setter = function (value) { if (this === ObjectPrototype$2) call$d(setter, ObjectPrototypeSymbols, value); if (hasOwn$c(this, HIDDEN) && hasOwn$c(this[HIDDEN], tag)) this[HIDDEN][tag] = false; setSymbolDescriptor(this, tag, createPropertyDescriptor$3(1, value)); }; if (DESCRIPTORS$b && USE_SETTER) setSymbolDescriptor(ObjectPrototype$2, tag, { configurable: true, set: setter }); return wrap(tag, description); }; SymbolPrototype = $Symbol[PROTOTYPE]; defineBuiltIn$4(SymbolPrototype, 'toString', function toString() { return getInternalState$2(this).tag; }); defineBuiltIn$4($Symbol, 'withoutSetter', function (description) { return wrap(uid$1(description), description); }); propertyIsEnumerableModule$1.f = $propertyIsEnumerable$1; definePropertyModule$1.f = $defineProperty; definePropertiesModule.f = $defineProperties; getOwnPropertyDescriptorModule$2.f = $getOwnPropertyDescriptor; getOwnPropertyNamesModule$2.f = getOwnPropertyNamesExternal.f = $getOwnPropertyNames; getOwnPropertySymbolsModule$3.f = $getOwnPropertySymbols; wrappedWellKnownSymbolModule.f = function (name) { return wrap(wellKnownSymbol$e(name), name); }; if (DESCRIPTORS$b) { // https://github.com/tc39/proposal-Symbol-description defineBuiltInAccessor$2(SymbolPrototype, 'description', { configurable: true, get: function description() { return getInternalState$2(this).description; } }); } } $$R({ global: true, constructor: true, wrap: true, forced: !NATIVE_SYMBOL$3, sham: !NATIVE_SYMBOL$3 }, { Symbol: $Symbol }); $forEach$1(objectKeys$2(WellKnownSymbolsStore$1), function (name) { defineWellKnownSymbol$l(name); }); $$R({ target: SYMBOL, stat: true, forced: !NATIVE_SYMBOL$3 }, { useSetter: function () { USE_SETTER = true; }, useSimple: function () { USE_SETTER = false; } }); $$R({ target: 'Object', stat: true, forced: !NATIVE_SYMBOL$3, sham: !DESCRIPTORS$b }, { // `Object.create` method // https://tc39.es/ecma262/#sec-object.create create: $create, // `Object.defineProperty` method // https://tc39.es/ecma262/#sec-object.defineproperty defineProperty: $defineProperty, // `Object.defineProperties` method // https://tc39.es/ecma262/#sec-object.defineproperties defineProperties: $defineProperties, // `Object.getOwnPropertyDescriptor` method // https://tc39.es/ecma262/#sec-object.getownpropertydescriptors getOwnPropertyDescriptor: $getOwnPropertyDescriptor }); $$R({ target: 'Object', stat: true, forced: !NATIVE_SYMBOL$3 }, { // `Object.getOwnPropertyNames` method // https://tc39.es/ecma262/#sec-object.getownpropertynames getOwnPropertyNames: $getOwnPropertyNames }); // `Symbol.prototype[@@toPrimitive]` method // https://tc39.es/ecma262/#sec-symbol.prototype-@@toprimitive defineSymbolToPrimitive$1(); // `Symbol.prototype[@@toStringTag]` property // https://tc39.es/ecma262/#sec-symbol.prototype-@@tostringtag setToStringTag$6($Symbol, SYMBOL); hiddenKeys$1[HIDDEN] = true; var NATIVE_SYMBOL$2 = symbolConstructorDetection; /* eslint-disable es/no-symbol -- safe */ var symbolRegistryDetection = NATIVE_SYMBOL$2 && !!Symbol['for'] && !!Symbol.keyFor; var $$Q = _export; var getBuiltIn$a = getBuiltIn$f; var hasOwn$b = hasOwnProperty_1; var toString$8 = toString$a; var shared$2 = sharedExports; var NATIVE_SYMBOL_REGISTRY$1 = symbolRegistryDetection; var StringToSymbolRegistry = shared$2('string-to-symbol-registry'); var SymbolToStringRegistry$1 = shared$2('symbol-to-string-registry'); // `Symbol.for` method // https://tc39.es/ecma262/#sec-symbol.for $$Q({ target: 'Symbol', stat: true, forced: !NATIVE_SYMBOL_REGISTRY$1 }, { 'for': function (key) { var string = toString$8(key); if (hasOwn$b(StringToSymbolRegistry, string)) return StringToSymbolRegistry[string]; var symbol = getBuiltIn$a('Symbol')(string); StringToSymbolRegistry[string] = symbol; SymbolToStringRegistry$1[symbol] = string; return symbol; } }); var $$P = _export; var hasOwn$a = hasOwnProperty_1; var isSymbol$2 = isSymbol$5; var tryToString$4 = tryToString$6; var shared$1 = sharedExports; var NATIVE_SYMBOL_REGISTRY = symbolRegistryDetection; var SymbolToStringRegistry = shared$1('symbol-to-string-registry'); // `Symbol.keyFor` method // https://tc39.es/ecma262/#sec-symbol.keyfor $$P({ target: 'Symbol', stat: true, forced: !NATIVE_SYMBOL_REGISTRY }, { keyFor: function keyFor(sym) { if (!isSymbol$2(sym)) throw TypeError(tryToString$4(sym) + ' is not a symbol'); if (hasOwn$a(SymbolToStringRegistry, sym)) return SymbolToStringRegistry[sym]; } }); var uncurryThis$j = functionUncurryThis; var arraySlice$5 = uncurryThis$j([].slice); var uncurryThis$i = functionUncurryThis; var isArray$c = isArray$f; var isCallable$a = isCallable$m; var classof$9 = classofRaw$2; var toString$7 = toString$a; var push$4 = uncurryThis$i([].push); var getJsonReplacerFunction = function (replacer) { if (isCallable$a(replacer)) return replacer; if (!isArray$c(replacer)) return; var rawLength = replacer.length; var keys = []; for (var i = 0; i < rawLength; i++) { var element = replacer[i]; if (typeof element == 'string') push$4(keys, element);else if (typeof element == 'number' || classof$9(element) == 'Number' || classof$9(element) == 'String') push$4(keys, toString$7(element)); } var keysLength = keys.length; var root = true; return function (key, value) { if (root) { root = false; return value; } if (isArray$c(this)) return value; for (var j = 0; j < keysLength; j++) if (keys[j] === key) return value; }; }; var $$O = _export; var getBuiltIn$9 = getBuiltIn$f; var apply$4 = functionApply; var call$c = functionCall; var uncurryThis$h = functionUncurryThis; var fails$k = fails$w; var isCallable$9 = isCallable$m; var isSymbol$1 = isSymbol$5; var arraySlice$4 = arraySlice$5; var getReplacerFunction = getJsonReplacerFunction; var NATIVE_SYMBOL$1 = symbolConstructorDetection; var $String$1 = String; var $stringify = getBuiltIn$9('JSON', 'stringify'); var exec$1 = uncurryThis$h(/./.exec); var charAt$2 = uncurryThis$h(''.charAt); var charCodeAt$1 = uncurryThis$h(''.charCodeAt); var replace$2 = uncurryThis$h(''.replace); var numberToString = uncurryThis$h(1.0.toString); var tester = /[\uD800-\uDFFF]/g; var low = /^[\uD800-\uDBFF]$/; var hi = /^[\uDC00-\uDFFF]$/; var WRONG_SYMBOLS_CONVERSION = !NATIVE_SYMBOL$1 || fails$k(function () { var symbol = getBuiltIn$9('Symbol')(); // MS Edge converts symbol values to JSON as {} return $stringify([symbol]) != '[null]' // WebKit converts symbol values to JSON as null || $stringify({ a: symbol }) != '{}' // V8 throws on boxed symbols || $stringify(Object(symbol)) != '{}'; }); // https://github.com/tc39/proposal-well-formed-stringify var ILL_FORMED_UNICODE = fails$k(function () { return $stringify('\uDF06\uD834') !== '"\\udf06\\ud834"' || $stringify('\uDEAD') !== '"\\udead"'; }); var stringifyWithSymbolsFix = function (it, replacer) { var args = arraySlice$4(arguments); var $replacer = getReplacerFunction(replacer); if (!isCallable$9($replacer) && (it === undefined || isSymbol$1(it))) return; // IE8 returns string on undefined args[1] = function (key, value) { // some old implementations (like WebKit) could pass numbers as keys if (isCallable$9($replacer)) value = call$c($replacer, this, $String$1(key), value); if (!isSymbol$1(value)) return value; }; return apply$4($stringify, null, args); }; var fixIllFormed = function (match, offset, string) { var prev = charAt$2(string, offset - 1); var next = charAt$2(string, offset + 1); if (exec$1(low, match) && !exec$1(hi, next) || exec$1(hi, match) && !exec$1(low, prev)) { return '\\u' + numberToString(charCodeAt$1(match, 0), 16); } return match; }; if ($stringify) { // `JSON.stringify` method // https://tc39.es/ecma262/#sec-json.stringify $$O({ target: 'JSON', stat: true, arity: 3, forced: WRONG_SYMBOLS_CONVERSION || ILL_FORMED_UNICODE }, { // eslint-disable-next-line no-unused-vars -- required for `.length` stringify: function stringify(it, replacer, space) { var args = arraySlice$4(arguments); var result = apply$4(WRONG_SYMBOLS_CONVERSION ? stringifyWithSymbolsFix : $stringify, null, args); return ILL_FORMED_UNICODE && typeof result == 'string' ? replace$2(result, tester, fixIllFormed) : result; } }); } var $$N = _export; var NATIVE_SYMBOL = symbolConstructorDetection; var fails$j = fails$w; var getOwnPropertySymbolsModule$2 = objectGetOwnPropertySymbols; var toObject$a = toObject$e; // V8 ~ Chrome 38 and 39 `Object.getOwnPropertySymbols` fails on primitives // https://bugs.chromium.org/p/v8/issues/detail?id=3443 var FORCED$6 = !NATIVE_SYMBOL || fails$j(function () { getOwnPropertySymbolsModule$2.f(1); }); // `Object.getOwnPropertySymbols` method // https://tc39.es/ecma262/#sec-object.getownpropertysymbols $$N({ target: 'Object', stat: true, forced: FORCED$6 }, { getOwnPropertySymbols: function getOwnPropertySymbols(it) { var $getOwnPropertySymbols = getOwnPropertySymbolsModule$2.f; return $getOwnPropertySymbols ? $getOwnPropertySymbols(toObject$a(it)) : []; } }); var defineWellKnownSymbol$k = wellKnownSymbolDefine; // `Symbol.asyncIterator` well-known symbol // https://tc39.es/ecma262/#sec-symbol.asynciterator defineWellKnownSymbol$k('asyncIterator'); var defineWellKnownSymbol$j = wellKnownSymbolDefine; // `Symbol.hasInstance` well-known symbol // https://tc39.es/ecma262/#sec-symbol.hasinstance defineWellKnownSymbol$j('hasInstance'); var defineWellKnownSymbol$i = wellKnownSymbolDefine; // `Symbol.isConcatSpreadable` well-known symbol // https://tc39.es/ecma262/#sec-symbol.isconcatspreadable defineWellKnownSymbol$i('isConcatSpreadable'); var defineWellKnownSymbol$h = wellKnownSymbolDefine; // `Symbol.iterator` well-known symbol // https://tc39.es/ecma262/#sec-symbol.iterator defineWellKnownSymbol$h('iterator'); var defineWellKnownSymbol$g = wellKnownSymbolDefine; // `Symbol.match` well-known symbol // https://tc39.es/ecma262/#sec-symbol.match defineWellKnownSymbol$g('match'); var defineWellKnownSymbol$f = wellKnownSymbolDefine; // `Symbol.matchAll` well-known symbol // https://tc39.es/ecma262/#sec-symbol.matchall defineWellKnownSymbol$f('matchAll'); var defineWellKnownSymbol$e = wellKnownSymbolDefine; // `Symbol.replace` well-known symbol // https://tc39.es/ecma262/#sec-symbol.replace defineWellKnownSymbol$e('replace'); var defineWellKnownSymbol$d = wellKnownSymbolDefine; // `Symbol.search` well-known symbol // https://tc39.es/ecma262/#sec-symbol.search defineWellKnownSymbol$d('search'); var defineWellKnownSymbol$c = wellKnownSymbolDefine; // `Symbol.species` well-known symbol // https://tc39.es/ecma262/#sec-symbol.species defineWellKnownSymbol$c('species'); var defineWellKnownSymbol$b = wellKnownSymbolDefine; // `Symbol.split` well-known symbol // https://tc39.es/ecma262/#sec-symbol.split defineWellKnownSymbol$b('split'); var defineWellKnownSymbol$a = wellKnownSymbolDefine; var defineSymbolToPrimitive = symbolDefineToPrimitive; // `Symbol.toPrimitive` well-known symbol // https://tc39.es/ecma262/#sec-symbol.toprimitive defineWellKnownSymbol$a('toPrimitive'); // `Symbol.prototype[@@toPrimitive]` method // https://tc39.es/ecma262/#sec-symbol.prototype-@@toprimitive defineSymbolToPrimitive(); var getBuiltIn$8 = getBuiltIn$f; var defineWellKnownSymbol$9 = wellKnownSymbolDefine; var setToStringTag$5 = setToStringTag$7; // `Symbol.toStringTag` well-known symbol // https://tc39.es/ecma262/#sec-symbol.tostringtag defineWellKnownSymbol$9('toStringTag'); // `Symbol.prototype[@@toStringTag]` property // https://tc39.es/ecma262/#sec-symbol.prototype-@@tostringtag setToStringTag$5(getBuiltIn$8('Symbol'), 'Symbol'); var defineWellKnownSymbol$8 = wellKnownSymbolDefine; // `Symbol.unscopables` well-known symbol // https://tc39.es/ecma262/#sec-symbol.unscopables defineWellKnownSymbol$8('unscopables'); var global$c = global$n; var setToStringTag$4 = setToStringTag$7; // JSON[@@toStringTag] property // https://tc39.es/ecma262/#sec-json-@@tostringtag setToStringTag$4(global$c.JSON, 'JSON', true); var path$m = path$r; var symbol$4 = path$m.Symbol; var iterators = {}; var DESCRIPTORS$a = descriptors; var hasOwn$9 = hasOwnProperty_1; var FunctionPrototype$1 = Function.prototype; // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe var getDescriptor = DESCRIPTORS$a && Object.getOwnPropertyDescriptor; var EXISTS = hasOwn$9(FunctionPrototype$1, 'name'); // additional protection from minified / mangled / dropped function names var PROPER = EXISTS && function something() {/* empty */}.name === 'something'; var CONFIGURABLE = EXISTS && (!DESCRIPTORS$a || DESCRIPTORS$a && getDescriptor(FunctionPrototype$1, 'name').configurable); var functionName = { EXISTS: EXISTS, PROPER: PROPER, CONFIGURABLE: CONFIGURABLE }; var fails$i = fails$w; var correctPrototypeGetter = !fails$i(function () { function F() {/* empty */} F.prototype.constructor = null; // eslint-disable-next-line es/no-object-getprototypeof -- required for testing return Object.getPrototypeOf(new F()) !== F.prototype; }); var hasOwn$8 = hasOwnProperty_1; var isCallable$8 = isCallable$m; var toObject$9 = toObject$e; var sharedKey = sharedKey$4; var CORRECT_PROTOTYPE_GETTER$1 = correctPrototypeGetter; var IE_PROTO = sharedKey('IE_PROTO'); var $Object = Object; var ObjectPrototype$1 = $Object.prototype; // `Object.getPrototypeOf` method // https://tc39.es/ecma262/#sec-object.getprototypeof // eslint-disable-next-line es/no-object-getprototypeof -- safe var objectGetPrototypeOf = CORRECT_PROTOTYPE_GETTER$1 ? $Object.getPrototypeOf : function (O) { var object = toObject$9(O); if (hasOwn$8(object, IE_PROTO)) return object[IE_PROTO]; var constructor = object.constructor; if (isCallable$8(constructor) && object instanceof constructor) { return constructor.prototype; } return object instanceof $Object ? ObjectPrototype$1 : null; }; var fails$h = fails$w; var isCallable$7 = isCallable$m; var isObject$a = isObject$i; var create$c = objectCreate; var getPrototypeOf$9 = objectGetPrototypeOf; var defineBuiltIn$3 = defineBuiltIn$6; var wellKnownSymbol$d = wellKnownSymbol$o; var ITERATOR$5 = wellKnownSymbol$d('iterator'); var BUGGY_SAFARI_ITERATORS$1 = false; // `%IteratorPrototype%` object // https://tc39.es/ecma262/#sec-%iteratorprototype%-object var IteratorPrototype$1, PrototypeOfArrayIteratorPrototype, arrayIterator; /* eslint-disable es/no-array-prototype-keys -- safe */ if ([].keys) { arrayIterator = [].keys(); // Safari 8 has buggy iterators w/o `next` if (!('next' in arrayIterator)) BUGGY_SAFARI_ITERATORS$1 = true;else { PrototypeOfArrayIteratorPrototype = getPrototypeOf$9(getPrototypeOf$9(arrayIterator)); if (PrototypeOfArrayIteratorPrototype !== Object.prototype) IteratorPrototype$1 = PrototypeOfArrayIteratorPrototype; } } var NEW_ITERATOR_PROTOTYPE = !isObject$a(IteratorPrototype$1) || fails$h(function () { var test = {}; // FF44- legacy iterators case return IteratorPrototype$1[ITERATOR$5].call(test) !== test; }); if (NEW_ITERATOR_PROTOTYPE) IteratorPrototype$1 = {};else IteratorPrototype$1 = create$c(IteratorPrototype$1); // `%IteratorPrototype%[@@iterator]()` method // https://tc39.es/ecma262/#sec-%iteratorprototype%-@@iterator if (!isCallable$7(IteratorPrototype$1[ITERATOR$5])) { defineBuiltIn$3(IteratorPrototype$1, ITERATOR$5, function () { return this; }); } var iteratorsCore = { IteratorPrototype: IteratorPrototype$1, BUGGY_SAFARI_ITERATORS: BUGGY_SAFARI_ITERATORS$1 }; var IteratorPrototype = iteratorsCore.IteratorPrototype; var create$b = objectCreate; var createPropertyDescriptor$2 = createPropertyDescriptor$7; var setToStringTag$3 = setToStringTag$7; var Iterators$5 = iterators; var returnThis$1 = function () { return this; }; var iteratorCreateConstructor = function (IteratorConstructor, NAME, next, ENUMERABLE_NEXT) { var TO_STRING_TAG = NAME + ' Iterator'; IteratorConstructor.prototype = create$b(IteratorPrototype, { next: createPropertyDescriptor$2(+!ENUMERABLE_NEXT, next) }); setToStringTag$3(IteratorConstructor, TO_STRING_TAG, false, true); Iterators$5[TO_STRING_TAG] = returnThis$1; return IteratorConstructor; }; var uncurryThis$g = functionUncurryThis; var aCallable$b = aCallable$e; var functionUncurryThisAccessor = function (object, key, method) { try { // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe return uncurryThis$g(aCallable$b(Object.getOwnPropertyDescriptor(object, key)[method])); } catch (error) {/* empty */} }; var isCallable$6 = isCallable$m; var $String = String; var $TypeError$a = TypeError; var aPossiblePrototype$1 = function (argument) { if (typeof argument == 'object' || isCallable$6(argument)) return argument; throw $TypeError$a("Can't set " + $String(argument) + ' as a prototype'); }; /* eslint-disable no-proto -- safe */ var uncurryThisAccessor = functionUncurryThisAccessor; var anObject$8 = anObject$d; var aPossiblePrototype = aPossiblePrototype$1; // `Object.setPrototypeOf` method // https://tc39.es/ecma262/#sec-object.setprototypeof // Works with __proto__ only. Old v8 can't work with null proto objects. // eslint-disable-next-line es/no-object-setprototypeof -- safe var objectSetPrototypeOf = Object.setPrototypeOf || ('__proto__' in {} ? function () { var CORRECT_SETTER = false; var test = {}; var setter; try { setter = uncurryThisAccessor(Object.prototype, '__proto__', 'set'); setter(test, []); CORRECT_SETTER = test instanceof Array; } catch (error) {/* empty */} return function setPrototypeOf(O, proto) { anObject$8(O); aPossiblePrototype(proto); if (CORRECT_SETTER) setter(O, proto);else O.__proto__ = proto; return O; }; }() : undefined); var $$M = _export; var call$b = functionCall; var FunctionName = functionName; var createIteratorConstructor = iteratorCreateConstructor; var getPrototypeOf$8 = objectGetPrototypeOf; var setToStringTag$2 = setToStringTag$7; var defineBuiltIn$2 = defineBuiltIn$6; var wellKnownSymbol$c = wellKnownSymbol$o; var Iterators$4 = iterators; var IteratorsCore = iteratorsCore; var PROPER_FUNCTION_NAME$1 = FunctionName.PROPER; var BUGGY_SAFARI_ITERATORS = IteratorsCore.BUGGY_SAFARI_ITERATORS; var ITERATOR$4 = wellKnownSymbol$c('iterator'); var KEYS = 'keys'; var VALUES = 'values'; var ENTRIES = 'entries'; var returnThis = function () { return this; }; var iteratorDefine = function (Iterable, NAME, IteratorConstructor, next, DEFAULT, IS_SET, FORCED) { createIteratorConstructor(IteratorConstructor, NAME, next); var getIterationMethod = function (KIND) { if (KIND === DEFAULT && defaultIterator) return defaultIterator; if (!BUGGY_SAFARI_ITERATORS && KIND in IterablePrototype) return IterablePrototype[KIND]; switch (KIND) { case KEYS: return function keys() { return new IteratorConstructor(this, KIND); }; case VALUES: return function values() { return new IteratorConstructor(this, KIND); }; case ENTRIES: return function entries() { return new IteratorConstructor(this, KIND); }; } return function () { return new IteratorConstructor(this); }; }; var TO_STRING_TAG = NAME + ' Iterator'; var INCORRECT_VALUES_NAME = false; var IterablePrototype = Iterable.prototype; var nativeIterator = IterablePrototype[ITERATOR$4] || IterablePrototype['@@iterator'] || DEFAULT && IterablePrototype[DEFAULT]; var defaultIterator = !BUGGY_SAFARI_ITERATORS && nativeIterator || getIterationMethod(DEFAULT); var anyNativeIterator = NAME == 'Array' ? IterablePrototype.entries || nativeIterator : nativeIterator; var CurrentIteratorPrototype, methods, KEY; // fix native if (anyNativeIterator) { CurrentIteratorPrototype = getPrototypeOf$8(anyNativeIterator.call(new Iterable())); if (CurrentIteratorPrototype !== Object.prototype && CurrentIteratorPrototype.next) { // Set @@toStringTag to native iterators setToStringTag$2(CurrentIteratorPrototype, TO_STRING_TAG, true, true); Iterators$4[TO_STRING_TAG] = returnThis; } } // fix Array.prototype.{ values, @@iterator }.name in V8 / FF if (PROPER_FUNCTION_NAME$1 && DEFAULT == VALUES && nativeIterator && nativeIterator.name !== VALUES) { { INCORRECT_VALUES_NAME = true; defaultIterator = function values() { return call$b(nativeIterator, this); }; } } // export additional methods if (DEFAULT) { methods = { values: getIterationMethod(VALUES), keys: IS_SET ? defaultIterator : getIterationMethod(KEYS), entries: getIterationMethod(ENTRIES) }; if (FORCED) for (KEY in methods) { if (BUGGY_SAFARI_ITERATORS || INCORRECT_VALUES_NAME || !(KEY in IterablePrototype)) { defineBuiltIn$2(IterablePrototype, KEY, methods[KEY]); } } else $$M({ target: NAME, proto: true, forced: BUGGY_SAFARI_ITERATORS || INCORRECT_VALUES_NAME }, methods); } // define iterator if (FORCED && IterablePrototype[ITERATOR$4] !== defaultIterator) { defineBuiltIn$2(IterablePrototype, ITERATOR$4, defaultIterator, { name: DEFAULT }); } Iterators$4[NAME] = defaultIterator; return methods; }; // `CreateIterResultObject` abstract operation // https://tc39.es/ecma262/#sec-createiterresultobject var createIterResultObject$3 = function (value, done) { return { value: value, done: done }; }; var toIndexedObject$4 = toIndexedObject$b; var Iterators$3 = iterators; var InternalStateModule$4 = internalState; objectDefineProperty.f; var defineIterator$2 = iteratorDefine; var createIterResultObject$2 = createIterResultObject$3; var ARRAY_ITERATOR = 'Array Iterator'; var setInternalState$4 = InternalStateModule$4.set; var getInternalState$1 = InternalStateModule$4.getterFor(ARRAY_ITERATOR); // `Array.prototype.entries` method // https://tc39.es/ecma262/#sec-array.prototype.entries // `Array.prototype.keys` method // https://tc39.es/ecma262/#sec-array.prototype.keys // `Array.prototype.values` method // https://tc39.es/ecma262/#sec-array.prototype.values // `Array.prototype[@@iterator]` method // https://tc39.es/ecma262/#sec-array.prototype-@@iterator // `CreateArrayIterator` internal method // https://tc39.es/ecma262/#sec-createarrayiterator defineIterator$2(Array, 'Array', function (iterated, kind) { setInternalState$4(this, { type: ARRAY_ITERATOR, target: toIndexedObject$4(iterated), // target index: 0, // next index kind: kind // kind }); // `%ArrayIteratorPrototype%.next` method // https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next }, function () { var state = getInternalState$1(this); var target = state.target; var kind = state.kind; var index = state.index++; if (!target || index >= target.length) { state.target = undefined; return createIterResultObject$2(undefined, true); } if (kind == 'keys') return createIterResultObject$2(index, false); if (kind == 'values') return createIterResultObject$2(target[index], false); return createIterResultObject$2([index, target[index]], false); }, 'values'); // argumentsList[@@iterator] is %ArrayProto_values% // https://tc39.es/ecma262/#sec-createunmappedargumentsobject // https://tc39.es/ecma262/#sec-createmappedargumentsobject Iterators$3.Arguments = Iterators$3.Array; // iterable DOM collections // flag - `iterable` interface - 'entries', 'keys', 'values', 'forEach' methods var domIterables = { CSSRuleList: 0, CSSStyleDeclaration: 0, CSSValueList: 0, ClientRectList: 0, DOMRectList: 0, DOMStringList: 0, DOMTokenList: 1, DataTransferItemList: 0, FileList: 0, HTMLAllCollection: 0, HTMLCollection: 0, HTMLFormElement: 0, HTMLSelectElement: 0, MediaList: 0, MimeTypeArray: 0, NamedNodeMap: 0, NodeList: 1, PaintRequestList: 0, Plugin: 0, PluginArray: 0, SVGLengthList: 0, SVGNumberList: 0, SVGPathSegList: 0, SVGPointList: 0, SVGStringList: 0, SVGTransformList: 0, SourceBufferList: 0, StyleSheetList: 0, TextTrackCueList: 0, TextTrackList: 0, TouchList: 0 }; var DOMIterables$4 = domIterables; var global$b = global$n; var classof$8 = classof$e; var createNonEnumerableProperty$4 = createNonEnumerableProperty$9; var Iterators$2 = iterators; var wellKnownSymbol$b = wellKnownSymbol$o; var TO_STRING_TAG$1 = wellKnownSymbol$b('toStringTag'); for (var COLLECTION_NAME in DOMIterables$4) { var Collection = global$b[COLLECTION_NAME]; var CollectionPrototype = Collection && Collection.prototype; if (CollectionPrototype && classof$8(CollectionPrototype) !== TO_STRING_TAG$1) { createNonEnumerableProperty$4(CollectionPrototype, TO_STRING_TAG$1, COLLECTION_NAME); } Iterators$2[COLLECTION_NAME] = Iterators$2.Array; } var parent$19 = symbol$4; var symbol$3 = parent$19; var defineWellKnownSymbol$7 = wellKnownSymbolDefine; // `Symbol.dispose` well-known symbol // https://github.com/tc39/proposal-explicit-resource-management defineWellKnownSymbol$7('dispose'); var parent$18 = symbol$3; var symbol$2 = parent$18; var defineWellKnownSymbol$6 = wellKnownSymbolDefine; // `Symbol.asyncDispose` well-known symbol // https://github.com/tc39/proposal-async-explicit-resource-management defineWellKnownSymbol$6('asyncDispose'); var $$L = _export; var getBuiltIn$7 = getBuiltIn$f; var uncurryThis$f = functionUncurryThis; var Symbol$3 = getBuiltIn$7('Symbol'); var keyFor = Symbol$3.keyFor; var thisSymbolValue$1 = uncurryThis$f(Symbol$3.prototype.valueOf); // `Symbol.isRegistered` method // https://tc39.es/proposal-symbol-predicates/#sec-symbol-isregistered $$L({ target: 'Symbol', stat: true }, { isRegistered: function isRegistered(value) { try { return keyFor(thisSymbolValue$1(value)) !== undefined; } catch (error) { return false; } } }); var $$K = _export; var shared = sharedExports; var getBuiltIn$6 = getBuiltIn$f; var uncurryThis$e = functionUncurryThis; var isSymbol = isSymbol$5; var wellKnownSymbol$a = wellKnownSymbol$o; var Symbol$2 = getBuiltIn$6('Symbol'); var $isWellKnown = Symbol$2.isWellKnown; var getOwnPropertyNames = getBuiltIn$6('Object', 'getOwnPropertyNames'); var thisSymbolValue = uncurryThis$e(Symbol$2.prototype.valueOf); var WellKnownSymbolsStore = shared('wks'); for (var i = 0, symbolKeys = getOwnPropertyNames(Symbol$2), symbolKeysLength = symbolKeys.length; i < symbolKeysLength; i++) { // some old engines throws on access to some keys like `arguments` or `caller` try { var symbolKey = symbolKeys[i]; if (isSymbol(Symbol$2[symbolKey])) wellKnownSymbol$a(symbolKey); } catch (error) {/* empty */} } // `Symbol.isWellKnown` method // https://tc39.es/proposal-symbol-predicates/#sec-symbol-iswellknown // We should patch it for newly added well-known symbols. If it's not required, this module just will not be injected $$K({ target: 'Symbol', stat: true, forced: true }, { isWellKnown: function isWellKnown(value) { if ($isWellKnown && $isWellKnown(value)) return true; try { var symbol = thisSymbolValue(value); for (var j = 0, keys = getOwnPropertyNames(WellKnownSymbolsStore), keysLength = keys.length; j < keysLength; j++) { if (WellKnownSymbolsStore[keys[j]] == symbol) return true; } } catch (error) {/* empty */} return false; } }); var defineWellKnownSymbol$5 = wellKnownSymbolDefine; // `Symbol.matcher` well-known symbol // https://github.com/tc39/proposal-pattern-matching defineWellKnownSymbol$5('matcher'); var defineWellKnownSymbol$4 = wellKnownSymbolDefine; // `Symbol.metadataKey` well-known symbol // https://github.com/tc39/proposal-decorator-metadata defineWellKnownSymbol$4('metadataKey'); var defineWellKnownSymbol$3 = wellKnownSymbolDefine; // `Symbol.observable` well-known symbol // https://github.com/tc39/proposal-observable defineWellKnownSymbol$3('observable'); // TODO: Remove from `core-js@4` var defineWellKnownSymbol$2 = wellKnownSymbolDefine; // `Symbol.metadata` well-known symbol // https://github.com/tc39/proposal-decorators defineWellKnownSymbol$2('metadata'); // TODO: remove from `core-js@4` var defineWellKnownSymbol$1 = wellKnownSymbolDefine; // `Symbol.patternMatch` well-known symbol // https://github.com/tc39/proposal-pattern-matching defineWellKnownSymbol$1('patternMatch'); // TODO: remove from `core-js@4` var defineWellKnownSymbol = wellKnownSymbolDefine; defineWellKnownSymbol('replaceAll'); var parent$17 = symbol$2; // TODO: Remove from `core-js@4` var symbol$1 = parent$17; (function (module) { module.exports = symbol$1; })(symbol$5); (function (module) { module.exports = symbolExports$1; })(symbol$6); var _Symbol$1 = /*@__PURE__*/getDefaultExportFromCjs(symbolExports$2); var iteratorExports$2 = {}; var iterator$6 = { get exports() { return iteratorExports$2; }, set exports(v) { iteratorExports$2 = v; } }; var iteratorExports$1 = {}; var iterator$5 = { get exports() { return iteratorExports$1; }, set exports(v) { iteratorExports$1 = v; } }; var uncurryThis$d = functionUncurryThis; var toIntegerOrInfinity$1 = toIntegerOrInfinity$4; var toString$6 = toString$a; var requireObjectCoercible$2 = requireObjectCoercible$5; var charAt$1 = uncurryThis$d(''.charAt); var charCodeAt = uncurryThis$d(''.charCodeAt); var stringSlice = uncurryThis$d(''.slice); var createMethod$3 = function (CONVERT_TO_STRING) { return function ($this, pos) { var S = toString$6(requireObjectCoercible$2($this)); var position = toIntegerOrInfinity$1(pos); var size = S.length; var first, second; if (position < 0 || position >= size) return CONVERT_TO_STRING ? '' : undefined; first = charCodeAt(S, position); return first < 0xD800 || first > 0xDBFF || position + 1 === size || (second = charCodeAt(S, position + 1)) < 0xDC00 || second > 0xDFFF ? CONVERT_TO_STRING ? charAt$1(S, position) : first : CONVERT_TO_STRING ? stringSlice(S, position, position + 2) : (first - 0xD800 << 10) + (second - 0xDC00) + 0x10000; }; }; var stringMultibyte = { // `String.prototype.codePointAt` method // https://tc39.es/ecma262/#sec-string.prototype.codepointat codeAt: createMethod$3(false), // `String.prototype.at` method // https://github.com/mathiasbynens/String.prototype.at charAt: createMethod$3(true) }; var charAt = stringMultibyte.charAt; var toString$5 = toString$a; var InternalStateModule$3 = internalState; var defineIterator$1 = iteratorDefine; var createIterResultObject$1 = createIterResultObject$3; var STRING_ITERATOR = 'String Iterator'; var setInternalState$3 = InternalStateModule$3.set; var getInternalState = InternalStateModule$3.getterFor(STRING_ITERATOR); // `String.prototype[@@iterator]` method // https://tc39.es/ecma262/#sec-string.prototype-@@iterator defineIterator$1(String, 'String', function (iterated) { setInternalState$3(this, { type: STRING_ITERATOR, string: toString$5(iterated), index: 0 }); // `%StringIteratorPrototype%.next` method // https://tc39.es/ecma262/#sec-%stringiteratorprototype%.next }, function next() { var state = getInternalState(this); var string = state.string; var index = state.index; var point; if (index >= string.length) return createIterResultObject$1(undefined, true); point = charAt(string, index); state.index += point.length; return createIterResultObject$1(point, false); }); var WrappedWellKnownSymbolModule$1 = wellKnownSymbolWrapped; var iterator$4 = WrappedWellKnownSymbolModule$1.f('iterator'); var parent$16 = iterator$4; var iterator$3 = parent$16; var parent$15 = iterator$3; var iterator$2 = parent$15; var parent$14 = iterator$2; var iterator$1 = parent$14; (function (module) { module.exports = iterator$1; })(iterator$5); (function (module) { module.exports = iteratorExports$1; })(iterator$6); var _Symbol$iterator$2 = /*@__PURE__*/getDefaultExportFromCjs(iteratorExports$2); function _typeof$1(obj) { "@babel/helpers - typeof"; return _typeof$1 = "function" == typeof _Symbol$1 && "symbol" == typeof _Symbol$iterator$2 ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof _Symbol$1 && obj.constructor === _Symbol$1 && obj !== _Symbol$1.prototype ? "symbol" : typeof obj; }, _typeof$1(obj); } var toPrimitiveExports$1 = {}; var toPrimitive$5 = { get exports() { return toPrimitiveExports$1; }, set exports(v) { toPrimitiveExports$1 = v; } }; var toPrimitiveExports = {}; var toPrimitive$4 = { get exports() { return toPrimitiveExports; }, set exports(v) { toPrimitiveExports = v; } }; var WrappedWellKnownSymbolModule = wellKnownSymbolWrapped; var toPrimitive$3 = WrappedWellKnownSymbolModule.f('toPrimitive'); var parent$13 = toPrimitive$3; var toPrimitive$2 = parent$13; var parent$12 = toPrimitive$2; var toPrimitive$1 = parent$12; var parent$11 = toPrimitive$1; var toPrimitive = parent$11; (function (module) { module.exports = toPrimitive; })(toPrimitive$4); (function (module) { module.exports = toPrimitiveExports; })(toPrimitive$5); var _Symbol$toPrimitive = /*@__PURE__*/getDefaultExportFromCjs(toPrimitiveExports$1); function _toPrimitive(input, hint) { if (_typeof$1(input) !== "object" || input === null) return input; var prim = input[_Symbol$toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof$1(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof$1(key) === "symbol" ? key : String(key); } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; _Object$defineProperty$1(target, _toPropertyKey(descriptor.key), descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); _Object$defineProperty$1(Constructor, "prototype", { writable: false }); return Constructor; } function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { _Object$defineProperty$1(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var bindExports$2 = {}; var bind$g = { get exports() { return bindExports$2; }, set exports(v) { bindExports$2 = v; } }; var uncurryThis$c = functionUncurryThis; var aCallable$a = aCallable$e; var isObject$9 = isObject$i; var hasOwn$7 = hasOwnProperty_1; var arraySlice$3 = arraySlice$5; var NATIVE_BIND = functionBindNative; var $Function = Function; var concat$6 = uncurryThis$c([].concat); var join = uncurryThis$c([].join); var factories = {}; var construct$3 = function (C, argsLength, args) { if (!hasOwn$7(factories, argsLength)) { for (var list = [], i = 0; i < argsLength; i++) list[i] = 'a[' + i + ']'; factories[argsLength] = $Function('C,a', 'return new C(' + join(list, ',') + ')'); } return factories[argsLength](C, args); }; // `Function.prototype.bind` method implementation // https://tc39.es/ecma262/#sec-function.prototype.bind // eslint-disable-next-line es/no-function-prototype-bind -- detection var functionBind = NATIVE_BIND ? $Function.bind : function bind(that /* , ...args */) { var F = aCallable$a(this); var Prototype = F.prototype; var partArgs = arraySlice$3(arguments, 1); var boundFunction = function bound( /* args... */ ) { var args = concat$6(partArgs, arraySlice$3(arguments)); return this instanceof boundFunction ? construct$3(F, args.length, args) : F.apply(that, args); }; if (isObject$9(Prototype)) boundFunction.prototype = Prototype; return boundFunction; }; // TODO: Remove from `core-js@4` var $$J = _export; var bind$f = functionBind; // `Function.prototype.bind` method // https://tc39.es/ecma262/#sec-function.prototype.bind // eslint-disable-next-line es/no-function-prototype-bind -- detection $$J({ target: 'Function', proto: true, forced: Function.bind !== bind$f }, { bind: bind$f }); var path$l = path$r; var entryVirtual$k = function (CONSTRUCTOR) { return path$l[CONSTRUCTOR + 'Prototype']; }; var entryVirtual$j = entryVirtual$k; var bind$e = entryVirtual$j('Function').bind; var isPrototypeOf$l = objectIsPrototypeOf; var method$h = bind$e; var FunctionPrototype = Function.prototype; var bind$d = function (it) { var own = it.bind; return it === FunctionPrototype || isPrototypeOf$l(FunctionPrototype, it) && own === FunctionPrototype.bind ? method$h : own; }; var parent$10 = bind$d; var bind$c = parent$10; (function (module) { module.exports = bind$c; })(bind$g); var _bindInstanceProperty$1 = /*@__PURE__*/getDefaultExportFromCjs(bindExports$2); var reduceExports = {}; var reduce$3 = { get exports() { return reduceExports; }, set exports(v) { reduceExports = v; } }; var aCallable$9 = aCallable$e; var toObject$8 = toObject$e; var IndexedObject$1 = indexedObject; var lengthOfArrayLike$8 = lengthOfArrayLike$d; var $TypeError$9 = TypeError; // `Array.prototype.{ reduce, reduceRight }` methods implementation var createMethod$2 = function (IS_RIGHT) { return function (that, callbackfn, argumentsLength, memo) { aCallable$9(callbackfn); var O = toObject$8(that); var self = IndexedObject$1(O); var length = lengthOfArrayLike$8(O); var index = IS_RIGHT ? length - 1 : 0; var i = IS_RIGHT ? -1 : 1; if (argumentsLength < 2) while (true) { if (index in self) { memo = self[index]; index += i; break; } index += i; if (IS_RIGHT ? index < 0 : length <= index) { throw $TypeError$9('Reduce of empty array with no initial value'); } } for (; IS_RIGHT ? index >= 0 : length > index; index += i) if (index in self) { memo = callbackfn(memo, self[index], index, O); } return memo; }; }; var arrayReduce = { // `Array.prototype.reduce` method // https://tc39.es/ecma262/#sec-array.prototype.reduce left: createMethod$2(false), // `Array.prototype.reduceRight` method // https://tc39.es/ecma262/#sec-array.prototype.reduceright right: createMethod$2(true) }; var fails$g = fails$w; var arrayMethodIsStrict$5 = function (METHOD_NAME, argument) { var method = [][METHOD_NAME]; return !!method && fails$g(function () { // eslint-disable-next-line no-useless-call -- required for testing method.call(null, argument || function () { return 1; }, 1); }); }; var classof$7 = classofRaw$2; var engineIsNode = typeof process != 'undefined' && classof$7(process) == 'process'; var $$I = _export; var $reduce = arrayReduce.left; var arrayMethodIsStrict$4 = arrayMethodIsStrict$5; var CHROME_VERSION = engineV8Version; var IS_NODE$4 = engineIsNode; // Chrome 80-82 has a critical bug // https://bugs.chromium.org/p/chromium/issues/detail?id=1049982 var CHROME_BUG = !IS_NODE$4 && CHROME_VERSION > 79 && CHROME_VERSION < 83; var FORCED$5 = CHROME_BUG || !arrayMethodIsStrict$4('reduce'); // `Array.prototype.reduce` method // https://tc39.es/ecma262/#sec-array.prototype.reduce $$I({ target: 'Array', proto: true, forced: FORCED$5 }, { reduce: function reduce(callbackfn /* , initialValue */) { var length = arguments.length; return $reduce(this, callbackfn, length, length > 1 ? arguments[1] : undefined); } }); var entryVirtual$i = entryVirtual$k; var reduce$2 = entryVirtual$i('Array').reduce; var isPrototypeOf$k = objectIsPrototypeOf; var method$g = reduce$2; var ArrayPrototype$h = Array.prototype; var reduce$1 = function (it) { var own = it.reduce; return it === ArrayPrototype$h || isPrototypeOf$k(ArrayPrototype$h, it) && own === ArrayPrototype$h.reduce ? method$g : own; }; var parent$$ = reduce$1; var reduce = parent$$; (function (module) { module.exports = reduce; })(reduce$3); var _reduceInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(reduceExports); var filterExports = {}; var filter$3 = { get exports() { return filterExports; }, set exports(v) { filterExports = v; } }; var $$H = _export; var $filter = arrayIteration.filter; var arrayMethodHasSpeciesSupport$3 = arrayMethodHasSpeciesSupport$5; var HAS_SPECIES_SUPPORT$3 = arrayMethodHasSpeciesSupport$3('filter'); // `Array.prototype.filter` method // https://tc39.es/ecma262/#sec-array.prototype.filter // with adding support of @@species $$H({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$3 }, { filter: function filter(callbackfn /* , thisArg */) { return $filter(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); } }); var entryVirtual$h = entryVirtual$k; var filter$2 = entryVirtual$h('Array').filter; var isPrototypeOf$j = objectIsPrototypeOf; var method$f = filter$2; var ArrayPrototype$g = Array.prototype; var filter$1 = function (it) { var own = it.filter; return it === ArrayPrototype$g || isPrototypeOf$j(ArrayPrototype$g, it) && own === ArrayPrototype$g.filter ? method$f : own; }; var parent$_ = filter$1; var filter = parent$_; (function (module) { module.exports = filter; })(filter$3); var _filterInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(filterExports); var mapExports$1 = {}; var map$6 = { get exports() { return mapExports$1; }, set exports(v) { mapExports$1 = v; } }; var $$G = _export; var $map = arrayIteration.map; var arrayMethodHasSpeciesSupport$2 = arrayMethodHasSpeciesSupport$5; var HAS_SPECIES_SUPPORT$2 = arrayMethodHasSpeciesSupport$2('map'); // `Array.prototype.map` method // https://tc39.es/ecma262/#sec-array.prototype.map // with adding support of @@species $$G({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$2 }, { map: function map(callbackfn /* , thisArg */) { return $map(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); } }); var entryVirtual$g = entryVirtual$k; var map$5 = entryVirtual$g('Array').map; var isPrototypeOf$i = objectIsPrototypeOf; var method$e = map$5; var ArrayPrototype$f = Array.prototype; var map$4 = function (it) { var own = it.map; return it === ArrayPrototype$f || isPrototypeOf$i(ArrayPrototype$f, it) && own === ArrayPrototype$f.map ? method$e : own; }; var parent$Z = map$4; var map$3 = parent$Z; (function (module) { module.exports = map$3; })(map$6); var _mapInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(mapExports$1); var flatMapExports = {}; var flatMap$3 = { get exports() { return flatMapExports; }, set exports(v) { flatMapExports = v; } }; var isArray$b = isArray$f; var lengthOfArrayLike$7 = lengthOfArrayLike$d; var doesNotExceedSafeInteger$1 = doesNotExceedSafeInteger$3; var bind$b = functionBindContext; // `FlattenIntoArray` abstract operation // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray var flattenIntoArray$1 = function (target, original, source, sourceLen, start, depth, mapper, thisArg) { var targetIndex = start; var sourceIndex = 0; var mapFn = mapper ? bind$b(mapper, thisArg) : false; var element, elementLen; while (sourceIndex < sourceLen) { if (sourceIndex in source) { element = mapFn ? mapFn(source[sourceIndex], sourceIndex, original) : source[sourceIndex]; if (depth > 0 && isArray$b(element)) { elementLen = lengthOfArrayLike$7(element); targetIndex = flattenIntoArray$1(target, original, element, elementLen, targetIndex, depth - 1) - 1; } else { doesNotExceedSafeInteger$1(targetIndex + 1); target[targetIndex] = element; } targetIndex++; } sourceIndex++; } return targetIndex; }; var flattenIntoArray_1 = flattenIntoArray$1; var $$F = _export; var flattenIntoArray = flattenIntoArray_1; var aCallable$8 = aCallable$e; var toObject$7 = toObject$e; var lengthOfArrayLike$6 = lengthOfArrayLike$d; var arraySpeciesCreate$1 = arraySpeciesCreate$4; // `Array.prototype.flatMap` method // https://tc39.es/ecma262/#sec-array.prototype.flatmap $$F({ target: 'Array', proto: true }, { flatMap: function flatMap(callbackfn /* , thisArg */) { var O = toObject$7(this); var sourceLen = lengthOfArrayLike$6(O); var A; aCallable$8(callbackfn); A = arraySpeciesCreate$1(O, 0); A.length = flattenIntoArray(A, O, O, sourceLen, 0, 1, callbackfn, arguments.length > 1 ? arguments[1] : undefined); return A; } }); var entryVirtual$f = entryVirtual$k; var flatMap$2 = entryVirtual$f('Array').flatMap; var isPrototypeOf$h = objectIsPrototypeOf; var method$d = flatMap$2; var ArrayPrototype$e = Array.prototype; var flatMap$1 = function (it) { var own = it.flatMap; return it === ArrayPrototype$e || isPrototypeOf$h(ArrayPrototype$e, it) && own === ArrayPrototype$e.flatMap ? method$d : own; }; var parent$Y = flatMap$1; var flatMap = parent$Y; (function (module) { module.exports = flatMap; })(flatMap$3); var _flatMapInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(flatMapExports); /** * Create new data pipe. * * @param from - The source data set or data view. * @remarks * Example usage: * ```typescript * interface AppItem { * whoami: string; * appData: unknown; * visData: VisItem; * } * interface VisItem { * id: number; * label: string; * color: string; * x: number; * y: number; * } * * const ds1 = new DataSet<AppItem, "whoami">([], { fieldId: "whoami" }); * const ds2 = new DataSet<VisItem, "id">(); * * const pipe = createNewDataPipeFrom(ds1) * .filter((item): boolean => item.enabled === true) * .map<VisItem, "id">((item): VisItem => item.visData) * .to(ds2); * * pipe.start(); * ``` * @returns A factory whose methods can be used to configure the pipe. */ function createNewDataPipeFrom(from) { return new DataPipeUnderConstruction(from); } /** * Internal implementation of the pipe. This should be accessible only through * `createNewDataPipeFrom` from the outside. * * @typeParam SI - Source item type. * @typeParam SP - Source item type's id property name. * @typeParam TI - Target item type. * @typeParam TP - Target item type's id property name. */ var SimpleDataPipe = /*#__PURE__*/function () { /** * Bound listeners for use with `DataInterface['on' | 'off']`. */ /** * Create a new data pipe. * * @param _source - The data set or data view that will be observed. * @param _transformers - An array of transforming functions to be used to * filter or transform the items in the pipe. * @param _target - The data set or data view that will receive the items. */ function SimpleDataPipe(_source, _transformers, _target) { var _context, _context2, _context3; _classCallCheck(this, SimpleDataPipe); _defineProperty(this, "_source", void 0); _defineProperty(this, "_transformers", void 0); _defineProperty(this, "_target", void 0); _defineProperty(this, "_listeners", { add: _bindInstanceProperty$1(_context = this._add).call(_context, this), remove: _bindInstanceProperty$1(_context2 = this._remove).call(_context2, this), update: _bindInstanceProperty$1(_context3 = this._update).call(_context3, this) }); this._source = _source; this._transformers = _transformers; this._target = _target; } /** @inheritDoc */ _createClass(SimpleDataPipe, [{ key: "all", value: function all() { this._target.update(this._transformItems(this._source.get())); return this; } /** @inheritDoc */ }, { key: "start", value: function start() { this._source.on("add", this._listeners.add); this._source.on("remove", this._listeners.remove); this._source.on("update", this._listeners.update); return this; } /** @inheritDoc */ }, { key: "stop", value: function stop() { this._source.off("add", this._listeners.add); this._source.off("remove", this._listeners.remove); this._source.off("update", this._listeners.update); return this; } /** * Apply the transformers to the items. * * @param items - The items to be transformed. * @returns The transformed items. */ }, { key: "_transformItems", value: function _transformItems(items) { var _context4; return _reduceInstanceProperty(_context4 = this._transformers).call(_context4, function (items, transform) { return transform(items); }, items); } /** * Handle an add event. * * @param _name - Ignored. * @param payload - The payload containing the ids of the added items. */ }, { key: "_add", value: function _add(_name, payload) { if (payload == null) { return; } this._target.add(this._transformItems(this._source.get(payload.items))); } /** * Handle an update event. * * @param _name - Ignored. * @param payload - The payload containing the ids of the updated items. */ }, { key: "_update", value: function _update(_name, payload) { if (payload == null) { return; } this._target.update(this._transformItems(this._source.get(payload.items))); } /** * Handle a remove event. * * @param _name - Ignored. * @param payload - The payload containing the data of the removed items. */ }, { key: "_remove", value: function _remove(_name, payload) { if (payload == null) { return; } this._target.remove(this._transformItems(payload.oldData)); } }]); return SimpleDataPipe; }(); /** * Internal implementation of the pipe factory. This should be accessible * only through `createNewDataPipeFrom` from the outside. * * @typeParam TI - Target item type. * @typeParam TP - Target item type's id property name. */ var DataPipeUnderConstruction = /*#__PURE__*/function () { /** * Array transformers used to transform items within the pipe. This is typed * as any for the sake of simplicity. */ /** * Create a new data pipe factory. This is an internal constructor that * should never be called from outside of this file. * * @param _source - The source data set or data view for this pipe. */ function DataPipeUnderConstruction(_source) { _classCallCheck(this, DataPipeUnderConstruction); _defineProperty(this, "_source", void 0); _defineProperty(this, "_transformers", []); this._source = _source; } /** * Filter the items. * * @param callback - A filtering function that returns true if given item * should be piped and false if not. * @returns This factory for further configuration. */ _createClass(DataPipeUnderConstruction, [{ key: "filter", value: function filter(callback) { this._transformers.push(function (input) { return _filterInstanceProperty(input).call(input, callback); }); return this; } /** * Map each source item to a new type. * * @param callback - A mapping function that takes a source item and returns * corresponding mapped item. * @typeParam TI - Target item type. * @typeParam TP - Target item type's id property name. * @returns This factory for further configuration. */ }, { key: "map", value: function map(callback) { this._transformers.push(function (input) { return _mapInstanceProperty(input).call(input, callback); }); return this; } /** * Map each source item to zero or more items of a new type. * * @param callback - A mapping function that takes a source item and returns * an array of corresponding mapped items. * @typeParam TI - Target item type. * @typeParam TP - Target item type's id property name. * @returns This factory for further configuration. */ }, { key: "flatMap", value: function flatMap(callback) { this._transformers.push(function (input) { return _flatMapInstanceProperty(input).call(input, callback); }); return this; } /** * Connect this pipe to given data set. * * @param target - The data set that will receive the items from this pipe. * @returns The pipe connected between given data sets and performing * configured transformation on the processed items. */ }, { key: "to", value: function to(target) { return new SimpleDataPipe(this._source, this._transformers, target); } }]); return DataPipeUnderConstruction; }(); var fromExports$2 = {}; var from$7 = { get exports() { return fromExports$2; }, set exports(v) { fromExports$2 = v; } }; var call$a = functionCall; var anObject$7 = anObject$d; var getMethod$1 = getMethod$3; var iteratorClose$2 = function (iterator, kind, value) { var innerResult, innerError; anObject$7(iterator); try { innerResult = getMethod$1(iterator, 'return'); if (!innerResult) { if (kind === 'throw') throw value; return value; } innerResult = call$a(innerResult, iterator); } catch (error) { innerError = true; innerResult = error; } if (kind === 'throw') throw value; if (innerError) throw innerResult; anObject$7(innerResult); return value; }; var anObject$6 = anObject$d; var iteratorClose$1 = iteratorClose$2; // call something on iterator step with safe closing on error var callWithSafeIterationClosing$1 = function (iterator, fn, value, ENTRIES) { try { return ENTRIES ? fn(anObject$6(value)[0], value[1]) : fn(value); } catch (error) { iteratorClose$1(iterator, 'throw', error); } }; var wellKnownSymbol$9 = wellKnownSymbol$o; var Iterators$1 = iterators; var ITERATOR$3 = wellKnownSymbol$9('iterator'); var ArrayPrototype$d = Array.prototype; // check on default Array iterator var isArrayIteratorMethod$2 = function (it) { return it !== undefined && (Iterators$1.Array === it || ArrayPrototype$d[ITERATOR$3] === it); }; var classof$6 = classof$e; var getMethod = getMethod$3; var isNullOrUndefined$2 = isNullOrUndefined$5; var Iterators = iterators; var wellKnownSymbol$8 = wellKnownSymbol$o; var ITERATOR$2 = wellKnownSymbol$8('iterator'); var getIteratorMethod$9 = function (it) { if (!isNullOrUndefined$2(it)) return getMethod(it, ITERATOR$2) || getMethod(it, '@@iterator') || Iterators[classof$6(it)]; }; var call$9 = functionCall; var aCallable$7 = aCallable$e; var anObject$5 = anObject$d; var tryToString$3 = tryToString$6; var getIteratorMethod$8 = getIteratorMethod$9; var $TypeError$8 = TypeError; var getIterator$8 = function (argument, usingIterator) { var iteratorMethod = arguments.length < 2 ? getIteratorMethod$8(argument) : usingIterator; if (aCallable$7(iteratorMethod)) return anObject$5(call$9(iteratorMethod, argument)); throw $TypeError$8(tryToString$3(argument) + ' is not iterable'); }; var bind$a = functionBindContext; var call$8 = functionCall; var toObject$6 = toObject$e; var callWithSafeIterationClosing = callWithSafeIterationClosing$1; var isArrayIteratorMethod$1 = isArrayIteratorMethod$2; var isConstructor$2 = isConstructor$4; var lengthOfArrayLike$5 = lengthOfArrayLike$d; var createProperty$3 = createProperty$6; var getIterator$7 = getIterator$8; var getIteratorMethod$7 = getIteratorMethod$9; var $Array$1 = Array; // `Array.from` method implementation // https://tc39.es/ecma262/#sec-array.from var arrayFrom = function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) { var O = toObject$6(arrayLike); var IS_CONSTRUCTOR = isConstructor$2(this); var argumentsLength = arguments.length; var mapfn = argumentsLength > 1 ? arguments[1] : undefined; var mapping = mapfn !== undefined; if (mapping) mapfn = bind$a(mapfn, argumentsLength > 2 ? arguments[2] : undefined); var iteratorMethod = getIteratorMethod$7(O); var index = 0; var length, result, step, iterator, next, value; // if the target is not iterable or it's an array with the default iterator - use a simple case if (iteratorMethod && !(this === $Array$1 && isArrayIteratorMethod$1(iteratorMethod))) { iterator = getIterator$7(O, iteratorMethod); next = iterator.next; result = IS_CONSTRUCTOR ? new this() : []; for (; !(step = call$8(next, iterator)).done; index++) { value = mapping ? callWithSafeIterationClosing(iterator, mapfn, [step.value, index], true) : step.value; createProperty$3(result, index, value); } } else { length = lengthOfArrayLike$5(O); result = IS_CONSTRUCTOR ? new this(length) : $Array$1(length); for (; length > index; index++) { value = mapping ? mapfn(O[index], index) : O[index]; createProperty$3(result, index, value); } } result.length = index; return result; }; var wellKnownSymbol$7 = wellKnownSymbol$o; var ITERATOR$1 = wellKnownSymbol$7('iterator'); var SAFE_CLOSING = false; try { var called = 0; var iteratorWithReturn = { next: function () { return { done: !!called++ }; }, 'return': function () { SAFE_CLOSING = true; } }; iteratorWithReturn[ITERATOR$1] = function () { return this; }; // eslint-disable-next-line es/no-array-from, no-throw-literal -- required for testing Array.from(iteratorWithReturn, function () { throw 2; }); } catch (error) {/* empty */} var checkCorrectnessOfIteration$2 = function (exec, SKIP_CLOSING) { if (!SKIP_CLOSING && !SAFE_CLOSING) return false; var ITERATION_SUPPORT = false; try { var object = {}; object[ITERATOR$1] = function () { return { next: function () { return { done: ITERATION_SUPPORT = true }; } }; }; exec(object); } catch (error) {/* empty */} return ITERATION_SUPPORT; }; var $$E = _export; var from$6 = arrayFrom; var checkCorrectnessOfIteration$1 = checkCorrectnessOfIteration$2; var INCORRECT_ITERATION = !checkCorrectnessOfIteration$1(function (iterable) { // eslint-disable-next-line es/no-array-from -- required for testing Array.from(iterable); }); // `Array.from` method // https://tc39.es/ecma262/#sec-array.from $$E({ target: 'Array', stat: true, forced: INCORRECT_ITERATION }, { from: from$6 }); var path$k = path$r; var from$5 = path$k.Array.from; var parent$X = from$5; var from$4 = parent$X; (function (module) { module.exports = from$4; })(from$7); var _Array$from$1 = /*@__PURE__*/getDefaultExportFromCjs(fromExports$2); var getIteratorMethodExports$1 = {}; var getIteratorMethod$6 = { get exports() { return getIteratorMethodExports$1; }, set exports(v) { getIteratorMethodExports$1 = v; } }; var getIteratorMethodExports = {}; var getIteratorMethod$5 = { get exports() { return getIteratorMethodExports; }, set exports(v) { getIteratorMethodExports = v; } }; var getIteratorMethod$4 = getIteratorMethod$9; var getIteratorMethod_1 = getIteratorMethod$4; var parent$W = getIteratorMethod_1; var getIteratorMethod$3 = parent$W; var parent$V = getIteratorMethod$3; var getIteratorMethod$2 = parent$V; var parent$U = getIteratorMethod$2; var getIteratorMethod$1 = parent$U; (function (module) { module.exports = getIteratorMethod$1; })(getIteratorMethod$5); (function (module) { module.exports = getIteratorMethodExports; })(getIteratorMethod$6); var _getIteratorMethod = /*@__PURE__*/getDefaultExportFromCjs(getIteratorMethodExports$1); var getOwnPropertySymbolsExports = {}; var getOwnPropertySymbols$2 = { get exports() { return getOwnPropertySymbolsExports; }, set exports(v) { getOwnPropertySymbolsExports = v; } }; var path$j = path$r; var getOwnPropertySymbols$1 = path$j.Object.getOwnPropertySymbols; var parent$T = getOwnPropertySymbols$1; var getOwnPropertySymbols = parent$T; (function (module) { module.exports = getOwnPropertySymbols; })(getOwnPropertySymbols$2); var _Object$getOwnPropertySymbols = /*@__PURE__*/getDefaultExportFromCjs(getOwnPropertySymbolsExports); var getOwnPropertyDescriptorExports$1 = {}; var getOwnPropertyDescriptor$5 = { get exports() { return getOwnPropertyDescriptorExports$1; }, set exports(v) { getOwnPropertyDescriptorExports$1 = v; } }; var getOwnPropertyDescriptorExports = {}; var getOwnPropertyDescriptor$4 = { get exports() { return getOwnPropertyDescriptorExports; }, set exports(v) { getOwnPropertyDescriptorExports = v; } }; var $$D = _export; var fails$f = fails$w; var toIndexedObject$3 = toIndexedObject$b; var nativeGetOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f; var DESCRIPTORS$9 = descriptors; var FORCED$4 = !DESCRIPTORS$9 || fails$f(function () { nativeGetOwnPropertyDescriptor(1); }); // `Object.getOwnPropertyDescriptor` method // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor $$D({ target: 'Object', stat: true, forced: FORCED$4, sham: !DESCRIPTORS$9 }, { getOwnPropertyDescriptor: function getOwnPropertyDescriptor(it, key) { return nativeGetOwnPropertyDescriptor(toIndexedObject$3(it), key); } }); var path$i = path$r; var Object$3 = path$i.Object; var getOwnPropertyDescriptor$3 = getOwnPropertyDescriptor$4.exports = function getOwnPropertyDescriptor(it, key) { return Object$3.getOwnPropertyDescriptor(it, key); }; if (Object$3.getOwnPropertyDescriptor.sham) getOwnPropertyDescriptor$3.sham = true; var parent$S = getOwnPropertyDescriptorExports; var getOwnPropertyDescriptor$2 = parent$S; (function (module) { module.exports = getOwnPropertyDescriptor$2; })(getOwnPropertyDescriptor$5); var _Object$getOwnPropertyDescriptor = /*@__PURE__*/getDefaultExportFromCjs(getOwnPropertyDescriptorExports$1); var getOwnPropertyDescriptorsExports = {}; var getOwnPropertyDescriptors$2 = { get exports() { return getOwnPropertyDescriptorsExports; }, set exports(v) { getOwnPropertyDescriptorsExports = v; } }; var getBuiltIn$5 = getBuiltIn$f; var uncurryThis$b = functionUncurryThis; var getOwnPropertyNamesModule$1 = objectGetOwnPropertyNames; var getOwnPropertySymbolsModule$1 = objectGetOwnPropertySymbols; var anObject$4 = anObject$d; var concat$5 = uncurryThis$b([].concat); // all object keys, includes non-enumerable and symbols var ownKeys$7 = getBuiltIn$5('Reflect', 'ownKeys') || function ownKeys(it) { var keys = getOwnPropertyNamesModule$1.f(anObject$4(it)); var getOwnPropertySymbols = getOwnPropertySymbolsModule$1.f; return getOwnPropertySymbols ? concat$5(keys, getOwnPropertySymbols(it)) : keys; }; var $$C = _export; var DESCRIPTORS$8 = descriptors; var ownKeys$6 = ownKeys$7; var toIndexedObject$2 = toIndexedObject$b; var getOwnPropertyDescriptorModule$1 = objectGetOwnPropertyDescriptor; var createProperty$2 = createProperty$6; // `Object.getOwnPropertyDescriptors` method // https://tc39.es/ecma262/#sec-object.getownpropertydescriptors $$C({ target: 'Object', stat: true, sham: !DESCRIPTORS$8 }, { getOwnPropertyDescriptors: function getOwnPropertyDescriptors(object) { var O = toIndexedObject$2(object); var getOwnPropertyDescriptor = getOwnPropertyDescriptorModule$1.f; var keys = ownKeys$6(O); var result = {}; var index = 0; var key, descriptor; while (keys.length > index) { descriptor = getOwnPropertyDescriptor(O, key = keys[index++]); if (descriptor !== undefined) createProperty$2(result, key, descriptor); } return result; } }); var path$h = path$r; var getOwnPropertyDescriptors$1 = path$h.Object.getOwnPropertyDescriptors; var parent$R = getOwnPropertyDescriptors$1; var getOwnPropertyDescriptors = parent$R; (function (module) { module.exports = getOwnPropertyDescriptors; })(getOwnPropertyDescriptors$2); var _Object$getOwnPropertyDescriptors = /*@__PURE__*/getDefaultExportFromCjs(getOwnPropertyDescriptorsExports); var definePropertiesExports$1 = {}; var defineProperties$4 = { get exports() { return definePropertiesExports$1; }, set exports(v) { definePropertiesExports$1 = v; } }; var definePropertiesExports = {}; var defineProperties$3 = { get exports() { return definePropertiesExports; }, set exports(v) { definePropertiesExports = v; } }; var $$B = _export; var DESCRIPTORS$7 = descriptors; var defineProperties$2 = objectDefineProperties.f; // `Object.defineProperties` method // https://tc39.es/ecma262/#sec-object.defineproperties // eslint-disable-next-line es/no-object-defineproperties -- safe $$B({ target: 'Object', stat: true, forced: Object.defineProperties !== defineProperties$2, sham: !DESCRIPTORS$7 }, { defineProperties: defineProperties$2 }); var path$g = path$r; var Object$2 = path$g.Object; var defineProperties$1 = defineProperties$3.exports = function defineProperties(T, D) { return Object$2.defineProperties(T, D); }; if (Object$2.defineProperties.sham) defineProperties$1.sham = true; var parent$Q = definePropertiesExports; var defineProperties = parent$Q; (function (module) { module.exports = defineProperties; })(defineProperties$4); var _Object$defineProperties = /*@__PURE__*/getDefaultExportFromCjs(definePropertiesExports$1); var definePropertyExports = {}; var defineProperty$3 = { get exports() { return definePropertyExports; }, set exports(v) { definePropertyExports = v; } }; (function (module) { module.exports = defineProperty$9; })(defineProperty$3); var _Object$defineProperty = /*@__PURE__*/getDefaultExportFromCjs(definePropertyExports); var isArrayExports$2 = {}; var isArray$a = { get exports() { return isArrayExports$2; }, set exports(v) { isArrayExports$2 = v; } }; var isArrayExports$1 = {}; var isArray$9 = { get exports() { return isArrayExports$1; }, set exports(v) { isArrayExports$1 = v; } }; var $$A = _export; var isArray$8 = isArray$f; // `Array.isArray` method // https://tc39.es/ecma262/#sec-array.isarray $$A({ target: 'Array', stat: true }, { isArray: isArray$8 }); var path$f = path$r; var isArray$7 = path$f.Array.isArray; var parent$P = isArray$7; var isArray$6 = parent$P; var parent$O = isArray$6; var isArray$5 = parent$O; var parent$N = isArray$5; var isArray$4 = parent$N; (function (module) { module.exports = isArray$4; })(isArray$9); (function (module) { module.exports = isArrayExports$1; })(isArray$a); var _Array$isArray$1 = /*@__PURE__*/getDefaultExportFromCjs(isArrayExports$2); function _arrayWithHoles(arr) { if (_Array$isArray$1(arr)) return arr; } function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof _Symbol$1 && _getIteratorMethod(arr) || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } } var sliceExports$2 = {}; var slice$7 = { get exports() { return sliceExports$2; }, set exports(v) { sliceExports$2 = v; } }; var sliceExports$1 = {}; var slice$6 = { get exports() { return sliceExports$1; }, set exports(v) { sliceExports$1 = v; } }; var $$z = _export; var isArray$3 = isArray$f; var isConstructor$1 = isConstructor$4; var isObject$8 = isObject$i; var toAbsoluteIndex$2 = toAbsoluteIndex$5; var lengthOfArrayLike$4 = lengthOfArrayLike$d; var toIndexedObject$1 = toIndexedObject$b; var createProperty$1 = createProperty$6; var wellKnownSymbol$6 = wellKnownSymbol$o; var arrayMethodHasSpeciesSupport$1 = arrayMethodHasSpeciesSupport$5; var nativeSlice = arraySlice$5; var HAS_SPECIES_SUPPORT$1 = arrayMethodHasSpeciesSupport$1('slice'); var SPECIES$3 = wellKnownSymbol$6('species'); var $Array = Array; var max$1 = Math.max; // `Array.prototype.slice` method // https://tc39.es/ecma262/#sec-array.prototype.slice // fallback for not array-like ES3 strings and DOM objects $$z({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$1 }, { slice: function slice(start, end) { var O = toIndexedObject$1(this); var length = lengthOfArrayLike$4(O); var k = toAbsoluteIndex$2(start, length); var fin = toAbsoluteIndex$2(end === undefined ? length : end, length); // inline `ArraySpeciesCreate` for usage native `Array#slice` where it's possible var Constructor, result, n; if (isArray$3(O)) { Constructor = O.constructor; // cross-realm fallback if (isConstructor$1(Constructor) && (Constructor === $Array || isArray$3(Constructor.prototype))) { Constructor = undefined; } else if (isObject$8(Constructor)) { Constructor = Constructor[SPECIES$3]; if (Constructor === null) Constructor = undefined; } if (Constructor === $Array || Constructor === undefined) { return nativeSlice(O, k, fin); } } result = new (Constructor === undefined ? $Array : Constructor)(max$1(fin - k, 0)); for (n = 0; k < fin; k++, n++) if (k in O) createProperty$1(result, n, O[k]); result.length = n; return result; } }); var entryVirtual$e = entryVirtual$k; var slice$5 = entryVirtual$e('Array').slice; var isPrototypeOf$g = objectIsPrototypeOf; var method$c = slice$5; var ArrayPrototype$c = Array.prototype; var slice$4 = function (it) { var own = it.slice; return it === ArrayPrototype$c || isPrototypeOf$g(ArrayPrototype$c, it) && own === ArrayPrototype$c.slice ? method$c : own; }; var parent$M = slice$4; var slice$3 = parent$M; var parent$L = slice$3; var slice$2 = parent$L; var parent$K = slice$2; var slice$1 = parent$K; (function (module) { module.exports = slice$1; })(slice$6); (function (module) { module.exports = sliceExports$1; })(slice$7); var _sliceInstanceProperty$1 = /*@__PURE__*/getDefaultExportFromCjs(sliceExports$2); var fromExports$1 = {}; var from$3 = { get exports() { return fromExports$1; }, set exports(v) { fromExports$1 = v; } }; var fromExports = {}; var from$2 = { get exports() { return fromExports; }, set exports(v) { fromExports = v; } }; var parent$J = from$4; var from$1 = parent$J; var parent$I = from$1; var from = parent$I; (function (module) { module.exports = from; })(from$2); (function (module) { module.exports = fromExports; })(from$3); var _Array$from = /*@__PURE__*/getDefaultExportFromCjs(fromExports$1); function _arrayLikeToArray$4(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _unsupportedIterableToArray$4(o, minLen) { var _context; if (!o) return; if (typeof o === "string") return _arrayLikeToArray$4(o, minLen); var n = _sliceInstanceProperty$1(_context = Object.prototype.toString.call(o)).call(_context, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$4(o, minLen); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray$4(arr, i) || _nonIterableRest(); } function _arrayWithoutHoles(arr) { if (_Array$isArray$1(arr)) return _arrayLikeToArray$4(arr); } function _iterableToArray(iter) { if (typeof _Symbol$1 !== "undefined" && _getIteratorMethod(iter) != null || iter["@@iterator"] != null) return _Array$from(iter); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray$4(arr) || _nonIterableSpread(); } var symbolExports = {}; var symbol = { get exports() { return symbolExports; }, set exports(v) { symbolExports = v; } }; (function (module) { module.exports = symbol$3; })(symbol); var _Symbol = /*@__PURE__*/getDefaultExportFromCjs(symbolExports); var concatExports = {}; var concat$4 = { get exports() { return concatExports; }, set exports(v) { concatExports = v; } }; var entryVirtual$d = entryVirtual$k; var concat$3 = entryVirtual$d('Array').concat; var isPrototypeOf$f = objectIsPrototypeOf; var method$b = concat$3; var ArrayPrototype$b = Array.prototype; var concat$2 = function (it) { var own = it.concat; return it === ArrayPrototype$b || isPrototypeOf$f(ArrayPrototype$b, it) && own === ArrayPrototype$b.concat ? method$b : own; }; var parent$H = concat$2; var concat$1 = parent$H; (function (module) { module.exports = concat$1; })(concat$4); var _concatInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(concatExports); var sliceExports = {}; var slice = { get exports() { return sliceExports; }, set exports(v) { sliceExports = v; } }; (function (module) { module.exports = slice$3; })(slice); var _sliceInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(sliceExports); var ownKeysExports = {}; var ownKeys$5 = { get exports() { return ownKeysExports; }, set exports(v) { ownKeysExports = v; } }; var $$y = _export; var ownKeys$4 = ownKeys$7; // `Reflect.ownKeys` method // https://tc39.es/ecma262/#sec-reflect.ownkeys $$y({ target: 'Reflect', stat: true }, { ownKeys: ownKeys$4 }); var path$e = path$r; var ownKeys$3 = path$e.Reflect.ownKeys; var parent$G = ownKeys$3; var ownKeys$2 = parent$G; (function (module) { module.exports = ownKeys$2; })(ownKeys$5); var _Reflect$ownKeys = /*@__PURE__*/getDefaultExportFromCjs(ownKeysExports); var isArrayExports = {}; var isArray$2 = { get exports() { return isArrayExports; }, set exports(v) { isArrayExports = v; } }; (function (module) { module.exports = isArray$6; })(isArray$2); var _Array$isArray = /*@__PURE__*/getDefaultExportFromCjs(isArrayExports); var keysExports$1 = {}; var keys$6 = { get exports() { return keysExports$1; }, set exports(v) { keysExports$1 = v; } }; var $$x = _export; var toObject$5 = toObject$e; var nativeKeys = objectKeys$4; var fails$e = fails$w; var FAILS_ON_PRIMITIVES$2 = fails$e(function () { nativeKeys(1); }); // `Object.keys` method // https://tc39.es/ecma262/#sec-object.keys $$x({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$2 }, { keys: function keys(it) { return nativeKeys(toObject$5(it)); } }); var path$d = path$r; var keys$5 = path$d.Object.keys; var parent$F = keys$5; var keys$4 = parent$F; (function (module) { module.exports = keys$4; })(keys$6); var _Object$keys = /*@__PURE__*/getDefaultExportFromCjs(keysExports$1); var nowExports = {}; var now$3 = { get exports() { return nowExports; }, set exports(v) { nowExports = v; } }; // TODO: Remove from `core-js@4` var $$w = _export; var uncurryThis$a = functionUncurryThis; var $Date = Date; var thisTimeValue = uncurryThis$a($Date.prototype.getTime); // `Date.now` method // https://tc39.es/ecma262/#sec-date.now $$w({ target: 'Date', stat: true }, { now: function now() { return thisTimeValue(new $Date()); } }); var path$c = path$r; var now$2 = path$c.Date.now; var parent$E = now$2; var now$1 = parent$E; (function (module) { module.exports = now$1; })(now$3); var forEachExports$2 = {}; var forEach$9 = { get exports() { return forEachExports$2; }, set exports(v) { forEachExports$2 = v; } }; var $forEach = arrayIteration.forEach; var arrayMethodIsStrict$3 = arrayMethodIsStrict$5; var STRICT_METHOD$2 = arrayMethodIsStrict$3('forEach'); // `Array.prototype.forEach` method implementation // https://tc39.es/ecma262/#sec-array.prototype.foreach var arrayForEach = !STRICT_METHOD$2 ? function forEach(callbackfn /* , thisArg */) { return $forEach(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); // eslint-disable-next-line es/no-array-prototype-foreach -- safe } : [].forEach; var $$v = _export; var forEach$8 = arrayForEach; // `Array.prototype.forEach` method // https://tc39.es/ecma262/#sec-array.prototype.foreach // eslint-disable-next-line es/no-array-prototype-foreach -- safe $$v({ target: 'Array', proto: true, forced: [].forEach != forEach$8 }, { forEach: forEach$8 }); var entryVirtual$c = entryVirtual$k; var forEach$7 = entryVirtual$c('Array').forEach; var parent$D = forEach$7; var forEach$6 = parent$D; var classof$5 = classof$e; var hasOwn$6 = hasOwnProperty_1; var isPrototypeOf$e = objectIsPrototypeOf; var method$a = forEach$6; var ArrayPrototype$a = Array.prototype; var DOMIterables$3 = { DOMTokenList: true, NodeList: true }; var forEach$5 = function (it) { var own = it.forEach; return it === ArrayPrototype$a || isPrototypeOf$e(ArrayPrototype$a, it) && own === ArrayPrototype$a.forEach || hasOwn$6(DOMIterables$3, classof$5(it)) ? method$a : own; }; (function (module) { module.exports = forEach$5; })(forEach$9); var _forEachInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(forEachExports$2); var reverseExports$2 = {}; var reverse$7 = { get exports() { return reverseExports$2; }, set exports(v) { reverseExports$2 = v; } }; var $$u = _export; var uncurryThis$9 = functionUncurryThis; var isArray$1 = isArray$f; var nativeReverse = uncurryThis$9([].reverse); var test$1 = [1, 2]; // `Array.prototype.reverse` method // https://tc39.es/ecma262/#sec-array.prototype.reverse // fix for Safari 12.0 bug // https://bugs.webkit.org/show_bug.cgi?id=188794 $$u({ target: 'Array', proto: true, forced: String(test$1) === String(test$1.reverse()) }, { reverse: function reverse() { // eslint-disable-next-line no-self-assign -- dirty hack if (isArray$1(this)) this.length = this.length; return nativeReverse(this); } }); var entryVirtual$b = entryVirtual$k; var reverse$6 = entryVirtual$b('Array').reverse; var isPrototypeOf$d = objectIsPrototypeOf; var method$9 = reverse$6; var ArrayPrototype$9 = Array.prototype; var reverse$5 = function (it) { var own = it.reverse; return it === ArrayPrototype$9 || isPrototypeOf$d(ArrayPrototype$9, it) && own === ArrayPrototype$9.reverse ? method$9 : own; }; var parent$C = reverse$5; var reverse$4 = parent$C; (function (module) { module.exports = reverse$4; })(reverse$7); var _reverseInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(reverseExports$2); var spliceExports = {}; var splice$3 = { get exports() { return spliceExports; }, set exports(v) { spliceExports = v; } }; var DESCRIPTORS$6 = descriptors; var isArray = isArray$f; var $TypeError$7 = TypeError; // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe var getOwnPropertyDescriptor$1 = Object.getOwnPropertyDescriptor; // Safari < 13 does not throw an error in this case var SILENT_ON_NON_WRITABLE_LENGTH_SET = DESCRIPTORS$6 && !function () { // makes no sense without proper strict mode support if (this !== undefined) return true; try { // eslint-disable-next-line es/no-object-defineproperty -- safe Object.defineProperty([], 'length', { writable: false }).length = 1; } catch (error) { return error instanceof TypeError; } }(); var arraySetLength = SILENT_ON_NON_WRITABLE_LENGTH_SET ? function (O, length) { if (isArray(O) && !getOwnPropertyDescriptor$1(O, 'length').writable) { throw $TypeError$7('Cannot set read only .length'); } return O.length = length; } : function (O, length) { return O.length = length; }; var tryToString$2 = tryToString$6; var $TypeError$6 = TypeError; var deletePropertyOrThrow$2 = function (O, P) { if (!delete O[P]) throw $TypeError$6('Cannot delete property ' + tryToString$2(P) + ' of ' + tryToString$2(O)); }; var $$t = _export; var toObject$4 = toObject$e; var toAbsoluteIndex$1 = toAbsoluteIndex$5; var toIntegerOrInfinity = toIntegerOrInfinity$4; var lengthOfArrayLike$3 = lengthOfArrayLike$d; var setArrayLength = arraySetLength; var doesNotExceedSafeInteger = doesNotExceedSafeInteger$3; var arraySpeciesCreate = arraySpeciesCreate$4; var createProperty = createProperty$6; var deletePropertyOrThrow$1 = deletePropertyOrThrow$2; var arrayMethodHasSpeciesSupport = arrayMethodHasSpeciesSupport$5; var HAS_SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('splice'); var max = Math.max; var min = Math.min; // `Array.prototype.splice` method // https://tc39.es/ecma262/#sec-array.prototype.splice // with adding support of @@species $$t({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT }, { splice: function splice(start, deleteCount /* , ...items */) { var O = toObject$4(this); var len = lengthOfArrayLike$3(O); var actualStart = toAbsoluteIndex$1(start, len); var argumentsLength = arguments.length; var insertCount, actualDeleteCount, A, k, from, to; if (argumentsLength === 0) { insertCount = actualDeleteCount = 0; } else if (argumentsLength === 1) { insertCount = 0; actualDeleteCount = len - actualStart; } else { insertCount = argumentsLength - 2; actualDeleteCount = min(max(toIntegerOrInfinity(deleteCount), 0), len - actualStart); } doesNotExceedSafeInteger(len + insertCount - actualDeleteCount); A = arraySpeciesCreate(O, actualDeleteCount); for (k = 0; k < actualDeleteCount; k++) { from = actualStart + k; if (from in O) createProperty(A, k, O[from]); } A.length = actualDeleteCount; if (insertCount < actualDeleteCount) { for (k = actualStart; k < len - actualDeleteCount; k++) { from = k + actualDeleteCount; to = k + insertCount; if (from in O) O[to] = O[from];else deletePropertyOrThrow$1(O, to); } for (k = len; k > len - actualDeleteCount + insertCount; k--) deletePropertyOrThrow$1(O, k - 1); } else if (insertCount > actualDeleteCount) { for (k = len - actualDeleteCount; k > actualStart; k--) { from = k + actualDeleteCount - 1; to = k + insertCount - 1; if (from in O) O[to] = O[from];else deletePropertyOrThrow$1(O, to); } } for (k = 0; k < insertCount; k++) { O[k + actualStart] = arguments[k + 2]; } setArrayLength(O, len - actualDeleteCount + insertCount); return A; } }); var entryVirtual$a = entryVirtual$k; var splice$2 = entryVirtual$a('Array').splice; var isPrototypeOf$c = objectIsPrototypeOf; var method$8 = splice$2; var ArrayPrototype$8 = Array.prototype; var splice$1 = function (it) { var own = it.splice; return it === ArrayPrototype$8 || isPrototypeOf$c(ArrayPrototype$8, it) && own === ArrayPrototype$8.splice ? method$8 : own; }; var parent$B = splice$1; var splice = parent$B; (function (module) { module.exports = splice; })(splice$3); var _spliceInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(spliceExports); var assignExports = {}; var assign$5 = { get exports() { return assignExports; }, set exports(v) { assignExports = v; } }; var DESCRIPTORS$5 = descriptors; var uncurryThis$8 = functionUncurryThis; var call$7 = functionCall; var fails$d = fails$w; var objectKeys$1 = objectKeys$4; var getOwnPropertySymbolsModule = objectGetOwnPropertySymbols; var propertyIsEnumerableModule = objectPropertyIsEnumerable; var toObject$3 = toObject$e; var IndexedObject = indexedObject; // eslint-disable-next-line es/no-object-assign -- safe var $assign = Object.assign; // eslint-disable-next-line es/no-object-defineproperty -- required for testing var defineProperty$2 = Object.defineProperty; var concat = uncurryThis$8([].concat); // `Object.assign` method // https://tc39.es/ecma262/#sec-object.assign var objectAssign = !$assign || fails$d(function () { // should have correct order of operations (Edge bug) if (DESCRIPTORS$5 && $assign({ b: 1 }, $assign(defineProperty$2({}, 'a', { enumerable: true, get: function () { defineProperty$2(this, 'b', { value: 3, enumerable: false }); } }), { b: 2 })).b !== 1) return true; // should work with symbols and should have deterministic property order (V8 bug) var A = {}; var B = {}; // eslint-disable-next-line es/no-symbol -- safe var symbol = Symbol(); var alphabet = 'abcdefghijklmnopqrst'; A[symbol] = 7; alphabet.split('').forEach(function (chr) { B[chr] = chr; }); return $assign({}, A)[symbol] != 7 || objectKeys$1($assign({}, B)).join('') != alphabet; }) ? function assign(target, source) { // eslint-disable-line no-unused-vars -- required for `.length` var T = toObject$3(target); var argumentsLength = arguments.length; var index = 1; var getOwnPropertySymbols = getOwnPropertySymbolsModule.f; var propertyIsEnumerable = propertyIsEnumerableModule.f; while (argumentsLength > index) { var S = IndexedObject(arguments[index++]); var keys = getOwnPropertySymbols ? concat(objectKeys$1(S), getOwnPropertySymbols(S)) : objectKeys$1(S); var length = keys.length; var j = 0; var key; while (length > j) { key = keys[j++]; if (!DESCRIPTORS$5 || call$7(propertyIsEnumerable, S, key)) T[key] = S[key]; } } return T; } : $assign; var $$s = _export; var assign$4 = objectAssign; // `Object.assign` method // https://tc39.es/ecma262/#sec-object.assign // eslint-disable-next-line es/no-object-assign -- required for testing $$s({ target: 'Object', stat: true, arity: 2, forced: Object.assign !== assign$4 }, { assign: assign$4 }); var path$b = path$r; var assign$3 = path$b.Object.assign; var parent$A = assign$3; var assign$2 = parent$A; (function (module) { module.exports = assign$2; })(assign$5); var _Object$assign = /*@__PURE__*/getDefaultExportFromCjs(assignExports); var includesExports = {}; var includes$4 = { get exports() { return includesExports; }, set exports(v) { includesExports = v; } }; var $$r = _export; var $includes = arrayIncludes.includes; var fails$c = fails$w; // FF99+ bug var BROKEN_ON_SPARSE = fails$c(function () { // eslint-disable-next-line es/no-array-prototype-includes -- detection return !Array(1).includes(); }); // `Array.prototype.includes` method // https://tc39.es/ecma262/#sec-array.prototype.includes $$r({ target: 'Array', proto: true, forced: BROKEN_ON_SPARSE }, { includes: function includes(el /* , fromIndex = 0 */) { return $includes(this, el, arguments.length > 1 ? arguments[1] : undefined); } }); var entryVirtual$9 = entryVirtual$k; var includes$3 = entryVirtual$9('Array').includes; var isObject$7 = isObject$i; var classof$4 = classofRaw$2; var wellKnownSymbol$5 = wellKnownSymbol$o; var MATCH$1 = wellKnownSymbol$5('match'); // `IsRegExp` abstract operation // https://tc39.es/ecma262/#sec-isregexp var isRegexp = function (it) { var isRegExp; return isObject$7(it) && ((isRegExp = it[MATCH$1]) !== undefined ? !!isRegExp : classof$4(it) == 'RegExp'); }; var isRegExp = isRegexp; var $TypeError$5 = TypeError; var notARegexp = function (it) { if (isRegExp(it)) { throw $TypeError$5("The method doesn't accept regular expressions"); } return it; }; var wellKnownSymbol$4 = wellKnownSymbol$o; var MATCH = wellKnownSymbol$4('match'); var correctIsRegexpLogic = function (METHOD_NAME) { var regexp = /./; try { '/./'[METHOD_NAME](regexp); } catch (error1) { try { regexp[MATCH] = false; return '/./'[METHOD_NAME](regexp); } catch (error2) {/* empty */} } return false; }; var $$q = _export; var uncurryThis$7 = functionUncurryThis; var notARegExp = notARegexp; var requireObjectCoercible$1 = requireObjectCoercible$5; var toString$4 = toString$a; var correctIsRegExpLogic = correctIsRegexpLogic; var stringIndexOf = uncurryThis$7(''.indexOf); // `String.prototype.includes` method // https://tc39.es/ecma262/#sec-string.prototype.includes $$q({ target: 'String', proto: true, forced: !correctIsRegExpLogic('includes') }, { includes: function includes(searchString /* , position = 0 */) { return !!~stringIndexOf(toString$4(requireObjectCoercible$1(this)), toString$4(notARegExp(searchString)), arguments.length > 1 ? arguments[1] : undefined); } }); var entryVirtual$8 = entryVirtual$k; var includes$2 = entryVirtual$8('String').includes; var isPrototypeOf$b = objectIsPrototypeOf; var arrayMethod = includes$3; var stringMethod = includes$2; var ArrayPrototype$7 = Array.prototype; var StringPrototype$1 = String.prototype; var includes$1 = function (it) { var own = it.includes; if (it === ArrayPrototype$7 || isPrototypeOf$b(ArrayPrototype$7, it) && own === ArrayPrototype$7.includes) return arrayMethod; if (typeof it == 'string' || it === StringPrototype$1 || isPrototypeOf$b(StringPrototype$1, it) && own === StringPrototype$1.includes) { return stringMethod; } return own; }; var parent$z = includes$1; var includes = parent$z; (function (module) { module.exports = includes; })(includes$4); var getPrototypeOfExports$2 = {}; var getPrototypeOf$7 = { get exports() { return getPrototypeOfExports$2; }, set exports(v) { getPrototypeOfExports$2 = v; } }; var $$p = _export; var fails$b = fails$w; var toObject$2 = toObject$e; var nativeGetPrototypeOf = objectGetPrototypeOf; var CORRECT_PROTOTYPE_GETTER = correctPrototypeGetter; var FAILS_ON_PRIMITIVES$1 = fails$b(function () { nativeGetPrototypeOf(1); }); // `Object.getPrototypeOf` method // https://tc39.es/ecma262/#sec-object.getprototypeof $$p({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$1, sham: !CORRECT_PROTOTYPE_GETTER }, { getPrototypeOf: function getPrototypeOf(it) { return nativeGetPrototypeOf(toObject$2(it)); } }); var path$a = path$r; var getPrototypeOf$6 = path$a.Object.getPrototypeOf; var parent$y = getPrototypeOf$6; var getPrototypeOf$5 = parent$y; (function (module) { module.exports = getPrototypeOf$5; })(getPrototypeOf$7); var valuesExports$1 = {}; var values$6 = { get exports() { return valuesExports$1; }, set exports(v) { valuesExports$1 = v; } }; var DESCRIPTORS$4 = descriptors; var uncurryThis$6 = functionUncurryThis; var objectKeys = objectKeys$4; var toIndexedObject = toIndexedObject$b; var $propertyIsEnumerable = objectPropertyIsEnumerable.f; var propertyIsEnumerable = uncurryThis$6($propertyIsEnumerable); var push$3 = uncurryThis$6([].push); // `Object.{ entries, values }` methods implementation var createMethod$1 = function (TO_ENTRIES) { return function (it) { var O = toIndexedObject(it); var keys = objectKeys(O); var length = keys.length; var i = 0; var result = []; var key; while (length > i) { key = keys[i++]; if (!DESCRIPTORS$4 || propertyIsEnumerable(O, key)) { push$3(result, TO_ENTRIES ? [key, O[key]] : O[key]); } } return result; }; }; var objectToArray = { // `Object.entries` method // https://tc39.es/ecma262/#sec-object.entries entries: createMethod$1(true), // `Object.values` method // https://tc39.es/ecma262/#sec-object.values values: createMethod$1(false) }; var $$o = _export; var $values = objectToArray.values; // `Object.values` method // https://tc39.es/ecma262/#sec-object.values $$o({ target: 'Object', stat: true }, { values: function values(O) { return $values(O); } }); var path$9 = path$r; var values$5 = path$9.Object.values; var parent$x = values$5; var values$4 = parent$x; (function (module) { module.exports = values$4; })(values$6); var _parseIntExports = {}; var _parseInt$2 = { get exports() { return _parseIntExports; }, set exports(v) { _parseIntExports = v; } }; // a string of all valid unicode whitespaces var whitespaces$3 = '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002' + '\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF'; var uncurryThis$5 = functionUncurryThis; var requireObjectCoercible = requireObjectCoercible$5; var toString$3 = toString$a; var whitespaces$2 = whitespaces$3; var replace$1 = uncurryThis$5(''.replace); var ltrim = RegExp('^[' + whitespaces$2 + ']+'); var rtrim = RegExp('(^|[^' + whitespaces$2 + '])[' + whitespaces$2 + ']+$'); // `String.prototype.{ trim, trimStart, trimEnd, trimLeft, trimRight }` methods implementation var createMethod = function (TYPE) { return function ($this) { var string = toString$3(requireObjectCoercible($this)); if (TYPE & 1) string = replace$1(string, ltrim, ''); if (TYPE & 2) string = replace$1(string, rtrim, '$1'); return string; }; }; var stringTrim = { // `String.prototype.{ trimLeft, trimStart }` methods // https://tc39.es/ecma262/#sec-string.prototype.trimstart start: createMethod(1), // `String.prototype.{ trimRight, trimEnd }` methods // https://tc39.es/ecma262/#sec-string.prototype.trimend end: createMethod(2), // `String.prototype.trim` method // https://tc39.es/ecma262/#sec-string.prototype.trim trim: createMethod(3) }; var global$a = global$n; var fails$a = fails$w; var uncurryThis$4 = functionUncurryThis; var toString$2 = toString$a; var trim$4 = stringTrim.trim; var whitespaces$1 = whitespaces$3; var $parseInt$1 = global$a.parseInt; var Symbol$1 = global$a.Symbol; var ITERATOR = Symbol$1 && Symbol$1.iterator; var hex = /^[+-]?0x/i; var exec = uncurryThis$4(hex.exec); var FORCED$3 = $parseInt$1(whitespaces$1 + '08') !== 8 || $parseInt$1(whitespaces$1 + '0x16') !== 22 // MS Edge 18- broken with boxed symbols || ITERATOR && !fails$a(function () { $parseInt$1(Object(ITERATOR)); }); // `parseInt` method // https://tc39.es/ecma262/#sec-parseint-string-radix var numberParseInt = FORCED$3 ? function parseInt(string, radix) { var S = trim$4(toString$2(string)); return $parseInt$1(S, radix >>> 0 || (exec(hex, S) ? 16 : 10)); } : $parseInt$1; var $$n = _export; var $parseInt = numberParseInt; // `parseInt` method // https://tc39.es/ecma262/#sec-parseint-string-radix $$n({ global: true, forced: parseInt != $parseInt }, { parseInt: $parseInt }); var path$8 = path$r; var _parseInt$1 = path$8.parseInt; var parent$w = _parseInt$1; var _parseInt = parent$w; (function (module) { module.exports = _parseInt; })(_parseInt$2); var indexOfExports = {}; var indexOf$3 = { get exports() { return indexOfExports; }, set exports(v) { indexOfExports = v; } }; /* eslint-disable es/no-array-prototype-indexof -- required for testing */ var $$m = _export; var uncurryThis$3 = functionUncurryThisClause; var $indexOf = arrayIncludes.indexOf; var arrayMethodIsStrict$2 = arrayMethodIsStrict$5; var nativeIndexOf = uncurryThis$3([].indexOf); var NEGATIVE_ZERO = !!nativeIndexOf && 1 / nativeIndexOf([1], 1, -0) < 0; var FORCED$2 = NEGATIVE_ZERO || !arrayMethodIsStrict$2('indexOf'); // `Array.prototype.indexOf` method // https://tc39.es/ecma262/#sec-array.prototype.indexof $$m({ target: 'Array', proto: true, forced: FORCED$2 }, { indexOf: function indexOf(searchElement /* , fromIndex = 0 */) { var fromIndex = arguments.length > 1 ? arguments[1] : undefined; return NEGATIVE_ZERO // convert -0 to +0 ? nativeIndexOf(this, searchElement, fromIndex) || 0 : $indexOf(this, searchElement, fromIndex); } }); var entryVirtual$7 = entryVirtual$k; var indexOf$2 = entryVirtual$7('Array').indexOf; var isPrototypeOf$a = objectIsPrototypeOf; var method$7 = indexOf$2; var ArrayPrototype$6 = Array.prototype; var indexOf$1 = function (it) { var own = it.indexOf; return it === ArrayPrototype$6 || isPrototypeOf$a(ArrayPrototype$6, it) && own === ArrayPrototype$6.indexOf ? method$7 : own; }; var parent$v = indexOf$1; var indexOf = parent$v; (function (module) { module.exports = indexOf; })(indexOf$3); var trimExports = {}; var trim$3 = { get exports() { return trimExports; }, set exports(v) { trimExports = v; } }; var PROPER_FUNCTION_NAME = functionName.PROPER; var fails$9 = fails$w; var whitespaces = whitespaces$3; var non = '\u200B\u0085\u180E'; // check that a method works with the correct list // of whitespaces and has a correct name var stringTrimForced = function (METHOD_NAME) { return fails$9(function () { return !!whitespaces[METHOD_NAME]() || non[METHOD_NAME]() !== non || PROPER_FUNCTION_NAME && whitespaces[METHOD_NAME].name !== METHOD_NAME; }); }; var $$l = _export; var $trim = stringTrim.trim; var forcedStringTrimMethod = stringTrimForced; // `String.prototype.trim` method // https://tc39.es/ecma262/#sec-string.prototype.trim $$l({ target: 'String', proto: true, forced: forcedStringTrimMethod('trim') }, { trim: function trim() { return $trim(this); } }); var entryVirtual$6 = entryVirtual$k; var trim$2 = entryVirtual$6('String').trim; var isPrototypeOf$9 = objectIsPrototypeOf; var method$6 = trim$2; var StringPrototype = String.prototype; var trim$1 = function (it) { var own = it.trim; return typeof it == 'string' || it === StringPrototype || isPrototypeOf$9(StringPrototype, it) && own === StringPrototype.trim ? method$6 : own; }; var parent$u = trim$1; var trim = parent$u; (function (module) { module.exports = trim; })(trim$3); var createExports$2 = {}; var create$a = { get exports() { return createExports$2; }, set exports(v) { createExports$2 = v; } }; // TODO: Remove from `core-js@4` var $$k = _export; var DESCRIPTORS$3 = descriptors; var create$9 = objectCreate; // `Object.create` method // https://tc39.es/ecma262/#sec-object.create $$k({ target: 'Object', stat: true, sham: !DESCRIPTORS$3 }, { create: create$9 }); var path$7 = path$r; var Object$1 = path$7.Object; var create$8 = function create(P, D) { return Object$1.create(P, D); }; var parent$t = create$8; var create$7 = parent$t; (function (module) { module.exports = create$7; })(create$a); var _Object$create$1 = /*@__PURE__*/getDefaultExportFromCjs(createExports$2); var stringifyExports = {}; var stringify$2 = { get exports() { return stringifyExports; }, set exports(v) { stringifyExports = v; } }; var path$6 = path$r; var apply$3 = functionApply; // eslint-disable-next-line es/no-json -- safe if (!path$6.JSON) path$6.JSON = { stringify: JSON.stringify }; // eslint-disable-next-line no-unused-vars -- required for `.length` var stringify$1 = function stringify(it, replacer, space) { return apply$3(path$6.JSON.stringify, null, arguments); }; var parent$s = stringify$1; var stringify = parent$s; (function (module) { module.exports = stringify; })(stringify$2); var _JSON$stringify = /*@__PURE__*/getDefaultExportFromCjs(stringifyExports); var setTimeoutExports = {}; var setTimeout$3 = { get exports() { return setTimeoutExports; }, set exports(v) { setTimeoutExports = v; } }; /* global Bun -- Deno case */ var engineIsBun = typeof Bun == 'function' && Bun && typeof Bun.version == 'string'; var $TypeError$4 = TypeError; var validateArgumentsLength$2 = function (passed, required) { if (passed < required) throw $TypeError$4('Not enough arguments'); return passed; }; var global$9 = global$n; var apply$2 = functionApply; var isCallable$5 = isCallable$m; var ENGINE_IS_BUN = engineIsBun; var USER_AGENT = engineUserAgent; var arraySlice$2 = arraySlice$5; var validateArgumentsLength$1 = validateArgumentsLength$2; var Function$2 = global$9.Function; // dirty IE9- and Bun 0.3.0- checks var WRAP = /MSIE .\./.test(USER_AGENT) || ENGINE_IS_BUN && function () { var version = global$9.Bun.version.split('.'); return version.length < 3 || version[0] == 0 && (version[1] < 3 || version[1] == 3 && version[2] == 0); }(); // IE9- / Bun 0.3.0- setTimeout / setInterval / setImmediate additional parameters fix // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers // https://github.com/oven-sh/bun/issues/1633 var schedulersFix$2 = function (scheduler, hasTimeArg) { var firstParamIndex = hasTimeArg ? 2 : 1; return WRAP ? function (handler, timeout /* , ...arguments */) { var boundArgs = validateArgumentsLength$1(arguments.length, 1) > firstParamIndex; var fn = isCallable$5(handler) ? handler : Function$2(handler); var params = boundArgs ? arraySlice$2(arguments, firstParamIndex) : []; var callback = boundArgs ? function () { apply$2(fn, this, params); } : fn; return hasTimeArg ? scheduler(callback, timeout) : scheduler(callback); } : scheduler; }; var $$j = _export; var global$8 = global$n; var schedulersFix$1 = schedulersFix$2; var setInterval = schedulersFix$1(global$8.setInterval, true); // Bun / IE9- setInterval additional parameters fix // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval $$j({ global: true, bind: true, forced: global$8.setInterval !== setInterval }, { setInterval: setInterval }); var $$i = _export; var global$7 = global$n; var schedulersFix = schedulersFix$2; var setTimeout$2 = schedulersFix(global$7.setTimeout, true); // Bun / IE9- setTimeout additional parameters fix // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-settimeout $$i({ global: true, bind: true, forced: global$7.setTimeout !== setTimeout$2 }, { setTimeout: setTimeout$2 }); var path$5 = path$r; var setTimeout$1 = path$5.setTimeout; (function (module) { module.exports = setTimeout$1; })(setTimeout$3); var _setTimeout = /*@__PURE__*/getDefaultExportFromCjs(setTimeoutExports); var fillExports = {}; var fill$4 = { get exports() { return fillExports; }, set exports(v) { fillExports = v; } }; var toObject$1 = toObject$e; var toAbsoluteIndex = toAbsoluteIndex$5; var lengthOfArrayLike$2 = lengthOfArrayLike$d; // `Array.prototype.fill` method implementation // https://tc39.es/ecma262/#sec-array.prototype.fill var arrayFill = function fill(value /* , start = 0, end = @length */) { var O = toObject$1(this); var length = lengthOfArrayLike$2(O); var argumentsLength = arguments.length; var index = toAbsoluteIndex(argumentsLength > 1 ? arguments[1] : undefined, length); var end = argumentsLength > 2 ? arguments[2] : undefined; var endPos = end === undefined ? length : toAbsoluteIndex(end, length); while (endPos > index) O[index++] = value; return O; }; var $$h = _export; var fill$3 = arrayFill; // `Array.prototype.fill` method // https://tc39.es/ecma262/#sec-array.prototype.fill $$h({ target: 'Array', proto: true }, { fill: fill$3 }); var entryVirtual$5 = entryVirtual$k; var fill$2 = entryVirtual$5('Array').fill; var isPrototypeOf$8 = objectIsPrototypeOf; var method$5 = fill$2; var ArrayPrototype$5 = Array.prototype; var fill$1 = function (it) { var own = it.fill; return it === ArrayPrototype$5 || isPrototypeOf$8(ArrayPrototype$5, it) && own === ArrayPrototype$5.fill ? method$5 : own; }; var parent$r = fill$1; var fill = parent$r; (function (module) { module.exports = fill; })(fill$4); var componentEmitterExports = {}; var componentEmitter = { get exports() { return componentEmitterExports; }, set exports(v) { componentEmitterExports = v; } }; (function (module) { /** * Expose `Emitter`. */ { module.exports = Emitter; } /** * Initialize a new `Emitter`. * * @api public */ function Emitter(obj) { if (obj) return mixin(obj); } /** * Mixin the emitter properties. * * @param {Object} obj * @return {Object} * @api private */ function mixin(obj) { for (var key in Emitter.prototype) { obj[key] = Emitter.prototype[key]; } return obj; } /** * Listen on the given `event` with `fn`. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.on = Emitter.prototype.addEventListener = function (event, fn) { this._callbacks = this._callbacks || {}; (this._callbacks['$' + event] = this._callbacks['$' + event] || []).push(fn); return this; }; /** * Adds an `event` listener that will be invoked a single * time then automatically removed. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.once = function (event, fn) { function on() { this.off(event, on); fn.apply(this, arguments); } on.fn = fn; this.on(event, on); return this; }; /** * Remove the given callback for `event` or all * registered callbacks. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.off = Emitter.prototype.removeListener = Emitter.prototype.removeAllListeners = Emitter.prototype.removeEventListener = function (event, fn) { this._callbacks = this._callbacks || {}; // all if (0 == arguments.length) { this._callbacks = {}; return this; } // specific event var callbacks = this._callbacks['$' + event]; if (!callbacks) return this; // remove all handlers if (1 == arguments.length) { delete this._callbacks['$' + event]; return this; } // remove specific handler var cb; for (var i = 0; i < callbacks.length; i++) { cb = callbacks[i]; if (cb === fn || cb.fn === fn) { callbacks.splice(i, 1); break; } } // Remove event specific arrays for event types that no // one is subscribed for to avoid memory leak. if (callbacks.length === 0) { delete this._callbacks['$' + event]; } return this; }; /** * Emit `event` with the given args. * * @param {String} event * @param {Mixed} ... * @return {Emitter} */ Emitter.prototype.emit = function (event) { this._callbacks = this._callbacks || {}; var args = new Array(arguments.length - 1), callbacks = this._callbacks['$' + event]; for (var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } if (callbacks) { callbacks = callbacks.slice(0); for (var i = 0, len = callbacks.length; i < len; ++i) { callbacks[i].apply(this, args); } } return this; }; /** * Return array of callbacks for `event`. * * @param {String} event * @return {Array} * @api public */ Emitter.prototype.listeners = function (event) { this._callbacks = this._callbacks || {}; return this._callbacks['$' + event] || []; }; /** * Check if this emitter has `event` handlers. * * @param {String} event * @return {Boolean} * @api public */ Emitter.prototype.hasListeners = function (event) { return !!this.listeners(event).length; }; })(componentEmitter); var Emitter = componentEmitterExports; /*! Hammer.JS - v2.0.17-rc - 2019-12-16 * http://naver.github.io/egjs * * Forked By Naver egjs * Copyright (c) hammerjs * Licensed under the MIT license */ function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } function _assertThisInitialized$1(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } /** * @private * extend object. * means that properties in dest will be overwritten by the ones in src. * @param {Object} target * @param {...Object} objects_to_assign * @returns {Object} target */ var assign; if (typeof Object.assign !== 'function') { assign = function assign(target) { if (target === undefined || target === null) { throw new TypeError('Cannot convert undefined or null to object'); } var output = Object(target); for (var index = 1; index < arguments.length; index++) { var source = arguments[index]; if (source !== undefined && source !== null) { for (var nextKey in source) { if (source.hasOwnProperty(nextKey)) { output[nextKey] = source[nextKey]; } } } } return output; }; } else { assign = Object.assign; } var assign$1 = assign; var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o']; var TEST_ELEMENT = typeof document === "undefined" ? { style: {} } : document.createElement('div'); var TYPE_FUNCTION = 'function'; var round = Math.round, abs = Math.abs; var now = Date.now; /** * @private * get the prefixed property * @param {Object} obj * @param {String} property * @returns {String|Undefined} prefixed */ function prefixed(obj, property) { var prefix; var prop; var camelProp = property[0].toUpperCase() + property.slice(1); var i = 0; while (i < VENDOR_PREFIXES.length) { prefix = VENDOR_PREFIXES[i]; prop = prefix ? prefix + camelProp : property; if (prop in obj) { return prop; } i++; } return undefined; } /* eslint-disable no-new-func, no-nested-ternary */ var win; if (typeof window === "undefined") { // window is undefined in node.js win = {}; } else { win = window; } var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; function getTouchActionProps() { if (!NATIVE_TOUCH_ACTION) { return false; } var touchMap = {}; var cssSupports = win.CSS && win.CSS.supports; ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function (val) { // If css.supports is not supported but there is native touch-action assume it supports // all values. This is the case for IE 10 and 11. return touchMap[val] = cssSupports ? win.CSS.supports('touch-action', val) : true; }); return touchMap; } var TOUCH_ACTION_COMPUTE = 'compute'; var TOUCH_ACTION_AUTO = 'auto'; var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented var TOUCH_ACTION_NONE = 'none'; var TOUCH_ACTION_PAN_X = 'pan-x'; var TOUCH_ACTION_PAN_Y = 'pan-y'; var TOUCH_ACTION_MAP = getTouchActionProps(); var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; var SUPPORT_TOUCH = ('ontouchstart' in win); var SUPPORT_POINTER_EVENTS = prefixed(win, 'PointerEvent') !== undefined; var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); var INPUT_TYPE_TOUCH = 'touch'; var INPUT_TYPE_PEN = 'pen'; var INPUT_TYPE_MOUSE = 'mouse'; var INPUT_TYPE_KINECT = 'kinect'; var COMPUTE_INTERVAL = 25; var INPUT_START = 1; var INPUT_MOVE = 2; var INPUT_END = 4; var INPUT_CANCEL = 8; var DIRECTION_NONE = 1; var DIRECTION_LEFT = 2; var DIRECTION_RIGHT = 4; var DIRECTION_UP = 8; var DIRECTION_DOWN = 16; var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; var PROPS_XY = ['x', 'y']; var PROPS_CLIENT_XY = ['clientX', 'clientY']; /** * @private * walk objects and arrays * @param {Object} obj * @param {Function} iterator * @param {Object} context */ function each(obj, iterator, context) { var i; if (!obj) { return; } if (obj.forEach) { obj.forEach(iterator, context); } else if (obj.length !== undefined) { i = 0; while (i < obj.length) { iterator.call(context, obj[i], i, obj); i++; } } else { for (i in obj) { obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); } } } /** * @private * let a boolean value also be a function that must return a boolean * this first item in args will be used as the context * @param {Boolean|Function} val * @param {Array} [args] * @returns {Boolean} */ function boolOrFn(val, args) { if (typeof val === TYPE_FUNCTION) { return val.apply(args ? args[0] || undefined : undefined, args); } return val; } /** * @private * small indexOf wrapper * @param {String} str * @param {String} find * @returns {Boolean} found */ function inStr(str, find) { return str.indexOf(find) > -1; } /** * @private * when the touchActions are collected they are not a valid value, so we need to clean things up. * * @param {String} actions * @returns {*} */ function cleanTouchActions(actions) { // none if (inStr(actions, TOUCH_ACTION_NONE)) { return TOUCH_ACTION_NONE; } var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); // if both pan-x and pan-y are set (different recognizers // for different directions, e.g. horizontal pan but vertical swipe?) // we need none (as otherwise with pan-x pan-y combined none of these // recognizers will work, since the browser would handle all panning if (hasPanX && hasPanY) { return TOUCH_ACTION_NONE; } // pan-x OR pan-y if (hasPanX || hasPanY) { return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; } // manipulation if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { return TOUCH_ACTION_MANIPULATION; } return TOUCH_ACTION_AUTO; } /** * @private * Touch Action * sets the touchAction property or uses the js alternative * @param {Manager} manager * @param {String} value * @constructor */ var TouchAction = /*#__PURE__*/ function () { function TouchAction(manager, value) { this.manager = manager; this.set(value); } /** * @private * set the touchAction value on the element or enable the polyfill * @param {String} value */ var _proto = TouchAction.prototype; _proto.set = function set(value) { // find out the touch-action by the event handlers if (value === TOUCH_ACTION_COMPUTE) { value = this.compute(); } if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) { this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; } this.actions = value.toLowerCase().trim(); }; /** * @private * just re-set the touchAction value */ _proto.update = function update() { this.set(this.manager.options.touchAction); }; /** * @private * compute the value for the touchAction property based on the recognizer's settings * @returns {String} value */ _proto.compute = function compute() { var actions = []; each(this.manager.recognizers, function (recognizer) { if (boolOrFn(recognizer.options.enable, [recognizer])) { actions = actions.concat(recognizer.getTouchAction()); } }); return cleanTouchActions(actions.join(' ')); }; /** * @private * this method is called on each input cycle and provides the preventing of the browser behavior * @param {Object} input */ _proto.preventDefaults = function preventDefaults(input) { var srcEvent = input.srcEvent; var direction = input.offsetDirection; // if the touch action did prevented once this session if (this.manager.session.prevented) { srcEvent.preventDefault(); return; } var actions = this.actions; var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE]; var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y]; var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X]; if (hasNone) { // do not prevent defaults if this is a tap gesture var isTapPointer = input.pointers.length === 1; var isTapMovement = input.distance < 2; var isTapTouchTime = input.deltaTime < 250; if (isTapPointer && isTapMovement && isTapTouchTime) { return; } } if (hasPanX && hasPanY) { // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent return; } if (hasNone || hasPanY && direction & DIRECTION_HORIZONTAL || hasPanX && direction & DIRECTION_VERTICAL) { return this.preventSrc(srcEvent); } }; /** * @private * call preventDefault to prevent the browser's default behavior (scrolling in most cases) * @param {Object} srcEvent */ _proto.preventSrc = function preventSrc(srcEvent) { this.manager.session.prevented = true; srcEvent.preventDefault(); }; return TouchAction; }(); /** * @private * find if a node is in the given parent * @method hasParent * @param {HTMLElement} node * @param {HTMLElement} parent * @return {Boolean} found */ function hasParent(node, parent) { while (node) { if (node === parent) { return true; } node = node.parentNode; } return false; } /** * @private * get the center of all the pointers * @param {Array} pointers * @return {Object} center contains `x` and `y` properties */ function getCenter(pointers) { var pointersLength = pointers.length; // no need to loop when only one touch if (pointersLength === 1) { return { x: round(pointers[0].clientX), y: round(pointers[0].clientY) }; } var x = 0; var y = 0; var i = 0; while (i < pointersLength) { x += pointers[i].clientX; y += pointers[i].clientY; i++; } return { x: round(x / pointersLength), y: round(y / pointersLength) }; } /** * @private * create a simple clone from the input used for storage of firstInput and firstMultiple * @param {Object} input * @returns {Object} clonedInputData */ function simpleCloneInputData(input) { // make a simple copy of the pointers because we will get a reference if we don't // we only need clientXY for the calculations var pointers = []; var i = 0; while (i < input.pointers.length) { pointers[i] = { clientX: round(input.pointers[i].clientX), clientY: round(input.pointers[i].clientY) }; i++; } return { timeStamp: now(), pointers: pointers, center: getCenter(pointers), deltaX: input.deltaX, deltaY: input.deltaY }; } /** * @private * calculate the absolute distance between two points * @param {Object} p1 {x, y} * @param {Object} p2 {x, y} * @param {Array} [props] containing x and y keys * @return {Number} distance */ function getDistance(p1, p2, props) { if (!props) { props = PROPS_XY; } var x = p2[props[0]] - p1[props[0]]; var y = p2[props[1]] - p1[props[1]]; return Math.sqrt(x * x + y * y); } /** * @private * calculate the angle between two coordinates * @param {Object} p1 * @param {Object} p2 * @param {Array} [props] containing x and y keys * @return {Number} angle */ function getAngle(p1, p2, props) { if (!props) { props = PROPS_XY; } var x = p2[props[0]] - p1[props[0]]; var y = p2[props[1]] - p1[props[1]]; return Math.atan2(y, x) * 180 / Math.PI; } /** * @private * get the direction between two points * @param {Number} x * @param {Number} y * @return {Number} direction */ function getDirection(x, y) { if (x === y) { return DIRECTION_NONE; } if (abs(x) >= abs(y)) { return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; } return y < 0 ? DIRECTION_UP : DIRECTION_DOWN; } function computeDeltaXY(session, input) { var center = input.center; // let { offsetDelta:offset = {}, prevDelta = {}, prevInput = {} } = session; // jscs throwing error on defalut destructured values and without defaults tests fail var offset = session.offsetDelta || {}; var prevDelta = session.prevDelta || {}; var prevInput = session.prevInput || {}; if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { prevDelta = session.prevDelta = { x: prevInput.deltaX || 0, y: prevInput.deltaY || 0 }; offset = session.offsetDelta = { x: center.x, y: center.y }; } input.deltaX = prevDelta.x + (center.x - offset.x); input.deltaY = prevDelta.y + (center.y - offset.y); } /** * @private * calculate the velocity between two points. unit is in px per ms. * @param {Number} deltaTime * @param {Number} x * @param {Number} y * @return {Object} velocity `x` and `y` */ function getVelocity(deltaTime, x, y) { return { x: x / deltaTime || 0, y: y / deltaTime || 0 }; } /** * @private * calculate the scale factor between two pointersets * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out * @param {Array} start array of pointers * @param {Array} end array of pointers * @return {Number} scale */ function getScale(start, end) { return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); } /** * @private * calculate the rotation degrees between two pointersets * @param {Array} start array of pointers * @param {Array} end array of pointers * @return {Number} rotation */ function getRotation(start, end) { return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY); } /** * @private * velocity is calculated every x ms * @param {Object} session * @param {Object} input */ function computeIntervalInputData(session, input) { var last = session.lastInterval || input; var deltaTime = input.timeStamp - last.timeStamp; var velocity; var velocityX; var velocityY; var direction; if (input.eventType !== INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { var deltaX = input.deltaX - last.deltaX; var deltaY = input.deltaY - last.deltaY; var v = getVelocity(deltaTime, deltaX, deltaY); velocityX = v.x; velocityY = v.y; velocity = abs(v.x) > abs(v.y) ? v.x : v.y; direction = getDirection(deltaX, deltaY); session.lastInterval = input; } else { // use latest velocity info if it doesn't overtake a minimum period velocity = last.velocity; velocityX = last.velocityX; velocityY = last.velocityY; direction = last.direction; } input.velocity = velocity; input.velocityX = velocityX; input.velocityY = velocityY; input.direction = direction; } /** * @private * extend the data with some usable properties like scale, rotate, velocity etc * @param {Object} manager * @param {Object} input */ function computeInputData(manager, input) { var session = manager.session; var pointers = input.pointers; var pointersLength = pointers.length; // store the first input to calculate the distance and direction if (!session.firstInput) { session.firstInput = simpleCloneInputData(input); } // to compute scale and rotation we need to store the multiple touches if (pointersLength > 1 && !session.firstMultiple) { session.firstMultiple = simpleCloneInputData(input); } else if (pointersLength === 1) { session.firstMultiple = false; } var firstInput = session.firstInput, firstMultiple = session.firstMultiple; var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; var center = input.center = getCenter(pointers); input.timeStamp = now(); input.deltaTime = input.timeStamp - firstInput.timeStamp; input.angle = getAngle(offsetCenter, center); input.distance = getDistance(offsetCenter, center); computeDeltaXY(session, input); input.offsetDirection = getDirection(input.deltaX, input.deltaY); var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY); input.overallVelocityX = overallVelocity.x; input.overallVelocityY = overallVelocity.y; input.overallVelocity = abs(overallVelocity.x) > abs(overallVelocity.y) ? overallVelocity.x : overallVelocity.y; input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; input.maxPointers = !session.prevInput ? input.pointers.length : input.pointers.length > session.prevInput.maxPointers ? input.pointers.length : session.prevInput.maxPointers; computeIntervalInputData(session, input); // find the correct target var target = manager.element; var srcEvent = input.srcEvent; var srcEventTarget; if (srcEvent.composedPath) { srcEventTarget = srcEvent.composedPath()[0]; } else if (srcEvent.path) { srcEventTarget = srcEvent.path[0]; } else { srcEventTarget = srcEvent.target; } if (hasParent(srcEventTarget, target)) { target = srcEventTarget; } input.target = target; } /** * @private * handle input events * @param {Manager} manager * @param {String} eventType * @param {Object} input */ function inputHandler(manager, eventType, input) { var pointersLen = input.pointers.length; var changedPointersLen = input.changedPointers.length; var isFirst = eventType & INPUT_START && pointersLen - changedPointersLen === 0; var isFinal = eventType & (INPUT_END | INPUT_CANCEL) && pointersLen - changedPointersLen === 0; input.isFirst = !!isFirst; input.isFinal = !!isFinal; if (isFirst) { manager.session = {}; } // source event is the normalized value of the domEvents // like 'touchstart, mouseup, pointerdown' input.eventType = eventType; // compute scale, rotation etc computeInputData(manager, input); // emit secret event manager.emit('hammer.input', input); manager.recognize(input); manager.session.prevInput = input; } /** * @private * split string on whitespace * @param {String} str * @returns {Array} words */ function splitStr(str) { return str.trim().split(/\s+/g); } /** * @private * addEventListener with multiple events at once * @param {EventTarget} target * @param {String} types * @param {Function} handler */ function addEventListeners(target, types, handler) { each(splitStr(types), function (type) { target.addEventListener(type, handler, false); }); } /** * @private * removeEventListener with multiple events at once * @param {EventTarget} target * @param {String} types * @param {Function} handler */ function removeEventListeners(target, types, handler) { each(splitStr(types), function (type) { target.removeEventListener(type, handler, false); }); } /** * @private * get the window object of an element * @param {HTMLElement} element * @returns {DocumentView|Window} */ function getWindowForElement(element) { var doc = element.ownerDocument || element; return doc.defaultView || doc.parentWindow || window; } /** * @private * create new input type manager * @param {Manager} manager * @param {Function} callback * @returns {Input} * @constructor */ var Input = /*#__PURE__*/ function () { function Input(manager, callback) { var self = this; this.manager = manager; this.callback = callback; this.element = manager.element; this.target = manager.options.inputTarget; // smaller wrapper around the handler, for the scope and the enabled state of the manager, // so when disabled the input events are completely bypassed. this.domHandler = function (ev) { if (boolOrFn(manager.options.enable, [manager])) { self.handler(ev); } }; this.init(); } /** * @private * should handle the inputEvent data and trigger the callback * @virtual */ var _proto = Input.prototype; _proto.handler = function handler() {}; /** * @private * bind the events */ _proto.init = function init() { this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); }; /** * @private * unbind the events */ _proto.destroy = function destroy() { this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); }; return Input; }(); /** * @private * find if a array contains the object using indexOf or a simple polyFill * @param {Array} src * @param {String} find * @param {String} [findByKey] * @return {Boolean|Number} false when not found, or the index */ function inArray(src, find, findByKey) { if (src.indexOf && !findByKey) { return src.indexOf(find); } else { var i = 0; while (i < src.length) { if (findByKey && src[i][findByKey] == find || !findByKey && src[i] === find) { // do not use === here, test fails return i; } i++; } return -1; } } var POINTER_INPUT_MAP = { pointerdown: INPUT_START, pointermove: INPUT_MOVE, pointerup: INPUT_END, pointercancel: INPUT_CANCEL, pointerout: INPUT_CANCEL }; // in IE10 the pointer types is defined as an enum var IE10_POINTER_TYPE_ENUM = { 2: INPUT_TYPE_TOUCH, 3: INPUT_TYPE_PEN, 4: INPUT_TYPE_MOUSE, 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 }; var POINTER_ELEMENT_EVENTS = 'pointerdown'; var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; // IE10 has prefixed support, and case-sensitive if (win.MSPointerEvent && !win.PointerEvent) { POINTER_ELEMENT_EVENTS = 'MSPointerDown'; POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; } /** * @private * Pointer events input * @constructor * @extends Input */ var PointerEventInput = /*#__PURE__*/ function (_Input) { _inheritsLoose(PointerEventInput, _Input); function PointerEventInput() { var _this; var proto = PointerEventInput.prototype; proto.evEl = POINTER_ELEMENT_EVENTS; proto.evWin = POINTER_WINDOW_EVENTS; _this = _Input.apply(this, arguments) || this; _this.store = _this.manager.session.pointerEvents = []; return _this; } /** * @private * handle mouse events * @param {Object} ev */ var _proto = PointerEventInput.prototype; _proto.handler = function handler(ev) { var store = this.store; var removePointer = false; var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; var isTouch = pointerType === INPUT_TYPE_TOUCH; // get index of the event in the store var storeIndex = inArray(store, ev.pointerId, 'pointerId'); // start and mouse must be down if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { if (storeIndex < 0) { store.push(ev); storeIndex = store.length - 1; } } else if (eventType & (INPUT_END | INPUT_CANCEL)) { removePointer = true; } // it not found, so the pointer hasn't been down (so it's probably a hover) if (storeIndex < 0) { return; } // update the event in the store store[storeIndex] = ev; this.callback(this.manager, eventType, { pointers: store, changedPointers: [ev], pointerType: pointerType, srcEvent: ev }); if (removePointer) { // remove from the store store.splice(storeIndex, 1); } }; return PointerEventInput; }(Input); /** * @private * convert array-like objects to real arrays * @param {Object} obj * @returns {Array} */ function toArray(obj) { return Array.prototype.slice.call(obj, 0); } /** * @private * unique array with objects based on a key (like 'id') or just by the array's value * @param {Array} src [{id:1},{id:2},{id:1}] * @param {String} [key] * @param {Boolean} [sort=False] * @returns {Array} [{id:1},{id:2}] */ function uniqueArray(src, key, sort) { var results = []; var values = []; var i = 0; while (i < src.length) { var val = key ? src[i][key] : src[i]; if (inArray(values, val) < 0) { results.push(src[i]); } values[i] = val; i++; } if (sort) { if (!key) { results = results.sort(); } else { results = results.sort(function (a, b) { return a[key] > b[key]; }); } } return results; } var TOUCH_INPUT_MAP = { touchstart: INPUT_START, touchmove: INPUT_MOVE, touchend: INPUT_END, touchcancel: INPUT_CANCEL }; var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; /** * @private * Multi-user touch events input * @constructor * @extends Input */ var TouchInput = /*#__PURE__*/ function (_Input) { _inheritsLoose(TouchInput, _Input); function TouchInput() { var _this; TouchInput.prototype.evTarget = TOUCH_TARGET_EVENTS; _this = _Input.apply(this, arguments) || this; _this.targetIds = {}; // this.evTarget = TOUCH_TARGET_EVENTS; return _this; } var _proto = TouchInput.prototype; _proto.handler = function handler(ev) { var type = TOUCH_INPUT_MAP[ev.type]; var touches = getTouches.call(this, ev, type); if (!touches) { return; } this.callback(this.manager, type, { pointers: touches[0], changedPointers: touches[1], pointerType: INPUT_TYPE_TOUCH, srcEvent: ev }); }; return TouchInput; }(Input); function getTouches(ev, type) { var allTouches = toArray(ev.touches); var targetIds = this.targetIds; // when there is only one touch, the process can be simplified if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { targetIds[allTouches[0].identifier] = true; return [allTouches, allTouches]; } var i; var targetTouches; var changedTouches = toArray(ev.changedTouches); var changedTargetTouches = []; var target = this.target; // get target touches from touches targetTouches = allTouches.filter(function (touch) { return hasParent(touch.target, target); }); // collect touches if (type === INPUT_START) { i = 0; while (i < targetTouches.length) { targetIds[targetTouches[i].identifier] = true; i++; } } // filter changed touches to only contain touches that exist in the collected target ids i = 0; while (i < changedTouches.length) { if (targetIds[changedTouches[i].identifier]) { changedTargetTouches.push(changedTouches[i]); } // cleanup removed touches if (type & (INPUT_END | INPUT_CANCEL)) { delete targetIds[changedTouches[i].identifier]; } i++; } if (!changedTargetTouches.length) { return; } return [ // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), changedTargetTouches]; } var MOUSE_INPUT_MAP = { mousedown: INPUT_START, mousemove: INPUT_MOVE, mouseup: INPUT_END }; var MOUSE_ELEMENT_EVENTS = 'mousedown'; var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; /** * @private * Mouse events input * @constructor * @extends Input */ var MouseInput = /*#__PURE__*/ function (_Input) { _inheritsLoose(MouseInput, _Input); function MouseInput() { var _this; var proto = MouseInput.prototype; proto.evEl = MOUSE_ELEMENT_EVENTS; proto.evWin = MOUSE_WINDOW_EVENTS; _this = _Input.apply(this, arguments) || this; _this.pressed = false; // mousedown state return _this; } /** * @private * handle mouse events * @param {Object} ev */ var _proto = MouseInput.prototype; _proto.handler = function handler(ev) { var eventType = MOUSE_INPUT_MAP[ev.type]; // on start we want to have the left mouse button down if (eventType & INPUT_START && ev.button === 0) { this.pressed = true; } if (eventType & INPUT_MOVE && ev.which !== 1) { eventType = INPUT_END; } // mouse must be down if (!this.pressed) { return; } if (eventType & INPUT_END) { this.pressed = false; } this.callback(this.manager, eventType, { pointers: [ev], changedPointers: [ev], pointerType: INPUT_TYPE_MOUSE, srcEvent: ev }); }; return MouseInput; }(Input); /** * @private * Combined touch and mouse input * * Touch has a higher priority then mouse, and while touching no mouse events are allowed. * This because touch devices also emit mouse events while doing a touch. * * @constructor * @extends Input */ var DEDUP_TIMEOUT = 2500; var DEDUP_DISTANCE = 25; function setLastTouch(eventData) { var _eventData$changedPoi = eventData.changedPointers, touch = _eventData$changedPoi[0]; if (touch.identifier === this.primaryTouch) { var lastTouch = { x: touch.clientX, y: touch.clientY }; var lts = this.lastTouches; this.lastTouches.push(lastTouch); var removeLastTouch = function removeLastTouch() { var i = lts.indexOf(lastTouch); if (i > -1) { lts.splice(i, 1); } }; setTimeout(removeLastTouch, DEDUP_TIMEOUT); } } function recordTouches(eventType, eventData) { if (eventType & INPUT_START) { this.primaryTouch = eventData.changedPointers[0].identifier; setLastTouch.call(this, eventData); } else if (eventType & (INPUT_END | INPUT_CANCEL)) { setLastTouch.call(this, eventData); } } function isSyntheticEvent(eventData) { var x = eventData.srcEvent.clientX; var y = eventData.srcEvent.clientY; for (var i = 0; i < this.lastTouches.length; i++) { var t = this.lastTouches[i]; var dx = Math.abs(x - t.x); var dy = Math.abs(y - t.y); if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) { return true; } } return false; } var TouchMouseInput = /*#__PURE__*/ function () { var TouchMouseInput = /*#__PURE__*/ function (_Input) { _inheritsLoose(TouchMouseInput, _Input); function TouchMouseInput(_manager, callback) { var _this; _this = _Input.call(this, _manager, callback) || this; _this.handler = function (manager, inputEvent, inputData) { var isTouch = inputData.pointerType === INPUT_TYPE_TOUCH; var isMouse = inputData.pointerType === INPUT_TYPE_MOUSE; if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) { return; } // when we're in a touch event, record touches to de-dupe synthetic mouse event if (isTouch) { recordTouches.call(_assertThisInitialized$1(_assertThisInitialized$1(_this)), inputEvent, inputData); } else if (isMouse && isSyntheticEvent.call(_assertThisInitialized$1(_assertThisInitialized$1(_this)), inputData)) { return; } _this.callback(manager, inputEvent, inputData); }; _this.touch = new TouchInput(_this.manager, _this.handler); _this.mouse = new MouseInput(_this.manager, _this.handler); _this.primaryTouch = null; _this.lastTouches = []; return _this; } /** * @private * handle mouse and touch events * @param {Hammer} manager * @param {String} inputEvent * @param {Object} inputData */ var _proto = TouchMouseInput.prototype; /** * @private * remove the event listeners */ _proto.destroy = function destroy() { this.touch.destroy(); this.mouse.destroy(); }; return TouchMouseInput; }(Input); return TouchMouseInput; }(); /** * @private * create new input type manager * called by the Manager constructor * @param {Hammer} manager * @returns {Input} */ function createInputInstance(manager) { var Type; // let inputClass = manager.options.inputClass; var inputClass = manager.options.inputClass; if (inputClass) { Type = inputClass; } else if (SUPPORT_POINTER_EVENTS) { Type = PointerEventInput; } else if (SUPPORT_ONLY_TOUCH) { Type = TouchInput; } else if (!SUPPORT_TOUCH) { Type = MouseInput; } else { Type = TouchMouseInput; } return new Type(manager, inputHandler); } /** * @private * if the argument is an array, we want to execute the fn on each entry * if it aint an array we don't want to do a thing. * this is used by all the methods that accept a single and array argument. * @param {*|Array} arg * @param {String} fn * @param {Object} [context] * @returns {Boolean} */ function invokeArrayArg(arg, fn, context) { if (Array.isArray(arg)) { each(arg, context[fn], context); return true; } return false; } var STATE_POSSIBLE = 1; var STATE_BEGAN = 2; var STATE_CHANGED = 4; var STATE_ENDED = 8; var STATE_RECOGNIZED = STATE_ENDED; var STATE_CANCELLED = 16; var STATE_FAILED = 32; /** * @private * get a unique id * @returns {number} uniqueId */ var _uniqueId = 1; function uniqueId() { return _uniqueId++; } /** * @private * get a recognizer by name if it is bound to a manager * @param {Recognizer|String} otherRecognizer * @param {Recognizer} recognizer * @returns {Recognizer} */ function getRecognizerByNameIfManager(otherRecognizer, recognizer) { var manager = recognizer.manager; if (manager) { return manager.get(otherRecognizer); } return otherRecognizer; } /** * @private * get a usable string, used as event postfix * @param {constant} state * @returns {String} state */ function stateStr(state) { if (state & STATE_CANCELLED) { return 'cancel'; } else if (state & STATE_ENDED) { return 'end'; } else if (state & STATE_CHANGED) { return 'move'; } else if (state & STATE_BEGAN) { return 'start'; } return ''; } /** * @private * Recognizer flow explained; * * All recognizers have the initial state of POSSIBLE when a input session starts. * The definition of a input session is from the first input until the last input, with all it's movement in it. * * Example session for mouse-input: mousedown -> mousemove -> mouseup * * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed * which determines with state it should be. * * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to * POSSIBLE to give it another change on the next cycle. * * Possible * | * +-----+---------------+ * | | * +-----+-----+ | * | | | * Failed Cancelled | * +-------+------+ * | | * Recognized Began * | * Changed * | * Ended/Recognized */ /** * @private * Recognizer * Every recognizer needs to extend from this class. * @constructor * @param {Object} options */ var Recognizer = /*#__PURE__*/ function () { function Recognizer(options) { if (options === void 0) { options = {}; } this.options = _extends({ enable: true }, options); this.id = uniqueId(); this.manager = null; // default is enable true this.state = STATE_POSSIBLE; this.simultaneous = {}; this.requireFail = []; } /** * @private * set options * @param {Object} options * @return {Recognizer} */ var _proto = Recognizer.prototype; _proto.set = function set(options) { assign$1(this.options, options); // also update the touchAction, in case something changed about the directions/enabled state this.manager && this.manager.touchAction.update(); return this; }; /** * @private * recognize simultaneous with an other recognizer. * @param {Recognizer} otherRecognizer * @returns {Recognizer} this */ _proto.recognizeWith = function recognizeWith(otherRecognizer) { if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { return this; } var simultaneous = this.simultaneous; otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); if (!simultaneous[otherRecognizer.id]) { simultaneous[otherRecognizer.id] = otherRecognizer; otherRecognizer.recognizeWith(this); } return this; }; /** * @private * drop the simultaneous link. it doesnt remove the link on the other recognizer. * @param {Recognizer} otherRecognizer * @returns {Recognizer} this */ _proto.dropRecognizeWith = function dropRecognizeWith(otherRecognizer) { if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { return this; } otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); delete this.simultaneous[otherRecognizer.id]; return this; }; /** * @private * recognizer can only run when an other is failing * @param {Recognizer} otherRecognizer * @returns {Recognizer} this */ _proto.requireFailure = function requireFailure(otherRecognizer) { if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { return this; } var requireFail = this.requireFail; otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); if (inArray(requireFail, otherRecognizer) === -1) { requireFail.push(otherRecognizer); otherRecognizer.requireFailure(this); } return this; }; /** * @private * drop the requireFailure link. it does not remove the link on the other recognizer. * @param {Recognizer} otherRecognizer * @returns {Recognizer} this */ _proto.dropRequireFailure = function dropRequireFailure(otherRecognizer) { if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { return this; } otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); var index = inArray(this.requireFail, otherRecognizer); if (index > -1) { this.requireFail.splice(index, 1); } return this; }; /** * @private * has require failures boolean * @returns {boolean} */ _proto.hasRequireFailures = function hasRequireFailures() { return this.requireFail.length > 0; }; /** * @private * if the recognizer can recognize simultaneous with an other recognizer * @param {Recognizer} otherRecognizer * @returns {Boolean} */ _proto.canRecognizeWith = function canRecognizeWith(otherRecognizer) { return !!this.simultaneous[otherRecognizer.id]; }; /** * @private * You should use `tryEmit` instead of `emit` directly to check * that all the needed recognizers has failed before emitting. * @param {Object} input */ _proto.emit = function emit(input) { var self = this; var state = this.state; function emit(event) { self.manager.emit(event, input); } // 'panstart' and 'panmove' if (state < STATE_ENDED) { emit(self.options.event + stateStr(state)); } emit(self.options.event); // simple 'eventName' events if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...) emit(input.additionalEvent); } // panend and pancancel if (state >= STATE_ENDED) { emit(self.options.event + stateStr(state)); } }; /** * @private * Check that all the require failure recognizers has failed, * if true, it emits a gesture event, * otherwise, setup the state to FAILED. * @param {Object} input */ _proto.tryEmit = function tryEmit(input) { if (this.canEmit()) { return this.emit(input); } // it's failing anyway this.state = STATE_FAILED; }; /** * @private * can we emit? * @returns {boolean} */ _proto.canEmit = function canEmit() { var i = 0; while (i < this.requireFail.length) { if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { return false; } i++; } return true; }; /** * @private * update the recognizer * @param {Object} inputData */ _proto.recognize = function recognize(inputData) { // make a new copy of the inputData // so we can change the inputData without messing up the other recognizers var inputDataClone = assign$1({}, inputData); // is is enabled and allow recognizing? if (!boolOrFn(this.options.enable, [this, inputDataClone])) { this.reset(); this.state = STATE_FAILED; return; } // reset when we've reached the end if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { this.state = STATE_POSSIBLE; } this.state = this.process(inputDataClone); // the recognizer has recognized a gesture // so trigger an event if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { this.tryEmit(inputDataClone); } }; /** * @private * return the state of the recognizer * the actual recognizing happens in this method * @virtual * @param {Object} inputData * @returns {constant} STATE */ /* jshint ignore:start */ _proto.process = function process(inputData) {}; /* jshint ignore:end */ /** * @private * return the preferred touch-action * @virtual * @returns {Array} */ _proto.getTouchAction = function getTouchAction() {}; /** * @private * called when the gesture isn't allowed to recognize * like when another is being recognized or it is disabled * @virtual */ _proto.reset = function reset() {}; return Recognizer; }(); /** * @private * A tap is recognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur * between the given interval and position. The delay option can be used to recognize multi-taps without firing * a single tap. * * The eventData from the emitted event contains the property `tapCount`, which contains the amount of * multi-taps being recognized. * @constructor * @extends Recognizer */ var TapRecognizer = /*#__PURE__*/ function (_Recognizer) { _inheritsLoose(TapRecognizer, _Recognizer); function TapRecognizer(options) { var _this; if (options === void 0) { options = {}; } _this = _Recognizer.call(this, _extends({ event: 'tap', pointers: 1, taps: 1, interval: 300, // max time between the multi-tap taps time: 250, // max time of the pointer to be down (like finger on the screen) threshold: 9, // a minimal movement is ok, but keep it low posThreshold: 10 }, options)) || this; // previous time and center, // used for tap counting _this.pTime = false; _this.pCenter = false; _this._timer = null; _this._input = null; _this.count = 0; return _this; } var _proto = TapRecognizer.prototype; _proto.getTouchAction = function getTouchAction() { return [TOUCH_ACTION_MANIPULATION]; }; _proto.process = function process(input) { var _this2 = this; var options = this.options; var validPointers = input.pointers.length === options.pointers; var validMovement = input.distance < options.threshold; var validTouchTime = input.deltaTime < options.time; this.reset(); if (input.eventType & INPUT_START && this.count === 0) { return this.failTimeout(); } // we only allow little movement // and we've reached an end event, so a tap is possible if (validMovement && validTouchTime && validPointers) { if (input.eventType !== INPUT_END) { return this.failTimeout(); } var validInterval = this.pTime ? input.timeStamp - this.pTime < options.interval : true; var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; this.pTime = input.timeStamp; this.pCenter = input.center; if (!validMultiTap || !validInterval) { this.count = 1; } else { this.count += 1; } this._input = input; // if tap count matches we have recognized it, // else it has began recognizing... var tapCount = this.count % options.taps; if (tapCount === 0) { // no failing requirements, immediately trigger the tap event // or wait as long as the multitap interval to trigger if (!this.hasRequireFailures()) { return STATE_RECOGNIZED; } else { this._timer = setTimeout(function () { _this2.state = STATE_RECOGNIZED; _this2.tryEmit(); }, options.interval); return STATE_BEGAN; } } } return STATE_FAILED; }; _proto.failTimeout = function failTimeout() { var _this3 = this; this._timer = setTimeout(function () { _this3.state = STATE_FAILED; }, this.options.interval); return STATE_FAILED; }; _proto.reset = function reset() { clearTimeout(this._timer); }; _proto.emit = function emit() { if (this.state === STATE_RECOGNIZED) { this._input.tapCount = this.count; this.manager.emit(this.options.event, this._input); } }; return TapRecognizer; }(Recognizer); /** * @private * This recognizer is just used as a base for the simple attribute recognizers. * @constructor * @extends Recognizer */ var AttrRecognizer = /*#__PURE__*/ function (_Recognizer) { _inheritsLoose(AttrRecognizer, _Recognizer); function AttrRecognizer(options) { if (options === void 0) { options = {}; } return _Recognizer.call(this, _extends({ pointers: 1 }, options)) || this; } /** * @private * Used to check if it the recognizer receives valid input, like input.distance > 10. * @memberof AttrRecognizer * @param {Object} input * @returns {Boolean} recognized */ var _proto = AttrRecognizer.prototype; _proto.attrTest = function attrTest(input) { var optionPointers = this.options.pointers; return optionPointers === 0 || input.pointers.length === optionPointers; }; /** * @private * Process the input and return the state for the recognizer * @memberof AttrRecognizer * @param {Object} input * @returns {*} State */ _proto.process = function process(input) { var state = this.state; var eventType = input.eventType; var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); var isValid = this.attrTest(input); // on cancel input and we've recognized before, return STATE_CANCELLED if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { return state | STATE_CANCELLED; } else if (isRecognized || isValid) { if (eventType & INPUT_END) { return state | STATE_ENDED; } else if (!(state & STATE_BEGAN)) { return STATE_BEGAN; } return state | STATE_CHANGED; } return STATE_FAILED; }; return AttrRecognizer; }(Recognizer); /** * @private * direction cons to string * @param {constant} direction * @returns {String} */ function directionStr(direction) { if (direction === DIRECTION_DOWN) { return 'down'; } else if (direction === DIRECTION_UP) { return 'up'; } else if (direction === DIRECTION_LEFT) { return 'left'; } else if (direction === DIRECTION_RIGHT) { return 'right'; } return ''; } /** * @private * Pan * Recognized when the pointer is down and moved in the allowed direction. * @constructor * @extends AttrRecognizer */ var PanRecognizer = /*#__PURE__*/ function (_AttrRecognizer) { _inheritsLoose(PanRecognizer, _AttrRecognizer); function PanRecognizer(options) { var _this; if (options === void 0) { options = {}; } _this = _AttrRecognizer.call(this, _extends({ event: 'pan', threshold: 10, pointers: 1, direction: DIRECTION_ALL }, options)) || this; _this.pX = null; _this.pY = null; return _this; } var _proto = PanRecognizer.prototype; _proto.getTouchAction = function getTouchAction() { var direction = this.options.direction; var actions = []; if (direction & DIRECTION_HORIZONTAL) { actions.push(TOUCH_ACTION_PAN_Y); } if (direction & DIRECTION_VERTICAL) { actions.push(TOUCH_ACTION_PAN_X); } return actions; }; _proto.directionTest = function directionTest(input) { var options = this.options; var hasMoved = true; var distance = input.distance; var direction = input.direction; var x = input.deltaX; var y = input.deltaY; // lock to axis? if (!(direction & options.direction)) { if (options.direction & DIRECTION_HORIZONTAL) { direction = x === 0 ? DIRECTION_NONE : x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; hasMoved = x !== this.pX; distance = Math.abs(input.deltaX); } else { direction = y === 0 ? DIRECTION_NONE : y < 0 ? DIRECTION_UP : DIRECTION_DOWN; hasMoved = y !== this.pY; distance = Math.abs(input.deltaY); } } input.direction = direction; return hasMoved && distance > options.threshold && direction & options.direction; }; _proto.attrTest = function attrTest(input) { return AttrRecognizer.prototype.attrTest.call(this, input) && ( // replace with a super call this.state & STATE_BEGAN || !(this.state & STATE_BEGAN) && this.directionTest(input)); }; _proto.emit = function emit(input) { this.pX = input.deltaX; this.pY = input.deltaY; var direction = directionStr(input.direction); if (direction) { input.additionalEvent = this.options.event + direction; } _AttrRecognizer.prototype.emit.call(this, input); }; return PanRecognizer; }(AttrRecognizer); /** * @private * Swipe * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. * @constructor * @extends AttrRecognizer */ var SwipeRecognizer = /*#__PURE__*/ function (_AttrRecognizer) { _inheritsLoose(SwipeRecognizer, _AttrRecognizer); function SwipeRecognizer(options) { if (options === void 0) { options = {}; } return _AttrRecognizer.call(this, _extends({ event: 'swipe', threshold: 10, velocity: 0.3, direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, pointers: 1 }, options)) || this; } var _proto = SwipeRecognizer.prototype; _proto.getTouchAction = function getTouchAction() { return PanRecognizer.prototype.getTouchAction.call(this); }; _proto.attrTest = function attrTest(input) { var direction = this.options.direction; var velocity; if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { velocity = input.overallVelocity; } else if (direction & DIRECTION_HORIZONTAL) { velocity = input.overallVelocityX; } else if (direction & DIRECTION_VERTICAL) { velocity = input.overallVelocityY; } return _AttrRecognizer.prototype.attrTest.call(this, input) && direction & input.offsetDirection && input.distance > this.options.threshold && input.maxPointers === this.options.pointers && abs(velocity) > this.options.velocity && input.eventType & INPUT_END; }; _proto.emit = function emit(input) { var direction = directionStr(input.offsetDirection); if (direction) { this.manager.emit(this.options.event + direction, input); } this.manager.emit(this.options.event, input); }; return SwipeRecognizer; }(AttrRecognizer); /** * @private * Pinch * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). * @constructor * @extends AttrRecognizer */ var PinchRecognizer = /*#__PURE__*/ function (_AttrRecognizer) { _inheritsLoose(PinchRecognizer, _AttrRecognizer); function PinchRecognizer(options) { if (options === void 0) { options = {}; } return _AttrRecognizer.call(this, _extends({ event: 'pinch', threshold: 0, pointers: 2 }, options)) || this; } var _proto = PinchRecognizer.prototype; _proto.getTouchAction = function getTouchAction() { return [TOUCH_ACTION_NONE]; }; _proto.attrTest = function attrTest(input) { return _AttrRecognizer.prototype.attrTest.call(this, input) && (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); }; _proto.emit = function emit(input) { if (input.scale !== 1) { var inOut = input.scale < 1 ? 'in' : 'out'; input.additionalEvent = this.options.event + inOut; } _AttrRecognizer.prototype.emit.call(this, input); }; return PinchRecognizer; }(AttrRecognizer); /** * @private * Rotate * Recognized when two or more pointer are moving in a circular motion. * @constructor * @extends AttrRecognizer */ var RotateRecognizer = /*#__PURE__*/ function (_AttrRecognizer) { _inheritsLoose(RotateRecognizer, _AttrRecognizer); function RotateRecognizer(options) { if (options === void 0) { options = {}; } return _AttrRecognizer.call(this, _extends({ event: 'rotate', threshold: 0, pointers: 2 }, options)) || this; } var _proto = RotateRecognizer.prototype; _proto.getTouchAction = function getTouchAction() { return [TOUCH_ACTION_NONE]; }; _proto.attrTest = function attrTest(input) { return _AttrRecognizer.prototype.attrTest.call(this, input) && (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); }; return RotateRecognizer; }(AttrRecognizer); /** * @private * Press * Recognized when the pointer is down for x ms without any movement. * @constructor * @extends Recognizer */ var PressRecognizer = /*#__PURE__*/ function (_Recognizer) { _inheritsLoose(PressRecognizer, _Recognizer); function PressRecognizer(options) { var _this; if (options === void 0) { options = {}; } _this = _Recognizer.call(this, _extends({ event: 'press', pointers: 1, time: 251, // minimal time of the pointer to be pressed threshold: 9 }, options)) || this; _this._timer = null; _this._input = null; return _this; } var _proto = PressRecognizer.prototype; _proto.getTouchAction = function getTouchAction() { return [TOUCH_ACTION_AUTO]; }; _proto.process = function process(input) { var _this2 = this; var options = this.options; var validPointers = input.pointers.length === options.pointers; var validMovement = input.distance < options.threshold; var validTime = input.deltaTime > options.time; this._input = input; // we only allow little movement // and we've reached an end event, so a tap is possible if (!validMovement || !validPointers || input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime) { this.reset(); } else if (input.eventType & INPUT_START) { this.reset(); this._timer = setTimeout(function () { _this2.state = STATE_RECOGNIZED; _this2.tryEmit(); }, options.time); } else if (input.eventType & INPUT_END) { return STATE_RECOGNIZED; } return STATE_FAILED; }; _proto.reset = function reset() { clearTimeout(this._timer); }; _proto.emit = function emit(input) { if (this.state !== STATE_RECOGNIZED) { return; } if (input && input.eventType & INPUT_END) { this.manager.emit(this.options.event + "up", input); } else { this._input.timeStamp = now(); this.manager.emit(this.options.event, this._input); } }; return PressRecognizer; }(Recognizer); var defaults = { /** * @private * set if DOM events are being triggered. * But this is slower and unused by simple implementations, so disabled by default. * @type {Boolean} * @default false */ domEvents: false, /** * @private * The value for the touchAction property/fallback. * When set to `compute` it will magically set the correct value based on the added recognizers. * @type {String} * @default compute */ touchAction: TOUCH_ACTION_COMPUTE, /** * @private * @type {Boolean} * @default true */ enable: true, /** * @private * EXPERIMENTAL FEATURE -- can be removed/changed * Change the parent input target element. * If Null, then it is being set the to main element. * @type {Null|EventTarget} * @default null */ inputTarget: null, /** * @private * force an input class * @type {Null|Function} * @default null */ inputClass: null, /** * @private * Some CSS properties can be used to improve the working of Hammer. * Add them to this method and they will be set when creating a new Manager. * @namespace */ cssProps: { /** * @private * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. * @type {String} * @default 'none' */ userSelect: "none", /** * @private * Disable the Windows Phone grippers when pressing an element. * @type {String} * @default 'none' */ touchSelect: "none", /** * @private * Disables the default callout shown when you touch and hold a touch target. * On iOS, when you touch and hold a touch target such as a link, Safari displays * a callout containing information about the link. This property allows you to disable that callout. * @type {String} * @default 'none' */ touchCallout: "none", /** * @private * Specifies whether zooming is enabled. Used by IE10> * @type {String} * @default 'none' */ contentZooming: "none", /** * @private * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. * @type {String} * @default 'none' */ userDrag: "none", /** * @private * Overrides the highlight color shown when the user taps a link or a JavaScript * clickable element in iOS. This property obeys the alpha value, if specified. * @type {String} * @default 'rgba(0,0,0,0)' */ tapHighlightColor: "rgba(0,0,0,0)" } }; /** * @private * Default recognizer setup when calling `Hammer()` * When creating a new Manager these will be skipped. * This is separated with other defaults because of tree-shaking. * @type {Array} */ var preset = [[RotateRecognizer, { enable: false }], [PinchRecognizer, { enable: false }, ['rotate']], [SwipeRecognizer, { direction: DIRECTION_HORIZONTAL }], [PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']], [TapRecognizer], [TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']], [PressRecognizer]]; var STOP = 1; var FORCED_STOP = 2; /** * @private * add/remove the css properties as defined in manager.options.cssProps * @param {Manager} manager * @param {Boolean} add */ function toggleCssProps(manager, add) { var element = manager.element; if (!element.style) { return; } var prop; each(manager.options.cssProps, function (value, name) { prop = prefixed(element.style, name); if (add) { manager.oldCssProps[prop] = element.style[prop]; element.style[prop] = value; } else { element.style[prop] = manager.oldCssProps[prop] || ""; } }); if (!add) { manager.oldCssProps = {}; } } /** * @private * trigger dom event * @param {String} event * @param {Object} data */ function triggerDomEvent(event, data) { var gestureEvent = document.createEvent("Event"); gestureEvent.initEvent(event, true, true); gestureEvent.gesture = data; data.target.dispatchEvent(gestureEvent); } /** * @private * Manager * @param {HTMLElement} element * @param {Object} [options] * @constructor */ var Manager = /*#__PURE__*/ function () { function Manager(element, options) { var _this = this; this.options = assign$1({}, defaults, options || {}); this.options.inputTarget = this.options.inputTarget || element; this.handlers = {}; this.session = {}; this.recognizers = []; this.oldCssProps = {}; this.element = element; this.input = createInputInstance(this); this.touchAction = new TouchAction(this, this.options.touchAction); toggleCssProps(this, true); each(this.options.recognizers, function (item) { var recognizer = _this.add(new item[0](item[1])); item[2] && recognizer.recognizeWith(item[2]); item[3] && recognizer.requireFailure(item[3]); }, this); } /** * @private * set options * @param {Object} options * @returns {Manager} */ var _proto = Manager.prototype; _proto.set = function set(options) { assign$1(this.options, options); // Options that need a little more setup if (options.touchAction) { this.touchAction.update(); } if (options.inputTarget) { // Clean up existing event listeners and reinitialize this.input.destroy(); this.input.target = options.inputTarget; this.input.init(); } return this; }; /** * @private * stop recognizing for this session. * This session will be discarded, when a new [input]start event is fired. * When forced, the recognizer cycle is stopped immediately. * @param {Boolean} [force] */ _proto.stop = function stop(force) { this.session.stopped = force ? FORCED_STOP : STOP; }; /** * @private * run the recognizers! * called by the inputHandler function on every movement of the pointers (touches) * it walks through all the recognizers and tries to detect the gesture that is being made * @param {Object} inputData */ _proto.recognize = function recognize(inputData) { var session = this.session; if (session.stopped) { return; } // run the touch-action polyfill this.touchAction.preventDefaults(inputData); var recognizer; var recognizers = this.recognizers; // this holds the recognizer that is being recognized. // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED // if no recognizer is detecting a thing, it is set to `null` var curRecognizer = session.curRecognizer; // reset when the last recognizer is recognized // or when we're in a new session if (!curRecognizer || curRecognizer && curRecognizer.state & STATE_RECOGNIZED) { session.curRecognizer = null; curRecognizer = null; } var i = 0; while (i < recognizers.length) { recognizer = recognizers[i]; // find out if we are allowed try to recognize the input for this one. // 1. allow if the session is NOT forced stopped (see the .stop() method) // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one // that is being recognized. // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. // this can be setup with the `recognizeWith()` method on the recognizer. if (session.stopped !== FORCED_STOP && ( // 1 !curRecognizer || recognizer === curRecognizer || // 2 recognizer.canRecognizeWith(curRecognizer))) { // 3 recognizer.recognize(inputData); } else { recognizer.reset(); } // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the // current active recognizer. but only if we don't already have an active recognizer if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { session.curRecognizer = recognizer; curRecognizer = recognizer; } i++; } }; /** * @private * get a recognizer by its event name. * @param {Recognizer|String} recognizer * @returns {Recognizer|Null} */ _proto.get = function get(recognizer) { if (recognizer instanceof Recognizer) { return recognizer; } var recognizers = this.recognizers; for (var i = 0; i < recognizers.length; i++) { if (recognizers[i].options.event === recognizer) { return recognizers[i]; } } return null; }; /** * @private add a recognizer to the manager * existing recognizers with the same event name will be removed * @param {Recognizer} recognizer * @returns {Recognizer|Manager} */ _proto.add = function add(recognizer) { if (invokeArrayArg(recognizer, "add", this)) { return this; } // remove existing var existing = this.get(recognizer.options.event); if (existing) { this.remove(existing); } this.recognizers.push(recognizer); recognizer.manager = this; this.touchAction.update(); return recognizer; }; /** * @private * remove a recognizer by name or instance * @param {Recognizer|String} recognizer * @returns {Manager} */ _proto.remove = function remove(recognizer) { if (invokeArrayArg(recognizer, "remove", this)) { return this; } var targetRecognizer = this.get(recognizer); // let's make sure this recognizer exists if (recognizer) { var recognizers = this.recognizers; var index = inArray(recognizers, targetRecognizer); if (index !== -1) { recognizers.splice(index, 1); this.touchAction.update(); } } return this; }; /** * @private * bind event * @param {String} events * @param {Function} handler * @returns {EventEmitter} this */ _proto.on = function on(events, handler) { if (events === undefined || handler === undefined) { return this; } var handlers = this.handlers; each(splitStr(events), function (event) { handlers[event] = handlers[event] || []; handlers[event].push(handler); }); return this; }; /** * @private unbind event, leave emit blank to remove all handlers * @param {String} events * @param {Function} [handler] * @returns {EventEmitter} this */ _proto.off = function off(events, handler) { if (events === undefined) { return this; } var handlers = this.handlers; each(splitStr(events), function (event) { if (!handler) { delete handlers[event]; } else { handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1); } }); return this; }; /** * @private emit event to the listeners * @param {String} event * @param {Object} data */ _proto.emit = function emit(event, data) { // we also want to trigger dom events if (this.options.domEvents) { triggerDomEvent(event, data); } // no handlers, so skip it all var handlers = this.handlers[event] && this.handlers[event].slice(); if (!handlers || !handlers.length) { return; } data.type = event; data.preventDefault = function () { data.srcEvent.preventDefault(); }; var i = 0; while (i < handlers.length) { handlers[i](data); i++; } }; /** * @private * destroy the manager and unbinds all events * it doesn't unbind dom events, that is the user own responsibility */ _proto.destroy = function destroy() { this.element && toggleCssProps(this, false); this.handlers = {}; this.session = {}; this.input.destroy(); this.element = null; }; return Manager; }(); var SINGLE_TOUCH_INPUT_MAP = { touchstart: INPUT_START, touchmove: INPUT_MOVE, touchend: INPUT_END, touchcancel: INPUT_CANCEL }; var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; /** * @private * Touch events input * @constructor * @extends Input */ var SingleTouchInput = /*#__PURE__*/ function (_Input) { _inheritsLoose(SingleTouchInput, _Input); function SingleTouchInput() { var _this; var proto = SingleTouchInput.prototype; proto.evTarget = SINGLE_TOUCH_TARGET_EVENTS; proto.evWin = SINGLE_TOUCH_WINDOW_EVENTS; _this = _Input.apply(this, arguments) || this; _this.started = false; return _this; } var _proto = SingleTouchInput.prototype; _proto.handler = function handler(ev) { var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; // should we handle the touch events? if (type === INPUT_START) { this.started = true; } if (!this.started) { return; } var touches = normalizeSingleTouches.call(this, ev, type); // when done, reset the started state if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { this.started = false; } this.callback(this.manager, type, { pointers: touches[0], changedPointers: touches[1], pointerType: INPUT_TYPE_TOUCH, srcEvent: ev }); }; return SingleTouchInput; }(Input); function normalizeSingleTouches(ev, type) { var all = toArray(ev.touches); var changed = toArray(ev.changedTouches); if (type & (INPUT_END | INPUT_CANCEL)) { all = uniqueArray(all.concat(changed), 'identifier', true); } return [all, changed]; } /** * @private * wrap a method with a deprecation warning and stack trace * @param {Function} method * @param {String} name * @param {String} message * @returns {Function} A new function wrapping the supplied method. */ function deprecate(method, name, message) { var deprecationMessage = "DEPRECATED METHOD: " + name + "\n" + message + " AT \n"; return function () { var e = new Error('get-stack-trace'); var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '').replace(/^\s+at\s+/gm, '').replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace'; var log = window.console && (window.console.warn || window.console.log); if (log) { log.call(window.console, deprecationMessage, stack); } return method.apply(this, arguments); }; } /** * @private * extend object. * means that properties in dest will be overwritten by the ones in src. * @param {Object} dest * @param {Object} src * @param {Boolean} [merge=false] * @returns {Object} dest */ var extend = deprecate(function (dest, src, merge) { var keys = Object.keys(src); var i = 0; while (i < keys.length) { if (!merge || merge && dest[keys[i]] === undefined) { dest[keys[i]] = src[keys[i]]; } i++; } return dest; }, 'extend', 'Use `assign`.'); /** * @private * merge the values from src in the dest. * means that properties that exist in dest will not be overwritten by src * @param {Object} dest * @param {Object} src * @returns {Object} dest */ var merge$1 = deprecate(function (dest, src) { return extend(dest, src, true); }, 'merge', 'Use `assign`.'); /** * @private * simple class inheritance * @param {Function} child * @param {Function} base * @param {Object} [properties] */ function inherit(child, base, properties) { var baseP = base.prototype; var childP; childP = child.prototype = Object.create(baseP); childP.constructor = child; childP._super = baseP; if (properties) { assign$1(childP, properties); } } /** * @private * simple function bind * @param {Function} fn * @param {Object} context * @returns {Function} */ function bindFn(fn, context) { return function boundFn() { return fn.apply(context, arguments); }; } /** * @private * Simple way to create a manager with a default set of recognizers. * @param {HTMLElement} element * @param {Object} [options] * @constructor */ var Hammer = /*#__PURE__*/ function () { var Hammer = /** * @private * @const {string} */ function Hammer(element, options) { if (options === void 0) { options = {}; } return new Manager(element, _extends({ recognizers: preset.concat() }, options)); }; Hammer.VERSION = "2.0.17-rc"; Hammer.DIRECTION_ALL = DIRECTION_ALL; Hammer.DIRECTION_DOWN = DIRECTION_DOWN; Hammer.DIRECTION_LEFT = DIRECTION_LEFT; Hammer.DIRECTION_RIGHT = DIRECTION_RIGHT; Hammer.DIRECTION_UP = DIRECTION_UP; Hammer.DIRECTION_HORIZONTAL = DIRECTION_HORIZONTAL; Hammer.DIRECTION_VERTICAL = DIRECTION_VERTICAL; Hammer.DIRECTION_NONE = DIRECTION_NONE; Hammer.DIRECTION_DOWN = DIRECTION_DOWN; Hammer.INPUT_START = INPUT_START; Hammer.INPUT_MOVE = INPUT_MOVE; Hammer.INPUT_END = INPUT_END; Hammer.INPUT_CANCEL = INPUT_CANCEL; Hammer.STATE_POSSIBLE = STATE_POSSIBLE; Hammer.STATE_BEGAN = STATE_BEGAN; Hammer.STATE_CHANGED = STATE_CHANGED; Hammer.STATE_ENDED = STATE_ENDED; Hammer.STATE_RECOGNIZED = STATE_RECOGNIZED; Hammer.STATE_CANCELLED = STATE_CANCELLED; Hammer.STATE_FAILED = STATE_FAILED; Hammer.Manager = Manager; Hammer.Input = Input; Hammer.TouchAction = TouchAction; Hammer.TouchInput = TouchInput; Hammer.MouseInput = MouseInput; Hammer.PointerEventInput = PointerEventInput; Hammer.TouchMouseInput = TouchMouseInput; Hammer.SingleTouchInput = SingleTouchInput; Hammer.Recognizer = Recognizer; Hammer.AttrRecognizer = AttrRecognizer; Hammer.Tap = TapRecognizer; Hammer.Pan = PanRecognizer; Hammer.Swipe = SwipeRecognizer; Hammer.Pinch = PinchRecognizer; Hammer.Rotate = RotateRecognizer; Hammer.Press = PressRecognizer; Hammer.on = addEventListeners; Hammer.off = removeEventListeners; Hammer.each = each; Hammer.merge = merge$1; Hammer.extend = extend; Hammer.bindFn = bindFn; Hammer.assign = assign$1; Hammer.inherit = inherit; Hammer.bindFn = bindFn; Hammer.prefixed = prefixed; Hammer.toArray = toArray; Hammer.inArray = inArray; Hammer.uniqueArray = uniqueArray; Hammer.splitStr = splitStr; Hammer.boolOrFn = boolOrFn; Hammer.hasParent = hasParent; Hammer.addEventListeners = addEventListeners; Hammer.removeEventListeners = removeEventListeners; Hammer.defaults = assign$1({}, defaults, { preset: preset }); return Hammer; }(); var RealHammer = Hammer; function _createForOfIteratorHelper$3(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray$3(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray$3(o, minLen) { var _context21; if (!o) return; if (typeof o === "string") return _arrayLikeToArray$3(o, minLen); var n = _sliceInstanceProperty(_context21 = Object.prototype.toString.call(o)).call(_context21, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from$1(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$3(o, minLen); } function _arrayLikeToArray$3(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } /** * Use this symbol to delete properies in deepObjectAssign. */ var DELETE = _Symbol("DELETE"); /** * Pure version of deepObjectAssign, it doesn't modify any of it's arguments. * * @param base - The base object that fullfils the whole interface T. * @param updates - Updates that may change or delete props. * @returns A brand new instance with all the supplied objects deeply merged. */ function pureDeepObjectAssign(base) { var _context; for (var _len = arguments.length, updates = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { updates[_key - 1] = arguments[_key]; } return deepObjectAssign.apply(void 0, _concatInstanceProperty(_context = [{}, base]).call(_context, updates)); } /** * Deep version of object assign with additional deleting by the DELETE symbol. * * @param values - Objects to be deeply merged. * @returns The first object from values. */ function deepObjectAssign() { var merged = deepObjectAssignNonentry.apply(void 0, arguments); stripDelete(merged); return merged; } /** * Deep version of object assign with additional deleting by the DELETE symbol. * * @remarks * This doesn't strip the DELETE symbols so they may end up in the final object. * @param values - Objects to be deeply merged. * @returns The first object from values. */ function deepObjectAssignNonentry() { for (var _len2 = arguments.length, values = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { values[_key2] = arguments[_key2]; } if (values.length < 2) { return values[0]; } else if (values.length > 2) { var _context2; return deepObjectAssignNonentry.apply(void 0, _concatInstanceProperty(_context2 = [deepObjectAssign(values[0], values[1])]).call(_context2, _toConsumableArray(_sliceInstanceProperty(values).call(values, 2)))); } var a = values[0]; var b = values[1]; var _iterator = _createForOfIteratorHelper$3(_Reflect$ownKeys(b)), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var prop = _step.value; if (!Object.prototype.propertyIsEnumerable.call(b, prop)) ;else if (b[prop] === DELETE) { delete a[prop]; } else if (a[prop] !== null && b[prop] !== null && _typeof$1(a[prop]) === "object" && _typeof$1(b[prop]) === "object" && !_Array$isArray(a[prop]) && !_Array$isArray(b[prop])) { a[prop] = deepObjectAssignNonentry(a[prop], b[prop]); } else { a[prop] = clone(b[prop]); } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return a; } /** * Deep clone given object or array. In case of primitive simply return. * * @param a - Anything. * @returns Deep cloned object/array or unchanged a. */ function clone(a) { if (_Array$isArray(a)) { return _mapInstanceProperty(a).call(a, function (value) { return clone(value); }); } else if (_typeof$1(a) === "object" && a !== null) { return deepObjectAssignNonentry({}, a); } else { return a; } } /** * Strip DELETE from given object. * * @param a - Object which may contain DELETE but won't after this is executed. */ function stripDelete(a) { for (var _i = 0, _Object$keys$1 = _Object$keys(a); _i < _Object$keys$1.length; _i++) { var prop = _Object$keys$1[_i]; if (a[prop] === DELETE) { delete a[prop]; } else if (_typeof$1(a[prop]) === "object" && a[prop] !== null) { stripDelete(a[prop]); } } } /** * Setup a mock hammer.js object, for unit testing. * * Inspiration: https://github.com/uber/deck.gl/pull/658 * * @returns {{on: noop, off: noop, destroy: noop, emit: noop, get: get}} */ function hammerMock() { var noop = function noop() {}; return { on: noop, off: noop, destroy: noop, emit: noop, get: function get() { return { set: noop }; } }; } var Hammer$1 = typeof window !== "undefined" ? window.Hammer || RealHammer : function () { // hammer.js is only available in a browser, not in node.js. Replacing it with a mock object. return hammerMock(); }; /** * Turn an element into an clickToUse element. * When not active, the element has a transparent overlay. When the overlay is * clicked, the mode is changed to active. * When active, the element is displayed with a blue border around it, and * the interactive contents of the element can be used. When clicked outside * the element, the elements mode is changed to inactive. * * @param {Element} container * @class Activator */ function Activator$1(container) { var _this = this, _context3; this._cleanupQueue = []; this.active = false; this._dom = { container: container, overlay: document.createElement("div") }; this._dom.overlay.classList.add("vis-overlay"); this._dom.container.appendChild(this._dom.overlay); this._cleanupQueue.push(function () { _this._dom.overlay.parentNode.removeChild(_this._dom.overlay); }); var hammer = Hammer$1(this._dom.overlay); hammer.on("tap", _bindInstanceProperty$1(_context3 = this._onTapOverlay).call(_context3, this)); this._cleanupQueue.push(function () { hammer.destroy(); // FIXME: cleaning up hammer instances doesn't work (Timeline not removed // from memory) }); // block all touch events (except tap) var events = ["tap", "doubletap", "press", "pinch", "pan", "panstart", "panmove", "panend"]; _forEachInstanceProperty(events).call(events, function (event) { hammer.on(event, function (event) { event.srcEvent.stopPropagation(); }); }); // attach a click event to the window, in order to deactivate when clicking outside the timeline if (document && document.body) { this._onClick = function (event) { if (!_hasParent(event.target, container)) { _this.deactivate(); } }; document.body.addEventListener("click", this._onClick); this._cleanupQueue.push(function () { document.body.removeEventListener("click", _this._onClick); }); } // prepare escape key listener for deactivating when active this._escListener = function (event) { if ("key" in event ? event.key === "Escape" : event.keyCode === 27 /* the keyCode is for IE11 */) { _this.deactivate(); } }; } // turn into an event emitter Emitter(Activator$1.prototype); // The currently active activator Activator$1.current = null; /** * Destroy the activator. Cleans up all created DOM and event listeners */ Activator$1.prototype.destroy = function () { var _context4, _context5; this.deactivate(); var _iterator2 = _createForOfIteratorHelper$3(_reverseInstanceProperty(_context4 = _spliceInstanceProperty(_context5 = this._cleanupQueue).call(_context5, 0)).call(_context4)), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var callback = _step2.value; callback(); } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } }; /** * Activate the element * Overlay is hidden, element is decorated with a blue shadow border */ Activator$1.prototype.activate = function () { // we allow only one active activator at a time if (Activator$1.current) { Activator$1.current.deactivate(); } Activator$1.current = this; this.active = true; this._dom.overlay.style.display = "none"; this._dom.container.classList.add("vis-active"); this.emit("change"); this.emit("activate"); // ugly hack: bind ESC after emitting the events, as the Network rebinds all // keyboard events on a 'change' event document.body.addEventListener("keydown", this._escListener); }; /** * Deactivate the element * Overlay is displayed on top of the element */ Activator$1.prototype.deactivate = function () { this.active = false; this._dom.overlay.style.display = "block"; this._dom.container.classList.remove("vis-active"); document.body.removeEventListener("keydown", this._escListener); this.emit("change"); this.emit("deactivate"); }; /** * Handle a tap event: activate the container * * @param {Event} event The event * @private */ Activator$1.prototype._onTapOverlay = function (event) { // activate the container this.activate(); event.srcEvent.stopPropagation(); }; /** * Test whether the element has the requested parent element somewhere in * its chain of parent nodes. * * @param {HTMLElement} element * @param {HTMLElement} parent * @returns {boolean} Returns true when the parent is found somewhere in the * chain of parent nodes. * @private */ function _hasParent(element, parent) { while (element) { if (element === parent) { return true; } element = element.parentNode; } return false; } var constructExports = {}; var construct$2 = { get exports() { return constructExports; }, set exports(v) { constructExports = v; } }; var isConstructor = isConstructor$4; var tryToString$1 = tryToString$6; var $TypeError$3 = TypeError; // `Assert: IsConstructor(argument) is true` var aConstructor$2 = function (argument) { if (isConstructor(argument)) return argument; throw $TypeError$3(tryToString$1(argument) + ' is not a constructor'); }; var $$g = _export; var getBuiltIn$4 = getBuiltIn$f; var apply$1 = functionApply; var bind$9 = functionBind; var aConstructor$1 = aConstructor$2; var anObject$3 = anObject$d; var isObject$6 = isObject$i; var create$6 = objectCreate; var fails$8 = fails$w; var nativeConstruct = getBuiltIn$4('Reflect', 'construct'); var ObjectPrototype = Object.prototype; var push$2 = [].push; // `Reflect.construct` method // https://tc39.es/ecma262/#sec-reflect.construct // MS Edge supports only 2 arguments and argumentsList argument is optional // FF Nightly sets third argument as `new.target`, but does not create `this` from it var NEW_TARGET_BUG = fails$8(function () { function F() {/* empty */} return !(nativeConstruct(function () {/* empty */}, [], F) instanceof F); }); var ARGS_BUG = !fails$8(function () { nativeConstruct(function () {/* empty */}); }); var FORCED$1 = NEW_TARGET_BUG || ARGS_BUG; $$g({ target: 'Reflect', stat: true, forced: FORCED$1, sham: FORCED$1 }, { construct: function construct(Target, args /* , newTarget */) { aConstructor$1(Target); anObject$3(args); var newTarget = arguments.length < 3 ? Target : aConstructor$1(arguments[2]); if (ARGS_BUG && !NEW_TARGET_BUG) return nativeConstruct(Target, args, newTarget); if (Target == newTarget) { // w/o altered newTarget, optimization for 0-4 arguments switch (args.length) { case 0: return new Target(); case 1: return new Target(args[0]); case 2: return new Target(args[0], args[1]); case 3: return new Target(args[0], args[1], args[2]); case 4: return new Target(args[0], args[1], args[2], args[3]); } // w/o altered newTarget, lot of arguments case var $args = [null]; apply$1(push$2, $args, args); return new (apply$1(bind$9, Target, $args))(); } // with altered newTarget, not support built-in constructors var proto = newTarget.prototype; var instance = create$6(isObject$6(proto) ? proto : ObjectPrototype); var result = apply$1(Target, instance, args); return isObject$6(result) ? result : instance; } }); var path$4 = path$r; var construct$1 = path$4.Reflect.construct; var parent$q = construct$1; var construct = parent$q; (function (module) { module.exports = construct; })(construct$2); var _Reflect$construct = /*@__PURE__*/getDefaultExportFromCjs(constructExports); function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } var createExports$1 = {}; var create$5 = { get exports() { return createExports$1; }, set exports(v) { createExports$1 = v; } }; var createExports = {}; var create$4 = { get exports() { return createExports; }, set exports(v) { createExports = v; } }; var parent$p = create$7; var create$3 = parent$p; var parent$o = create$3; var create$2 = parent$o; (function (module) { module.exports = create$2; })(create$4); (function (module) { module.exports = createExports; })(create$5); var _Object$create = /*@__PURE__*/getDefaultExportFromCjs(createExports$1); var setPrototypeOfExports$1 = {}; var setPrototypeOf$7 = { get exports() { return setPrototypeOfExports$1; }, set exports(v) { setPrototypeOfExports$1 = v; } }; var setPrototypeOfExports = {}; var setPrototypeOf$6 = { get exports() { return setPrototypeOfExports; }, set exports(v) { setPrototypeOfExports = v; } }; var $$f = _export; var setPrototypeOf$5 = objectSetPrototypeOf; // `Object.setPrototypeOf` method // https://tc39.es/ecma262/#sec-object.setprototypeof $$f({ target: 'Object', stat: true }, { setPrototypeOf: setPrototypeOf$5 }); var path$3 = path$r; var setPrototypeOf$4 = path$3.Object.setPrototypeOf; var parent$n = setPrototypeOf$4; var setPrototypeOf$3 = parent$n; var parent$m = setPrototypeOf$3; var setPrototypeOf$2 = parent$m; var parent$l = setPrototypeOf$2; var setPrototypeOf$1 = parent$l; (function (module) { module.exports = setPrototypeOf$1; })(setPrototypeOf$6); (function (module) { module.exports = setPrototypeOfExports; })(setPrototypeOf$7); var _Object$setPrototypeOf = /*@__PURE__*/getDefaultExportFromCjs(setPrototypeOfExports$1); var bindExports$1 = {}; var bind$8 = { get exports() { return bindExports$1; }, set exports(v) { bindExports$1 = v; } }; var bindExports = {}; var bind$7 = { get exports() { return bindExports; }, set exports(v) { bindExports = v; } }; var parent$k = bind$c; var bind$6 = parent$k; var parent$j = bind$6; var bind$5 = parent$j; (function (module) { module.exports = bind$5; })(bind$7); (function (module) { module.exports = bindExports; })(bind$8); var _bindInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(bindExports$1); function _setPrototypeOf(o, p) { var _context; _setPrototypeOf = _Object$setPrototypeOf ? _bindInstanceProperty(_context = _Object$setPrototypeOf).call(_context) : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = _Object$create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); _Object$defineProperty$1(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } function _possibleConstructorReturn(self, call) { if (call && (_typeof$1(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } var getPrototypeOfExports$1 = {}; var getPrototypeOf$4 = { get exports() { return getPrototypeOfExports$1; }, set exports(v) { getPrototypeOfExports$1 = v; } }; var getPrototypeOfExports = {}; var getPrototypeOf$3 = { get exports() { return getPrototypeOfExports; }, set exports(v) { getPrototypeOfExports = v; } }; var parent$i = getPrototypeOf$5; var getPrototypeOf$2 = parent$i; var parent$h = getPrototypeOf$2; var getPrototypeOf$1 = parent$h; (function (module) { module.exports = getPrototypeOf$1; })(getPrototypeOf$3); (function (module) { module.exports = getPrototypeOfExports; })(getPrototypeOf$4); var _Object$getPrototypeOf = /*@__PURE__*/getDefaultExportFromCjs(getPrototypeOfExports$1); function _getPrototypeOf(o) { var _context; _getPrototypeOf = _Object$setPrototypeOf ? _bindInstanceProperty(_context = _Object$getPrototypeOf).call(_context) : function _getPrototypeOf(o) { return o.__proto__ || _Object$getPrototypeOf(o); }; return _getPrototypeOf(o); } var regeneratorRuntimeExports = {}; var regeneratorRuntime$1 = { get exports() { return regeneratorRuntimeExports; }, set exports(v) { regeneratorRuntimeExports = v; } }; var _typeofExports = {}; var _typeof = { get exports() { return _typeofExports; }, set exports(v) { _typeofExports = v; } }; (function (module) { var _Symbol = symbolExports$2; var _Symbol$iterator = iteratorExports$2; function _typeof(obj) { "@babel/helpers - typeof"; return (module.exports = _typeof = "function" == typeof _Symbol && "symbol" == typeof _Symbol$iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof _Symbol && obj.constructor === _Symbol && obj !== _Symbol.prototype ? "symbol" : typeof obj; }, module.exports.__esModule = true, module.exports["default"] = module.exports), _typeof(obj); } module.exports = _typeof, module.exports.__esModule = true, module.exports["default"] = module.exports; })(_typeof); var forEachExports$1 = {}; var forEach$4 = { get exports() { return forEachExports$1; }, set exports(v) { forEachExports$1 = v; } }; var forEachExports = {}; var forEach$3 = { get exports() { return forEachExports; }, set exports(v) { forEachExports = v; } }; var parent$g = forEach$5; var forEach$2 = parent$g; var parent$f = forEach$2; var forEach$1 = parent$f; (function (module) { module.exports = forEach$1; })(forEach$3); (function (module) { module.exports = forEachExports; })(forEach$4); var promiseExports$1 = {}; var promise$6 = { get exports() { return promiseExports$1; }, set exports(v) { promiseExports$1 = v; } }; var promiseExports = {}; var promise$5 = { get exports() { return promiseExports; }, set exports(v) { promiseExports = v; } }; var hasOwn$5 = hasOwnProperty_1; var ownKeys$1 = ownKeys$7; var getOwnPropertyDescriptorModule = objectGetOwnPropertyDescriptor; var definePropertyModule = objectDefineProperty; var copyConstructorProperties$1 = function (target, source, exceptions) { var keys = ownKeys$1(source); var defineProperty = definePropertyModule.f; var getOwnPropertyDescriptor = getOwnPropertyDescriptorModule.f; for (var i = 0; i < keys.length; i++) { var key = keys[i]; if (!hasOwn$5(target, key) && !(exceptions && hasOwn$5(exceptions, key))) { defineProperty(target, key, getOwnPropertyDescriptor(source, key)); } } }; var isObject$5 = isObject$i; var createNonEnumerableProperty$3 = createNonEnumerableProperty$9; // `InstallErrorCause` abstract operation // https://tc39.es/proposal-error-cause/#sec-errorobjects-install-error-cause var installErrorCause$1 = function (O, options) { if (isObject$5(options) && 'cause' in options) { createNonEnumerableProperty$3(O, 'cause', options.cause); } }; var uncurryThis$2 = functionUncurryThis; var $Error$1 = Error; var replace = uncurryThis$2(''.replace); var TEST = function (arg) { return String($Error$1(arg).stack); }('zxcasd'); // eslint-disable-next-line redos/no-vulnerable -- safe var V8_OR_CHAKRA_STACK_ENTRY = /\n\s*at [^:]*:[^\n]*/; var IS_V8_OR_CHAKRA_STACK = V8_OR_CHAKRA_STACK_ENTRY.test(TEST); var errorStackClear = function (stack, dropEntries) { if (IS_V8_OR_CHAKRA_STACK && typeof stack == 'string' && !$Error$1.prepareStackTrace) { while (dropEntries--) stack = replace(stack, V8_OR_CHAKRA_STACK_ENTRY, ''); } return stack; }; var fails$7 = fails$w; var createPropertyDescriptor$1 = createPropertyDescriptor$7; var errorStackInstallable = !fails$7(function () { var error = Error('a'); if (!('stack' in error)) return true; // eslint-disable-next-line es/no-object-defineproperty -- safe Object.defineProperty(error, 'stack', createPropertyDescriptor$1(1, 7)); return error.stack !== 7; }); var createNonEnumerableProperty$2 = createNonEnumerableProperty$9; var clearErrorStack = errorStackClear; var ERROR_STACK_INSTALLABLE = errorStackInstallable; // non-standard V8 var captureStackTrace = Error.captureStackTrace; var errorStackInstall = function (error, C, stack, dropEntries) { if (ERROR_STACK_INSTALLABLE) { if (captureStackTrace) captureStackTrace(error, C);else createNonEnumerableProperty$2(error, 'stack', clearErrorStack(stack, dropEntries)); } }; var bind$4 = functionBindContext; var call$6 = functionCall; var anObject$2 = anObject$d; var tryToString = tryToString$6; var isArrayIteratorMethod = isArrayIteratorMethod$2; var lengthOfArrayLike$1 = lengthOfArrayLike$d; var isPrototypeOf$7 = objectIsPrototypeOf; var getIterator$6 = getIterator$8; var getIteratorMethod = getIteratorMethod$9; var iteratorClose = iteratorClose$2; var $TypeError$2 = TypeError; var Result = function (stopped, result) { this.stopped = stopped; this.result = result; }; var ResultPrototype = Result.prototype; var iterate$7 = function (iterable, unboundFunction, options) { var that = options && options.that; var AS_ENTRIES = !!(options && options.AS_ENTRIES); var IS_RECORD = !!(options && options.IS_RECORD); var IS_ITERATOR = !!(options && options.IS_ITERATOR); var INTERRUPTED = !!(options && options.INTERRUPTED); var fn = bind$4(unboundFunction, that); var iterator, iterFn, index, length, result, next, step; var stop = function (condition) { if (iterator) iteratorClose(iterator, 'normal', condition); return new Result(true, condition); }; var callFn = function (value) { if (AS_ENTRIES) { anObject$2(value); return INTERRUPTED ? fn(value[0], value[1], stop) : fn(value[0], value[1]); } return INTERRUPTED ? fn(value, stop) : fn(value); }; if (IS_RECORD) { iterator = iterable.iterator; } else if (IS_ITERATOR) { iterator = iterable; } else { iterFn = getIteratorMethod(iterable); if (!iterFn) throw $TypeError$2(tryToString(iterable) + ' is not iterable'); // optimisation for array iterators if (isArrayIteratorMethod(iterFn)) { for (index = 0, length = lengthOfArrayLike$1(iterable); length > index; index++) { result = callFn(iterable[index]); if (result && isPrototypeOf$7(ResultPrototype, result)) return result; } return new Result(false); } iterator = getIterator$6(iterable, iterFn); } next = IS_RECORD ? iterable.next : iterator.next; while (!(step = call$6(next, iterator)).done) { try { result = callFn(step.value); } catch (error) { iteratorClose(iterator, 'throw', error); } if (typeof result == 'object' && result && isPrototypeOf$7(ResultPrototype, result)) return result; } return new Result(false); }; var toString$1 = toString$a; var normalizeStringArgument$1 = function (argument, $default) { return argument === undefined ? arguments.length < 2 ? '' : $default : toString$1(argument); }; var $$e = _export; var isPrototypeOf$6 = objectIsPrototypeOf; var getPrototypeOf = objectGetPrototypeOf; var setPrototypeOf = objectSetPrototypeOf; var copyConstructorProperties = copyConstructorProperties$1; var create$1 = objectCreate; var createNonEnumerableProperty$1 = createNonEnumerableProperty$9; var createPropertyDescriptor = createPropertyDescriptor$7; var installErrorCause = installErrorCause$1; var installErrorStack = errorStackInstall; var iterate$6 = iterate$7; var normalizeStringArgument = normalizeStringArgument$1; var wellKnownSymbol$3 = wellKnownSymbol$o; var TO_STRING_TAG = wellKnownSymbol$3('toStringTag'); var $Error = Error; var push$1 = [].push; var $AggregateError = function AggregateError(errors, message /* , options */) { var isInstance = isPrototypeOf$6(AggregateErrorPrototype, this); var that; if (setPrototypeOf) { that = setPrototypeOf($Error(), isInstance ? getPrototypeOf(this) : AggregateErrorPrototype); } else { that = isInstance ? this : create$1(AggregateErrorPrototype); createNonEnumerableProperty$1(that, TO_STRING_TAG, 'Error'); } if (message !== undefined) createNonEnumerableProperty$1(that, 'message', normalizeStringArgument(message)); installErrorStack(that, $AggregateError, that.stack, 1); if (arguments.length > 2) installErrorCause(that, arguments[2]); var errorsArray = []; iterate$6(errors, push$1, { that: errorsArray }); createNonEnumerableProperty$1(that, 'errors', errorsArray); return that; }; if (setPrototypeOf) setPrototypeOf($AggregateError, $Error);else copyConstructorProperties($AggregateError, $Error, { name: true }); var AggregateErrorPrototype = $AggregateError.prototype = create$1($Error.prototype, { constructor: createPropertyDescriptor(1, $AggregateError), message: createPropertyDescriptor(1, ''), name: createPropertyDescriptor(1, 'AggregateError') }); // `AggregateError` constructor // https://tc39.es/ecma262/#sec-aggregate-error-constructor $$e({ global: true, constructor: true, arity: 2 }, { AggregateError: $AggregateError }); var getBuiltIn$3 = getBuiltIn$f; var defineBuiltInAccessor$1 = defineBuiltInAccessor$3; var wellKnownSymbol$2 = wellKnownSymbol$o; var DESCRIPTORS$2 = descriptors; var SPECIES$2 = wellKnownSymbol$2('species'); var setSpecies$2 = function (CONSTRUCTOR_NAME) { var Constructor = getBuiltIn$3(CONSTRUCTOR_NAME); if (DESCRIPTORS$2 && Constructor && !Constructor[SPECIES$2]) { defineBuiltInAccessor$1(Constructor, SPECIES$2, { configurable: true, get: function () { return this; } }); } }; var isPrototypeOf$5 = objectIsPrototypeOf; var $TypeError$1 = TypeError; var anInstance$3 = function (it, Prototype) { if (isPrototypeOf$5(Prototype, it)) return it; throw $TypeError$1('Incorrect invocation'); }; var anObject$1 = anObject$d; var aConstructor = aConstructor$2; var isNullOrUndefined$1 = isNullOrUndefined$5; var wellKnownSymbol$1 = wellKnownSymbol$o; var SPECIES$1 = wellKnownSymbol$1('species'); // `SpeciesConstructor` abstract operation // https://tc39.es/ecma262/#sec-speciesconstructor var speciesConstructor$2 = function (O, defaultConstructor) { var C = anObject$1(O).constructor; var S; return C === undefined || isNullOrUndefined$1(S = anObject$1(C)[SPECIES$1]) ? defaultConstructor : aConstructor(S); }; var userAgent$4 = engineUserAgent; // eslint-disable-next-line redos/no-vulnerable -- safe var engineIsIos = /(?:ipad|iphone|ipod).*applewebkit/i.test(userAgent$4); var global$6 = global$n; var apply = functionApply; var bind$3 = functionBindContext; var isCallable$4 = isCallable$m; var hasOwn$4 = hasOwnProperty_1; var fails$6 = fails$w; var html = html$2; var arraySlice$1 = arraySlice$5; var createElement = documentCreateElement$1; var validateArgumentsLength = validateArgumentsLength$2; var IS_IOS$1 = engineIsIos; var IS_NODE$3 = engineIsNode; var set$3 = global$6.setImmediate; var clear = global$6.clearImmediate; var process$3 = global$6.process; var Dispatch = global$6.Dispatch; var Function$1 = global$6.Function; var MessageChannel = global$6.MessageChannel; var String$1 = global$6.String; var counter = 0; var queue$2 = {}; var ONREADYSTATECHANGE = 'onreadystatechange'; var $location, defer, channel, port; fails$6(function () { // Deno throws a ReferenceError on `location` access without `--location` flag $location = global$6.location; }); var run = function (id) { if (hasOwn$4(queue$2, id)) { var fn = queue$2[id]; delete queue$2[id]; fn(); } }; var runner = function (id) { return function () { run(id); }; }; var eventListener = function (event) { run(event.data); }; var globalPostMessageDefer = function (id) { // old engines have not location.origin global$6.postMessage(String$1(id), $location.protocol + '//' + $location.host); }; // Node.js 0.9+ & IE10+ has setImmediate, otherwise: if (!set$3 || !clear) { set$3 = function setImmediate(handler) { validateArgumentsLength(arguments.length, 1); var fn = isCallable$4(handler) ? handler : Function$1(handler); var args = arraySlice$1(arguments, 1); queue$2[++counter] = function () { apply(fn, undefined, args); }; defer(counter); return counter; }; clear = function clearImmediate(id) { delete queue$2[id]; }; // Node.js 0.8- if (IS_NODE$3) { defer = function (id) { process$3.nextTick(runner(id)); }; // Sphere (JS game engine) Dispatch API } else if (Dispatch && Dispatch.now) { defer = function (id) { Dispatch.now(runner(id)); }; // Browsers with MessageChannel, includes WebWorkers // except iOS - https://github.com/zloirock/core-js/issues/624 } else if (MessageChannel && !IS_IOS$1) { channel = new MessageChannel(); port = channel.port2; channel.port1.onmessage = eventListener; defer = bind$3(port.postMessage, port); // Browsers with postMessage, skip WebWorkers // IE8 has postMessage, but it's sync & typeof its postMessage is 'object' } else if (global$6.addEventListener && isCallable$4(global$6.postMessage) && !global$6.importScripts && $location && $location.protocol !== 'file:' && !fails$6(globalPostMessageDefer)) { defer = globalPostMessageDefer; global$6.addEventListener('message', eventListener, false); // IE8- } else if (ONREADYSTATECHANGE in createElement('script')) { defer = function (id) { html.appendChild(createElement('script'))[ONREADYSTATECHANGE] = function () { html.removeChild(this); run(id); }; }; // Rest old browsers } else { defer = function (id) { setTimeout(runner(id), 0); }; } } var task$1 = { set: set$3, clear: clear }; var Queue$3 = function () { this.head = null; this.tail = null; }; Queue$3.prototype = { add: function (item) { var entry = { item: item, next: null }; var tail = this.tail; if (tail) tail.next = entry;else this.head = entry; this.tail = entry; }, get: function () { var entry = this.head; if (entry) { var next = this.head = entry.next; if (next === null) this.tail = null; return entry.item; } } }; var queue$1 = Queue$3; var userAgent$3 = engineUserAgent; var engineIsIosPebble = /ipad|iphone|ipod/i.test(userAgent$3) && typeof Pebble != 'undefined'; var userAgent$2 = engineUserAgent; var engineIsWebosWebkit = /web0s(?!.*chrome)/i.test(userAgent$2); var global$5 = global$n; var bind$2 = functionBindContext; var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f; var macrotask = task$1.set; var Queue$2 = queue$1; var IS_IOS = engineIsIos; var IS_IOS_PEBBLE = engineIsIosPebble; var IS_WEBOS_WEBKIT = engineIsWebosWebkit; var IS_NODE$2 = engineIsNode; var MutationObserver = global$5.MutationObserver || global$5.WebKitMutationObserver; var document$2 = global$5.document; var process$2 = global$5.process; var Promise$1 = global$5.Promise; // Node.js 11 shows ExperimentalWarning on getting `queueMicrotask` var queueMicrotaskDescriptor = getOwnPropertyDescriptor(global$5, 'queueMicrotask'); var microtask$1 = queueMicrotaskDescriptor && queueMicrotaskDescriptor.value; var notify$1, toggle, node, promise$4, then; // modern engines have queueMicrotask method if (!microtask$1) { var queue = new Queue$2(); var flush = function () { var parent, fn; if (IS_NODE$2 && (parent = process$2.domain)) parent.exit(); while (fn = queue.get()) try { fn(); } catch (error) { if (queue.head) notify$1(); throw error; } if (parent) parent.enter(); }; // browsers with MutationObserver, except iOS - https://github.com/zloirock/core-js/issues/339 // also except WebOS Webkit https://github.com/zloirock/core-js/issues/898 if (!IS_IOS && !IS_NODE$2 && !IS_WEBOS_WEBKIT && MutationObserver && document$2) { toggle = true; node = document$2.createTextNode(''); new MutationObserver(flush).observe(node, { characterData: true }); notify$1 = function () { node.data = toggle = !toggle; }; // environments with maybe non-completely correct, but existent Promise } else if (!IS_IOS_PEBBLE && Promise$1 && Promise$1.resolve) { // Promise.resolve without an argument throws an error in LG WebOS 2 promise$4 = Promise$1.resolve(undefined); // workaround of WebKit ~ iOS Safari 10.1 bug promise$4.constructor = Promise$1; then = bind$2(promise$4.then, promise$4); notify$1 = function () { then(flush); }; // Node.js without promises } else if (IS_NODE$2) { notify$1 = function () { process$2.nextTick(flush); }; // for other environments - macrotask based on: // - setImmediate // - MessageChannel // - window.postMessage // - onreadystatechange // - setTimeout } else { // `webpack` dev server bug on IE global methods - use bind(fn, global) macrotask = bind$2(macrotask, global$5); notify$1 = function () { macrotask(flush); }; } microtask$1 = function (fn) { if (!queue.head) notify$1(); queue.add(fn); }; } var microtask_1 = microtask$1; var hostReportErrors$1 = function (a, b) { try { // eslint-disable-next-line no-console -- safe arguments.length == 1 ? console.error(a) : console.error(a, b); } catch (error) {/* empty */} }; var perform$6 = function (exec) { try { return { error: false, value: exec() }; } catch (error) { return { error: true, value: error }; } }; var global$4 = global$n; var promiseNativeConstructor = global$4.Promise; /* global Deno -- Deno case */ var engineIsDeno = typeof Deno == 'object' && Deno && typeof Deno.version == 'object'; var IS_DENO$1 = engineIsDeno; var IS_NODE$1 = engineIsNode; var engineIsBrowser = !IS_DENO$1 && !IS_NODE$1 && typeof window == 'object' && typeof document == 'object'; var global$3 = global$n; var NativePromiseConstructor$5 = promiseNativeConstructor; var isCallable$3 = isCallable$m; var isForced = isForced_1; var inspectSource = inspectSource$2; var wellKnownSymbol = wellKnownSymbol$o; var IS_BROWSER = engineIsBrowser; var IS_DENO = engineIsDeno; var V8_VERSION = engineV8Version; var NativePromisePrototype$2 = NativePromiseConstructor$5 && NativePromiseConstructor$5.prototype; var SPECIES = wellKnownSymbol('species'); var SUBCLASSING = false; var NATIVE_PROMISE_REJECTION_EVENT$1 = isCallable$3(global$3.PromiseRejectionEvent); var FORCED_PROMISE_CONSTRUCTOR$5 = isForced('Promise', function () { var PROMISE_CONSTRUCTOR_SOURCE = inspectSource(NativePromiseConstructor$5); var GLOBAL_CORE_JS_PROMISE = PROMISE_CONSTRUCTOR_SOURCE !== String(NativePromiseConstructor$5); // V8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables // https://bugs.chromium.org/p/chromium/issues/detail?id=830565 // We can't detect it synchronously, so just check versions if (!GLOBAL_CORE_JS_PROMISE && V8_VERSION === 66) return true; // We need Promise#{ catch, finally } in the pure version for preventing prototype pollution if (!(NativePromisePrototype$2['catch'] && NativePromisePrototype$2['finally'])) return true; // We can't use @@species feature detection in V8 since it causes // deoptimization and performance degradation // https://github.com/zloirock/core-js/issues/679 if (!V8_VERSION || V8_VERSION < 51 || !/native code/.test(PROMISE_CONSTRUCTOR_SOURCE)) { // Detect correctness of subclassing with @@species support var promise = new NativePromiseConstructor$5(function (resolve) { resolve(1); }); var FakePromise = function (exec) { exec(function () {/* empty */}, function () {/* empty */}); }; var constructor = promise.constructor = {}; constructor[SPECIES] = FakePromise; SUBCLASSING = promise.then(function () {/* empty */}) instanceof FakePromise; if (!SUBCLASSING) return true; // Unhandled rejections tracking support, NodeJS Promise without it fails @@species test } return !GLOBAL_CORE_JS_PROMISE && (IS_BROWSER || IS_DENO) && !NATIVE_PROMISE_REJECTION_EVENT$1; }); var promiseConstructorDetection = { CONSTRUCTOR: FORCED_PROMISE_CONSTRUCTOR$5, REJECTION_EVENT: NATIVE_PROMISE_REJECTION_EVENT$1, SUBCLASSING: SUBCLASSING }; var newPromiseCapability$2 = {}; var aCallable$6 = aCallable$e; var $TypeError = TypeError; var PromiseCapability = function (C) { var resolve, reject; this.promise = new C(function ($$resolve, $$reject) { if (resolve !== undefined || reject !== undefined) throw $TypeError('Bad Promise constructor'); resolve = $$resolve; reject = $$reject; }); this.resolve = aCallable$6(resolve); this.reject = aCallable$6(reject); }; // `NewPromiseCapability` abstract operation // https://tc39.es/ecma262/#sec-newpromisecapability newPromiseCapability$2.f = function (C) { return new PromiseCapability(C); }; var $$d = _export; var IS_NODE = engineIsNode; var global$2 = global$n; var call$5 = functionCall; var defineBuiltIn$1 = defineBuiltIn$6; var setToStringTag$1 = setToStringTag$7; var setSpecies$1 = setSpecies$2; var aCallable$5 = aCallable$e; var isCallable$2 = isCallable$m; var isObject$4 = isObject$i; var anInstance$2 = anInstance$3; var speciesConstructor$1 = speciesConstructor$2; var task = task$1.set; var microtask = microtask_1; var hostReportErrors = hostReportErrors$1; var perform$5 = perform$6; var Queue$1 = queue$1; var InternalStateModule$2 = internalState; var NativePromiseConstructor$4 = promiseNativeConstructor; var PromiseConstructorDetection = promiseConstructorDetection; var newPromiseCapabilityModule$6 = newPromiseCapability$2; var PROMISE = 'Promise'; var FORCED_PROMISE_CONSTRUCTOR$4 = PromiseConstructorDetection.CONSTRUCTOR; var NATIVE_PROMISE_REJECTION_EVENT = PromiseConstructorDetection.REJECTION_EVENT; var getInternalPromiseState = InternalStateModule$2.getterFor(PROMISE); var setInternalState$2 = InternalStateModule$2.set; var NativePromisePrototype$1 = NativePromiseConstructor$4 && NativePromiseConstructor$4.prototype; var PromiseConstructor = NativePromiseConstructor$4; var PromisePrototype = NativePromisePrototype$1; var TypeError$1 = global$2.TypeError; var document$1 = global$2.document; var process$1 = global$2.process; var newPromiseCapability$1 = newPromiseCapabilityModule$6.f; var newGenericPromiseCapability = newPromiseCapability$1; var DISPATCH_EVENT = !!(document$1 && document$1.createEvent && global$2.dispatchEvent); var UNHANDLED_REJECTION = 'unhandledrejection'; var REJECTION_HANDLED = 'rejectionhandled'; var PENDING = 0; var FULFILLED = 1; var REJECTED = 2; var HANDLED = 1; var UNHANDLED = 2; var Internal, OwnPromiseCapability, PromiseWrapper; // helpers var isThenable = function (it) { var then; return isObject$4(it) && isCallable$2(then = it.then) ? then : false; }; var callReaction = function (reaction, state) { var value = state.value; var ok = state.state == FULFILLED; var handler = ok ? reaction.ok : reaction.fail; var resolve = reaction.resolve; var reject = reaction.reject; var domain = reaction.domain; var result, then, exited; try { if (handler) { if (!ok) { if (state.rejection === UNHANDLED) onHandleUnhandled(state); state.rejection = HANDLED; } if (handler === true) result = value;else { if (domain) domain.enter(); result = handler(value); // can throw if (domain) { domain.exit(); exited = true; } } if (result === reaction.promise) { reject(TypeError$1('Promise-chain cycle')); } else if (then = isThenable(result)) { call$5(then, result, resolve, reject); } else resolve(result); } else reject(value); } catch (error) { if (domain && !exited) domain.exit(); reject(error); } }; var notify = function (state, isReject) { if (state.notified) return; state.notified = true; microtask(function () { var reactions = state.reactions; var reaction; while (reaction = reactions.get()) { callReaction(reaction, state); } state.notified = false; if (isReject && !state.rejection) onUnhandled(state); }); }; var dispatchEvent = function (name, promise, reason) { var event, handler; if (DISPATCH_EVENT) { event = document$1.createEvent('Event'); event.promise = promise; event.reason = reason; event.initEvent(name, false, true); global$2.dispatchEvent(event); } else event = { promise: promise, reason: reason }; if (!NATIVE_PROMISE_REJECTION_EVENT && (handler = global$2['on' + name])) handler(event);else if (name === UNHANDLED_REJECTION) hostReportErrors('Unhandled promise rejection', reason); }; var onUnhandled = function (state) { call$5(task, global$2, function () { var promise = state.facade; var value = state.value; var IS_UNHANDLED = isUnhandled(state); var result; if (IS_UNHANDLED) { result = perform$5(function () { if (IS_NODE) { process$1.emit('unhandledRejection', value, promise); } else dispatchEvent(UNHANDLED_REJECTION, promise, value); }); // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should state.rejection = IS_NODE || isUnhandled(state) ? UNHANDLED : HANDLED; if (result.error) throw result.value; } }); }; var isUnhandled = function (state) { return state.rejection !== HANDLED && !state.parent; }; var onHandleUnhandled = function (state) { call$5(task, global$2, function () { var promise = state.facade; if (IS_NODE) { process$1.emit('rejectionHandled', promise); } else dispatchEvent(REJECTION_HANDLED, promise, state.value); }); }; var bind$1 = function (fn, state, unwrap) { return function (value) { fn(state, value, unwrap); }; }; var internalReject = function (state, value, unwrap) { if (state.done) return; state.done = true; if (unwrap) state = unwrap; state.value = value; state.state = REJECTED; notify(state, true); }; var internalResolve = function (state, value, unwrap) { if (state.done) return; state.done = true; if (unwrap) state = unwrap; try { if (state.facade === value) throw TypeError$1("Promise can't be resolved itself"); var then = isThenable(value); if (then) { microtask(function () { var wrapper = { done: false }; try { call$5(then, value, bind$1(internalResolve, wrapper, state), bind$1(internalReject, wrapper, state)); } catch (error) { internalReject(wrapper, error, state); } }); } else { state.value = value; state.state = FULFILLED; notify(state, false); } } catch (error) { internalReject({ done: false }, error, state); } }; // constructor polyfill if (FORCED_PROMISE_CONSTRUCTOR$4) { // 25.4.3.1 Promise(executor) PromiseConstructor = function Promise(executor) { anInstance$2(this, PromisePrototype); aCallable$5(executor); call$5(Internal, this); var state = getInternalPromiseState(this); try { executor(bind$1(internalResolve, state), bind$1(internalReject, state)); } catch (error) { internalReject(state, error); } }; PromisePrototype = PromiseConstructor.prototype; // eslint-disable-next-line no-unused-vars -- required for `.length` Internal = function Promise(executor) { setInternalState$2(this, { type: PROMISE, done: false, notified: false, parent: false, reactions: new Queue$1(), rejection: false, state: PENDING, value: undefined }); }; // `Promise.prototype.then` method // https://tc39.es/ecma262/#sec-promise.prototype.then Internal.prototype = defineBuiltIn$1(PromisePrototype, 'then', function then(onFulfilled, onRejected) { var state = getInternalPromiseState(this); var reaction = newPromiseCapability$1(speciesConstructor$1(this, PromiseConstructor)); state.parent = true; reaction.ok = isCallable$2(onFulfilled) ? onFulfilled : true; reaction.fail = isCallable$2(onRejected) && onRejected; reaction.domain = IS_NODE ? process$1.domain : undefined; if (state.state == PENDING) state.reactions.add(reaction);else microtask(function () { callReaction(reaction, state); }); return reaction.promise; }); OwnPromiseCapability = function () { var promise = new Internal(); var state = getInternalPromiseState(promise); this.promise = promise; this.resolve = bind$1(internalResolve, state); this.reject = bind$1(internalReject, state); }; newPromiseCapabilityModule$6.f = newPromiseCapability$1 = function (C) { return C === PromiseConstructor || C === PromiseWrapper ? new OwnPromiseCapability(C) : newGenericPromiseCapability(C); }; } $$d({ global: true, constructor: true, wrap: true, forced: FORCED_PROMISE_CONSTRUCTOR$4 }, { Promise: PromiseConstructor }); setToStringTag$1(PromiseConstructor, PROMISE, false, true); setSpecies$1(PROMISE); var NativePromiseConstructor$3 = promiseNativeConstructor; var checkCorrectnessOfIteration = checkCorrectnessOfIteration$2; var FORCED_PROMISE_CONSTRUCTOR$3 = promiseConstructorDetection.CONSTRUCTOR; var promiseStaticsIncorrectIteration = FORCED_PROMISE_CONSTRUCTOR$3 || !checkCorrectnessOfIteration(function (iterable) { NativePromiseConstructor$3.all(iterable).then(undefined, function () {/* empty */}); }); var $$c = _export; var call$4 = functionCall; var aCallable$4 = aCallable$e; var newPromiseCapabilityModule$5 = newPromiseCapability$2; var perform$4 = perform$6; var iterate$5 = iterate$7; var PROMISE_STATICS_INCORRECT_ITERATION$3 = promiseStaticsIncorrectIteration; // `Promise.all` method // https://tc39.es/ecma262/#sec-promise.all $$c({ target: 'Promise', stat: true, forced: PROMISE_STATICS_INCORRECT_ITERATION$3 }, { all: function all(iterable) { var C = this; var capability = newPromiseCapabilityModule$5.f(C); var resolve = capability.resolve; var reject = capability.reject; var result = perform$4(function () { var $promiseResolve = aCallable$4(C.resolve); var values = []; var counter = 0; var remaining = 1; iterate$5(iterable, function (promise) { var index = counter++; var alreadyCalled = false; remaining++; call$4($promiseResolve, C, promise).then(function (value) { if (alreadyCalled) return; alreadyCalled = true; values[index] = value; --remaining || resolve(values); }, reject); }); --remaining || resolve(values); }); if (result.error) reject(result.value); return capability.promise; } }); var $$b = _export; var FORCED_PROMISE_CONSTRUCTOR$2 = promiseConstructorDetection.CONSTRUCTOR; var NativePromiseConstructor$2 = promiseNativeConstructor; NativePromiseConstructor$2 && NativePromiseConstructor$2.prototype; // `Promise.prototype.catch` method // https://tc39.es/ecma262/#sec-promise.prototype.catch $$b({ target: 'Promise', proto: true, forced: FORCED_PROMISE_CONSTRUCTOR$2, real: true }, { 'catch': function (onRejected) { return this.then(undefined, onRejected); } }); var $$a = _export; var call$3 = functionCall; var aCallable$3 = aCallable$e; var newPromiseCapabilityModule$4 = newPromiseCapability$2; var perform$3 = perform$6; var iterate$4 = iterate$7; var PROMISE_STATICS_INCORRECT_ITERATION$2 = promiseStaticsIncorrectIteration; // `Promise.race` method // https://tc39.es/ecma262/#sec-promise.race $$a({ target: 'Promise', stat: true, forced: PROMISE_STATICS_INCORRECT_ITERATION$2 }, { race: function race(iterable) { var C = this; var capability = newPromiseCapabilityModule$4.f(C); var reject = capability.reject; var result = perform$3(function () { var $promiseResolve = aCallable$3(C.resolve); iterate$4(iterable, function (promise) { call$3($promiseResolve, C, promise).then(capability.resolve, reject); }); }); if (result.error) reject(result.value); return capability.promise; } }); var $$9 = _export; var call$2 = functionCall; var newPromiseCapabilityModule$3 = newPromiseCapability$2; var FORCED_PROMISE_CONSTRUCTOR$1 = promiseConstructorDetection.CONSTRUCTOR; // `Promise.reject` method // https://tc39.es/ecma262/#sec-promise.reject $$9({ target: 'Promise', stat: true, forced: FORCED_PROMISE_CONSTRUCTOR$1 }, { reject: function reject(r) { var capability = newPromiseCapabilityModule$3.f(this); call$2(capability.reject, undefined, r); return capability.promise; } }); var anObject = anObject$d; var isObject$3 = isObject$i; var newPromiseCapability = newPromiseCapability$2; var promiseResolve$2 = function (C, x) { anObject(C); if (isObject$3(x) && x.constructor === C) return x; var promiseCapability = newPromiseCapability.f(C); var resolve = promiseCapability.resolve; resolve(x); return promiseCapability.promise; }; var $$8 = _export; var getBuiltIn$2 = getBuiltIn$f; var IS_PURE = isPure; var NativePromiseConstructor$1 = promiseNativeConstructor; var FORCED_PROMISE_CONSTRUCTOR = promiseConstructorDetection.CONSTRUCTOR; var promiseResolve$1 = promiseResolve$2; var PromiseConstructorWrapper = getBuiltIn$2('Promise'); var CHECK_WRAPPER = !FORCED_PROMISE_CONSTRUCTOR; // `Promise.resolve` method // https://tc39.es/ecma262/#sec-promise.resolve $$8({ target: 'Promise', stat: true, forced: IS_PURE }, { resolve: function resolve(x) { return promiseResolve$1(CHECK_WRAPPER && this === PromiseConstructorWrapper ? NativePromiseConstructor$1 : this, x); } }); var $$7 = _export; var call$1 = functionCall; var aCallable$2 = aCallable$e; var newPromiseCapabilityModule$2 = newPromiseCapability$2; var perform$2 = perform$6; var iterate$3 = iterate$7; var PROMISE_STATICS_INCORRECT_ITERATION$1 = promiseStaticsIncorrectIteration; // `Promise.allSettled` method // https://tc39.es/ecma262/#sec-promise.allsettled $$7({ target: 'Promise', stat: true, forced: PROMISE_STATICS_INCORRECT_ITERATION$1 }, { allSettled: function allSettled(iterable) { var C = this; var capability = newPromiseCapabilityModule$2.f(C); var resolve = capability.resolve; var reject = capability.reject; var result = perform$2(function () { var promiseResolve = aCallable$2(C.resolve); var values = []; var counter = 0; var remaining = 1; iterate$3(iterable, function (promise) { var index = counter++; var alreadyCalled = false; remaining++; call$1(promiseResolve, C, promise).then(function (value) { if (alreadyCalled) return; alreadyCalled = true; values[index] = { status: 'fulfilled', value: value }; --remaining || resolve(values); }, function (error) { if (alreadyCalled) return; alreadyCalled = true; values[index] = { status: 'rejected', reason: error }; --remaining || resolve(values); }); }); --remaining || resolve(values); }); if (result.error) reject(result.value); return capability.promise; } }); var $$6 = _export; var call = functionCall; var aCallable$1 = aCallable$e; var getBuiltIn$1 = getBuiltIn$f; var newPromiseCapabilityModule$1 = newPromiseCapability$2; var perform$1 = perform$6; var iterate$2 = iterate$7; var PROMISE_STATICS_INCORRECT_ITERATION = promiseStaticsIncorrectIteration; var PROMISE_ANY_ERROR = 'No one promise resolved'; // `Promise.any` method // https://tc39.es/ecma262/#sec-promise.any $$6({ target: 'Promise', stat: true, forced: PROMISE_STATICS_INCORRECT_ITERATION }, { any: function any(iterable) { var C = this; var AggregateError = getBuiltIn$1('AggregateError'); var capability = newPromiseCapabilityModule$1.f(C); var resolve = capability.resolve; var reject = capability.reject; var result = perform$1(function () { var promiseResolve = aCallable$1(C.resolve); var errors = []; var counter = 0; var remaining = 1; var alreadyResolved = false; iterate$2(iterable, function (promise) { var index = counter++; var alreadyRejected = false; remaining++; call(promiseResolve, C, promise).then(function (value) { if (alreadyRejected || alreadyResolved) return; alreadyResolved = true; resolve(value); }, function (error) { if (alreadyRejected || alreadyResolved) return; alreadyRejected = true; errors[index] = error; --remaining || reject(new AggregateError(errors, PROMISE_ANY_ERROR)); }); }); --remaining || reject(new AggregateError(errors, PROMISE_ANY_ERROR)); }); if (result.error) reject(result.value); return capability.promise; } }); var $$5 = _export; var NativePromiseConstructor = promiseNativeConstructor; var fails$5 = fails$w; var getBuiltIn = getBuiltIn$f; var isCallable$1 = isCallable$m; var speciesConstructor = speciesConstructor$2; var promiseResolve = promiseResolve$2; var NativePromisePrototype = NativePromiseConstructor && NativePromiseConstructor.prototype; // Safari bug https://bugs.webkit.org/show_bug.cgi?id=200829 var NON_GENERIC = !!NativePromiseConstructor && fails$5(function () { // eslint-disable-next-line unicorn/no-thenable -- required for testing NativePromisePrototype['finally'].call({ then: function () {/* empty */} }, function () {/* empty */}); }); // `Promise.prototype.finally` method // https://tc39.es/ecma262/#sec-promise.prototype.finally $$5({ target: 'Promise', proto: true, real: true, forced: NON_GENERIC }, { 'finally': function (onFinally) { var C = speciesConstructor(this, getBuiltIn('Promise')); var isFunction = isCallable$1(onFinally); return this.then(isFunction ? function (x) { return promiseResolve(C, onFinally()).then(function () { return x; }); } : onFinally, isFunction ? function (e) { return promiseResolve(C, onFinally()).then(function () { throw e; }); } : onFinally); } }); var path$2 = path$r; var promise$3 = path$2.Promise; var parent$e = promise$3; var promise$2 = parent$e; var parent$d = promise$2; var promise$1 = parent$d; // TODO: Remove from `core-js@4` var $$4 = _export; var newPromiseCapabilityModule = newPromiseCapability$2; var perform = perform$6; // `Promise.try` method // https://github.com/tc39/proposal-promise-try $$4({ target: 'Promise', stat: true, forced: true }, { 'try': function (callbackfn) { var promiseCapability = newPromiseCapabilityModule.f(this); var result = perform(callbackfn); (result.error ? promiseCapability.reject : promiseCapability.resolve)(result.value); return promiseCapability.promise; } }); var parent$c = promise$1; // TODO: Remove from `core-js@4` var promise = parent$c; (function (module) { module.exports = promise; })(promise$5); (function (module) { module.exports = promiseExports; })(promise$6); var reverseExports$1 = {}; var reverse$3 = { get exports() { return reverseExports$1; }, set exports(v) { reverseExports$1 = v; } }; var reverseExports = {}; var reverse$2 = { get exports() { return reverseExports; }, set exports(v) { reverseExports = v; } }; var parent$b = reverse$4; var reverse$1 = parent$b; var parent$a = reverse$1; var reverse = parent$a; (function (module) { module.exports = reverse; })(reverse$2); (function (module) { module.exports = reverseExports; })(reverse$3); (function (module) { var _typeof = _typeofExports["default"]; var _Object$defineProperty = definePropertyExports$3; var _Symbol = symbolExports$2; var _Object$create = createExports$1; var _Object$getPrototypeOf = getPrototypeOfExports$1; var _forEachInstanceProperty = forEachExports$1; var _Object$setPrototypeOf = setPrototypeOfExports$1; var _Promise = promiseExports$1; var _reverseInstanceProperty = reverseExports$1; var _sliceInstanceProperty = sliceExports$2; function _regeneratorRuntime() { module.exports = _regeneratorRuntime = function _regeneratorRuntime() { return exports; }, module.exports.__esModule = true, module.exports["default"] = module.exports; var exports = {}, Op = Object.prototype, hasOwn = Op.hasOwnProperty, defineProperty = _Object$defineProperty || function (obj, key, desc) { obj[key] = desc.value; }, $Symbol = "function" == typeof _Symbol ? _Symbol : {}, iteratorSymbol = $Symbol.iterator || "@@iterator", asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator", toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag"; function define(obj, key, value) { return _Object$defineProperty(obj, key, { value: value, enumerable: !0, configurable: !0, writable: !0 }), obj[key]; } try { define({}, ""); } catch (err) { define = function define(obj, key, value) { return obj[key] = value; }; } function wrap(innerFn, outerFn, self, tryLocsList) { var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator, generator = _Object$create(protoGenerator.prototype), context = new Context(tryLocsList || []); return defineProperty(generator, "_invoke", { value: makeInvokeMethod(innerFn, self, context) }), generator; } function tryCatch(fn, obj, arg) { try { return { type: "normal", arg: fn.call(obj, arg) }; } catch (err) { return { type: "throw", arg: err }; } } exports.wrap = wrap; var ContinueSentinel = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var IteratorPrototype = {}; define(IteratorPrototype, iteratorSymbol, function () { return this; }); var getProto = _Object$getPrototypeOf, NativeIteratorPrototype = getProto && getProto(getProto(values([]))); NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol) && (IteratorPrototype = NativeIteratorPrototype); var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = _Object$create(IteratorPrototype); function defineIteratorMethods(prototype) { var _context; _forEachInstanceProperty(_context = ["next", "throw", "return"]).call(_context, function (method) { define(prototype, method, function (arg) { return this._invoke(method, arg); }); }); } function AsyncIterator(generator, PromiseImpl) { function invoke(method, arg, resolve, reject) { var record = tryCatch(generator[method], generator, arg); if ("throw" !== record.type) { var result = record.arg, value = result.value; return value && "object" == _typeof(value) && hasOwn.call(value, "__await") ? PromiseImpl.resolve(value.__await).then(function (value) { invoke("next", value, resolve, reject); }, function (err) { invoke("throw", err, resolve, reject); }) : PromiseImpl.resolve(value).then(function (unwrapped) { result.value = unwrapped, resolve(result); }, function (error) { return invoke("throw", error, resolve, reject); }); } reject(record.arg); } var previousPromise; defineProperty(this, "_invoke", { value: function value(method, arg) { function callInvokeWithMethodAndArg() { return new PromiseImpl(function (resolve, reject) { invoke(method, arg, resolve, reject); }); } return previousPromise = previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(innerFn, self, context) { var state = "suspendedStart"; return function (method, arg) { if ("executing" === state) throw new Error("Generator is already running"); if ("completed" === state) { if ("throw" === method) throw arg; return doneResult(); } for (context.method = method, context.arg = arg;;) { var delegate = context.delegate; if (delegate) { var delegateResult = maybeInvokeDelegate(delegate, context); if (delegateResult) { if (delegateResult === ContinueSentinel) continue; return delegateResult; } } if ("next" === context.method) context.sent = context._sent = context.arg;else if ("throw" === context.method) { if ("suspendedStart" === state) throw state = "completed", context.arg; context.dispatchException(context.arg); } else "return" === context.method && context.abrupt("return", context.arg); state = "executing"; var record = tryCatch(innerFn, self, context); if ("normal" === record.type) { if (state = context.done ? "completed" : "suspendedYield", record.arg === ContinueSentinel) continue; return { value: record.arg, done: context.done }; } "throw" === record.type && (state = "completed", context.method = "throw", context.arg = record.arg); } }; } function maybeInvokeDelegate(delegate, context) { var methodName = context.method, method = delegate.iterator[methodName]; if (undefined === method) return context.delegate = null, "throw" === methodName && delegate.iterator["return"] && (context.method = "return", context.arg = undefined, maybeInvokeDelegate(delegate, context), "throw" === context.method) || "return" !== methodName && (context.method = "throw", context.arg = new TypeError("The iterator does not provide a '" + methodName + "' method")), ContinueSentinel; var record = tryCatch(method, delegate.iterator, context.arg); if ("throw" === record.type) return context.method = "throw", context.arg = record.arg, context.delegate = null, ContinueSentinel; var info = record.arg; return info ? info.done ? (context[delegate.resultName] = info.value, context.next = delegate.nextLoc, "return" !== context.method && (context.method = "next", context.arg = undefined), context.delegate = null, ContinueSentinel) : info : (context.method = "throw", context.arg = new TypeError("iterator result is not an object"), context.delegate = null, ContinueSentinel); } function pushTryEntry(locs) { var entry = { tryLoc: locs[0] }; 1 in locs && (entry.catchLoc = locs[1]), 2 in locs && (entry.finallyLoc = locs[2], entry.afterLoc = locs[3]), this.tryEntries.push(entry); } function resetTryEntry(entry) { var record = entry.completion || {}; record.type = "normal", delete record.arg, entry.completion = record; } function Context(tryLocsList) { this.tryEntries = [{ tryLoc: "root" }], _forEachInstanceProperty(tryLocsList).call(tryLocsList, pushTryEntry, this), this.reset(!0); } function values(iterable) { if (iterable) { var iteratorMethod = iterable[iteratorSymbol]; if (iteratorMethod) return iteratorMethod.call(iterable); if ("function" == typeof iterable.next) return iterable; if (!isNaN(iterable.length)) { var i = -1, next = function next() { for (; ++i < iterable.length;) if (hasOwn.call(iterable, i)) return next.value = iterable[i], next.done = !1, next; return next.value = undefined, next.done = !0, next; }; return next.next = next; } } return { next: doneResult }; } function doneResult() { return { value: undefined, done: !0 }; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, defineProperty(Gp, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), defineProperty(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"), exports.isGeneratorFunction = function (genFun) { var ctor = "function" == typeof genFun && genFun.constructor; return !!ctor && (ctor === GeneratorFunction || "GeneratorFunction" === (ctor.displayName || ctor.name)); }, exports.mark = function (genFun) { return _Object$setPrototypeOf ? _Object$setPrototypeOf(genFun, GeneratorFunctionPrototype) : (genFun.__proto__ = GeneratorFunctionPrototype, define(genFun, toStringTagSymbol, "GeneratorFunction")), genFun.prototype = _Object$create(Gp), genFun; }, exports.awrap = function (arg) { return { __await: arg }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, asyncIteratorSymbol, function () { return this; }), exports.AsyncIterator = AsyncIterator, exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) { void 0 === PromiseImpl && (PromiseImpl = _Promise); var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl); return exports.isGeneratorFunction(outerFn) ? iter : iter.next().then(function (result) { return result.done ? result.value : iter.next(); }); }, defineIteratorMethods(Gp), define(Gp, toStringTagSymbol, "Generator"), define(Gp, iteratorSymbol, function () { return this; }), define(Gp, "toString", function () { return "[object Generator]"; }), exports.keys = function (val) { var object = Object(val), keys = []; for (var key in object) keys.push(key); return _reverseInstanceProperty(keys).call(keys), function next() { for (; keys.length;) { var key = keys.pop(); if (key in object) return next.value = key, next.done = !1, next; } return next.done = !0, next; }; }, exports.values = values, Context.prototype = { constructor: Context, reset: function reset(skipTempReset) { var _context2; if (this.prev = 0, this.next = 0, this.sent = this._sent = undefined, this.done = !1, this.delegate = null, this.method = "next", this.arg = undefined, _forEachInstanceProperty(_context2 = this.tryEntries).call(_context2, resetTryEntry), !skipTempReset) for (var name in this) "t" === name.charAt(0) && hasOwn.call(this, name) && !isNaN(+_sliceInstanceProperty(name).call(name, 1)) && (this[name] = undefined); }, stop: function stop() { this.done = !0; var rootRecord = this.tryEntries[0].completion; if ("throw" === rootRecord.type) throw rootRecord.arg; return this.rval; }, dispatchException: function dispatchException(exception) { if (this.done) throw exception; var context = this; function handle(loc, caught) { return record.type = "throw", record.arg = exception, context.next = loc, caught && (context.method = "next", context.arg = undefined), !!caught; } for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i], record = entry.completion; if ("root" === entry.tryLoc) return handle("end"); if (entry.tryLoc <= this.prev) { var hasCatch = hasOwn.call(entry, "catchLoc"), hasFinally = hasOwn.call(entry, "finallyLoc"); if (hasCatch && hasFinally) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } else if (hasCatch) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); } else { if (!hasFinally) throw new Error("try statement without catch or finally"); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } } } }, abrupt: function abrupt(type, arg) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) { var finallyEntry = entry; break; } } finallyEntry && ("break" === type || "continue" === type) && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc && (finallyEntry = null); var record = finallyEntry ? finallyEntry.completion : {}; return record.type = type, record.arg = arg, finallyEntry ? (this.method = "next", this.next = finallyEntry.finallyLoc, ContinueSentinel) : this.complete(record); }, complete: function complete(record, afterLoc) { if ("throw" === record.type) throw record.arg; return "break" === record.type || "continue" === record.type ? this.next = record.arg : "return" === record.type ? (this.rval = this.arg = record.arg, this.method = "return", this.next = "end") : "normal" === record.type && afterLoc && (this.next = afterLoc), ContinueSentinel; }, finish: function finish(finallyLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.finallyLoc === finallyLoc) return this.complete(entry.completion, entry.afterLoc), resetTryEntry(entry), ContinueSentinel; } }, "catch": function _catch(tryLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc === tryLoc) { var record = entry.completion; if ("throw" === record.type) { var thrown = record.arg; resetTryEntry(entry); } return thrown; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(iterable, resultName, nextLoc) { return this.delegate = { iterator: values(iterable), resultName: resultName, nextLoc: nextLoc }, "next" === this.method && (this.arg = undefined), ContinueSentinel; } }, exports; } module.exports = _regeneratorRuntime, module.exports.__esModule = true, module.exports["default"] = module.exports; })(regeneratorRuntime$1); // TODO(Babel 8): Remove this file. var runtime = regeneratorRuntimeExports(); var regenerator = runtime; // Copied from https://github.com/facebook/regenerator/blob/main/packages/runtime/runtime.js#L736= try { regeneratorRuntime = runtime; } catch (accidentalStrictMode) { if (typeof globalThis === "object") { globalThis.regeneratorRuntime = runtime; } else { Function("r", "regeneratorRuntime = r")(runtime); } } var mapExports = {}; var map$2 = { get exports() { return mapExports; }, set exports(v) { mapExports = v; } }; var internalMetadataExports = {}; var internalMetadata = { get exports() { return internalMetadataExports; }, set exports(v) { internalMetadataExports = v; } }; // FF26- bug: ArrayBuffers are non-extensible, but Object.isExtensible does not report it var fails$4 = fails$w; var arrayBufferNonExtensible = fails$4(function () { if (typeof ArrayBuffer == 'function') { var buffer = new ArrayBuffer(8); // eslint-disable-next-line es/no-object-isextensible, es/no-object-defineproperty -- safe if (Object.isExtensible(buffer)) Object.defineProperty(buffer, 'a', { value: 8 }); } }); var fails$3 = fails$w; var isObject$2 = isObject$i; var classof$3 = classofRaw$2; var ARRAY_BUFFER_NON_EXTENSIBLE = arrayBufferNonExtensible; // eslint-disable-next-line es/no-object-isextensible -- safe var $isExtensible = Object.isExtensible; var FAILS_ON_PRIMITIVES = fails$3(function () { $isExtensible(1); }); // `Object.isExtensible` method // https://tc39.es/ecma262/#sec-object.isextensible var objectIsExtensible = FAILS_ON_PRIMITIVES || ARRAY_BUFFER_NON_EXTENSIBLE ? function isExtensible(it) { if (!isObject$2(it)) return false; if (ARRAY_BUFFER_NON_EXTENSIBLE && classof$3(it) == 'ArrayBuffer') return false; return $isExtensible ? $isExtensible(it) : true; } : $isExtensible; var fails$2 = fails$w; var freezing = !fails$2(function () { // eslint-disable-next-line es/no-object-isextensible, es/no-object-preventextensions -- required for testing return Object.isExtensible(Object.preventExtensions({})); }); var $$3 = _export; var uncurryThis$1 = functionUncurryThis; var hiddenKeys = hiddenKeys$6; var isObject$1 = isObject$i; var hasOwn$3 = hasOwnProperty_1; var defineProperty$1 = objectDefineProperty.f; var getOwnPropertyNamesModule = objectGetOwnPropertyNames; var getOwnPropertyNamesExternalModule = objectGetOwnPropertyNamesExternal; var isExtensible = objectIsExtensible; var uid = uid$4; var FREEZING = freezing; var REQUIRED = false; var METADATA = uid('meta'); var id = 0; var setMetadata = function (it) { defineProperty$1(it, METADATA, { value: { objectID: 'O' + id++, // object ID weakData: {} // weak collections IDs } }); }; var fastKey$1 = function (it, create) { // return a primitive with prefix if (!isObject$1(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it; if (!hasOwn$3(it, METADATA)) { // can't set metadata to uncaught frozen object if (!isExtensible(it)) return 'F'; // not necessary to add metadata if (!create) return 'E'; // add missing metadata setMetadata(it); // return object ID } return it[METADATA].objectID; }; var getWeakData = function (it, create) { if (!hasOwn$3(it, METADATA)) { // can't set metadata to uncaught frozen object if (!isExtensible(it)) return true; // not necessary to add metadata if (!create) return false; // add missing metadata setMetadata(it); // return the store of weak collections IDs } return it[METADATA].weakData; }; // add metadata on freeze-family methods calling var onFreeze = function (it) { if (FREEZING && REQUIRED && isExtensible(it) && !hasOwn$3(it, METADATA)) setMetadata(it); return it; }; var enable = function () { meta.enable = function () {/* empty */}; REQUIRED = true; var getOwnPropertyNames = getOwnPropertyNamesModule.f; var splice = uncurryThis$1([].splice); var test = {}; test[METADATA] = 1; // prevent exposing of metadata key if (getOwnPropertyNames(test).length) { getOwnPropertyNamesModule.f = function (it) { var result = getOwnPropertyNames(it); for (var i = 0, length = result.length; i < length; i++) { if (result[i] === METADATA) { splice(result, i, 1); break; } } return result; }; $$3({ target: 'Object', stat: true, forced: true }, { getOwnPropertyNames: getOwnPropertyNamesExternalModule.f }); } }; var meta = internalMetadata.exports = { enable: enable, fastKey: fastKey$1, getWeakData: getWeakData, onFreeze: onFreeze }; hiddenKeys[METADATA] = true; var $$2 = _export; var global$1 = global$n; var InternalMetadataModule = internalMetadataExports; var fails$1 = fails$w; var createNonEnumerableProperty = createNonEnumerableProperty$9; var iterate$1 = iterate$7; var anInstance$1 = anInstance$3; var isCallable = isCallable$m; var isObject = isObject$i; var setToStringTag = setToStringTag$7; var defineProperty = objectDefineProperty.f; var forEach = arrayIteration.forEach; var DESCRIPTORS$1 = descriptors; var InternalStateModule$1 = internalState; var setInternalState$1 = InternalStateModule$1.set; var internalStateGetterFor$1 = InternalStateModule$1.getterFor; var collection$2 = function (CONSTRUCTOR_NAME, wrapper, common) { var IS_MAP = CONSTRUCTOR_NAME.indexOf('Map') !== -1; var IS_WEAK = CONSTRUCTOR_NAME.indexOf('Weak') !== -1; var ADDER = IS_MAP ? 'set' : 'add'; var NativeConstructor = global$1[CONSTRUCTOR_NAME]; var NativePrototype = NativeConstructor && NativeConstructor.prototype; var exported = {}; var Constructor; if (!DESCRIPTORS$1 || !isCallable(NativeConstructor) || !(IS_WEAK || NativePrototype.forEach && !fails$1(function () { new NativeConstructor().entries().next(); }))) { // create collection constructor Constructor = common.getConstructor(wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER); InternalMetadataModule.enable(); } else { Constructor = wrapper(function (target, iterable) { setInternalState$1(anInstance$1(target, Prototype), { type: CONSTRUCTOR_NAME, collection: new NativeConstructor() }); if (iterable != undefined) iterate$1(iterable, target[ADDER], { that: target, AS_ENTRIES: IS_MAP }); }); var Prototype = Constructor.prototype; var getInternalState = internalStateGetterFor$1(CONSTRUCTOR_NAME); forEach(['add', 'clear', 'delete', 'forEach', 'get', 'has', 'set', 'keys', 'values', 'entries'], function (KEY) { var IS_ADDER = KEY == 'add' || KEY == 'set'; if (KEY in NativePrototype && !(IS_WEAK && KEY == 'clear')) { createNonEnumerableProperty(Prototype, KEY, function (a, b) { var collection = getInternalState(this).collection; if (!IS_ADDER && IS_WEAK && !isObject(a)) return KEY == 'get' ? undefined : false; var result = collection[KEY](a === 0 ? 0 : a, b); return IS_ADDER ? this : result; }); } }); IS_WEAK || defineProperty(Prototype, 'size', { configurable: true, get: function () { return getInternalState(this).collection.size; } }); } setToStringTag(Constructor, CONSTRUCTOR_NAME, false, true); exported[CONSTRUCTOR_NAME] = Constructor; $$2({ global: true, forced: true }, exported); if (!IS_WEAK) common.setStrong(Constructor, CONSTRUCTOR_NAME, IS_MAP); return Constructor; }; var defineBuiltIn = defineBuiltIn$6; var defineBuiltIns$1 = function (target, src, options) { for (var key in src) { if (options && options.unsafe && target[key]) target[key] = src[key];else defineBuiltIn(target, key, src[key], options); } return target; }; var create = objectCreate; var defineBuiltInAccessor = defineBuiltInAccessor$3; var defineBuiltIns = defineBuiltIns$1; var bind = functionBindContext; var anInstance = anInstance$3; var isNullOrUndefined = isNullOrUndefined$5; var iterate = iterate$7; var defineIterator = iteratorDefine; var createIterResultObject = createIterResultObject$3; var setSpecies = setSpecies$2; var DESCRIPTORS = descriptors; var fastKey = internalMetadataExports.fastKey; var InternalStateModule = internalState; var setInternalState = InternalStateModule.set; var internalStateGetterFor = InternalStateModule.getterFor; var collectionStrong$2 = { getConstructor: function (wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER) { var Constructor = wrapper(function (that, iterable) { anInstance(that, Prototype); setInternalState(that, { type: CONSTRUCTOR_NAME, index: create(null), first: undefined, last: undefined, size: 0 }); if (!DESCRIPTORS) that.size = 0; if (!isNullOrUndefined(iterable)) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP }); }); var Prototype = Constructor.prototype; var getInternalState = internalStateGetterFor(CONSTRUCTOR_NAME); var define = function (that, key, value) { var state = getInternalState(that); var entry = getEntry(that, key); var previous, index; // change existing entry if (entry) { entry.value = value; // create new entry } else { state.last = entry = { index: index = fastKey(key, true), key: key, value: value, previous: previous = state.last, next: undefined, removed: false }; if (!state.first) state.first = entry; if (previous) previous.next = entry; if (DESCRIPTORS) state.size++;else that.size++; // add to index if (index !== 'F') state.index[index] = entry; } return that; }; var getEntry = function (that, key) { var state = getInternalState(that); // fast case var index = fastKey(key); var entry; if (index !== 'F') return state.index[index]; // frozen object case for (entry = state.first; entry; entry = entry.next) { if (entry.key == key) return entry; } }; defineBuiltIns(Prototype, { // `{ Map, Set }.prototype.clear()` methods // https://tc39.es/ecma262/#sec-map.prototype.clear // https://tc39.es/ecma262/#sec-set.prototype.clear clear: function clear() { var that = this; var state = getInternalState(that); var data = state.index; var entry = state.first; while (entry) { entry.removed = true; if (entry.previous) entry.previous = entry.previous.next = undefined; delete data[entry.index]; entry = entry.next; } state.first = state.last = undefined; if (DESCRIPTORS) state.size = 0;else that.size = 0; }, // `{ Map, Set }.prototype.delete(key)` methods // https://tc39.es/ecma262/#sec-map.prototype.delete // https://tc39.es/ecma262/#sec-set.prototype.delete 'delete': function (key) { var that = this; var state = getInternalState(that); var entry = getEntry(that, key); if (entry) { var next = entry.next; var prev = entry.previous; delete state.index[entry.index]; entry.removed = true; if (prev) prev.next = next; if (next) next.previous = prev; if (state.first == entry) state.first = next; if (state.last == entry) state.last = prev; if (DESCRIPTORS) state.size--;else that.size--; } return !!entry; }, // `{ Map, Set }.prototype.forEach(callbackfn, thisArg = undefined)` methods // https://tc39.es/ecma262/#sec-map.prototype.foreach // https://tc39.es/ecma262/#sec-set.prototype.foreach forEach: function forEach(callbackfn /* , that = undefined */) { var state = getInternalState(this); var boundFunction = bind(callbackfn, arguments.length > 1 ? arguments[1] : undefined); var entry; while (entry = entry ? entry.next : state.first) { boundFunction(entry.value, entry.key, this); // revert to the last existing entry while (entry && entry.removed) entry = entry.previous; } }, // `{ Map, Set}.prototype.has(key)` methods // https://tc39.es/ecma262/#sec-map.prototype.has // https://tc39.es/ecma262/#sec-set.prototype.has has: function has(key) { return !!getEntry(this, key); } }); defineBuiltIns(Prototype, IS_MAP ? { // `Map.prototype.get(key)` method // https://tc39.es/ecma262/#sec-map.prototype.get get: function get(key) { var entry = getEntry(this, key); return entry && entry.value; }, // `Map.prototype.set(key, value)` method // https://tc39.es/ecma262/#sec-map.prototype.set set: function set(key, value) { return define(this, key === 0 ? 0 : key, value); } } : { // `Set.prototype.add(value)` method // https://tc39.es/ecma262/#sec-set.prototype.add add: function add(value) { return define(this, value = value === 0 ? 0 : value, value); } }); if (DESCRIPTORS) defineBuiltInAccessor(Prototype, 'size', { configurable: true, get: function () { return getInternalState(this).size; } }); return Constructor; }, setStrong: function (Constructor, CONSTRUCTOR_NAME, IS_MAP) { var ITERATOR_NAME = CONSTRUCTOR_NAME + ' Iterator'; var getInternalCollectionState = internalStateGetterFor(CONSTRUCTOR_NAME); var getInternalIteratorState = internalStateGetterFor(ITERATOR_NAME); // `{ Map, Set }.prototype.{ keys, values, entries, @@iterator }()` methods // https://tc39.es/ecma262/#sec-map.prototype.entries // https://tc39.es/ecma262/#sec-map.prototype.keys // https://tc39.es/ecma262/#sec-map.prototype.values // https://tc39.es/ecma262/#sec-map.prototype-@@iterator // https://tc39.es/ecma262/#sec-set.prototype.entries // https://tc39.es/ecma262/#sec-set.prototype.keys // https://tc39.es/ecma262/#sec-set.prototype.values // https://tc39.es/ecma262/#sec-set.prototype-@@iterator defineIterator(Constructor, CONSTRUCTOR_NAME, function (iterated, kind) { setInternalState(this, { type: ITERATOR_NAME, target: iterated, state: getInternalCollectionState(iterated), kind: kind, last: undefined }); }, function () { var state = getInternalIteratorState(this); var kind = state.kind; var entry = state.last; // revert to the last existing entry while (entry && entry.removed) entry = entry.previous; // get next entry if (!state.target || !(state.last = entry = entry ? entry.next : state.state.first)) { // or finish the iteration state.target = undefined; return createIterResultObject(undefined, true); } // return step by kind if (kind == 'keys') return createIterResultObject(entry.key, false); if (kind == 'values') return createIterResultObject(entry.value, false); return createIterResultObject([entry.key, entry.value], false); }, IS_MAP ? 'entries' : 'values', !IS_MAP, true); // `{ Map, Set }.prototype[@@species]` accessors // https://tc39.es/ecma262/#sec-get-map-@@species // https://tc39.es/ecma262/#sec-get-set-@@species setSpecies(CONSTRUCTOR_NAME); } }; var collection$1 = collection$2; var collectionStrong$1 = collectionStrong$2; // `Map` constructor // https://tc39.es/ecma262/#sec-map-objects collection$1('Map', function (init) { return function Map() { return init(this, arguments.length ? arguments[0] : undefined); }; }, collectionStrong$1); var path$1 = path$r; var map$1 = path$1.Map; var parent$9 = map$1; var map = parent$9; (function (module) { module.exports = map; })(map$2); var _Map = /*@__PURE__*/getDefaultExportFromCjs(mapExports); var someExports = {}; var some$3 = { get exports() { return someExports; }, set exports(v) { someExports = v; } }; var $$1 = _export; var $some = arrayIteration.some; var arrayMethodIsStrict$1 = arrayMethodIsStrict$5; var STRICT_METHOD$1 = arrayMethodIsStrict$1('some'); // `Array.prototype.some` method // https://tc39.es/ecma262/#sec-array.prototype.some $$1({ target: 'Array', proto: true, forced: !STRICT_METHOD$1 }, { some: function some(callbackfn /* , thisArg */) { return $some(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); } }); var entryVirtual$4 = entryVirtual$k; var some$2 = entryVirtual$4('Array').some; var isPrototypeOf$4 = objectIsPrototypeOf; var method$4 = some$2; var ArrayPrototype$4 = Array.prototype; var some$1 = function (it) { var own = it.some; return it === ArrayPrototype$4 || isPrototypeOf$4(ArrayPrototype$4, it) && own === ArrayPrototype$4.some ? method$4 : own; }; var parent$8 = some$1; var some = parent$8; (function (module) { module.exports = some; })(some$3); var _someInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(someExports); var keysExports = {}; var keys$3 = { get exports() { return keysExports; }, set exports(v) { keysExports = v; } }; var entryVirtual$3 = entryVirtual$k; var keys$2 = entryVirtual$3('Array').keys; var parent$7 = keys$2; var keys$1 = parent$7; var classof$2 = classof$e; var hasOwn$2 = hasOwnProperty_1; var isPrototypeOf$3 = objectIsPrototypeOf; var method$3 = keys$1; var ArrayPrototype$3 = Array.prototype; var DOMIterables$2 = { DOMTokenList: true, NodeList: true }; var keys = function (it) { var own = it.keys; return it === ArrayPrototype$3 || isPrototypeOf$3(ArrayPrototype$3, it) && own === ArrayPrototype$3.keys || hasOwn$2(DOMIterables$2, classof$2(it)) ? method$3 : own; }; (function (module) { module.exports = keys; })(keys$3); var _keysInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(keysExports); var sortExports = {}; var sort$3 = { get exports() { return sortExports; }, set exports(v) { sortExports = v; } }; var arraySlice = arraySliceSimple; var floor = Math.floor; var mergeSort = function (array, comparefn) { var length = array.length; var middle = floor(length / 2); return length < 8 ? insertionSort(array, comparefn) : merge(array, mergeSort(arraySlice(array, 0, middle), comparefn), mergeSort(arraySlice(array, middle), comparefn), comparefn); }; var insertionSort = function (array, comparefn) { var length = array.length; var i = 1; var element, j; while (i < length) { j = i; element = array[i]; while (j && comparefn(array[j - 1], element) > 0) { array[j] = array[--j]; } if (j !== i++) array[j] = element; } return array; }; var merge = function (array, left, right, comparefn) { var llength = left.length; var rlength = right.length; var lindex = 0; var rindex = 0; while (lindex < llength || rindex < rlength) { array[lindex + rindex] = lindex < llength && rindex < rlength ? comparefn(left[lindex], right[rindex]) <= 0 ? left[lindex++] : right[rindex++] : lindex < llength ? left[lindex++] : right[rindex++]; } return array; }; var arraySort = mergeSort; var userAgent$1 = engineUserAgent; var firefox = userAgent$1.match(/firefox\/(\d+)/i); var engineFfVersion = !!firefox && +firefox[1]; var UA = engineUserAgent; var engineIsIeOrEdge = /MSIE|Trident/.test(UA); var userAgent = engineUserAgent; var webkit = userAgent.match(/AppleWebKit\/(\d+)\./); var engineWebkitVersion = !!webkit && +webkit[1]; var $ = _export; var uncurryThis = functionUncurryThis; var aCallable = aCallable$e; var toObject = toObject$e; var lengthOfArrayLike = lengthOfArrayLike$d; var deletePropertyOrThrow = deletePropertyOrThrow$2; var toString = toString$a; var fails = fails$w; var internalSort = arraySort; var arrayMethodIsStrict = arrayMethodIsStrict$5; var FF = engineFfVersion; var IE_OR_EDGE = engineIsIeOrEdge; var V8 = engineV8Version; var WEBKIT = engineWebkitVersion; var test = []; var nativeSort = uncurryThis(test.sort); var push = uncurryThis(test.push); // IE8- var FAILS_ON_UNDEFINED = fails(function () { test.sort(undefined); }); // V8 bug var FAILS_ON_NULL = fails(function () { test.sort(null); }); // Old WebKit var STRICT_METHOD = arrayMethodIsStrict('sort'); var STABLE_SORT = !fails(function () { // feature detection can be too slow, so check engines versions if (V8) return V8 < 70; if (FF && FF > 3) return; if (IE_OR_EDGE) return true; if (WEBKIT) return WEBKIT < 603; var result = ''; var code, chr, value, index; // generate an array with more 512 elements (Chakra and old V8 fails only in this case) for (code = 65; code < 76; code++) { chr = String.fromCharCode(code); switch (code) { case 66: case 69: case 70: case 72: value = 3; break; case 68: case 71: value = 4; break; default: value = 2; } for (index = 0; index < 47; index++) { test.push({ k: chr + index, v: value }); } } test.sort(function (a, b) { return b.v - a.v; }); for (index = 0; index < test.length; index++) { chr = test[index].k.charAt(0); if (result.charAt(result.length - 1) !== chr) result += chr; } return result !== 'DGBEFHACIJK'; }); var FORCED = FAILS_ON_UNDEFINED || !FAILS_ON_NULL || !STRICT_METHOD || !STABLE_SORT; var getSortCompare = function (comparefn) { return function (x, y) { if (y === undefined) return -1; if (x === undefined) return 1; if (comparefn !== undefined) return +comparefn(x, y) || 0; return toString(x) > toString(y) ? 1 : -1; }; }; // `Array.prototype.sort` method // https://tc39.es/ecma262/#sec-array.prototype.sort $({ target: 'Array', proto: true, forced: FORCED }, { sort: function sort(comparefn) { if (comparefn !== undefined) aCallable(comparefn); var array = toObject(this); if (STABLE_SORT) return comparefn === undefined ? nativeSort(array) : nativeSort(array, comparefn); var items = []; var arrayLength = lengthOfArrayLike(array); var itemsLength, index; for (index = 0; index < arrayLength; index++) { if (index in array) push(items, array[index]); } internalSort(items, getSortCompare(comparefn)); itemsLength = lengthOfArrayLike(items); index = 0; while (index < itemsLength) array[index] = items[index++]; while (index < arrayLength) deletePropertyOrThrow(array, index++); return array; } }); var entryVirtual$2 = entryVirtual$k; var sort$2 = entryVirtual$2('Array').sort; var isPrototypeOf$2 = objectIsPrototypeOf; var method$2 = sort$2; var ArrayPrototype$2 = Array.prototype; var sort$1 = function (it) { var own = it.sort; return it === ArrayPrototype$2 || isPrototypeOf$2(ArrayPrototype$2, it) && own === ArrayPrototype$2.sort ? method$2 : own; }; var parent$6 = sort$1; var sort = parent$6; (function (module) { module.exports = sort; })(sort$3); var _sortInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(sortExports); var valuesExports = {}; var values$3 = { get exports() { return valuesExports; }, set exports(v) { valuesExports = v; } }; var entryVirtual$1 = entryVirtual$k; var values$2 = entryVirtual$1('Array').values; var parent$5 = values$2; var values$1 = parent$5; var classof$1 = classof$e; var hasOwn$1 = hasOwnProperty_1; var isPrototypeOf$1 = objectIsPrototypeOf; var method$1 = values$1; var ArrayPrototype$1 = Array.prototype; var DOMIterables$1 = { DOMTokenList: true, NodeList: true }; var values = function (it) { var own = it.values; return it === ArrayPrototype$1 || isPrototypeOf$1(ArrayPrototype$1, it) && own === ArrayPrototype$1.values || hasOwn$1(DOMIterables$1, classof$1(it)) ? method$1 : own; }; (function (module) { module.exports = values; })(values$3); var _valuesInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(valuesExports); var iteratorExports = {}; var iterator = { get exports() { return iteratorExports; }, set exports(v) { iteratorExports = v; } }; (function (module) { module.exports = iterator$3; })(iterator); var _Symbol$iterator$1 = /*@__PURE__*/getDefaultExportFromCjs(iteratorExports); var entriesExports = {}; var entries$3 = { get exports() { return entriesExports; }, set exports(v) { entriesExports = v; } }; var entryVirtual = entryVirtual$k; var entries$2 = entryVirtual('Array').entries; var parent$4 = entries$2; var entries$1 = parent$4; var classof = classof$e; var hasOwn = hasOwnProperty_1; var isPrototypeOf = objectIsPrototypeOf; var method = entries$1; var ArrayPrototype = Array.prototype; var DOMIterables = { DOMTokenList: true, NodeList: true }; var entries = function (it) { var own = it.entries; return it === ArrayPrototype || isPrototypeOf(ArrayPrototype, it) && own === ArrayPrototype.entries || hasOwn(DOMIterables, classof(it)) ? method : own; }; (function (module) { module.exports = entries; })(entries$3); var _entriesInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(entriesExports); // Unique ID creation requires a high quality random # generator. In the browser we therefore // require the crypto API and do not support built-in fallback to lower quality random number // generators (like Math.random()). let getRandomValues; const rnds8 = new Uint8Array(16); function rng() { // lazy load so that environments that need to polyfill have a chance to do so if (!getRandomValues) { // getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto); if (!getRandomValues) { throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported'); } } return getRandomValues(rnds8); } /** * Convert array of 16 byte values to UUID string format of the form: * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX */ const byteToHex = []; for (let i = 0; i < 256; ++i) { byteToHex.push((i + 0x100).toString(16).slice(1)); } function unsafeStringify(arr, offset = 0) { // Note: Be careful editing this code! It's been tuned for performance // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); } const randomUUID = typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto); var native = { randomUUID }; function v4(options, buf, offset) { if (native.randomUUID && !buf && !options) { return native.randomUUID(); } options = options || {}; const rnds = options.random || (options.rng || rng)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` rnds[6] = rnds[6] & 0x0f | 0x40; rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided if (buf) { offset = offset || 0; for (let i = 0; i < 16; ++i) { buf[offset + i] = rnds[i]; } return buf; } return unsafeStringify(rnds); } /** * Determine whether a value can be used as an id. * * @param value - Input value of unknown type. * @returns True if the value is valid id, false otherwise. */ function isId(value) { return typeof value === "string" || typeof value === "number"; } /** * A queue. * * @typeParam T - The type of method names to be replaced by queued versions. */ var Queue = /*#__PURE__*/function () { /** Delay in milliseconds. If defined the queue will be periodically flushed. */ /** Maximum number of entries in the queue before it will be flushed. */ /** * Construct a new Queue. * * @param options - Queue configuration. */ function Queue(options) { _classCallCheck(this, Queue); _defineProperty(this, "delay", void 0); _defineProperty(this, "max", void 0); _defineProperty(this, "_queue", []); _defineProperty(this, "_timeout", null); _defineProperty(this, "_extended", null); // options this.delay = null; this.max = Infinity; this.setOptions(options); } /** * Update the configuration of the queue. * * @param options - Queue configuration. */ _createClass(Queue, [{ key: "setOptions", value: function setOptions(options) { if (options && typeof options.delay !== "undefined") { this.delay = options.delay; } if (options && typeof options.max !== "undefined") { this.max = options.max; } this._flushIfNeeded(); } /** * Extend an object with queuing functionality. * The object will be extended with a function flush, and the methods provided in options.replace will be replaced with queued ones. * * @param object - The object to be extended. * @param options - Additional options. * @returns The created queue. */ }, { key: "destroy", value: /** * Destroy the queue. The queue will first flush all queued actions, and in case it has extended an object, will restore the original object. */ function destroy() { this.flush(); if (this._extended) { var object = this._extended.object; var methods = this._extended.methods; for (var i = 0; i < methods.length; i++) { var method = methods[i]; if (method.original) { // @TODO: better solution? object[method.name] = method.original; } else { // @TODO: better solution? delete object[method.name]; } } this._extended = null; } } /** * Replace a method on an object with a queued version. * * @param object - Object having the method. * @param method - The method name. */ }, { key: "replace", value: function replace(object, method) { /* eslint-disable-next-line @typescript-eslint/no-this-alias -- Function this is necessary in the function bellow, so class this has to be saved into a variable here. */ var me = this; var original = object[method]; if (!original) { throw new Error("Method " + method + " undefined"); } object[method] = function () { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } // add this call to the queue me.queue({ args: args, fn: original, context: this }); }; } /** * Queue a call. * * @param entry - The function or entry to be queued. */ }, { key: "queue", value: function queue(entry) { if (typeof entry === "function") { this._queue.push({ fn: entry }); } else { this._queue.push(entry); } this._flushIfNeeded(); } /** * Check whether the queue needs to be flushed. */ }, { key: "_flushIfNeeded", value: function _flushIfNeeded() { var _this = this; // flush when the maximum is exceeded. if (this._queue.length > this.max) { this.flush(); } // flush after a period of inactivity when a delay is configured if (this._timeout != null) { clearTimeout(this._timeout); this._timeout = null; } if (this.queue.length > 0 && typeof this.delay === "number") { this._timeout = _setTimeout(function () { _this.flush(); }, this.delay); } } /** * Flush all queued calls */ }, { key: "flush", value: function flush() { var _context, _context2; _forEachInstanceProperty(_context = _spliceInstanceProperty(_context2 = this._queue).call(_context2, 0)).call(_context, function (entry) { entry.fn.apply(entry.context || entry.fn, entry.args || []); }); } }], [{ key: "extend", value: function extend(object, options) { var queue = new Queue(options); if (object.flush !== undefined) { throw new Error("Target object already has a property flush"); } object.flush = function () { queue.flush(); }; var methods = [{ name: "flush", original: undefined }]; if (options && options.replace) { for (var i = 0; i < options.replace.length; i++) { var name = options.replace[i]; methods.push({ name: name, // @TODO: better solution? original: object[name] }); // @TODO: better solution? queue.replace(object, name); } } queue._extended = { object: object, methods: methods }; return queue; } }]); return Queue; }(); /** * {@link DataSet} code that can be reused in {@link DataView} or other similar implementations of {@link DataInterface}. * * @typeParam Item - Item type that may or may not have an id. * @typeParam IdProp - Name of the property that contains the id. */ var DataSetPart = /*#__PURE__*/function () { function DataSetPart() { _classCallCheck(this, DataSetPart); _defineProperty(this, "_subscribers", { "*": [], add: [], remove: [], update: [] }); /** * @deprecated Use on instead (PS: DataView.subscribe === DataView.on). */ _defineProperty(this, "subscribe", DataSetPart.prototype.on); /** * @deprecated Use off instead (PS: DataView.unsubscribe === DataView.off). */ _defineProperty(this, "unsubscribe", DataSetPart.prototype.off); } _createClass(DataSetPart, [{ key: "_trigger", value: /** * Trigger an event * * @param event - Event name. * @param payload - Event payload. * @param senderId - Id of the sender. */ function _trigger(event, payload, senderId) { var _context, _context2; if (event === "*") { throw new Error("Cannot trigger event *"); } _forEachInstanceProperty(_context = _concatInstanceProperty(_context2 = []).call(_context2, _toConsumableArray(this._subscribers[event]), _toConsumableArray(this._subscribers["*"]))).call(_context, function (subscriber) { subscriber(event, payload, senderId != null ? senderId : null); }); } /** * Subscribe to an event, add an event listener. * * @remarks Non-function callbacks are ignored. * @param event - Event name. * @param callback - Callback method. */ }, { key: "on", value: function on(event, callback) { if (typeof callback === "function") { this._subscribers[event].push(callback); } // @TODO: Maybe throw for invalid callbacks? } /** * Unsubscribe from an event, remove an event listener. * * @remarks If the same callback was subscribed more than once **all** occurences will be removed. * @param event - Event name. * @param callback - Callback method. */ }, { key: "off", value: function off(event, callback) { var _context3; this._subscribers[event] = _filterInstanceProperty(_context3 = this._subscribers[event]).call(_context3, function (subscriber) { return subscriber !== callback; }); } }]); return DataSetPart; }(); var setExports = {}; var set$2 = { get exports() { return setExports; }, set exports(v) { setExports = v; } }; var collection = collection$2; var collectionStrong = collectionStrong$2; // `Set` constructor // https://tc39.es/ecma262/#sec-set-objects collection('Set', function (init) { return function Set() { return init(this, arguments.length ? arguments[0] : undefined); }; }, collectionStrong); var path = path$r; var set$1 = path.Set; var parent$3 = set$1; var set = parent$3; (function (module) { module.exports = set; })(set$2); var _Set = /*@__PURE__*/getDefaultExportFromCjs(setExports); var getIteratorExports$1 = {}; var getIterator$5 = { get exports() { return getIteratorExports$1; }, set exports(v) { getIteratorExports$1 = v; } }; var getIteratorExports = {}; var getIterator$4 = { get exports() { return getIteratorExports; }, set exports(v) { getIteratorExports = v; } }; var getIterator$3 = getIterator$8; var getIterator_1 = getIterator$3; var parent$2 = getIterator_1; var getIterator$2 = parent$2; var parent$1 = getIterator$2; var getIterator$1 = parent$1; var parent = getIterator$1; var getIterator = parent; (function (module) { module.exports = getIterator; })(getIterator$4); (function (module) { module.exports = getIteratorExports; })(getIterator$5); var _getIterator = /*@__PURE__*/getDefaultExportFromCjs(getIteratorExports$1); var _Symbol$iterator; function _createForOfIteratorHelper$2(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray$2(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray$2(o, minLen) { var _context10; if (!o) return; if (typeof o === "string") return _arrayLikeToArray$2(o, minLen); var n = _sliceInstanceProperty(_context10 = Object.prototype.toString.call(o)).call(_context10, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from$1(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$2(o, minLen); } function _arrayLikeToArray$2(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } _Symbol$iterator = _Symbol$iterator$1; /** * Data stream * * @remarks * {@link DataStream} offers an always up to date stream of items from a {@link DataSet} or {@link DataView}. * That means that the stream is evaluated at the time of iteration, conversion to another data type or when {@link cache} is called, not when the {@link DataStream} was created. * Multiple invocations of for example {@link toItemArray} may yield different results (if the data source like for example {@link DataSet} gets modified). * @typeParam Item - The item type this stream is going to work with. */ var DataStream = /*#__PURE__*/function () { /** * Create a new data stream. * * @param pairs - The id, item pairs. */ function DataStream(pairs) { _classCallCheck(this, DataStream); _defineProperty(this, "_pairs", void 0); this._pairs = pairs; } /** * Return an iterable of key, value pairs for every entry in the stream. */ _createClass(DataStream, [{ key: _Symbol$iterator, value: /*#__PURE__*/ regenerator.mark(function value() { var _iterator, _step, _step$value, id, item; return regenerator.wrap(function value$(_context) { while (1) switch (_context.prev = _context.next) { case 0: _iterator = _createForOfIteratorHelper$2(this._pairs); _context.prev = 1; _iterator.s(); case 3: if ((_step = _iterator.n()).done) { _context.next = 9; break; } _step$value = _slicedToArray(_step.value, 2), id = _step$value[0], item = _step$value[1]; _context.next = 7; return [id, item]; case 7: _context.next = 3; break; case 9: _context.next = 14; break; case 11: _context.prev = 11; _context.t0 = _context["catch"](1); _iterator.e(_context.t0); case 14: _context.prev = 14; _iterator.f(); return _context.finish(14); case 17: case "end": return _context.stop(); } }, value, this, [[1, 11, 14, 17]]); }) /** * Return an iterable of key, value pairs for every entry in the stream. */ }, { key: "entries", value: /*#__PURE__*/ regenerator.mark(function entries() { var _iterator2, _step2, _step2$value, id, item; return regenerator.wrap(function entries$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: _iterator2 = _createForOfIteratorHelper$2(this._pairs); _context2.prev = 1; _iterator2.s(); case 3: if ((_step2 = _iterator2.n()).done) { _context2.next = 9; break; } _step2$value = _slicedToArray(_step2.value, 2), id = _step2$value[0], item = _step2$value[1]; _context2.next = 7; return [id, item]; case 7: _context2.next = 3; break; case 9: _context2.next = 14; break; case 11: _context2.prev = 11; _context2.t0 = _context2["catch"](1); _iterator2.e(_context2.t0); case 14: _context2.prev = 14; _iterator2.f(); return _context2.finish(14); case 17: case "end": return _context2.stop(); } }, entries, this, [[1, 11, 14, 17]]); }) /** * Return an iterable of keys in the stream. */ }, { key: "keys", value: /*#__PURE__*/ regenerator.mark(function keys() { var _iterator3, _step3, _step3$value, id; return regenerator.wrap(function keys$(_context3) { while (1) switch (_context3.prev = _context3.next) { case 0: _iterator3 = _createForOfIteratorHelper$2(this._pairs); _context3.prev = 1; _iterator3.s(); case 3: if ((_step3 = _iterator3.n()).done) { _context3.next = 9; break; } _step3$value = _slicedToArray(_step3.value, 1), id = _step3$value[0]; _context3.next = 7; return id; case 7: _context3.next = 3; break; case 9: _context3.next = 14; break; case 11: _context3.prev = 11; _context3.t0 = _context3["catch"](1); _iterator3.e(_context3.t0); case 14: _context3.prev = 14; _iterator3.f(); return _context3.finish(14); case 17: case "end": return _context3.stop(); } }, keys, this, [[1, 11, 14, 17]]); }) /** * Return an iterable of values in the stream. */ }, { key: "values", value: /*#__PURE__*/ regenerator.mark(function values() { var _iterator4, _step4, _step4$value, item; return regenerator.wrap(function values$(_context4) { while (1) switch (_context4.prev = _context4.next) { case 0: _iterator4 = _createForOfIteratorHelper$2(this._pairs); _context4.prev = 1; _iterator4.s(); case 3: if ((_step4 = _iterator4.n()).done) { _context4.next = 9; break; } _step4$value = _slicedToArray(_step4.value, 2), item = _step4$value[1]; _context4.next = 7; return item; case 7: _context4.next = 3; break; case 9: _context4.next = 14; break; case 11: _context4.prev = 11; _context4.t0 = _context4["catch"](1); _iterator4.e(_context4.t0); case 14: _context4.prev = 14; _iterator4.f(); return _context4.finish(14); case 17: case "end": return _context4.stop(); } }, values, this, [[1, 11, 14, 17]]); }) /** * Return an array containing all the ids in this stream. * * @remarks * The array may contain duplicities. * @returns The array with all ids from this stream. */ }, { key: "toIdArray", value: function toIdArray() { var _context5; return _mapInstanceProperty(_context5 = _toConsumableArray(this._pairs)).call(_context5, function (pair) { return pair[0]; }); } /** * Return an array containing all the items in this stream. * * @remarks * The array may contain duplicities. * @returns The array with all items from this stream. */ }, { key: "toItemArray", value: function toItemArray() { var _context6; return _mapInstanceProperty(_context6 = _toConsumableArray(this._pairs)).call(_context6, function (pair) { return pair[1]; }); } /** * Return an array containing all the entries in this stream. * * @remarks * The array may contain duplicities. * @returns The array with all entries from this stream. */ }, { key: "toEntryArray", value: function toEntryArray() { return _toConsumableArray(this._pairs); } /** * Return an object map containing all the items in this stream accessible by ids. * * @remarks * In case of duplicate ids (coerced to string so `7 == '7'`) the last encoutered appears in the returned object. * @returns The object map of all id → item pairs from this stream. */ }, { key: "toObjectMap", value: function toObjectMap() { var map = _Object$create$1(null); var _iterator5 = _createForOfIteratorHelper$2(this._pairs), _step5; try { for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) { var _step5$value = _slicedToArray(_step5.value, 2), id = _step5$value[0], item = _step5$value[1]; map[id] = item; } } catch (err) { _iterator5.e(err); } finally { _iterator5.f(); } return map; } /** * Return a map containing all the items in this stream accessible by ids. * * @returns The map of all id → item pairs from this stream. */ }, { key: "toMap", value: function toMap() { return new _Map(this._pairs); } /** * Return a set containing all the (unique) ids in this stream. * * @returns The set of all ids from this stream. */ }, { key: "toIdSet", value: function toIdSet() { return new _Set(this.toIdArray()); } /** * Return a set containing all the (unique) items in this stream. * * @returns The set of all items from this stream. */ }, { key: "toItemSet", value: function toItemSet() { return new _Set(this.toItemArray()); } /** * Cache the items from this stream. * * @remarks * This method allows for items to be fetched immediatelly and used (possibly multiple times) later. * It can also be used to optimize performance as {@link DataStream} would otherwise reevaluate everything upon each iteration. * * ## Example * ```javascript * const ds = new DataSet([…]) * * const cachedStream = ds.stream() * .filter(…) * .sort(…) * .map(…) * .cached(…) // Data are fetched, processed and cached here. * * ds.clear() * chachedStream // Still has all the items. * ``` * @returns A new {@link DataStream} with cached items (detached from the original {@link DataSet}). */ }, { key: "cache", value: function cache() { return new DataStream(_toConsumableArray(this._pairs)); } /** * Get the distinct values of given property. * * @param callback - The function that picks and possibly converts the property. * @typeParam T - The type of the distinct value. * @returns A set of all distinct properties. */ }, { key: "distinct", value: function distinct(callback) { var set = new _Set(); var _iterator6 = _createForOfIteratorHelper$2(this._pairs), _step6; try { for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) { var _step6$value = _slicedToArray(_step6.value, 2), id = _step6$value[0], item = _step6$value[1]; set.add(callback(item, id)); } } catch (err) { _iterator6.e(err); } finally { _iterator6.f(); } return set; } /** * Filter the items of the stream. * * @param callback - The function that decides whether an item will be included. * @returns A new data stream with the filtered items. */ }, { key: "filter", value: function filter(callback) { var pairs = this._pairs; return new DataStream(_defineProperty({}, _Symbol$iterator$1, /*#__PURE__*/regenerator.mark(function _callee() { var _iterator7, _step7, _step7$value, id, item; return regenerator.wrap(function _callee$(_context7) { while (1) switch (_context7.prev = _context7.next) { case 0: _iterator7 = _createForOfIteratorHelper$2(pairs); _context7.prev = 1; _iterator7.s(); case 3: if ((_step7 = _iterator7.n()).done) { _context7.next = 10; break; } _step7$value = _slicedToArray(_step7.value, 2), id = _step7$value[0], item = _step7$value[1]; if (!callback(item, id)) { _context7.next = 8; break; } _context7.next = 8; return [id, item]; case 8: _context7.next = 3; break; case 10: _context7.next = 15; break; case 12: _context7.prev = 12; _context7.t0 = _context7["catch"](1); _iterator7.e(_context7.t0); case 15: _context7.prev = 15; _iterator7.f(); return _context7.finish(15); case 18: case "end": return _context7.stop(); } }, _callee, null, [[1, 12, 15, 18]]); }))); } /** * Execute a callback for each item of the stream. * * @param callback - The function that will be invoked for each item. */ }, { key: "forEach", value: function forEach(callback) { var _iterator8 = _createForOfIteratorHelper$2(this._pairs), _step8; try { for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) { var _step8$value = _slicedToArray(_step8.value, 2), id = _step8$value[0], item = _step8$value[1]; callback(item, id); } } catch (err) { _iterator8.e(err); } finally { _iterator8.f(); } } /** * Map the items into a different type. * * @param callback - The function that does the conversion. * @typeParam Mapped - The type of the item after mapping. * @returns A new data stream with the mapped items. */ }, { key: "map", value: function map(callback) { var pairs = this._pairs; return new DataStream(_defineProperty({}, _Symbol$iterator$1, /*#__PURE__*/regenerator.mark(function _callee2() { var _iterator9, _step9, _step9$value, id, item; return regenerator.wrap(function _callee2$(_context8) { while (1) switch (_context8.prev = _context8.next) { case 0: _iterator9 = _createForOfIteratorHelper$2(pairs); _context8.prev = 1; _iterator9.s(); case 3: if ((_step9 = _iterator9.n()).done) { _context8.next = 9; break; } _step9$value = _slicedToArray(_step9.value, 2), id = _step9$value[0], item = _step9$value[1]; _context8.next = 7; return [id, callback(item, id)]; case 7: _context8.next = 3; break; case 9: _context8.next = 14; break; case 11: _context8.prev = 11; _context8.t0 = _context8["catch"](1); _iterator9.e(_context8.t0); case 14: _context8.prev = 14; _iterator9.f(); return _context8.finish(14); case 17: case "end": return _context8.stop(); } }, _callee2, null, [[1, 11, 14, 17]]); }))); } /** * Get the item with the maximum value of given property. * * @param callback - The function that picks and possibly converts the property. * @returns The item with the maximum if found otherwise null. */ }, { key: "max", value: function max(callback) { var iter = _getIterator(this._pairs); var curr = iter.next(); if (curr.done) { return null; } var maxItem = curr.value[1]; var maxValue = callback(curr.value[1], curr.value[0]); while (!(curr = iter.next()).done) { var _curr$value = _slicedToArray(curr.value, 2), id = _curr$value[0], item = _curr$value[1]; var _value = callback(item, id); if (_value > maxValue) { maxValue = _value; maxItem = item; } } return maxItem; } /** * Get the item with the minimum value of given property. * * @param callback - The function that picks and possibly converts the property. * @returns The item with the minimum if found otherwise null. */ }, { key: "min", value: function min(callback) { var iter = _getIterator(this._pairs); var curr = iter.next(); if (curr.done) { return null; } var minItem = curr.value[1]; var minValue = callback(curr.value[1], curr.value[0]); while (!(curr = iter.next()).done) { var _curr$value2 = _slicedToArray(curr.value, 2), id = _curr$value2[0], item = _curr$value2[1]; var _value2 = callback(item, id); if (_value2 < minValue) { minValue = _value2; minItem = item; } } return minItem; } /** * Reduce the items into a single value. * * @param callback - The function that does the reduction. * @param accumulator - The initial value of the accumulator. * @typeParam T - The type of the accumulated value. * @returns The reduced value. */ }, { key: "reduce", value: function reduce(callback, accumulator) { var _iterator10 = _createForOfIteratorHelper$2(this._pairs), _step10; try { for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) { var _step10$value = _slicedToArray(_step10.value, 2), id = _step10$value[0], item = _step10$value[1]; accumulator = callback(accumulator, item, id); } } catch (err) { _iterator10.e(err); } finally { _iterator10.f(); } return accumulator; } /** * Sort the items. * * @param callback - Item comparator. * @returns A new stream with sorted items. */ }, { key: "sort", value: function sort(callback) { var _this = this; return new DataStream(_defineProperty({}, _Symbol$iterator$1, function () { var _context9; return _getIterator(_sortInstanceProperty(_context9 = _toConsumableArray(_this._pairs)).call(_context9, function (_ref, _ref2) { var _ref3 = _slicedToArray(_ref, 2), idA = _ref3[0], itemA = _ref3[1]; var _ref4 = _slicedToArray(_ref2, 2), idB = _ref4[0], itemB = _ref4[1]; return callback(itemA, itemB, idA, idB); })); })); } }]); return DataStream; }(); function ownKeys(object, enumerableOnly) { var keys = _Object$keys(object); if (_Object$getOwnPropertySymbols) { var symbols = _Object$getOwnPropertySymbols(object); enumerableOnly && (symbols = _filterInstanceProperty(symbols).call(symbols, function (sym) { return _Object$getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var _context10, _context11; var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? _forEachInstanceProperty(_context10 = ownKeys(Object(source), !0)).call(_context10, function (key) { _defineProperty(target, key, source[key]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(target, _Object$getOwnPropertyDescriptors(source)) : _forEachInstanceProperty(_context11 = ownKeys(Object(source))).call(_context11, function (key) { _Object$defineProperty(target, key, _Object$getOwnPropertyDescriptor(source, key)); }); } return target; } function _createForOfIteratorHelper$1(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray$1(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray$1(o, minLen) { var _context9; if (!o) return; if (typeof o === "string") return _arrayLikeToArray$1(o, minLen); var n = _sliceInstanceProperty(_context9 = Object.prototype.toString.call(o)).call(_context9, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from$1(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$1(o, minLen); } function _arrayLikeToArray$1(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _createSuper$1(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$1(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$1() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * Add an id to given item if it doesn't have one already. * * @remarks * The item will be modified. * @param item - The item that will have an id after a call to this function. * @param idProp - The key of the id property. * @typeParam Item - Item type that may or may not have an id. * @typeParam IdProp - Name of the property that contains the id. * @returns true */ function ensureFullItem(item, idProp) { if (item[idProp] == null) { // generate an id item[idProp] = v4(); } return item; } /** * # DataSet * * Vis.js comes with a flexible DataSet, which can be used to hold and * manipulate unstructured data and listen for changes in the data. The DataSet * is key/value based. Data items can be added, updated and removed from the * DataSet, and one can subscribe to changes in the DataSet. The data in the * DataSet can be filtered and ordered. Data can be normalized when appending it * to the DataSet as well. * * ## Example * * The following example shows how to use a DataSet. * * ```javascript * // create a DataSet * var options = {}; * var data = new vis.DataSet(options); * * // add items * // note that the data items can contain different properties and data formats * data.add([ * {id: 1, text: 'item 1', date: new Date(2013, 6, 20), group: 1, first: true}, * {id: 2, text: 'item 2', date: '2013-06-23', group: 2}, * {id: 3, text: 'item 3', date: '2013-06-25', group: 2}, * {id: 4, text: 'item 4'} * ]); * * // subscribe to any change in the DataSet * data.on('*', function (event, properties, senderId) { * console.log('event', event, properties); * }); * * // update an existing item * data.update({id: 2, group: 1}); * * // remove an item * data.remove(4); * * // get all ids * var ids = data.getIds(); * console.log('ids', ids); * * // get a specific item * var item1 = data.get(1); * console.log('item1', item1); * * // retrieve a filtered subset of the data * var items = data.get({ * filter: function (item) { * return item.group == 1; * } * }); * console.log('filtered items', items); * ``` * * @typeParam Item - Item type that may or may not have an id. * @typeParam IdProp - Name of the property that contains the id. */ var DataSet = /*#__PURE__*/function (_DataSetPart) { _inherits(DataSet, _DataSetPart); var _super = _createSuper$1(DataSet); /** * Construct a new DataSet. * * @param data - Initial data or options. * @param options - Options (type error if data is also options). */ function DataSet(data, options) { var _this; _classCallCheck(this, DataSet); _this = _super.call(this); // correctly read optional arguments /** Flush all queued calls. */ _defineProperty(_assertThisInitialized(_this), "flush", void 0); /** @inheritDoc */ _defineProperty(_assertThisInitialized(_this), "length", void 0); _defineProperty(_assertThisInitialized(_this), "_options", void 0); _defineProperty(_assertThisInitialized(_this), "_data", void 0); _defineProperty(_assertThisInitialized(_this), "_idProp", void 0); _defineProperty(_assertThisInitialized(_this), "_queue", null); if (data && !_Array$isArray(data)) { options = data; data = []; } _this._options = options || {}; _this._data = new _Map(); // map with data indexed by id _this.length = 0; // number of items in the DataSet _this._idProp = _this._options.fieldId || "id"; // name of the field containing id // add initial data when provided if (data && data.length) { _this.add(data); } _this.setOptions(options); return _this; } /** * Set new options. * * @param options - The new options. */ _createClass(DataSet, [{ key: "idProp", get: /** @inheritDoc */ function get() { return this._idProp; } }, { key: "setOptions", value: function setOptions(options) { if (options && options.queue !== undefined) { if (options.queue === false) { // delete queue if loaded if (this._queue) { this._queue.destroy(); this._queue = null; } } else { // create queue and update its options if (!this._queue) { this._queue = Queue.extend(this, { replace: ["add", "update", "remove"] }); } if (options.queue && _typeof$1(options.queue) === "object") { this._queue.setOptions(options.queue); } } } } /** * Add a data item or an array with items. * * After the items are added to the DataSet, the DataSet will trigger an event `add`. When a `senderId` is provided, this id will be passed with the triggered event to all subscribers. * * ## Example * * ```javascript * // create a DataSet * const data = new vis.DataSet() * * // add items * const ids = data.add([ * { id: 1, text: 'item 1' }, * { id: 2, text: 'item 2' }, * { text: 'item without an id' } * ]) * * console.log(ids) // [1, 2, '<UUIDv4>'] * ``` * * @param data - Items to be added (ids will be generated if missing). * @param senderId - Sender id. * @returns addedIds - Array with the ids (generated if not present) of the added items. * @throws When an item with the same id as any of the added items already exists. */ }, { key: "add", value: function add(data, senderId) { var _this2 = this; var addedIds = []; var id; if (_Array$isArray(data)) { // Array var idsToAdd = _mapInstanceProperty(data).call(data, function (d) { return d[_this2._idProp]; }); if (_someInstanceProperty(idsToAdd).call(idsToAdd, function (id) { return _this2._data.has(id); })) { throw new Error("A duplicate id was found in the parameter array."); } for (var i = 0, len = data.length; i < len; i++) { id = this._addItem(data[i]); addedIds.push(id); } } else if (data && _typeof$1(data) === "object") { // Single item id = this._addItem(data); addedIds.push(id); } else { throw new Error("Unknown dataType"); } if (addedIds.length) { this._trigger("add", { items: addedIds }, senderId); } return addedIds; } /** * Update existing items. When an item does not exist, it will be created. * * @remarks * The provided properties will be merged in the existing item. When an item does not exist, it will be created. * * After the items are updated, the DataSet will trigger an event `add` for the added items, and an event `update`. When a `senderId` is provided, this id will be passed with the triggered event to all subscribers. * * ## Example * * ```javascript * // create a DataSet * const data = new vis.DataSet([ * { id: 1, text: 'item 1' }, * { id: 2, text: 'item 2' }, * { id: 3, text: 'item 3' } * ]) * * // update items * const ids = data.update([ * { id: 2, text: 'item 2 (updated)' }, * { id: 4, text: 'item 4 (new)' } * ]) * * console.log(ids) // [2, 4] * ``` * * ## Warning for TypeScript users * This method may introduce partial items into the data set. Use add or updateOnly instead for better type safety. * @param data - Items to be updated (if the id is already present) or added (if the id is missing). * @param senderId - Sender id. * @returns updatedIds - The ids of the added (these may be newly generated if there was no id in the item from the data) or updated items. * @throws When the supplied data is neither an item nor an array of items. */ }, { key: "update", value: function update(data, senderId) { var _this3 = this; var addedIds = []; var updatedIds = []; var oldData = []; var updatedData = []; var idProp = this._idProp; var addOrUpdate = function addOrUpdate(item) { var origId = item[idProp]; if (origId != null && _this3._data.has(origId)) { var fullItem = item; // it has an id, therefore it is a fullitem var oldItem = _Object$assign({}, _this3._data.get(origId)); // update item var id = _this3._updateItem(fullItem); updatedIds.push(id); updatedData.push(fullItem); oldData.push(oldItem); } else { // add new item var _id = _this3._addItem(item); addedIds.push(_id); } }; if (_Array$isArray(data)) { // Array for (var i = 0, len = data.length; i < len; i++) { if (data[i] && _typeof$1(data[i]) === "object") { addOrUpdate(data[i]); } else { console.warn("Ignoring input item, which is not an object at index " + i); } } } else if (data && _typeof$1(data) === "object") { // Single item addOrUpdate(data); } else { throw new Error("Unknown dataType"); } if (addedIds.length) { this._trigger("add", { items: addedIds }, senderId); } if (updatedIds.length) { var props = { items: updatedIds, oldData: oldData, data: updatedData }; // TODO: remove deprecated property 'data' some day //Object.defineProperty(props, 'data', { // 'get': (function() { // console.warn('Property data is deprecated. Use DataSet.get(ids) to retrieve the new data, use the oldData property on this object to get the old data'); // return updatedData; // }).bind(this) //}); this._trigger("update", props, senderId); } return _concatInstanceProperty(addedIds).call(addedIds, updatedIds); } /** * Update existing items. When an item does not exist, an error will be thrown. * * @remarks * The provided properties will be deeply merged into the existing item. * When an item does not exist (id not present in the data set or absent), an error will be thrown and nothing will be changed. * * After the items are updated, the DataSet will trigger an event `update`. * When a `senderId` is provided, this id will be passed with the triggered event to all subscribers. * * ## Example * * ```javascript * // create a DataSet * const data = new vis.DataSet([ * { id: 1, text: 'item 1' }, * { id: 2, text: 'item 2' }, * { id: 3, text: 'item 3' }, * ]) * * // update items * const ids = data.update([ * { id: 2, text: 'item 2 (updated)' }, // works * // { id: 4, text: 'item 4 (new)' }, // would throw * // { text: 'item 4 (new)' }, // would also throw * ]) * * console.log(ids) // [2] * ``` * @param data - Updates (the id and optionally other props) to the items in this data set. * @param senderId - Sender id. * @returns updatedIds - The ids of the updated items. * @throws When the supplied data is neither an item nor an array of items, when the ids are missing. */ }, { key: "updateOnly", value: function updateOnly(data, senderId) { var _context, _this4 = this; if (!_Array$isArray(data)) { data = [data]; } var updateEventData = _mapInstanceProperty(_context = _mapInstanceProperty(data).call(data, function (update) { var oldData = _this4._data.get(update[_this4._idProp]); if (oldData == null) { throw new Error("Updating non-existent items is not allowed."); } return { oldData: oldData, update: update }; })).call(_context, function (_ref) { var oldData = _ref.oldData, update = _ref.update; var id = oldData[_this4._idProp]; var updatedData = pureDeepObjectAssign(oldData, update); _this4._data.set(id, updatedData); return { id: id, oldData: oldData, updatedData: updatedData }; }); if (updateEventData.length) { var props = { items: _mapInstanceProperty(updateEventData).call(updateEventData, function (value) { return value.id; }), oldData: _mapInstanceProperty(updateEventData).call(updateEventData, function (value) { return value.oldData; }), data: _mapInstanceProperty(updateEventData).call(updateEventData, function (value) { return value.updatedData; }) }; // TODO: remove deprecated property 'data' some day //Object.defineProperty(props, 'data', { // 'get': (function() { // console.warn('Property data is deprecated. Use DataSet.get(ids) to retrieve the new data, use the oldData property on this object to get the old data'); // return updatedData; // }).bind(this) //}); this._trigger("update", props, senderId); return props.items; } else { return []; } } /** @inheritDoc */ }, { key: "get", value: function get(first, second) { // @TODO: Woudn't it be better to split this into multiple methods? // parse the arguments var id = undefined; var ids = undefined; var options = undefined; if (isId(first)) { // get(id [, options]) id = first; options = second; } else if (_Array$isArray(first)) { // get(ids [, options]) ids = first; options = second; } else { // get([, options]) options = first; } // determine the return type var returnType = options && options.returnType === "Object" ? "Object" : "Array"; // @TODO: WTF is this? Or am I missing something? // var returnType // if (options && options.returnType) { // var allowedValues = ['Array', 'Object'] // returnType = // allowedValues.indexOf(options.returnType) == -1 // ? 'Array' // : options.returnType // } else { // returnType = 'Array' // } // build options var filter = options && _filterInstanceProperty(options); var items = []; var item = undefined; var itemIds = undefined; var itemId = undefined; // convert items if (id != null) { // return a single item item = this._data.get(id); if (item && filter && !filter(item)) { item = undefined; } } else if (ids != null) { // return a subset of items for (var i = 0, len = ids.length; i < len; i++) { item = this._data.get(ids[i]); if (item != null && (!filter || filter(item))) { items.push(item); } } } else { var _context2; // return all items itemIds = _toConsumableArray(_keysInstanceProperty(_context2 = this._data).call(_context2)); for (var _i = 0, _len = itemIds.length; _i < _len; _i++) { itemId = itemIds[_i]; item = this._data.get(itemId); if (item != null && (!filter || filter(item))) { items.push(item); } } } // order the results if (options && options.order && id == undefined) { this._sort(items, options.order); } // filter fields of the items if (options && options.fields) { var fields = options.fields; if (id != undefined && item != null) { item = this._filterFields(item, fields); } else { for (var _i2 = 0, _len2 = items.length; _i2 < _len2; _i2++) { items[_i2] = this._filterFields(items[_i2], fields); } } } // return the results if (returnType == "Object") { var result = {}; for (var _i3 = 0, _len3 = items.length; _i3 < _len3; _i3++) { var resultant = items[_i3]; // @TODO: Shoudn't this be this._fieldId? // result[resultant.id] = resultant var _id2 = resultant[this._idProp]; result[_id2] = resultant; } return result; } else { if (id != null) { var _item; // a single item return (_item = item) !== null && _item !== void 0 ? _item : null; } else { // just return our array return items; } } } /** @inheritDoc */ }, { key: "getIds", value: function getIds(options) { var data = this._data; var filter = options && _filterInstanceProperty(options); var order = options && options.order; var itemIds = _toConsumableArray(_keysInstanceProperty(data).call(data)); var ids = []; if (filter) { // get filtered items if (order) { // create ordered list var items = []; for (var i = 0, len = itemIds.length; i < len; i++) { var id = itemIds[i]; var item = this._data.get(id); if (item != null && filter(item)) { items.push(item); } } this._sort(items, order); for (var _i4 = 0, _len4 = items.length; _i4 < _len4; _i4++) { ids.push(items[_i4][this._idProp]); } } else { // create unordered list for (var _i5 = 0, _len5 = itemIds.length; _i5 < _len5; _i5++) { var _id3 = itemIds[_i5]; var _item2 = this._data.get(_id3); if (_item2 != null && filter(_item2)) { ids.push(_item2[this._idProp]); } } } } else { // get all items if (order) { // create an ordered list var _items = []; for (var _i6 = 0, _len6 = itemIds.length; _i6 < _len6; _i6++) { var _id4 = itemIds[_i6]; _items.push(data.get(_id4)); } this._sort(_items, order); for (var _i7 = 0, _len7 = _items.length; _i7 < _len7; _i7++) { ids.push(_items[_i7][this._idProp]); } } else { // create unordered list for (var _i8 = 0, _len8 = itemIds.length; _i8 < _len8; _i8++) { var _id5 = itemIds[_i8]; var _item3 = data.get(_id5); if (_item3 != null) { ids.push(_item3[this._idProp]); } } } } return ids; } /** @inheritDoc */ }, { key: "getDataSet", value: function getDataSet() { return this; } /** @inheritDoc */ }, { key: "forEach", value: function forEach(callback, options) { var filter = options && _filterInstanceProperty(options); var data = this._data; var itemIds = _toConsumableArray(_keysInstanceProperty(data).call(data)); if (options && options.order) { // execute forEach on ordered list var items = this.get(options); for (var i = 0, len = items.length; i < len; i++) { var item = items[i]; var id = item[this._idProp]; callback(item, id); } } else { // unordered for (var _i9 = 0, _len9 = itemIds.length; _i9 < _len9; _i9++) { var _id6 = itemIds[_i9]; var _item4 = this._data.get(_id6); if (_item4 != null && (!filter || filter(_item4))) { callback(_item4, _id6); } } } } /** @inheritDoc */ }, { key: "map", value: function map(callback, options) { var filter = options && _filterInstanceProperty(options); var mappedItems = []; var data = this._data; var itemIds = _toConsumableArray(_keysInstanceProperty(data).call(data)); // convert and filter items for (var i = 0, len = itemIds.length; i < len; i++) { var id = itemIds[i]; var item = this._data.get(id); if (item != null && (!filter || filter(item))) { mappedItems.push(callback(item, id)); } } // order items if (options && options.order) { this._sort(mappedItems, options.order); } return mappedItems; } /** * Filter the fields of an item. * * @param item - The item whose fields should be filtered. * @param fields - The names of the fields that will be kept. * @typeParam K - Field name type. * @returns The item without any additional fields. */ }, { key: "_filterFields", value: function _filterFields(item, fields) { var _context3; if (!item) { // item is null return item; } return _reduceInstanceProperty(_context3 = _Array$isArray(fields) ? // Use the supplied array fields : // Use the keys of the supplied object _Object$keys(fields)).call(_context3, function (filteredItem, field) { filteredItem[field] = item[field]; return filteredItem; }, {}); } /** * Sort the provided array with items. * * @param items - Items to be sorted in place. * @param order - A field name or custom sort function. * @typeParam T - The type of the items in the items array. */ }, { key: "_sort", value: function _sort(items, order) { if (typeof order === "string") { // order by provided field name var name = order; // field name _sortInstanceProperty(items).call(items, function (a, b) { // @TODO: How to treat missing properties? var av = a[name]; var bv = b[name]; return av > bv ? 1 : av < bv ? -1 : 0; }); } else if (typeof order === "function") { // order by sort function _sortInstanceProperty(items).call(items, order); } else { // TODO: extend order by an Object {field:string, direction:string} // where direction can be 'asc' or 'desc' throw new TypeError("Order must be a function or a string"); } } /** * Remove an item or multiple items by “reference” (only the id is used) or by id. * * The method ignores removal of non-existing items, and returns an array containing the ids of the items which are actually removed from the DataSet. * * After the items are removed, the DataSet will trigger an event `remove` for the removed items. When a `senderId` is provided, this id will be passed with the triggered event to all subscribers. * * ## Example * ```javascript * // create a DataSet * const data = new vis.DataSet([ * { id: 1, text: 'item 1' }, * { id: 2, text: 'item 2' }, * { id: 3, text: 'item 3' } * ]) * * // remove items * const ids = data.remove([2, { id: 3 }, 4]) * * console.log(ids) // [2, 3] * ``` * * @param id - One or more items or ids of items to be removed. * @param senderId - Sender id. * @returns The ids of the removed items. */ }, { key: "remove", value: function remove(id, senderId) { var removedIds = []; var removedItems = []; // force everything to be an array for simplicity var ids = _Array$isArray(id) ? id : [id]; for (var i = 0, len = ids.length; i < len; i++) { var item = this._remove(ids[i]); if (item) { var itemId = item[this._idProp]; if (itemId != null) { removedIds.push(itemId); removedItems.push(item); } } } if (removedIds.length) { this._trigger("remove", { items: removedIds, oldData: removedItems }, senderId); } return removedIds; } /** * Remove an item by its id or reference. * * @param id - Id of an item or the item itself. * @returns The removed item if removed, null otherwise. */ }, { key: "_remove", value: function _remove(id) { // @TODO: It origianlly returned the item although the docs say id. // The code expects the item, so probably an error in the docs. var ident; // confirm the id to use based on the args type if (isId(id)) { ident = id; } else if (id && _typeof$1(id) === "object") { ident = id[this._idProp]; // look for the identifier field using ._idProp } // do the removing if the item is found if (ident != null && this._data.has(ident)) { var item = this._data.get(ident) || null; this._data.delete(ident); --this.length; return item; } return null; } /** * Clear the entire data set. * * After the items are removed, the {@link DataSet} will trigger an event `remove` for all removed items. When a `senderId` is provided, this id will be passed with the triggered event to all subscribers. * * @param senderId - Sender id. * @returns removedIds - The ids of all removed items. */ }, { key: "clear", value: function clear(senderId) { var _context4; var ids = _toConsumableArray(_keysInstanceProperty(_context4 = this._data).call(_context4)); var items = []; for (var i = 0, len = ids.length; i < len; i++) { items.push(this._data.get(ids[i])); } this._data.clear(); this.length = 0; this._trigger("remove", { items: ids, oldData: items }, senderId); return ids; } /** * Find the item with maximum value of a specified field. * * @param field - Name of the property that should be searched for max value. * @returns Item containing max value, or null if no items. */ }, { key: "max", value: function max(field) { var _context5; var max = null; var maxField = null; var _iterator = _createForOfIteratorHelper$1(_valuesInstanceProperty(_context5 = this._data).call(_context5)), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var item = _step.value; var itemField = item[field]; if (typeof itemField === "number" && (maxField == null || itemField > maxField)) { max = item; maxField = itemField; } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return max || null; } /** * Find the item with minimum value of a specified field. * * @param field - Name of the property that should be searched for min value. * @returns Item containing min value, or null if no items. */ }, { key: "min", value: function min(field) { var _context6; var min = null; var minField = null; var _iterator2 = _createForOfIteratorHelper$1(_valuesInstanceProperty(_context6 = this._data).call(_context6)), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var item = _step2.value; var itemField = item[field]; if (typeof itemField === "number" && (minField == null || itemField < minField)) { min = item; minField = itemField; } } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } return min || null; } /** * Find all distinct values of a specified field * * @param prop - The property name whose distinct values should be returned. * @returns Unordered array containing all distinct values. Items without specified property are ignored. */ }, { key: "distinct", value: function distinct(prop) { var data = this._data; var itemIds = _toConsumableArray(_keysInstanceProperty(data).call(data)); var values = []; var count = 0; for (var i = 0, len = itemIds.length; i < len; i++) { var id = itemIds[i]; var item = data.get(id); var value = item[prop]; var exists = false; for (var j = 0; j < count; j++) { if (values[j] == value) { exists = true; break; } } if (!exists && value !== undefined) { values[count] = value; count++; } } return values; } /** * Add a single item. Will fail when an item with the same id already exists. * * @param item - A new item to be added. * @returns Added item's id. An id is generated when it is not present in the item. */ }, { key: "_addItem", value: function _addItem(item) { var fullItem = ensureFullItem(item, this._idProp); var id = fullItem[this._idProp]; // check whether this id is already taken if (this._data.has(id)) { // item already exists throw new Error("Cannot add item: item with id " + id + " already exists"); } this._data.set(id, fullItem); ++this.length; return id; } /** * Update a single item: merge with existing item. * Will fail when the item has no id, or when there does not exist an item with the same id. * * @param update - The new item * @returns The id of the updated item. */ }, { key: "_updateItem", value: function _updateItem(update) { var id = update[this._idProp]; if (id == null) { throw new Error("Cannot update item: item has no id (item: " + _JSON$stringify(update) + ")"); } var item = this._data.get(id); if (!item) { // item doesn't exist throw new Error("Cannot update item: no item with id " + id + " found"); } this._data.set(id, _objectSpread(_objectSpread({}, item), update)); return id; } /** @inheritDoc */ }, { key: "stream", value: function stream(ids) { if (ids) { var data = this._data; return new DataStream(_defineProperty({}, _Symbol$iterator$1, /*#__PURE__*/regenerator.mark(function _callee() { var _iterator3, _step3, id, item; return regenerator.wrap(function _callee$(_context7) { while (1) switch (_context7.prev = _context7.next) { case 0: _iterator3 = _createForOfIteratorHelper$1(ids); _context7.prev = 1; _iterator3.s(); case 3: if ((_step3 = _iterator3.n()).done) { _context7.next = 11; break; } id = _step3.value; item = data.get(id); if (!(item != null)) { _context7.next = 9; break; } _context7.next = 9; return [id, item]; case 9: _context7.next = 3; break; case 11: _context7.next = 16; break; case 13: _context7.prev = 13; _context7.t0 = _context7["catch"](1); _iterator3.e(_context7.t0); case 16: _context7.prev = 16; _iterator3.f(); return _context7.finish(16); case 19: case "end": return _context7.stop(); } }, _callee, null, [[1, 13, 16, 19]]); }))); } else { var _context8; return new DataStream(_defineProperty({}, _Symbol$iterator$1, _bindInstanceProperty$1(_context8 = _entriesInstanceProperty(this._data)).call(_context8, this._data))); } } }]); return DataSet; }(DataSetPart); function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray(o, minLen) { var _context5; if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = _sliceInstanceProperty(_context5 = Object.prototype.toString.call(o)).call(_context5, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from$1(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * DataView * * A DataView offers a filtered and/or formatted view on a DataSet. One can subscribe to changes in a DataView, and easily get filtered or formatted data without having to specify filters and field types all the time. * * ## Example * ```javascript * // create a DataSet * var data = new vis.DataSet(); * data.add([ * {id: 1, text: 'item 1', date: new Date(2013, 6, 20), group: 1, first: true}, * {id: 2, text: 'item 2', date: '2013-06-23', group: 2}, * {id: 3, text: 'item 3', date: '2013-06-25', group: 2}, * {id: 4, text: 'item 4'} * ]); * * // create a DataView * // the view will only contain items having a property group with value 1, * // and will only output fields id, text, and date. * var view = new vis.DataView(data, { * filter: function (item) { * return (item.group == 1); * }, * fields: ['id', 'text', 'date'] * }); * * // subscribe to any change in the DataView * view.on('*', function (event, properties, senderId) { * console.log('event', event, properties); * }); * * // update an item in the data set * data.update({id: 2, group: 1}); * * // get all ids in the view * var ids = view.getIds(); * console.log('ids', ids); // will output [1, 2] * * // get all items in the view * var items = view.get(); * ``` * * @typeParam Item - Item type that may or may not have an id. * @typeParam IdProp - Name of the property that contains the id. */ var DataView = /*#__PURE__*/function (_DataSetPart) { _inherits(DataView, _DataSetPart); var _super = _createSuper(DataView); /** * Create a DataView. * * @param data - The instance containing data (directly or indirectly). * @param options - Options to configure this data view. */ function DataView(data, options) { var _context; var _this; _classCallCheck(this, DataView); _this = _super.call(this); /** @inheritDoc */ _defineProperty(_assertThisInitialized(_this), "length", 0); _defineProperty(_assertThisInitialized(_this), "_listener", void 0); _defineProperty(_assertThisInitialized(_this), "_data", void 0); // constructor → setData _defineProperty(_assertThisInitialized(_this), "_ids", new _Set()); // ids of the items currently in memory (just contains a boolean true) _defineProperty(_assertThisInitialized(_this), "_options", void 0); _this._options = options || {}; _this._listener = _bindInstanceProperty$1(_context = _this._onEvent).call(_context, _assertThisInitialized(_this)); _this.setData(data); return _this; } // TODO: implement a function .config() to dynamically update things like configured filter // and trigger changes accordingly /** * Set a data source for the view. * * @param data - The instance containing data (directly or indirectly). * @remarks * Note that when the data view is bound to a data set it won't be garbage * collected unless the data set is too. Use `dataView.setData(null)` or * `dataView.dispose()` to enable garbage collection before you lose the last * reference. */ _createClass(DataView, [{ key: "idProp", get: /** @inheritDoc */ function get() { return this.getDataSet().idProp; } }, { key: "setData", value: function setData(data) { if (this._data) { // unsubscribe from current dataset if (this._data.off) { this._data.off("*", this._listener); } // trigger a remove of all items in memory var ids = this._data.getIds({ filter: _filterInstanceProperty(this._options) }); var items = this._data.get(ids); this._ids.clear(); this.length = 0; this._trigger("remove", { items: ids, oldData: items }); } if (data != null) { this._data = data; // trigger an add of all added items var _ids = this._data.getIds({ filter: _filterInstanceProperty(this._options) }); for (var i = 0, len = _ids.length; i < len; i++) { var id = _ids[i]; this._ids.add(id); } this.length = _ids.length; this._trigger("add", { items: _ids }); } else { this._data = new DataSet(); } // subscribe to new dataset if (this._data.on) { this._data.on("*", this._listener); } } /** * Refresh the DataView. * Useful when the DataView has a filter function containing a variable parameter. */ }, { key: "refresh", value: function refresh() { var ids = this._data.getIds({ filter: _filterInstanceProperty(this._options) }); var oldIds = _toConsumableArray(this._ids); var newIds = {}; var addedIds = []; var removedIds = []; var removedItems = []; // check for additions for (var i = 0, len = ids.length; i < len; i++) { var id = ids[i]; newIds[id] = true; if (!this._ids.has(id)) { addedIds.push(id); this._ids.add(id); } } // check for removals for (var _i = 0, _len = oldIds.length; _i < _len; _i++) { var _id = oldIds[_i]; var item = this._data.get(_id); if (item == null) { // @TODO: Investigate. // Doesn't happen during tests or examples. // Is it really impossible or could it eventually happen? // How to handle it if it does? The types guarantee non-nullable items. console.error("If you see this, report it please."); } else if (!newIds[_id]) { removedIds.push(_id); removedItems.push(item); this._ids.delete(_id); } } this.length += addedIds.length - removedIds.length; // trigger events if (addedIds.length) { this._trigger("add", { items: addedIds }); } if (removedIds.length) { this._trigger("remove", { items: removedIds, oldData: removedItems }); } } /** @inheritDoc */ }, { key: "get", value: function get(first, second) { if (this._data == null) { return null; } // parse the arguments var ids = null; var options; if (isId(first) || _Array$isArray(first)) { ids = first; options = second; } else { options = first; } // extend the options with the default options and provided options var viewOptions = _Object$assign({}, this._options, options); // create a combined filter method when needed var thisFilter = _filterInstanceProperty(this._options); var optionsFilter = options && _filterInstanceProperty(options); if (thisFilter && optionsFilter) { viewOptions.filter = function (item) { return thisFilter(item) && optionsFilter(item); }; } if (ids == null) { return this._data.get(viewOptions); } else { return this._data.get(ids, viewOptions); } } /** @inheritDoc */ }, { key: "getIds", value: function getIds(options) { if (this._data.length) { var defaultFilter = _filterInstanceProperty(this._options); var optionsFilter = options != null ? _filterInstanceProperty(options) : null; var filter; if (optionsFilter) { if (defaultFilter) { filter = function filter(item) { return defaultFilter(item) && optionsFilter(item); }; } else { filter = optionsFilter; } } else { filter = defaultFilter; } return this._data.getIds({ filter: filter, order: options && options.order }); } else { return []; } } /** @inheritDoc */ }, { key: "forEach", value: function forEach(callback, options) { if (this._data) { var _context2; var defaultFilter = _filterInstanceProperty(this._options); var optionsFilter = options && _filterInstanceProperty(options); var filter; if (optionsFilter) { if (defaultFilter) { filter = function filter(item) { return defaultFilter(item) && optionsFilter(item); }; } else { filter = optionsFilter; } } else { filter = defaultFilter; } _forEachInstanceProperty(_context2 = this._data).call(_context2, callback, { filter: filter, order: options && options.order }); } } /** @inheritDoc */ }, { key: "map", value: function map(callback, options) { if (this._data) { var _context3; var defaultFilter = _filterInstanceProperty(this._options); var optionsFilter = options && _filterInstanceProperty(options); var filter; if (optionsFilter) { if (defaultFilter) { filter = function filter(item) { return defaultFilter(item) && optionsFilter(item); }; } else { filter = optionsFilter; } } else { filter = defaultFilter; } return _mapInstanceProperty(_context3 = this._data).call(_context3, callback, { filter: filter, order: options && options.order }); } else { return []; } } /** @inheritDoc */ }, { key: "getDataSet", value: function getDataSet() { return this._data.getDataSet(); } /** @inheritDoc */ }, { key: "stream", value: function stream(ids) { var _context4; return this._data.stream(ids || _defineProperty({}, _Symbol$iterator$1, _bindInstanceProperty$1(_context4 = _keysInstanceProperty(this._ids)).call(_context4, this._ids))); } /** * Render the instance unusable prior to garbage collection. * * @remarks * The intention of this method is to help discover scenarios where the data * view is being used when the programmer thinks it has been garbage collected * already. It's stricter version of `dataView.setData(null)`. */ }, { key: "dispose", value: function dispose() { var _this$_data; if ((_this$_data = this._data) !== null && _this$_data !== void 0 && _this$_data.off) { this._data.off("*", this._listener); } var message = "This data view has already been disposed of."; var replacement = { get: function get() { throw new Error(message); }, set: function set() { throw new Error(message); }, configurable: false }; var _iterator = _createForOfIteratorHelper(_Reflect$ownKeys(DataView.prototype)), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var key = _step.value; _Object$defineProperty(this, key, replacement); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } /** * Event listener. Will propagate all events from the connected data set to the subscribers of the DataView, but will filter the items and only trigger when there are changes in the filtered data set. * * @param event - The name of the event. * @param params - Parameters of the event. * @param senderId - Id supplied by the sender. */ }, { key: "_onEvent", value: function _onEvent(event, params, senderId) { if (!params || !params.items || !this._data) { return; } var ids = params.items; var addedIds = []; var updatedIds = []; var removedIds = []; var oldItems = []; var updatedItems = []; var removedItems = []; switch (event) { case "add": // filter the ids of the added items for (var i = 0, len = ids.length; i < len; i++) { var id = ids[i]; var item = this.get(id); if (item) { this._ids.add(id); addedIds.push(id); } } break; case "update": // determine the event from the views viewpoint: an updated // item can be added, updated, or removed from this view. for (var _i2 = 0, _len2 = ids.length; _i2 < _len2; _i2++) { var _id2 = ids[_i2]; var _item = this.get(_id2); if (_item) { if (this._ids.has(_id2)) { updatedIds.push(_id2); updatedItems.push(params.data[_i2]); oldItems.push(params.oldData[_i2]); } else { this._ids.add(_id2); addedIds.push(_id2); } } else { if (this._ids.has(_id2)) { this._ids.delete(_id2); removedIds.push(_id2); removedItems.push(params.oldData[_i2]); } } } break; case "remove": // filter the ids of the removed items for (var _i3 = 0, _len3 = ids.length; _i3 < _len3; _i3++) { var _id3 = ids[_i3]; if (this._ids.has(_id3)) { this._ids.delete(_id3); removedIds.push(_id3); removedItems.push(params.oldData[_i3]); } } break; } this.length += addedIds.length - removedIds.length; if (addedIds.length) { this._trigger("add", { items: addedIds }, senderId); } if (updatedIds.length) { this._trigger("update", { items: updatedIds, oldData: oldItems, data: updatedItems }, senderId); } if (removedIds.length) { this._trigger("remove", { items: removedIds, oldData: removedItems }, senderId); } } }]); return DataView; }(DataSetPart); /** * Check that given value is compatible with Vis Data Set interface. * * @param idProp - The expected property to contain item id. * @param v - The value to be tested. * @returns True if all expected values and methods match, false otherwise. */ function isDataSetLike(idProp, v) { return _typeof$1(v) === "object" && v !== null && idProp === v.idProp && typeof v.add === "function" && typeof v.clear === "function" && typeof v.distinct === "function" && typeof _forEachInstanceProperty(v) === "function" && typeof v.get === "function" && typeof v.getDataSet === "function" && typeof v.getIds === "function" && typeof v.length === "number" && typeof _mapInstanceProperty(v) === "function" && typeof v.max === "function" && typeof v.min === "function" && typeof v.off === "function" && typeof v.on === "function" && typeof v.remove === "function" && typeof v.setOptions === "function" && typeof v.stream === "function" && typeof v.update === "function" && typeof v.updateOnly === "function"; } /** * Check that given value is compatible with Vis Data View interface. * * @param idProp - The expected property to contain item id. * @param v - The value to be tested. * @returns True if all expected values and methods match, false otherwise. */ function isDataViewLike(idProp, v) { return _typeof$1(v) === "object" && v !== null && idProp === v.idProp && typeof _forEachInstanceProperty(v) === "function" && typeof v.get === "function" && typeof v.getDataSet === "function" && typeof v.getIds === "function" && typeof v.length === "number" && typeof _mapInstanceProperty(v) === "function" && typeof v.off === "function" && typeof v.on === "function" && typeof v.stream === "function" && isDataSetLike(idProp, v.getDataSet()); } /***/ }), /***/ 2532: /*!*********************************************!*\ !*** ./node_modules/vis-data/peer/index.js ***! \*********************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "DELETE": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.DELETE), /* harmony export */ "DataSet": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.DataSet), /* harmony export */ "DataStream": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.DataStream), /* harmony export */ "DataView": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.DataView), /* harmony export */ "Queue": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.Queue), /* harmony export */ "createNewDataPipeFrom": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.createNewDataPipeFrom), /* harmony export */ "isDataSetLike": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.isDataSetLike), /* harmony export */ "isDataViewLike": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.isDataViewLike) /* harmony export */ }); /* harmony import */ var _esm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./esm */ 280); /***/ }), /***/ 7818: /*!****************************************************!*\ !*** ./node_modules/vis-network/peer/esm/index.js ***! \****************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "Network": () => (/* reexport safe */ _vis_network__WEBPACK_IMPORTED_MODULE_0__.Network), /* harmony export */ "NetworkImages": () => (/* reexport safe */ _vis_network__WEBPACK_IMPORTED_MODULE_0__.NetworkImages), /* harmony export */ "networkDOTParser": () => (/* reexport safe */ _vis_network__WEBPACK_IMPORTED_MODULE_0__.networkDOTParser), /* harmony export */ "networkGephiParser": () => (/* reexport safe */ _vis_network__WEBPACK_IMPORTED_MODULE_0__.networkGephiParser), /* harmony export */ "networkOptions": () => (/* reexport safe */ _vis_network__WEBPACK_IMPORTED_MODULE_0__.networkOptions), /* harmony export */ "parseDOTNetwork": () => (/* reexport safe */ _vis_network__WEBPACK_IMPORTED_MODULE_0__.parseDOTNetwork), /* harmony export */ "parseGephiNetwork": () => (/* reexport safe */ _vis_network__WEBPACK_IMPORTED_MODULE_0__.parseGephiNetwork) /* harmony export */ }); /* harmony import */ var _vis_network__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./vis-network */ 1102); /***/ }), /***/ 1102: /*!**********************************************************!*\ !*** ./node_modules/vis-network/peer/esm/vis-network.js ***! \**********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "Network": () => (/* binding */ Network), /* harmony export */ "NetworkImages": () => (/* binding */ Images), /* harmony export */ "networkDOTParser": () => (/* binding */ dotparser), /* harmony export */ "networkGephiParser": () => (/* binding */ gephiParser), /* harmony export */ "networkOptions": () => (/* binding */ options), /* harmony export */ "parseDOTNetwork": () => (/* binding */ parseDOTNetwork), /* harmony export */ "parseGephiNetwork": () => (/* binding */ parseGephi) /* harmony export */ }); /* harmony import */ var vis_data_peer_esm_vis_data_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vis-data/peer/esm/vis-data.js */ 6115); /** * vis-network * https://visjs.github.io/vis-network/ * * A dynamic, browser-based visualization library. * * @version 9.1.6 * @date 2023-03-23T21:31:19.223Z * * @copyright (c) 2011-2017 Almende B.V, http://almende.com * @copyright (c) 2017-2019 visjs contributors, https://github.com/visjs * * @license * vis.js is dual licensed under both * * 1. The Apache 2.0 License * http://www.apache.org/licenses/LICENSE-2.0 * * and * * 2. The MIT License * http://opensource.org/licenses/MIT * * vis.js may be distributed under either license. */ var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function getDefaultExportFromCjs(x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var assignExports = {}; var assign$5 = { get exports() { return assignExports; }, set exports(v) { assignExports = v; } }; var check = function (it) { return it && it.Math == Math && it; }; // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 var global$l = // eslint-disable-next-line es/no-global-this -- safe check(typeof globalThis == 'object' && globalThis) || check(typeof window == 'object' && window) || // eslint-disable-next-line no-restricted-globals -- safe check(typeof self == 'object' && self) || check(typeof commonjsGlobal == 'object' && commonjsGlobal) || // eslint-disable-next-line no-new-func -- fallback function () { return this; }() || Function('return this')(); var fails$w = function (exec) { try { return !!exec(); } catch (error) { return true; } }; var fails$v = fails$w; var functionBindNative = !fails$v(function () { // eslint-disable-next-line es/no-function-prototype-bind -- safe var test = function () {/* empty */}.bind(); // eslint-disable-next-line no-prototype-builtins -- safe return typeof test != 'function' || test.hasOwnProperty('prototype'); }); var NATIVE_BIND$4 = functionBindNative; var FunctionPrototype$3 = Function.prototype; var apply$5 = FunctionPrototype$3.apply; var call$f = FunctionPrototype$3.call; // eslint-disable-next-line es/no-reflect -- safe var functionApply = typeof Reflect == 'object' && Reflect.apply || (NATIVE_BIND$4 ? call$f.bind(apply$5) : function () { return call$f.apply(apply$5, arguments); }); var NATIVE_BIND$3 = functionBindNative; var FunctionPrototype$2 = Function.prototype; var call$e = FunctionPrototype$2.call; var uncurryThisWithBind = NATIVE_BIND$3 && FunctionPrototype$2.bind.bind(call$e, call$e); var functionUncurryThis = NATIVE_BIND$3 ? uncurryThisWithBind : function (fn) { return function () { return call$e.apply(fn, arguments); }; }; var uncurryThis$y = functionUncurryThis; var toString$c = uncurryThis$y({}.toString); var stringSlice$1 = uncurryThis$y(''.slice); var classofRaw$2 = function (it) { return stringSlice$1(toString$c(it), 8, -1); }; var classofRaw$1 = classofRaw$2; var uncurryThis$x = functionUncurryThis; var functionUncurryThisClause = function (fn) { // Nashorn bug: // https://github.com/zloirock/core-js/issues/1128 // https://github.com/zloirock/core-js/issues/1130 if (classofRaw$1(fn) === 'Function') return uncurryThis$x(fn); }; var documentAll$2 = typeof document == 'object' && document.all; // https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot // eslint-disable-next-line unicorn/no-typeof-undefined -- required for testing var IS_HTMLDDA = typeof documentAll$2 == 'undefined' && documentAll$2 !== undefined; var documentAll_1 = { all: documentAll$2, IS_HTMLDDA: IS_HTMLDDA }; var $documentAll$1 = documentAll_1; var documentAll$1 = $documentAll$1.all; // `IsCallable` abstract operation // https://tc39.es/ecma262/#sec-iscallable var isCallable$i = $documentAll$1.IS_HTMLDDA ? function (argument) { return typeof argument == 'function' || argument === documentAll$1; } : function (argument) { return typeof argument == 'function'; }; var objectGetOwnPropertyDescriptor = {}; var fails$u = fails$w; // Detect IE8's incomplete defineProperty implementation var descriptors = !fails$u(function () { // eslint-disable-next-line es/no-object-defineproperty -- required for testing return Object.defineProperty({}, 1, { get: function () { return 7; } })[1] != 7; }); var NATIVE_BIND$2 = functionBindNative; var call$d = Function.prototype.call; var functionCall = NATIVE_BIND$2 ? call$d.bind(call$d) : function () { return call$d.apply(call$d, arguments); }; var objectPropertyIsEnumerable = {}; var $propertyIsEnumerable$2 = {}.propertyIsEnumerable; // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe var getOwnPropertyDescriptor$a = Object.getOwnPropertyDescriptor; // Nashorn ~ JDK8 bug var NASHORN_BUG = getOwnPropertyDescriptor$a && !$propertyIsEnumerable$2.call({ 1: 2 }, 1); // `Object.prototype.propertyIsEnumerable` method implementation // https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable objectPropertyIsEnumerable.f = NASHORN_BUG ? function propertyIsEnumerable(V) { var descriptor = getOwnPropertyDescriptor$a(this, V); return !!descriptor && descriptor.enumerable; } : $propertyIsEnumerable$2; var createPropertyDescriptor$5 = function (bitmap, value) { return { enumerable: !(bitmap & 1), configurable: !(bitmap & 2), writable: !(bitmap & 4), value: value }; }; var uncurryThis$w = functionUncurryThis; var fails$t = fails$w; var classof$e = classofRaw$2; var $Object$5 = Object; var split = uncurryThis$w(''.split); // fallback for non-array-like ES3 and non-enumerable old V8 strings var indexedObject = fails$t(function () { // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346 // eslint-disable-next-line no-prototype-builtins -- safe return !$Object$5('z').propertyIsEnumerable(0); }) ? function (it) { return classof$e(it) == 'String' ? split(it, '') : $Object$5(it); } : $Object$5; // we can't use just `it == null` since of `document.all` special case // https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot-aec var isNullOrUndefined$5 = function (it) { return it === null || it === undefined; }; var isNullOrUndefined$4 = isNullOrUndefined$5; var $TypeError$g = TypeError; // `RequireObjectCoercible` abstract operation // https://tc39.es/ecma262/#sec-requireobjectcoercible var requireObjectCoercible$5 = function (it) { if (isNullOrUndefined$4(it)) throw $TypeError$g("Can't call method on " + it); return it; }; // toObject with fallback for non-array-like ES3 strings var IndexedObject$3 = indexedObject; var requireObjectCoercible$4 = requireObjectCoercible$5; var toIndexedObject$b = function (it) { return IndexedObject$3(requireObjectCoercible$4(it)); }; var isCallable$h = isCallable$i; var $documentAll = documentAll_1; var documentAll = $documentAll.all; var isObject$j = $documentAll.IS_HTMLDDA ? function (it) { return typeof it == 'object' ? it !== null : isCallable$h(it) || it === documentAll; } : function (it) { return typeof it == 'object' ? it !== null : isCallable$h(it); }; var path$y = {}; var path$x = path$y; var global$k = global$l; var isCallable$g = isCallable$i; var aFunction = function (variable) { return isCallable$g(variable) ? variable : undefined; }; var getBuiltIn$c = function (namespace, method) { return arguments.length < 2 ? aFunction(path$x[namespace]) || aFunction(global$k[namespace]) : path$x[namespace] && path$x[namespace][method] || global$k[namespace] && global$k[namespace][method]; }; var uncurryThis$v = functionUncurryThis; var objectIsPrototypeOf = uncurryThis$v({}.isPrototypeOf); var engineUserAgent = typeof navigator != 'undefined' && String(navigator.userAgent) || ''; var global$j = global$l; var userAgent$2 = engineUserAgent; var process$1 = global$j.process; var Deno = global$j.Deno; var versions = process$1 && process$1.versions || Deno && Deno.version; var v8 = versions && versions.v8; var match, version; if (v8) { match = v8.split('.'); // in old Chrome, versions of V8 isn't V8 = Chrome / 10 // but their correct versions are not interesting for us version = match[0] > 0 && match[0] < 4 ? 1 : +(match[0] + match[1]); } // BrowserFS NodeJS `process` polyfill incorrectly set `.v8` to `0.0` // so check `userAgent` even if `.v8` exists, but 0 if (!version && userAgent$2) { match = userAgent$2.match(/Edge\/(\d+)/); if (!match || match[1] >= 74) { match = userAgent$2.match(/Chrome\/(\d+)/); if (match) version = +match[1]; } } var engineV8Version = version; /* eslint-disable es/no-symbol -- required for testing */ var V8_VERSION$2 = engineV8Version; var fails$s = fails$w; // eslint-disable-next-line es/no-object-getownpropertysymbols -- required for testing var symbolConstructorDetection = !!Object.getOwnPropertySymbols && !fails$s(function () { var symbol = Symbol(); // Chrome 38 Symbol has incorrect toString conversion // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances return !String(symbol) || !(Object(symbol) instanceof Symbol) || // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances !Symbol.sham && V8_VERSION$2 && V8_VERSION$2 < 41; }); /* eslint-disable es/no-symbol -- required for testing */ var NATIVE_SYMBOL$5 = symbolConstructorDetection; var useSymbolAsUid = NATIVE_SYMBOL$5 && !Symbol.sham && typeof Symbol.iterator == 'symbol'; var getBuiltIn$b = getBuiltIn$c; var isCallable$f = isCallable$i; var isPrototypeOf$k = objectIsPrototypeOf; var USE_SYMBOL_AS_UID$1 = useSymbolAsUid; var $Object$4 = Object; var isSymbol$5 = USE_SYMBOL_AS_UID$1 ? function (it) { return typeof it == 'symbol'; } : function (it) { var $Symbol = getBuiltIn$b('Symbol'); return isCallable$f($Symbol) && isPrototypeOf$k($Symbol.prototype, $Object$4(it)); }; var $String$4 = String; var tryToString$6 = function (argument) { try { return $String$4(argument); } catch (error) { return 'Object'; } }; var isCallable$e = isCallable$i; var tryToString$5 = tryToString$6; var $TypeError$f = TypeError; // `Assert: IsCallable(argument) is true` var aCallable$7 = function (argument) { if (isCallable$e(argument)) return argument; throw $TypeError$f(tryToString$5(argument) + ' is not a function'); }; var aCallable$6 = aCallable$7; var isNullOrUndefined$3 = isNullOrUndefined$5; // `GetMethod` abstract operation // https://tc39.es/ecma262/#sec-getmethod var getMethod$3 = function (V, P) { var func = V[P]; return isNullOrUndefined$3(func) ? undefined : aCallable$6(func); }; var call$c = functionCall; var isCallable$d = isCallable$i; var isObject$i = isObject$j; var $TypeError$e = TypeError; // `OrdinaryToPrimitive` abstract operation // https://tc39.es/ecma262/#sec-ordinarytoprimitive var ordinaryToPrimitive$1 = function (input, pref) { var fn, val; if (pref === 'string' && isCallable$d(fn = input.toString) && !isObject$i(val = call$c(fn, input))) return val; if (isCallable$d(fn = input.valueOf) && !isObject$i(val = call$c(fn, input))) return val; if (pref !== 'string' && isCallable$d(fn = input.toString) && !isObject$i(val = call$c(fn, input))) return val; throw $TypeError$e("Can't convert object to primitive value"); }; var sharedExports = {}; var shared$7 = { get exports() { return sharedExports; }, set exports(v) { sharedExports = v; } }; var global$i = global$l; // eslint-disable-next-line es/no-object-defineproperty -- safe var defineProperty$f = Object.defineProperty; var defineGlobalProperty$1 = function (key, value) { try { defineProperty$f(global$i, key, { value: value, configurable: true, writable: true }); } catch (error) { global$i[key] = value; } return value; }; var global$h = global$l; var defineGlobalProperty = defineGlobalProperty$1; var SHARED = '__core-js_shared__'; var store$3 = global$h[SHARED] || defineGlobalProperty(SHARED, {}); var sharedStore = store$3; var store$2 = sharedStore; (shared$7.exports = function (key, value) { return store$2[key] || (store$2[key] = value !== undefined ? value : {}); })('versions', []).push({ version: '3.29.0', mode: 'pure', copyright: '© 2014-2023 Denis Pushkarev (zloirock.ru)', license: 'https://github.com/zloirock/core-js/blob/v3.29.0/LICENSE', source: 'https://github.com/zloirock/core-js' }); var requireObjectCoercible$3 = requireObjectCoercible$5; var $Object$3 = Object; // `ToObject` abstract operation // https://tc39.es/ecma262/#sec-toobject var toObject$d = function (argument) { return $Object$3(requireObjectCoercible$3(argument)); }; var uncurryThis$u = functionUncurryThis; var toObject$c = toObject$d; var hasOwnProperty = uncurryThis$u({}.hasOwnProperty); // `HasOwnProperty` abstract operation // https://tc39.es/ecma262/#sec-hasownproperty // eslint-disable-next-line es/no-object-hasown -- safe var hasOwnProperty_1 = Object.hasOwn || function hasOwn(it, key) { return hasOwnProperty(toObject$c(it), key); }; var uncurryThis$t = functionUncurryThis; var id$2 = 0; var postfix = Math.random(); var toString$b = uncurryThis$t(1.0.toString); var uid$4 = function (key) { return 'Symbol(' + (key === undefined ? '' : key) + ')_' + toString$b(++id$2 + postfix, 36); }; var global$g = global$l; var shared$6 = sharedExports; var hasOwn$h = hasOwnProperty_1; var uid$3 = uid$4; var NATIVE_SYMBOL$4 = symbolConstructorDetection; var USE_SYMBOL_AS_UID = useSymbolAsUid; var Symbol$5 = global$g.Symbol; var WellKnownSymbolsStore$2 = shared$6('wks'); var createWellKnownSymbol = USE_SYMBOL_AS_UID ? Symbol$5['for'] || Symbol$5 : Symbol$5 && Symbol$5.withoutSetter || uid$3; var wellKnownSymbol$l = function (name) { if (!hasOwn$h(WellKnownSymbolsStore$2, name)) { WellKnownSymbolsStore$2[name] = NATIVE_SYMBOL$4 && hasOwn$h(Symbol$5, name) ? Symbol$5[name] : createWellKnownSymbol('Symbol.' + name); } return WellKnownSymbolsStore$2[name]; }; var call$b = functionCall; var isObject$h = isObject$j; var isSymbol$4 = isSymbol$5; var getMethod$2 = getMethod$3; var ordinaryToPrimitive = ordinaryToPrimitive$1; var wellKnownSymbol$k = wellKnownSymbol$l; var $TypeError$d = TypeError; var TO_PRIMITIVE = wellKnownSymbol$k('toPrimitive'); // `ToPrimitive` abstract operation // https://tc39.es/ecma262/#sec-toprimitive var toPrimitive$7 = function (input, pref) { if (!isObject$h(input) || isSymbol$4(input)) return input; var exoticToPrim = getMethod$2(input, TO_PRIMITIVE); var result; if (exoticToPrim) { if (pref === undefined) pref = 'default'; result = call$b(exoticToPrim, input, pref); if (!isObject$h(result) || isSymbol$4(result)) return result; throw $TypeError$d("Can't convert object to primitive value"); } if (pref === undefined) pref = 'number'; return ordinaryToPrimitive(input, pref); }; var toPrimitive$6 = toPrimitive$7; var isSymbol$3 = isSymbol$5; // `ToPropertyKey` abstract operation // https://tc39.es/ecma262/#sec-topropertykey var toPropertyKey$4 = function (argument) { var key = toPrimitive$6(argument, 'string'); return isSymbol$3(key) ? key : key + ''; }; var global$f = global$l; var isObject$g = isObject$j; var document$1 = global$f.document; // typeof document.createElement is 'object' in old IE var EXISTS$1 = isObject$g(document$1) && isObject$g(document$1.createElement); var documentCreateElement$1 = function (it) { return EXISTS$1 ? document$1.createElement(it) : {}; }; var DESCRIPTORS$i = descriptors; var fails$r = fails$w; var createElement = documentCreateElement$1; // Thanks to IE8 for its funny defineProperty var ie8DomDefine = !DESCRIPTORS$i && !fails$r(function () { // eslint-disable-next-line es/no-object-defineproperty -- required for testing return Object.defineProperty(createElement('div'), 'a', { get: function () { return 7; } }).a != 7; }); var DESCRIPTORS$h = descriptors; var call$a = functionCall; var propertyIsEnumerableModule$2 = objectPropertyIsEnumerable; var createPropertyDescriptor$4 = createPropertyDescriptor$5; var toIndexedObject$a = toIndexedObject$b; var toPropertyKey$3 = toPropertyKey$4; var hasOwn$g = hasOwnProperty_1; var IE8_DOM_DEFINE$1 = ie8DomDefine; // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe var $getOwnPropertyDescriptor$2 = Object.getOwnPropertyDescriptor; // `Object.getOwnPropertyDescriptor` method // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor objectGetOwnPropertyDescriptor.f = DESCRIPTORS$h ? $getOwnPropertyDescriptor$2 : function getOwnPropertyDescriptor(O, P) { O = toIndexedObject$a(O); P = toPropertyKey$3(P); if (IE8_DOM_DEFINE$1) try { return $getOwnPropertyDescriptor$2(O, P); } catch (error) {/* empty */} if (hasOwn$g(O, P)) return createPropertyDescriptor$4(!call$a(propertyIsEnumerableModule$2.f, O, P), O[P]); }; var fails$q = fails$w; var isCallable$c = isCallable$i; var replacement = /#|\.prototype\./; var isForced$1 = function (feature, detection) { var value = data[normalize(feature)]; return value == POLYFILL ? true : value == NATIVE ? false : isCallable$c(detection) ? fails$q(detection) : !!detection; }; var normalize = isForced$1.normalize = function (string) { return String(string).replace(replacement, '.').toLowerCase(); }; var data = isForced$1.data = {}; var NATIVE = isForced$1.NATIVE = 'N'; var POLYFILL = isForced$1.POLYFILL = 'P'; var isForced_1 = isForced$1; var uncurryThis$s = functionUncurryThisClause; var aCallable$5 = aCallable$7; var NATIVE_BIND$1 = functionBindNative; var bind$f = uncurryThis$s(uncurryThis$s.bind); // optional / simple context binding var functionBindContext = function (fn, that) { aCallable$5(fn); return that === undefined ? fn : NATIVE_BIND$1 ? bind$f(fn, that) : function /* ...args */ () { return fn.apply(that, arguments); }; }; var objectDefineProperty = {}; var DESCRIPTORS$g = descriptors; var fails$p = fails$w; // V8 ~ Chrome 36- // https://bugs.chromium.org/p/v8/issues/detail?id=3334 var v8PrototypeDefineBug = DESCRIPTORS$g && fails$p(function () { // eslint-disable-next-line es/no-object-defineproperty -- required for testing return Object.defineProperty(function () {/* empty */}, 'prototype', { value: 42, writable: false }).prototype != 42; }); var isObject$f = isObject$j; var $String$3 = String; var $TypeError$c = TypeError; // `Assert: Type(argument) is Object` var anObject$d = function (argument) { if (isObject$f(argument)) return argument; throw $TypeError$c($String$3(argument) + ' is not an object'); }; var DESCRIPTORS$f = descriptors; var IE8_DOM_DEFINE = ie8DomDefine; var V8_PROTOTYPE_DEFINE_BUG$1 = v8PrototypeDefineBug; var anObject$c = anObject$d; var toPropertyKey$2 = toPropertyKey$4; var $TypeError$b = TypeError; // eslint-disable-next-line es/no-object-defineproperty -- safe var $defineProperty$1 = Object.defineProperty; // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe var $getOwnPropertyDescriptor$1 = Object.getOwnPropertyDescriptor; var ENUMERABLE = 'enumerable'; var CONFIGURABLE$1 = 'configurable'; var WRITABLE = 'writable'; // `Object.defineProperty` method // https://tc39.es/ecma262/#sec-object.defineproperty objectDefineProperty.f = DESCRIPTORS$f ? V8_PROTOTYPE_DEFINE_BUG$1 ? function defineProperty(O, P, Attributes) { anObject$c(O); P = toPropertyKey$2(P); anObject$c(Attributes); if (typeof O === 'function' && P === 'prototype' && 'value' in Attributes && WRITABLE in Attributes && !Attributes[WRITABLE]) { var current = $getOwnPropertyDescriptor$1(O, P); if (current && current[WRITABLE]) { O[P] = Attributes.value; Attributes = { configurable: CONFIGURABLE$1 in Attributes ? Attributes[CONFIGURABLE$1] : current[CONFIGURABLE$1], enumerable: ENUMERABLE in Attributes ? Attributes[ENUMERABLE] : current[ENUMERABLE], writable: false }; } } return $defineProperty$1(O, P, Attributes); } : $defineProperty$1 : function defineProperty(O, P, Attributes) { anObject$c(O); P = toPropertyKey$2(P); anObject$c(Attributes); if (IE8_DOM_DEFINE) try { return $defineProperty$1(O, P, Attributes); } catch (error) {/* empty */} if ('get' in Attributes || 'set' in Attributes) throw $TypeError$b('Accessors not supported'); if ('value' in Attributes) O[P] = Attributes.value; return O; }; var DESCRIPTORS$e = descriptors; var definePropertyModule$3 = objectDefineProperty; var createPropertyDescriptor$3 = createPropertyDescriptor$5; var createNonEnumerableProperty$6 = DESCRIPTORS$e ? function (object, key, value) { return definePropertyModule$3.f(object, key, createPropertyDescriptor$3(1, value)); } : function (object, key, value) { object[key] = value; return object; }; var global$e = global$l; var apply$4 = functionApply; var uncurryThis$r = functionUncurryThisClause; var isCallable$b = isCallable$i; var getOwnPropertyDescriptor$9 = objectGetOwnPropertyDescriptor.f; var isForced = isForced_1; var path$w = path$y; var bind$e = functionBindContext; var createNonEnumerableProperty$5 = createNonEnumerableProperty$6; var hasOwn$f = hasOwnProperty_1; var wrapConstructor = function (NativeConstructor) { var Wrapper = function (a, b, c) { if (this instanceof Wrapper) { switch (arguments.length) { case 0: return new NativeConstructor(); case 1: return new NativeConstructor(a); case 2: return new NativeConstructor(a, b); } return new NativeConstructor(a, b, c); } return apply$4(NativeConstructor, this, arguments); }; Wrapper.prototype = NativeConstructor.prototype; return Wrapper; }; /* options.target - name of the target object options.global - target is the global object options.stat - export as static methods of target options.proto - export as prototype methods of target options.real - real prototype method for the `pure` version options.forced - export even if the native feature is available options.bind - bind methods to the target, required for the `pure` version options.wrap - wrap constructors to preventing global pollution, required for the `pure` version options.unsafe - use the simple assignment of property instead of delete + defineProperty options.sham - add a flag to not completely full polyfills options.enumerable - export as enumerable property options.dontCallGetSet - prevent calling a getter on target options.name - the .name of the function if it does not match the key */ var _export = function (options, source) { var TARGET = options.target; var GLOBAL = options.global; var STATIC = options.stat; var PROTO = options.proto; var nativeSource = GLOBAL ? global$e : STATIC ? global$e[TARGET] : (global$e[TARGET] || {}).prototype; var target = GLOBAL ? path$w : path$w[TARGET] || createNonEnumerableProperty$5(path$w, TARGET, {})[TARGET]; var targetPrototype = target.prototype; var FORCED, USE_NATIVE, VIRTUAL_PROTOTYPE; var key, sourceProperty, targetProperty, nativeProperty, resultProperty, descriptor; for (key in source) { FORCED = isForced(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced); // contains in native USE_NATIVE = !FORCED && nativeSource && hasOwn$f(nativeSource, key); targetProperty = target[key]; if (USE_NATIVE) if (options.dontCallGetSet) { descriptor = getOwnPropertyDescriptor$9(nativeSource, key); nativeProperty = descriptor && descriptor.value; } else nativeProperty = nativeSource[key]; // export native or implementation sourceProperty = USE_NATIVE && nativeProperty ? nativeProperty : source[key]; if (USE_NATIVE && typeof targetProperty == typeof sourceProperty) continue; // bind methods to global for calling from export context if (options.bind && USE_NATIVE) resultProperty = bind$e(sourceProperty, global$e); // wrap global constructors for prevent changes in this version else if (options.wrap && USE_NATIVE) resultProperty = wrapConstructor(sourceProperty); // make static versions for prototype methods else if (PROTO && isCallable$b(sourceProperty)) resultProperty = uncurryThis$r(sourceProperty); // default case else resultProperty = sourceProperty; // add a flag to not completely full polyfills if (options.sham || sourceProperty && sourceProperty.sham || targetProperty && targetProperty.sham) { createNonEnumerableProperty$5(resultProperty, 'sham', true); } createNonEnumerableProperty$5(target, key, resultProperty); if (PROTO) { VIRTUAL_PROTOTYPE = TARGET + 'Prototype'; if (!hasOwn$f(path$w, VIRTUAL_PROTOTYPE)) { createNonEnumerableProperty$5(path$w, VIRTUAL_PROTOTYPE, {}); } // export virtual prototype methods createNonEnumerableProperty$5(path$w[VIRTUAL_PROTOTYPE], key, sourceProperty); // export real prototype methods if (options.real && targetPrototype && (FORCED || !targetPrototype[key])) { createNonEnumerableProperty$5(targetPrototype, key, sourceProperty); } } } }; var ceil = Math.ceil; var floor$1 = Math.floor; // `Math.trunc` method // https://tc39.es/ecma262/#sec-math.trunc // eslint-disable-next-line es/no-math-trunc -- safe var mathTrunc = Math.trunc || function trunc(x) { var n = +x; return (n > 0 ? floor$1 : ceil)(n); }; var trunc = mathTrunc; // `ToIntegerOrInfinity` abstract operation // https://tc39.es/ecma262/#sec-tointegerorinfinity var toIntegerOrInfinity$4 = function (argument) { var number = +argument; // eslint-disable-next-line no-self-compare -- NaN check return number !== number || number === 0 ? 0 : trunc(number); }; var toIntegerOrInfinity$3 = toIntegerOrInfinity$4; var max$3 = Math.max; var min$2 = Math.min; // Helper for a popular repeating case of the spec: // Let integer be ? ToInteger(index). // If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length). var toAbsoluteIndex$5 = function (index, length) { var integer = toIntegerOrInfinity$3(index); return integer < 0 ? max$3(integer + length, 0) : min$2(integer, length); }; var toIntegerOrInfinity$2 = toIntegerOrInfinity$4; var min$1 = Math.min; // `ToLength` abstract operation // https://tc39.es/ecma262/#sec-tolength var toLength$1 = function (argument) { return argument > 0 ? min$1(toIntegerOrInfinity$2(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991 }; var toLength = toLength$1; // `LengthOfArrayLike` abstract operation // https://tc39.es/ecma262/#sec-lengthofarraylike var lengthOfArrayLike$b = function (obj) { return toLength(obj.length); }; var toIndexedObject$9 = toIndexedObject$b; var toAbsoluteIndex$4 = toAbsoluteIndex$5; var lengthOfArrayLike$a = lengthOfArrayLike$b; // `Array.prototype.{ indexOf, includes }` methods implementation var createMethod$5 = function (IS_INCLUDES) { return function ($this, el, fromIndex) { var O = toIndexedObject$9($this); var length = lengthOfArrayLike$a(O); var index = toAbsoluteIndex$4(fromIndex, length); var value; // Array#includes uses SameValueZero equality algorithm // eslint-disable-next-line no-self-compare -- NaN check if (IS_INCLUDES && el != el) while (length > index) { value = O[index++]; // eslint-disable-next-line no-self-compare -- NaN check if (value != value) return true; // Array#indexOf ignores holes, Array#includes - not } else for (; length > index; index++) { if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0; } return !IS_INCLUDES && -1; }; }; var arrayIncludes = { // `Array.prototype.includes` method // https://tc39.es/ecma262/#sec-array.prototype.includes includes: createMethod$5(true), // `Array.prototype.indexOf` method // https://tc39.es/ecma262/#sec-array.prototype.indexof indexOf: createMethod$5(false) }; var hiddenKeys$6 = {}; var uncurryThis$q = functionUncurryThis; var hasOwn$e = hasOwnProperty_1; var toIndexedObject$8 = toIndexedObject$b; var indexOf$4 = arrayIncludes.indexOf; var hiddenKeys$5 = hiddenKeys$6; var push$6 = uncurryThis$q([].push); var objectKeysInternal = function (object, names) { var O = toIndexedObject$8(object); var i = 0; var result = []; var key; for (key in O) !hasOwn$e(hiddenKeys$5, key) && hasOwn$e(O, key) && push$6(result, key); // Don't enum bug & hidden keys while (names.length > i) if (hasOwn$e(O, key = names[i++])) { ~indexOf$4(result, key) || push$6(result, key); } return result; }; // IE8- don't enum bug keys var enumBugKeys$3 = ['constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf']; var internalObjectKeys$1 = objectKeysInternal; var enumBugKeys$2 = enumBugKeys$3; // `Object.keys` method // https://tc39.es/ecma262/#sec-object.keys // eslint-disable-next-line es/no-object-keys -- safe var objectKeys$4 = Object.keys || function keys(O) { return internalObjectKeys$1(O, enumBugKeys$2); }; var objectGetOwnPropertySymbols = {}; // eslint-disable-next-line es/no-object-getownpropertysymbols -- safe objectGetOwnPropertySymbols.f = Object.getOwnPropertySymbols; var DESCRIPTORS$d = descriptors; var uncurryThis$p = functionUncurryThis; var call$9 = functionCall; var fails$o = fails$w; var objectKeys$3 = objectKeys$4; var getOwnPropertySymbolsModule$3 = objectGetOwnPropertySymbols; var propertyIsEnumerableModule$1 = objectPropertyIsEnumerable; var toObject$b = toObject$d; var IndexedObject$2 = indexedObject; // eslint-disable-next-line es/no-object-assign -- safe var $assign = Object.assign; // eslint-disable-next-line es/no-object-defineproperty -- required for testing var defineProperty$e = Object.defineProperty; var concat$6 = uncurryThis$p([].concat); // `Object.assign` method // https://tc39.es/ecma262/#sec-object.assign var objectAssign = !$assign || fails$o(function () { // should have correct order of operations (Edge bug) if (DESCRIPTORS$d && $assign({ b: 1 }, $assign(defineProperty$e({}, 'a', { enumerable: true, get: function () { defineProperty$e(this, 'b', { value: 3, enumerable: false }); } }), { b: 2 })).b !== 1) return true; // should work with symbols and should have deterministic property order (V8 bug) var A = {}; var B = {}; // eslint-disable-next-line es/no-symbol -- safe var symbol = Symbol(); var alphabet = 'abcdefghijklmnopqrst'; A[symbol] = 7; alphabet.split('').forEach(function (chr) { B[chr] = chr; }); return $assign({}, A)[symbol] != 7 || objectKeys$3($assign({}, B)).join('') != alphabet; }) ? function assign(target, source) { // eslint-disable-line no-unused-vars -- required for `.length` var T = toObject$b(target); var argumentsLength = arguments.length; var index = 1; var getOwnPropertySymbols = getOwnPropertySymbolsModule$3.f; var propertyIsEnumerable = propertyIsEnumerableModule$1.f; while (argumentsLength > index) { var S = IndexedObject$2(arguments[index++]); var keys = getOwnPropertySymbols ? concat$6(objectKeys$3(S), getOwnPropertySymbols(S)) : objectKeys$3(S); var length = keys.length; var j = 0; var key; while (length > j) { key = keys[j++]; if (!DESCRIPTORS$d || call$9(propertyIsEnumerable, S, key)) T[key] = S[key]; } } return T; } : $assign; var $$O = _export; var assign$4 = objectAssign; // `Object.assign` method // https://tc39.es/ecma262/#sec-object.assign // eslint-disable-next-line es/no-object-assign -- required for testing $$O({ target: 'Object', stat: true, arity: 2, forced: Object.assign !== assign$4 }, { assign: assign$4 }); var path$v = path$y; var assign$3 = path$v.Object.assign; var parent$1b = assign$3; var assign$2 = parent$1b; (function (module) { module.exports = assign$2; })(assign$5); var _Object$assign = /*@__PURE__*/getDefaultExportFromCjs(assignExports); var bindExports$2 = {}; var bind$d = { get exports() { return bindExports$2; }, set exports(v) { bindExports$2 = v; } }; var uncurryThis$o = functionUncurryThis; var arraySlice$5 = uncurryThis$o([].slice); var uncurryThis$n = functionUncurryThis; var aCallable$4 = aCallable$7; var isObject$e = isObject$j; var hasOwn$d = hasOwnProperty_1; var arraySlice$4 = arraySlice$5; var NATIVE_BIND = functionBindNative; var $Function = Function; var concat$5 = uncurryThis$n([].concat); var join = uncurryThis$n([].join); var factories = {}; var construct$4 = function (C, argsLength, args) { if (!hasOwn$d(factories, argsLength)) { for (var list = [], i = 0; i < argsLength; i++) list[i] = 'a[' + i + ']'; factories[argsLength] = $Function('C,a', 'return new C(' + join(list, ',') + ')'); } return factories[argsLength](C, args); }; // `Function.prototype.bind` method implementation // https://tc39.es/ecma262/#sec-function.prototype.bind // eslint-disable-next-line es/no-function-prototype-bind -- detection var functionBind = NATIVE_BIND ? $Function.bind : function bind(that /* , ...args */) { var F = aCallable$4(this); var Prototype = F.prototype; var partArgs = arraySlice$4(arguments, 1); var boundFunction = function bound( /* args... */ ) { var args = concat$5(partArgs, arraySlice$4(arguments)); return this instanceof boundFunction ? construct$4(F, args.length, args) : F.apply(that, args); }; if (isObject$e(Prototype)) boundFunction.prototype = Prototype; return boundFunction; }; // TODO: Remove from `core-js@4` var $$N = _export; var bind$c = functionBind; // `Function.prototype.bind` method // https://tc39.es/ecma262/#sec-function.prototype.bind // eslint-disable-next-line es/no-function-prototype-bind -- detection $$N({ target: 'Function', proto: true, forced: Function.bind !== bind$c }, { bind: bind$c }); var path$u = path$y; var entryVirtual$i = function (CONSTRUCTOR) { return path$u[CONSTRUCTOR + 'Prototype']; }; var entryVirtual$h = entryVirtual$i; var bind$b = entryVirtual$h('Function').bind; var isPrototypeOf$j = objectIsPrototypeOf; var method$f = bind$b; var FunctionPrototype$1 = Function.prototype; var bind$a = function (it) { var own = it.bind; return it === FunctionPrototype$1 || isPrototypeOf$j(FunctionPrototype$1, it) && own === FunctionPrototype$1.bind ? method$f : own; }; var parent$1a = bind$a; var bind$9 = parent$1a; (function (module) { module.exports = bind$9; })(bind$d); var _bindInstanceProperty$1 = /*@__PURE__*/getDefaultExportFromCjs(bindExports$2); /** * Draw a circle. * * @param ctx - The context this shape will be rendered to. * @param x - The position of the center on the x axis. * @param y - The position of the center on the y axis. * @param r - The radius of the circle. */ function drawCircle(ctx, x, y, r) { ctx.beginPath(); ctx.arc(x, y, r, 0, 2 * Math.PI, false); ctx.closePath(); } /** * Draw a square. * * @param ctx - The context this shape will be rendered to. * @param x - The position of the center on the x axis. * @param y - The position of the center on the y axis. * @param r - Half of the width and height of the square. */ function drawSquare(ctx, x, y, r) { ctx.beginPath(); ctx.rect(x - r, y - r, r * 2, r * 2); ctx.closePath(); } /** * Draw an equilateral triangle standing on a side. * * @param ctx - The context this shape will be rendered to. * @param x - The position of the center on the x axis. * @param y - The position of the center on the y axis. * @param r - Half of the length of the sides. * @remarks * http://en.wikipedia.org/wiki/Equilateral_triangle */ function drawTriangle(ctx, x, y, r) { ctx.beginPath(); // the change in radius and the offset is here to center the shape r *= 1.15; y += 0.275 * r; var s = r * 2; var s2 = s / 2; var ir = Math.sqrt(3) / 6 * s; // radius of inner circle var h = Math.sqrt(s * s - s2 * s2); // height ctx.moveTo(x, y - (h - ir)); ctx.lineTo(x + s2, y + ir); ctx.lineTo(x - s2, y + ir); ctx.lineTo(x, y - (h - ir)); ctx.closePath(); } /** * Draw an equilateral triangle standing on a vertex. * * @param ctx - The context this shape will be rendered to. * @param x - The position of the center on the x axis. * @param y - The position of the center on the y axis. * @param r - Half of the length of the sides. * @remarks * http://en.wikipedia.org/wiki/Equilateral_triangle */ function drawTriangleDown(ctx, x, y, r) { ctx.beginPath(); // the change in radius and the offset is here to center the shape r *= 1.15; y -= 0.275 * r; var s = r * 2; var s2 = s / 2; var ir = Math.sqrt(3) / 6 * s; // radius of inner circle var h = Math.sqrt(s * s - s2 * s2); // height ctx.moveTo(x, y + (h - ir)); ctx.lineTo(x + s2, y - ir); ctx.lineTo(x - s2, y - ir); ctx.lineTo(x, y + (h - ir)); ctx.closePath(); } /** * Draw a star. * * @param ctx - The context this shape will be rendered to. * @param x - The position of the center on the x axis. * @param y - The position of the center on the y axis. * @param r - The outer radius of the star. */ function drawStar(ctx, x, y, r) { // http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/ ctx.beginPath(); // the change in radius and the offset is here to center the shape r *= 0.82; y += 0.1 * r; for (var n = 0; n < 10; n++) { var radius = n % 2 === 0 ? r * 1.3 : r * 0.5; ctx.lineTo(x + radius * Math.sin(n * 2 * Math.PI / 10), y - radius * Math.cos(n * 2 * Math.PI / 10)); } ctx.closePath(); } /** * Draw a diamond. * * @param ctx - The context this shape will be rendered to. * @param x - The position of the center on the x axis. * @param y - The position of the center on the y axis. * @param r - Half of the width and height of the diamond. * @remarks * http://www.html5canvastutorials.com/labs/html5-canvas-star-spinner/ */ function drawDiamond(ctx, x, y, r) { ctx.beginPath(); ctx.lineTo(x, y + r); ctx.lineTo(x + r, y); ctx.lineTo(x, y - r); ctx.lineTo(x - r, y); ctx.closePath(); } /** * Draw a rectangle with rounded corners. * * @param ctx - The context this shape will be rendered to. * @param x - The position of the center on the x axis. * @param y - The position of the center on the y axis. * @param w - The width of the rectangle. * @param h - The height of the rectangle. * @param r - The radius of the corners. * @remarks * http://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-on-html-canvas */ function drawRoundRect(ctx, x, y, w, h, r) { var r2d = Math.PI / 180; if (w - 2 * r < 0) { r = w / 2; } //ensure that the radius isn't too large for x if (h - 2 * r < 0) { r = h / 2; } //ensure that the radius isn't too large for y ctx.beginPath(); ctx.moveTo(x + r, y); ctx.lineTo(x + w - r, y); ctx.arc(x + w - r, y + r, r, r2d * 270, r2d * 360, false); ctx.lineTo(x + w, y + h - r); ctx.arc(x + w - r, y + h - r, r, 0, r2d * 90, false); ctx.lineTo(x + r, y + h); ctx.arc(x + r, y + h - r, r, r2d * 90, r2d * 180, false); ctx.lineTo(x, y + r); ctx.arc(x + r, y + r, r, r2d * 180, r2d * 270, false); ctx.closePath(); } /** * Draw an ellipse. * * @param ctx - The context this shape will be rendered to. * @param x - The position of the center on the x axis. * @param y - The position of the center on the y axis. * @param w - The width of the ellipse. * @param h - The height of the ellipse. * @remarks * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas * * Postfix '_vis' added to discern it from standard method ellipse(). */ function drawEllipse(ctx, x, y, w, h) { var kappa = 0.5522848, ox = w / 2 * kappa, // control point offset horizontal oy = h / 2 * kappa, // control point offset vertical xe = x + w, // x-end ye = y + h, // y-end xm = x + w / 2, // x-middle ym = y + h / 2; // y-middle ctx.beginPath(); ctx.moveTo(x, ym); ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); ctx.closePath(); } /** * Draw an isometric cylinder. * * @param ctx - The context this shape will be rendered to. * @param x - The position of the center on the x axis. * @param y - The position of the center on the y axis. * @param w - The width of the database. * @param h - The height of the database. * @remarks * http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas */ function drawDatabase(ctx, x, y, w, h) { var f = 1 / 3; var wEllipse = w; var hEllipse = h * f; var kappa = 0.5522848, ox = wEllipse / 2 * kappa, // control point offset horizontal oy = hEllipse / 2 * kappa, // control point offset vertical xe = x + wEllipse, // x-end ye = y + hEllipse, // y-end xm = x + wEllipse / 2, // x-middle ym = y + hEllipse / 2, // y-middle ymb = y + (h - hEllipse / 2), // y-midlle, bottom ellipse yeb = y + h; // y-end, bottom ellipse ctx.beginPath(); ctx.moveTo(xe, ym); ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); ctx.lineTo(xe, ymb); ctx.bezierCurveTo(xe, ymb + oy, xm + ox, yeb, xm, yeb); ctx.bezierCurveTo(xm - ox, yeb, x, ymb + oy, x, ymb); ctx.lineTo(x, ym); } /** * Draw a dashed line. * * @param ctx - The context this shape will be rendered to. * @param x - The start position on the x axis. * @param y - The start position on the y axis. * @param x2 - The end position on the x axis. * @param y2 - The end position on the y axis. * @param pattern - List of lengths starting with line and then alternating between space and line. * @author David Jordan * @remarks * date 2012-08-08 * http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas */ function drawDashedLine(ctx, x, y, x2, y2, pattern) { ctx.beginPath(); ctx.moveTo(x, y); var patternLength = pattern.length; var dx = x2 - x; var dy = y2 - y; var slope = dy / dx; var distRemaining = Math.sqrt(dx * dx + dy * dy); var patternIndex = 0; var draw = true; var xStep = 0; var dashLength = +pattern[0]; while (distRemaining >= 0.1) { dashLength = +pattern[patternIndex++ % patternLength]; if (dashLength > distRemaining) { dashLength = distRemaining; } xStep = Math.sqrt(dashLength * dashLength / (1 + slope * slope)); xStep = dx < 0 ? -xStep : xStep; x += xStep; y += slope * xStep; if (draw === true) { ctx.lineTo(x, y); } else { ctx.moveTo(x, y); } distRemaining -= dashLength; draw = !draw; } } /** * Draw a hexagon. * * @param ctx - The context this shape will be rendered to. * @param x - The position of the center on the x axis. * @param y - The position of the center on the y axis. * @param r - The radius of the hexagon. */ function drawHexagon(ctx, x, y, r) { ctx.beginPath(); var sides = 6; var a = Math.PI * 2 / sides; ctx.moveTo(x + r, y); for (var i = 1; i < sides; i++) { ctx.lineTo(x + r * Math.cos(a * i), y + r * Math.sin(a * i)); } ctx.closePath(); } var shapeMap = { circle: drawCircle, dashedLine: drawDashedLine, database: drawDatabase, diamond: drawDiamond, ellipse: drawEllipse, ellipse_vis: drawEllipse, hexagon: drawHexagon, roundRect: drawRoundRect, square: drawSquare, star: drawStar, triangle: drawTriangle, triangleDown: drawTriangleDown }; /** * Returns either custom or native drawing function base on supplied name. * * @param name - The name of the function. Either the name of a * CanvasRenderingContext2D property or an export from shapes.ts without the * draw prefix. * @returns The function that can be used for rendering. In case of native * CanvasRenderingContext2D function the API is normalized to * `(ctx: CanvasRenderingContext2D, ...originalArgs) => void`. */ function getShape(name) { if (Object.prototype.hasOwnProperty.call(shapeMap, name)) { return shapeMap[name]; } else { return function (ctx) { for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } CanvasRenderingContext2D.prototype[name].call(ctx, args); }; } } var componentEmitterExports = {}; var componentEmitter = { get exports() { return componentEmitterExports; }, set exports(v) { componentEmitterExports = v; } }; (function (module) { /** * Expose `Emitter`. */ { module.exports = Emitter; } /** * Initialize a new `Emitter`. * * @api public */ function Emitter(obj) { if (obj) return mixin(obj); } /** * Mixin the emitter properties. * * @param {Object} obj * @return {Object} * @api private */ function mixin(obj) { for (var key in Emitter.prototype) { obj[key] = Emitter.prototype[key]; } return obj; } /** * Listen on the given `event` with `fn`. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.on = Emitter.prototype.addEventListener = function (event, fn) { this._callbacks = this._callbacks || {}; (this._callbacks['$' + event] = this._callbacks['$' + event] || []).push(fn); return this; }; /** * Adds an `event` listener that will be invoked a single * time then automatically removed. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.once = function (event, fn) { function on() { this.off(event, on); fn.apply(this, arguments); } on.fn = fn; this.on(event, on); return this; }; /** * Remove the given callback for `event` or all * registered callbacks. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.off = Emitter.prototype.removeListener = Emitter.prototype.removeAllListeners = Emitter.prototype.removeEventListener = function (event, fn) { this._callbacks = this._callbacks || {}; // all if (0 == arguments.length) { this._callbacks = {}; return this; } // specific event var callbacks = this._callbacks['$' + event]; if (!callbacks) return this; // remove all handlers if (1 == arguments.length) { delete this._callbacks['$' + event]; return this; } // remove specific handler var cb; for (var i = 0; i < callbacks.length; i++) { cb = callbacks[i]; if (cb === fn || cb.fn === fn) { callbacks.splice(i, 1); break; } } // Remove event specific arrays for event types that no // one is subscribed for to avoid memory leak. if (callbacks.length === 0) { delete this._callbacks['$' + event]; } return this; }; /** * Emit `event` with the given args. * * @param {String} event * @param {Mixed} ... * @return {Emitter} */ Emitter.prototype.emit = function (event) { this._callbacks = this._callbacks || {}; var args = new Array(arguments.length - 1), callbacks = this._callbacks['$' + event]; for (var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } if (callbacks) { callbacks = callbacks.slice(0); for (var i = 0, len = callbacks.length; i < len; ++i) { callbacks[i].apply(this, args); } } return this; }; /** * Return array of callbacks for `event`. * * @param {String} event * @return {Array} * @api public */ Emitter.prototype.listeners = function (event) { this._callbacks = this._callbacks || {}; return this._callbacks['$' + event] || []; }; /** * Check if this emitter has `event` handlers. * * @param {String} event * @return {Boolean} * @api public */ Emitter.prototype.hasListeners = function (event) { return !!this.listeners(event).length; }; })(componentEmitter); var Emitter = componentEmitterExports; var fromExports$2 = {}; var from$7 = { get exports() { return fromExports$2; }, set exports(v) { fromExports$2 = v; } }; var wellKnownSymbol$j = wellKnownSymbol$l; var TO_STRING_TAG$3 = wellKnownSymbol$j('toStringTag'); var test$2 = {}; test$2[TO_STRING_TAG$3] = 'z'; var toStringTagSupport = String(test$2) === '[object z]'; var TO_STRING_TAG_SUPPORT$2 = toStringTagSupport; var isCallable$a = isCallable$i; var classofRaw = classofRaw$2; var wellKnownSymbol$i = wellKnownSymbol$l; var TO_STRING_TAG$2 = wellKnownSymbol$i('toStringTag'); var $Object$2 = Object; // ES3 wrong here var CORRECT_ARGUMENTS = classofRaw(function () { return arguments; }()) == 'Arguments'; // fallback for IE11 Script Access Denied error var tryGet = function (it, key) { try { return it[key]; } catch (error) {/* empty */} }; // getting tag from ES6+ `Object.prototype.toString` var classof$d = TO_STRING_TAG_SUPPORT$2 ? classofRaw : function (it) { var O, tag, result; return it === undefined ? 'Undefined' : it === null ? 'Null' // @@toStringTag case : typeof (tag = tryGet(O = $Object$2(it), TO_STRING_TAG$2)) == 'string' ? tag // builtinTag case : CORRECT_ARGUMENTS ? classofRaw(O) // ES3 arguments fallback : (result = classofRaw(O)) == 'Object' && isCallable$a(O.callee) ? 'Arguments' : result; }; var classof$c = classof$d; var $String$2 = String; var toString$a = function (argument) { if (classof$c(argument) === 'Symbol') throw TypeError('Cannot convert a Symbol value to a string'); return $String$2(argument); }; var uncurryThis$m = functionUncurryThis; var toIntegerOrInfinity$1 = toIntegerOrInfinity$4; var toString$9 = toString$a; var requireObjectCoercible$2 = requireObjectCoercible$5; var charAt$3 = uncurryThis$m(''.charAt); var charCodeAt$1 = uncurryThis$m(''.charCodeAt); var stringSlice = uncurryThis$m(''.slice); var createMethod$4 = function (CONVERT_TO_STRING) { return function ($this, pos) { var S = toString$9(requireObjectCoercible$2($this)); var position = toIntegerOrInfinity$1(pos); var size = S.length; var first, second; if (position < 0 || position >= size) return CONVERT_TO_STRING ? '' : undefined; first = charCodeAt$1(S, position); return first < 0xD800 || first > 0xDBFF || position + 1 === size || (second = charCodeAt$1(S, position + 1)) < 0xDC00 || second > 0xDFFF ? CONVERT_TO_STRING ? charAt$3(S, position) : first : CONVERT_TO_STRING ? stringSlice(S, position, position + 2) : (first - 0xD800 << 10) + (second - 0xDC00) + 0x10000; }; }; var stringMultibyte = { // `String.prototype.codePointAt` method // https://tc39.es/ecma262/#sec-string.prototype.codepointat codeAt: createMethod$4(false), // `String.prototype.at` method // https://github.com/mathiasbynens/String.prototype.at charAt: createMethod$4(true) }; var global$d = global$l; var isCallable$9 = isCallable$i; var WeakMap$1 = global$d.WeakMap; var weakMapBasicDetection = isCallable$9(WeakMap$1) && /native code/.test(String(WeakMap$1)); var shared$5 = sharedExports; var uid$2 = uid$4; var keys$3 = shared$5('keys'); var sharedKey$4 = function (key) { return keys$3[key] || (keys$3[key] = uid$2(key)); }; var NATIVE_WEAK_MAP$1 = weakMapBasicDetection; var global$c = global$l; var isObject$d = isObject$j; var createNonEnumerableProperty$4 = createNonEnumerableProperty$6; var hasOwn$c = hasOwnProperty_1; var shared$4 = sharedStore; var sharedKey$3 = sharedKey$4; var hiddenKeys$4 = hiddenKeys$6; var OBJECT_ALREADY_INITIALIZED = 'Object already initialized'; var TypeError$2 = global$c.TypeError; var WeakMap = global$c.WeakMap; var set$3, get$7, has; var enforce = function (it) { return has(it) ? get$7(it) : set$3(it, {}); }; var getterFor = function (TYPE) { return function (it) { var state; if (!isObject$d(it) || (state = get$7(it)).type !== TYPE) { throw TypeError$2('Incompatible receiver, ' + TYPE + ' required'); } return state; }; }; if (NATIVE_WEAK_MAP$1 || shared$4.state) { var store$1 = shared$4.state || (shared$4.state = new WeakMap()); /* eslint-disable no-self-assign -- prototype methods protection */ store$1.get = store$1.get; store$1.has = store$1.has; store$1.set = store$1.set; /* eslint-enable no-self-assign -- prototype methods protection */ set$3 = function (it, metadata) { if (store$1.has(it)) throw TypeError$2(OBJECT_ALREADY_INITIALIZED); metadata.facade = it; store$1.set(it, metadata); return metadata; }; get$7 = function (it) { return store$1.get(it) || {}; }; has = function (it) { return store$1.has(it); }; } else { var STATE = sharedKey$3('state'); hiddenKeys$4[STATE] = true; set$3 = function (it, metadata) { if (hasOwn$c(it, STATE)) throw TypeError$2(OBJECT_ALREADY_INITIALIZED); metadata.facade = it; createNonEnumerableProperty$4(it, STATE, metadata); return metadata; }; get$7 = function (it) { return hasOwn$c(it, STATE) ? it[STATE] : {}; }; has = function (it) { return hasOwn$c(it, STATE); }; } var internalState = { set: set$3, get: get$7, has: has, enforce: enforce, getterFor: getterFor }; var DESCRIPTORS$c = descriptors; var hasOwn$b = hasOwnProperty_1; var FunctionPrototype = Function.prototype; // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe var getDescriptor = DESCRIPTORS$c && Object.getOwnPropertyDescriptor; var EXISTS = hasOwn$b(FunctionPrototype, 'name'); // additional protection from minified / mangled / dropped function names var PROPER = EXISTS && function something() {/* empty */}.name === 'something'; var CONFIGURABLE = EXISTS && (!DESCRIPTORS$c || DESCRIPTORS$c && getDescriptor(FunctionPrototype, 'name').configurable); var functionName = { EXISTS: EXISTS, PROPER: PROPER, CONFIGURABLE: CONFIGURABLE }; var objectDefineProperties = {}; var DESCRIPTORS$b = descriptors; var V8_PROTOTYPE_DEFINE_BUG = v8PrototypeDefineBug; var definePropertyModule$2 = objectDefineProperty; var anObject$b = anObject$d; var toIndexedObject$7 = toIndexedObject$b; var objectKeys$2 = objectKeys$4; // `Object.defineProperties` method // https://tc39.es/ecma262/#sec-object.defineproperties // eslint-disable-next-line es/no-object-defineproperties -- safe objectDefineProperties.f = DESCRIPTORS$b && !V8_PROTOTYPE_DEFINE_BUG ? Object.defineProperties : function defineProperties(O, Properties) { anObject$b(O); var props = toIndexedObject$7(Properties); var keys = objectKeys$2(Properties); var length = keys.length; var index = 0; var key; while (length > index) definePropertyModule$2.f(O, key = keys[index++], props[key]); return O; }; var getBuiltIn$a = getBuiltIn$c; var html$1 = getBuiltIn$a('document', 'documentElement'); /* global ActiveXObject -- old IE, WSH */ var anObject$a = anObject$d; var definePropertiesModule$1 = objectDefineProperties; var enumBugKeys$1 = enumBugKeys$3; var hiddenKeys$3 = hiddenKeys$6; var html = html$1; var documentCreateElement = documentCreateElement$1; var sharedKey$2 = sharedKey$4; var GT = '>'; var LT = '<'; var PROTOTYPE$1 = 'prototype'; var SCRIPT = 'script'; var IE_PROTO$1 = sharedKey$2('IE_PROTO'); var EmptyConstructor = function () {/* empty */}; var scriptTag = function (content) { return LT + SCRIPT + GT + content + LT + '/' + SCRIPT + GT; }; // Create object with fake `null` prototype: use ActiveX Object with cleared prototype var NullProtoObjectViaActiveX = function (activeXDocument) { activeXDocument.write(scriptTag('')); activeXDocument.close(); var temp = activeXDocument.parentWindow.Object; activeXDocument = null; // avoid memory leak return temp; }; // Create object with fake `null` prototype: use iframe Object with cleared prototype var NullProtoObjectViaIFrame = function () { // Thrash, waste and sodomy: IE GC bug var iframe = documentCreateElement('iframe'); var JS = 'java' + SCRIPT + ':'; var iframeDocument; iframe.style.display = 'none'; html.appendChild(iframe); // https://github.com/zloirock/core-js/issues/475 iframe.src = String(JS); iframeDocument = iframe.contentWindow.document; iframeDocument.open(); iframeDocument.write(scriptTag('document.F=Object')); iframeDocument.close(); return iframeDocument.F; }; // Check for document.domain and active x support // No need to use active x approach when document.domain is not set // see https://github.com/es-shims/es5-shim/issues/150 // variation of https://github.com/kitcambridge/es5-shim/commit/4f738ac066346 // avoid IE GC bug var activeXDocument; var NullProtoObject = function () { try { activeXDocument = new ActiveXObject('htmlfile'); } catch (error) {/* ignore */} NullProtoObject = typeof document != 'undefined' ? document.domain && activeXDocument ? NullProtoObjectViaActiveX(activeXDocument) // old IE : NullProtoObjectViaIFrame() : NullProtoObjectViaActiveX(activeXDocument); // WSH var length = enumBugKeys$1.length; while (length--) delete NullProtoObject[PROTOTYPE$1][enumBugKeys$1[length]]; return NullProtoObject(); }; hiddenKeys$3[IE_PROTO$1] = true; // `Object.create` method // https://tc39.es/ecma262/#sec-object.create // eslint-disable-next-line es/no-object-create -- safe var objectCreate = Object.create || function create(O, Properties) { var result; if (O !== null) { EmptyConstructor[PROTOTYPE$1] = anObject$a(O); result = new EmptyConstructor(); EmptyConstructor[PROTOTYPE$1] = null; // add "__proto__" for Object.getPrototypeOf polyfill result[IE_PROTO$1] = O; } else result = NullProtoObject(); return Properties === undefined ? result : definePropertiesModule$1.f(result, Properties); }; var fails$n = fails$w; var correctPrototypeGetter = !fails$n(function () { function F() {/* empty */} F.prototype.constructor = null; // eslint-disable-next-line es/no-object-getprototypeof -- required for testing return Object.getPrototypeOf(new F()) !== F.prototype; }); var hasOwn$a = hasOwnProperty_1; var isCallable$8 = isCallable$i; var toObject$a = toObject$d; var sharedKey$1 = sharedKey$4; var CORRECT_PROTOTYPE_GETTER$1 = correctPrototypeGetter; var IE_PROTO = sharedKey$1('IE_PROTO'); var $Object$1 = Object; var ObjectPrototype$2 = $Object$1.prototype; // `Object.getPrototypeOf` method // https://tc39.es/ecma262/#sec-object.getprototypeof // eslint-disable-next-line es/no-object-getprototypeof -- safe var objectGetPrototypeOf = CORRECT_PROTOTYPE_GETTER$1 ? $Object$1.getPrototypeOf : function (O) { var object = toObject$a(O); if (hasOwn$a(object, IE_PROTO)) return object[IE_PROTO]; var constructor = object.constructor; if (isCallable$8(constructor) && object instanceof constructor) { return constructor.prototype; } return object instanceof $Object$1 ? ObjectPrototype$2 : null; }; var createNonEnumerableProperty$3 = createNonEnumerableProperty$6; var defineBuiltIn$5 = function (target, key, value, options) { if (options && options.enumerable) target[key] = value;else createNonEnumerableProperty$3(target, key, value); return target; }; var fails$m = fails$w; var isCallable$7 = isCallable$i; var isObject$c = isObject$j; var create$b = objectCreate; var getPrototypeOf$9 = objectGetPrototypeOf; var defineBuiltIn$4 = defineBuiltIn$5; var wellKnownSymbol$h = wellKnownSymbol$l; var ITERATOR$6 = wellKnownSymbol$h('iterator'); var BUGGY_SAFARI_ITERATORS$1 = false; // `%IteratorPrototype%` object // https://tc39.es/ecma262/#sec-%iteratorprototype%-object var IteratorPrototype$1, PrototypeOfArrayIteratorPrototype, arrayIterator; /* eslint-disable es/no-array-prototype-keys -- safe */ if ([].keys) { arrayIterator = [].keys(); // Safari 8 has buggy iterators w/o `next` if (!('next' in arrayIterator)) BUGGY_SAFARI_ITERATORS$1 = true;else { PrototypeOfArrayIteratorPrototype = getPrototypeOf$9(getPrototypeOf$9(arrayIterator)); if (PrototypeOfArrayIteratorPrototype !== Object.prototype) IteratorPrototype$1 = PrototypeOfArrayIteratorPrototype; } } var NEW_ITERATOR_PROTOTYPE = !isObject$c(IteratorPrototype$1) || fails$m(function () { var test = {}; // FF44- legacy iterators case return IteratorPrototype$1[ITERATOR$6].call(test) !== test; }); if (NEW_ITERATOR_PROTOTYPE) IteratorPrototype$1 = {};else IteratorPrototype$1 = create$b(IteratorPrototype$1); // `%IteratorPrototype%[@@iterator]()` method // https://tc39.es/ecma262/#sec-%iteratorprototype%-@@iterator if (!isCallable$7(IteratorPrototype$1[ITERATOR$6])) { defineBuiltIn$4(IteratorPrototype$1, ITERATOR$6, function () { return this; }); } var iteratorsCore = { IteratorPrototype: IteratorPrototype$1, BUGGY_SAFARI_ITERATORS: BUGGY_SAFARI_ITERATORS$1 }; var TO_STRING_TAG_SUPPORT$1 = toStringTagSupport; var classof$b = classof$d; // `Object.prototype.toString` method implementation // https://tc39.es/ecma262/#sec-object.prototype.tostring var objectToString = TO_STRING_TAG_SUPPORT$1 ? {}.toString : function toString() { return '[object ' + classof$b(this) + ']'; }; var TO_STRING_TAG_SUPPORT = toStringTagSupport; var defineProperty$d = objectDefineProperty.f; var createNonEnumerableProperty$2 = createNonEnumerableProperty$6; var hasOwn$9 = hasOwnProperty_1; var toString$8 = objectToString; var wellKnownSymbol$g = wellKnownSymbol$l; var TO_STRING_TAG$1 = wellKnownSymbol$g('toStringTag'); var setToStringTag$6 = function (it, TAG, STATIC, SET_METHOD) { if (it) { var target = STATIC ? it : it.prototype; if (!hasOwn$9(target, TO_STRING_TAG$1)) { defineProperty$d(target, TO_STRING_TAG$1, { configurable: true, value: TAG }); } if (SET_METHOD && !TO_STRING_TAG_SUPPORT) { createNonEnumerableProperty$2(target, 'toString', toString$8); } } }; var iterators = {}; var IteratorPrototype = iteratorsCore.IteratorPrototype; var create$a = objectCreate; var createPropertyDescriptor$2 = createPropertyDescriptor$5; var setToStringTag$5 = setToStringTag$6; var Iterators$5 = iterators; var returnThis$1 = function () { return this; }; var iteratorCreateConstructor = function (IteratorConstructor, NAME, next, ENUMERABLE_NEXT) { var TO_STRING_TAG = NAME + ' Iterator'; IteratorConstructor.prototype = create$a(IteratorPrototype, { next: createPropertyDescriptor$2(+!ENUMERABLE_NEXT, next) }); setToStringTag$5(IteratorConstructor, TO_STRING_TAG, false, true); Iterators$5[TO_STRING_TAG] = returnThis$1; return IteratorConstructor; }; var uncurryThis$l = functionUncurryThis; var aCallable$3 = aCallable$7; var functionUncurryThisAccessor = function (object, key, method) { try { // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe return uncurryThis$l(aCallable$3(Object.getOwnPropertyDescriptor(object, key)[method])); } catch (error) {/* empty */} }; var isCallable$6 = isCallable$i; var $String$1 = String; var $TypeError$a = TypeError; var aPossiblePrototype$1 = function (argument) { if (typeof argument == 'object' || isCallable$6(argument)) return argument; throw $TypeError$a("Can't set " + $String$1(argument) + ' as a prototype'); }; /* eslint-disable no-proto -- safe */ var uncurryThisAccessor = functionUncurryThisAccessor; var anObject$9 = anObject$d; var aPossiblePrototype = aPossiblePrototype$1; // `Object.setPrototypeOf` method // https://tc39.es/ecma262/#sec-object.setprototypeof // Works with __proto__ only. Old v8 can't work with null proto objects. // eslint-disable-next-line es/no-object-setprototypeof -- safe var objectSetPrototypeOf = Object.setPrototypeOf || ('__proto__' in {} ? function () { var CORRECT_SETTER = false; var test = {}; var setter; try { setter = uncurryThisAccessor(Object.prototype, '__proto__', 'set'); setter(test, []); CORRECT_SETTER = test instanceof Array; } catch (error) {/* empty */} return function setPrototypeOf(O, proto) { anObject$9(O); aPossiblePrototype(proto); if (CORRECT_SETTER) setter(O, proto);else O.__proto__ = proto; return O; }; }() : undefined); var $$M = _export; var call$8 = functionCall; var FunctionName = functionName; var createIteratorConstructor = iteratorCreateConstructor; var getPrototypeOf$8 = objectGetPrototypeOf; var setToStringTag$4 = setToStringTag$6; var defineBuiltIn$3 = defineBuiltIn$5; var wellKnownSymbol$f = wellKnownSymbol$l; var Iterators$4 = iterators; var IteratorsCore = iteratorsCore; var PROPER_FUNCTION_NAME$1 = FunctionName.PROPER; var BUGGY_SAFARI_ITERATORS = IteratorsCore.BUGGY_SAFARI_ITERATORS; var ITERATOR$5 = wellKnownSymbol$f('iterator'); var KEYS = 'keys'; var VALUES = 'values'; var ENTRIES = 'entries'; var returnThis = function () { return this; }; var iteratorDefine = function (Iterable, NAME, IteratorConstructor, next, DEFAULT, IS_SET, FORCED) { createIteratorConstructor(IteratorConstructor, NAME, next); var getIterationMethod = function (KIND) { if (KIND === DEFAULT && defaultIterator) return defaultIterator; if (!BUGGY_SAFARI_ITERATORS && KIND in IterablePrototype) return IterablePrototype[KIND]; switch (KIND) { case KEYS: return function keys() { return new IteratorConstructor(this, KIND); }; case VALUES: return function values() { return new IteratorConstructor(this, KIND); }; case ENTRIES: return function entries() { return new IteratorConstructor(this, KIND); }; } return function () { return new IteratorConstructor(this); }; }; var TO_STRING_TAG = NAME + ' Iterator'; var INCORRECT_VALUES_NAME = false; var IterablePrototype = Iterable.prototype; var nativeIterator = IterablePrototype[ITERATOR$5] || IterablePrototype['@@iterator'] || DEFAULT && IterablePrototype[DEFAULT]; var defaultIterator = !BUGGY_SAFARI_ITERATORS && nativeIterator || getIterationMethod(DEFAULT); var anyNativeIterator = NAME == 'Array' ? IterablePrototype.entries || nativeIterator : nativeIterator; var CurrentIteratorPrototype, methods, KEY; // fix native if (anyNativeIterator) { CurrentIteratorPrototype = getPrototypeOf$8(anyNativeIterator.call(new Iterable())); if (CurrentIteratorPrototype !== Object.prototype && CurrentIteratorPrototype.next) { // Set @@toStringTag to native iterators setToStringTag$4(CurrentIteratorPrototype, TO_STRING_TAG, true, true); Iterators$4[TO_STRING_TAG] = returnThis; } } // fix Array.prototype.{ values, @@iterator }.name in V8 / FF if (PROPER_FUNCTION_NAME$1 && DEFAULT == VALUES && nativeIterator && nativeIterator.name !== VALUES) { { INCORRECT_VALUES_NAME = true; defaultIterator = function values() { return call$8(nativeIterator, this); }; } } // export additional methods if (DEFAULT) { methods = { values: getIterationMethod(VALUES), keys: IS_SET ? defaultIterator : getIterationMethod(KEYS), entries: getIterationMethod(ENTRIES) }; if (FORCED) for (KEY in methods) { if (BUGGY_SAFARI_ITERATORS || INCORRECT_VALUES_NAME || !(KEY in IterablePrototype)) { defineBuiltIn$3(IterablePrototype, KEY, methods[KEY]); } } else $$M({ target: NAME, proto: true, forced: BUGGY_SAFARI_ITERATORS || INCORRECT_VALUES_NAME }, methods); } // define iterator if (FORCED && IterablePrototype[ITERATOR$5] !== defaultIterator) { defineBuiltIn$3(IterablePrototype, ITERATOR$5, defaultIterator, { name: DEFAULT }); } Iterators$4[NAME] = defaultIterator; return methods; }; // `CreateIterResultObject` abstract operation // https://tc39.es/ecma262/#sec-createiterresultobject var createIterResultObject$3 = function (value, done) { return { value: value, done: done }; }; var charAt$2 = stringMultibyte.charAt; var toString$7 = toString$a; var InternalStateModule$5 = internalState; var defineIterator$2 = iteratorDefine; var createIterResultObject$2 = createIterResultObject$3; var STRING_ITERATOR = 'String Iterator'; var setInternalState$5 = InternalStateModule$5.set; var getInternalState$2 = InternalStateModule$5.getterFor(STRING_ITERATOR); // `String.prototype[@@iterator]` method // https://tc39.es/ecma262/#sec-string.prototype-@@iterator defineIterator$2(String, 'String', function (iterated) { setInternalState$5(this, { type: STRING_ITERATOR, string: toString$7(iterated), index: 0 }); // `%StringIteratorPrototype%.next` method // https://tc39.es/ecma262/#sec-%stringiteratorprototype%.next }, function next() { var state = getInternalState$2(this); var string = state.string; var index = state.index; var point; if (index >= string.length) return createIterResultObject$2(undefined, true); point = charAt$2(string, index); state.index += point.length; return createIterResultObject$2(point, false); }); var call$7 = functionCall; var anObject$8 = anObject$d; var getMethod$1 = getMethod$3; var iteratorClose$2 = function (iterator, kind, value) { var innerResult, innerError; anObject$8(iterator); try { innerResult = getMethod$1(iterator, 'return'); if (!innerResult) { if (kind === 'throw') throw value; return value; } innerResult = call$7(innerResult, iterator); } catch (error) { innerError = true; innerResult = error; } if (kind === 'throw') throw value; if (innerError) throw innerResult; anObject$8(innerResult); return value; }; var anObject$7 = anObject$d; var iteratorClose$1 = iteratorClose$2; // call something on iterator step with safe closing on error var callWithSafeIterationClosing$1 = function (iterator, fn, value, ENTRIES) { try { return ENTRIES ? fn(anObject$7(value)[0], value[1]) : fn(value); } catch (error) { iteratorClose$1(iterator, 'throw', error); } }; var wellKnownSymbol$e = wellKnownSymbol$l; var Iterators$3 = iterators; var ITERATOR$4 = wellKnownSymbol$e('iterator'); var ArrayPrototype$f = Array.prototype; // check on default Array iterator var isArrayIteratorMethod$2 = function (it) { return it !== undefined && (Iterators$3.Array === it || ArrayPrototype$f[ITERATOR$4] === it); }; var uncurryThis$k = functionUncurryThis; var isCallable$5 = isCallable$i; var store = sharedStore; var functionToString = uncurryThis$k(Function.toString); // this helper broken in `core-js@3.4.1-3.4.4`, so we can't use `shared` helper if (!isCallable$5(store.inspectSource)) { store.inspectSource = function (it) { return functionToString(it); }; } var inspectSource$1 = store.inspectSource; var uncurryThis$j = functionUncurryThis; var fails$l = fails$w; var isCallable$4 = isCallable$i; var classof$a = classof$d; var getBuiltIn$9 = getBuiltIn$c; var inspectSource = inspectSource$1; var noop = function () {/* empty */}; var empty = []; var construct$3 = getBuiltIn$9('Reflect', 'construct'); var constructorRegExp = /^\s*(?:class|function)\b/; var exec$2 = uncurryThis$j(constructorRegExp.exec); var INCORRECT_TO_STRING = !constructorRegExp.exec(noop); var isConstructorModern = function isConstructor(argument) { if (!isCallable$4(argument)) return false; try { construct$3(noop, empty, argument); return true; } catch (error) { return false; } }; var isConstructorLegacy = function isConstructor(argument) { if (!isCallable$4(argument)) return false; switch (classof$a(argument)) { case 'AsyncFunction': case 'GeneratorFunction': case 'AsyncGeneratorFunction': return false; } try { // we can't check .prototype since constructors produced by .bind haven't it // `Function#toString` throws on some built-it function in some legacy engines // (for example, `DOMQuad` and similar in FF41-) return INCORRECT_TO_STRING || !!exec$2(constructorRegExp, inspectSource(argument)); } catch (error) { return true; } }; isConstructorLegacy.sham = true; // `IsConstructor` abstract operation // https://tc39.es/ecma262/#sec-isconstructor var isConstructor$4 = !construct$3 || fails$l(function () { var called; return isConstructorModern(isConstructorModern.call) || !isConstructorModern(Object) || !isConstructorModern(function () { called = true; }) || called; }) ? isConstructorLegacy : isConstructorModern; var toPropertyKey$1 = toPropertyKey$4; var definePropertyModule$1 = objectDefineProperty; var createPropertyDescriptor$1 = createPropertyDescriptor$5; var createProperty$6 = function (object, key, value) { var propertyKey = toPropertyKey$1(key); if (propertyKey in object) definePropertyModule$1.f(object, propertyKey, createPropertyDescriptor$1(0, value));else object[propertyKey] = value; }; var classof$9 = classof$d; var getMethod = getMethod$3; var isNullOrUndefined$2 = isNullOrUndefined$5; var Iterators$2 = iterators; var wellKnownSymbol$d = wellKnownSymbol$l; var ITERATOR$3 = wellKnownSymbol$d('iterator'); var getIteratorMethod$9 = function (it) { if (!isNullOrUndefined$2(it)) return getMethod(it, ITERATOR$3) || getMethod(it, '@@iterator') || Iterators$2[classof$9(it)]; }; var call$6 = functionCall; var aCallable$2 = aCallable$7; var anObject$6 = anObject$d; var tryToString$4 = tryToString$6; var getIteratorMethod$8 = getIteratorMethod$9; var $TypeError$9 = TypeError; var getIterator$2 = function (argument, usingIterator) { var iteratorMethod = arguments.length < 2 ? getIteratorMethod$8(argument) : usingIterator; if (aCallable$2(iteratorMethod)) return anObject$6(call$6(iteratorMethod, argument)); throw $TypeError$9(tryToString$4(argument) + ' is not iterable'); }; var bind$8 = functionBindContext; var call$5 = functionCall; var toObject$9 = toObject$d; var callWithSafeIterationClosing = callWithSafeIterationClosing$1; var isArrayIteratorMethod$1 = isArrayIteratorMethod$2; var isConstructor$3 = isConstructor$4; var lengthOfArrayLike$9 = lengthOfArrayLike$b; var createProperty$5 = createProperty$6; var getIterator$1 = getIterator$2; var getIteratorMethod$7 = getIteratorMethod$9; var $Array$3 = Array; // `Array.from` method implementation // https://tc39.es/ecma262/#sec-array.from var arrayFrom = function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) { var O = toObject$9(arrayLike); var IS_CONSTRUCTOR = isConstructor$3(this); var argumentsLength = arguments.length; var mapfn = argumentsLength > 1 ? arguments[1] : undefined; var mapping = mapfn !== undefined; if (mapping) mapfn = bind$8(mapfn, argumentsLength > 2 ? arguments[2] : undefined); var iteratorMethod = getIteratorMethod$7(O); var index = 0; var length, result, step, iterator, next, value; // if the target is not iterable or it's an array with the default iterator - use a simple case if (iteratorMethod && !(this === $Array$3 && isArrayIteratorMethod$1(iteratorMethod))) { iterator = getIterator$1(O, iteratorMethod); next = iterator.next; result = IS_CONSTRUCTOR ? new this() : []; for (; !(step = call$5(next, iterator)).done; index++) { value = mapping ? callWithSafeIterationClosing(iterator, mapfn, [step.value, index], true) : step.value; createProperty$5(result, index, value); } } else { length = lengthOfArrayLike$9(O); result = IS_CONSTRUCTOR ? new this(length) : $Array$3(length); for (; length > index; index++) { value = mapping ? mapfn(O[index], index) : O[index]; createProperty$5(result, index, value); } } result.length = index; return result; }; var wellKnownSymbol$c = wellKnownSymbol$l; var ITERATOR$2 = wellKnownSymbol$c('iterator'); var SAFE_CLOSING = false; try { var called = 0; var iteratorWithReturn = { next: function () { return { done: !!called++ }; }, 'return': function () { SAFE_CLOSING = true; } }; iteratorWithReturn[ITERATOR$2] = function () { return this; }; // eslint-disable-next-line es/no-array-from, no-throw-literal -- required for testing Array.from(iteratorWithReturn, function () { throw 2; }); } catch (error) {/* empty */} var checkCorrectnessOfIteration$1 = function (exec, SKIP_CLOSING) { if (!SKIP_CLOSING && !SAFE_CLOSING) return false; var ITERATION_SUPPORT = false; try { var object = {}; object[ITERATOR$2] = function () { return { next: function () { return { done: ITERATION_SUPPORT = true }; } }; }; exec(object); } catch (error) {/* empty */} return ITERATION_SUPPORT; }; var $$L = _export; var from$6 = arrayFrom; var checkCorrectnessOfIteration = checkCorrectnessOfIteration$1; var INCORRECT_ITERATION = !checkCorrectnessOfIteration(function (iterable) { // eslint-disable-next-line es/no-array-from -- required for testing Array.from(iterable); }); // `Array.from` method // https://tc39.es/ecma262/#sec-array.from $$L({ target: 'Array', stat: true, forced: INCORRECT_ITERATION }, { from: from$6 }); var path$t = path$y; var from$5 = path$t.Array.from; var parent$19 = from$5; var from$4 = parent$19; (function (module) { module.exports = from$4; })(from$7); var _Array$from$1 = /*@__PURE__*/getDefaultExportFromCjs(fromExports$2); var getIteratorMethodExports$1 = {}; var getIteratorMethod$6 = { get exports() { return getIteratorMethodExports$1; }, set exports(v) { getIteratorMethodExports$1 = v; } }; var getIteratorMethodExports = {}; var getIteratorMethod$5 = { get exports() { return getIteratorMethodExports; }, set exports(v) { getIteratorMethodExports = v; } }; var toIndexedObject$6 = toIndexedObject$b; var Iterators$1 = iterators; var InternalStateModule$4 = internalState; objectDefineProperty.f; var defineIterator$1 = iteratorDefine; var createIterResultObject$1 = createIterResultObject$3; var ARRAY_ITERATOR = 'Array Iterator'; var setInternalState$4 = InternalStateModule$4.set; var getInternalState$1 = InternalStateModule$4.getterFor(ARRAY_ITERATOR); // `Array.prototype.entries` method // https://tc39.es/ecma262/#sec-array.prototype.entries // `Array.prototype.keys` method // https://tc39.es/ecma262/#sec-array.prototype.keys // `Array.prototype.values` method // https://tc39.es/ecma262/#sec-array.prototype.values // `Array.prototype[@@iterator]` method // https://tc39.es/ecma262/#sec-array.prototype-@@iterator // `CreateArrayIterator` internal method // https://tc39.es/ecma262/#sec-createarrayiterator defineIterator$1(Array, 'Array', function (iterated, kind) { setInternalState$4(this, { type: ARRAY_ITERATOR, target: toIndexedObject$6(iterated), // target index: 0, // next index kind: kind // kind }); // `%ArrayIteratorPrototype%.next` method // https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next }, function () { var state = getInternalState$1(this); var target = state.target; var kind = state.kind; var index = state.index++; if (!target || index >= target.length) { state.target = undefined; return createIterResultObject$1(undefined, true); } if (kind == 'keys') return createIterResultObject$1(index, false); if (kind == 'values') return createIterResultObject$1(target[index], false); return createIterResultObject$1([index, target[index]], false); }, 'values'); // argumentsList[@@iterator] is %ArrayProto_values% // https://tc39.es/ecma262/#sec-createunmappedargumentsobject // https://tc39.es/ecma262/#sec-createmappedargumentsobject Iterators$1.Arguments = Iterators$1.Array; var getIteratorMethod$4 = getIteratorMethod$9; var getIteratorMethod_1 = getIteratorMethod$4; // iterable DOM collections // flag - `iterable` interface - 'entries', 'keys', 'values', 'forEach' methods var domIterables = { CSSRuleList: 0, CSSStyleDeclaration: 0, CSSValueList: 0, ClientRectList: 0, DOMRectList: 0, DOMStringList: 0, DOMTokenList: 1, DataTransferItemList: 0, FileList: 0, HTMLAllCollection: 0, HTMLCollection: 0, HTMLFormElement: 0, HTMLSelectElement: 0, MediaList: 0, MimeTypeArray: 0, NamedNodeMap: 0, NodeList: 1, PaintRequestList: 0, Plugin: 0, PluginArray: 0, SVGLengthList: 0, SVGNumberList: 0, SVGPathSegList: 0, SVGPointList: 0, SVGStringList: 0, SVGTransformList: 0, SourceBufferList: 0, StyleSheetList: 0, TextTrackCueList: 0, TextTrackList: 0, TouchList: 0 }; var DOMIterables$2 = domIterables; var global$b = global$l; var classof$8 = classof$d; var createNonEnumerableProperty$1 = createNonEnumerableProperty$6; var Iterators = iterators; var wellKnownSymbol$b = wellKnownSymbol$l; var TO_STRING_TAG = wellKnownSymbol$b('toStringTag'); for (var COLLECTION_NAME in DOMIterables$2) { var Collection = global$b[COLLECTION_NAME]; var CollectionPrototype = Collection && Collection.prototype; if (CollectionPrototype && classof$8(CollectionPrototype) !== TO_STRING_TAG) { createNonEnumerableProperty$1(CollectionPrototype, TO_STRING_TAG, COLLECTION_NAME); } Iterators[COLLECTION_NAME] = Iterators.Array; } var parent$18 = getIteratorMethod_1; var getIteratorMethod$3 = parent$18; var parent$17 = getIteratorMethod$3; var getIteratorMethod$2 = parent$17; var parent$16 = getIteratorMethod$2; var getIteratorMethod$1 = parent$16; (function (module) { module.exports = getIteratorMethod$1; })(getIteratorMethod$5); (function (module) { module.exports = getIteratorMethodExports; })(getIteratorMethod$6); var _getIteratorMethod = /*@__PURE__*/getDefaultExportFromCjs(getIteratorMethodExports$1); var getOwnPropertySymbolsExports = {}; var getOwnPropertySymbols$2 = { get exports() { return getOwnPropertySymbolsExports; }, set exports(v) { getOwnPropertySymbolsExports = v; } }; var objectGetOwnPropertyNames = {}; var internalObjectKeys = objectKeysInternal; var enumBugKeys = enumBugKeys$3; var hiddenKeys$2 = enumBugKeys.concat('length', 'prototype'); // `Object.getOwnPropertyNames` method // https://tc39.es/ecma262/#sec-object.getownpropertynames // eslint-disable-next-line es/no-object-getownpropertynames -- safe objectGetOwnPropertyNames.f = Object.getOwnPropertyNames || function getOwnPropertyNames(O) { return internalObjectKeys(O, hiddenKeys$2); }; var objectGetOwnPropertyNamesExternal = {}; var toAbsoluteIndex$3 = toAbsoluteIndex$5; var lengthOfArrayLike$8 = lengthOfArrayLike$b; var createProperty$4 = createProperty$6; var $Array$2 = Array; var max$2 = Math.max; var arraySliceSimple = function (O, start, end) { var length = lengthOfArrayLike$8(O); var k = toAbsoluteIndex$3(start, length); var fin = toAbsoluteIndex$3(end === undefined ? length : end, length); var result = $Array$2(max$2(fin - k, 0)); for (var n = 0; k < fin; k++, n++) createProperty$4(result, n, O[k]); result.length = n; return result; }; /* eslint-disable es/no-object-getownpropertynames -- safe */ var classof$7 = classofRaw$2; var toIndexedObject$5 = toIndexedObject$b; var $getOwnPropertyNames$1 = objectGetOwnPropertyNames.f; var arraySlice$3 = arraySliceSimple; var windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames ? Object.getOwnPropertyNames(window) : []; var getWindowNames = function (it) { try { return $getOwnPropertyNames$1(it); } catch (error) { return arraySlice$3(windowNames); } }; // fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window objectGetOwnPropertyNamesExternal.f = function getOwnPropertyNames(it) { return windowNames && classof$7(it) == 'Window' ? getWindowNames(it) : $getOwnPropertyNames$1(toIndexedObject$5(it)); }; var defineProperty$c = objectDefineProperty; var defineBuiltInAccessor$3 = function (target, name, descriptor) { return defineProperty$c.f(target, name, descriptor); }; var wellKnownSymbolWrapped = {}; var wellKnownSymbol$a = wellKnownSymbol$l; wellKnownSymbolWrapped.f = wellKnownSymbol$a; var path$s = path$y; var hasOwn$8 = hasOwnProperty_1; var wrappedWellKnownSymbolModule$1 = wellKnownSymbolWrapped; var defineProperty$b = objectDefineProperty.f; var wellKnownSymbolDefine = function (NAME) { var Symbol = path$s.Symbol || (path$s.Symbol = {}); if (!hasOwn$8(Symbol, NAME)) defineProperty$b(Symbol, NAME, { value: wrappedWellKnownSymbolModule$1.f(NAME) }); }; var call$4 = functionCall; var getBuiltIn$8 = getBuiltIn$c; var wellKnownSymbol$9 = wellKnownSymbol$l; var defineBuiltIn$2 = defineBuiltIn$5; var symbolDefineToPrimitive = function () { var Symbol = getBuiltIn$8('Symbol'); var SymbolPrototype = Symbol && Symbol.prototype; var valueOf = SymbolPrototype && SymbolPrototype.valueOf; var TO_PRIMITIVE = wellKnownSymbol$9('toPrimitive'); if (SymbolPrototype && !SymbolPrototype[TO_PRIMITIVE]) { // `Symbol.prototype[@@toPrimitive]` method // https://tc39.es/ecma262/#sec-symbol.prototype-@@toprimitive // eslint-disable-next-line no-unused-vars -- required for .length defineBuiltIn$2(SymbolPrototype, TO_PRIMITIVE, function (hint) { return call$4(valueOf, this); }, { arity: 1 }); } }; var classof$6 = classofRaw$2; // `IsArray` abstract operation // https://tc39.es/ecma262/#sec-isarray // eslint-disable-next-line es/no-array-isarray -- safe var isArray$f = Array.isArray || function isArray(argument) { return classof$6(argument) == 'Array'; }; var isArray$e = isArray$f; var isConstructor$2 = isConstructor$4; var isObject$b = isObject$j; var wellKnownSymbol$8 = wellKnownSymbol$l; var SPECIES$3 = wellKnownSymbol$8('species'); var $Array$1 = Array; // a part of `ArraySpeciesCreate` abstract operation // https://tc39.es/ecma262/#sec-arrayspeciescreate var arraySpeciesConstructor$1 = function (originalArray) { var C; if (isArray$e(originalArray)) { C = originalArray.constructor; // cross-realm fallback if (isConstructor$2(C) && (C === $Array$1 || isArray$e(C.prototype))) C = undefined;else if (isObject$b(C)) { C = C[SPECIES$3]; if (C === null) C = undefined; } } return C === undefined ? $Array$1 : C; }; var arraySpeciesConstructor = arraySpeciesConstructor$1; // `ArraySpeciesCreate` abstract operation // https://tc39.es/ecma262/#sec-arrayspeciescreate var arraySpeciesCreate$3 = function (originalArray, length) { return new (arraySpeciesConstructor(originalArray))(length === 0 ? 0 : length); }; var bind$7 = functionBindContext; var uncurryThis$i = functionUncurryThis; var IndexedObject$1 = indexedObject; var toObject$8 = toObject$d; var lengthOfArrayLike$7 = lengthOfArrayLike$b; var arraySpeciesCreate$2 = arraySpeciesCreate$3; var push$5 = uncurryThis$i([].push); // `Array.prototype.{ forEach, map, filter, some, every, find, findIndex, filterReject }` methods implementation var createMethod$3 = function (TYPE) { var IS_MAP = TYPE == 1; var IS_FILTER = TYPE == 2; var IS_SOME = TYPE == 3; var IS_EVERY = TYPE == 4; var IS_FIND_INDEX = TYPE == 6; var IS_FILTER_REJECT = TYPE == 7; var NO_HOLES = TYPE == 5 || IS_FIND_INDEX; return function ($this, callbackfn, that, specificCreate) { var O = toObject$8($this); var self = IndexedObject$1(O); var boundFunction = bind$7(callbackfn, that); var length = lengthOfArrayLike$7(self); var index = 0; var create = specificCreate || arraySpeciesCreate$2; var target = IS_MAP ? create($this, length) : IS_FILTER || IS_FILTER_REJECT ? create($this, 0) : undefined; var value, result; for (; length > index; index++) if (NO_HOLES || index in self) { value = self[index]; result = boundFunction(value, index, O); if (TYPE) { if (IS_MAP) target[index] = result; // map else if (result) switch (TYPE) { case 3: return true; // some case 5: return value; // find case 6: return index; // findIndex case 2: push$5(target, value); // filter } else switch (TYPE) { case 4: return false; // every case 7: push$5(target, value); // filterReject } } } return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : target; }; }; var arrayIteration = { // `Array.prototype.forEach` method // https://tc39.es/ecma262/#sec-array.prototype.foreach forEach: createMethod$3(0), // `Array.prototype.map` method // https://tc39.es/ecma262/#sec-array.prototype.map map: createMethod$3(1), // `Array.prototype.filter` method // https://tc39.es/ecma262/#sec-array.prototype.filter filter: createMethod$3(2), // `Array.prototype.some` method // https://tc39.es/ecma262/#sec-array.prototype.some some: createMethod$3(3), // `Array.prototype.every` method // https://tc39.es/ecma262/#sec-array.prototype.every every: createMethod$3(4), // `Array.prototype.find` method // https://tc39.es/ecma262/#sec-array.prototype.find find: createMethod$3(5), // `Array.prototype.findIndex` method // https://tc39.es/ecma262/#sec-array.prototype.findIndex findIndex: createMethod$3(6), // `Array.prototype.filterReject` method // https://github.com/tc39/proposal-array-filtering filterReject: createMethod$3(7) }; var $$K = _export; var global$a = global$l; var call$3 = functionCall; var uncurryThis$h = functionUncurryThis; var DESCRIPTORS$a = descriptors; var NATIVE_SYMBOL$3 = symbolConstructorDetection; var fails$k = fails$w; var hasOwn$7 = hasOwnProperty_1; var isPrototypeOf$i = objectIsPrototypeOf; var anObject$5 = anObject$d; var toIndexedObject$4 = toIndexedObject$b; var toPropertyKey = toPropertyKey$4; var $toString = toString$a; var createPropertyDescriptor = createPropertyDescriptor$5; var nativeObjectCreate = objectCreate; var objectKeys$1 = objectKeys$4; var getOwnPropertyNamesModule$2 = objectGetOwnPropertyNames; var getOwnPropertyNamesExternal = objectGetOwnPropertyNamesExternal; var getOwnPropertySymbolsModule$2 = objectGetOwnPropertySymbols; var getOwnPropertyDescriptorModule$2 = objectGetOwnPropertyDescriptor; var definePropertyModule = objectDefineProperty; var definePropertiesModule = objectDefineProperties; var propertyIsEnumerableModule = objectPropertyIsEnumerable; var defineBuiltIn$1 = defineBuiltIn$5; var defineBuiltInAccessor$2 = defineBuiltInAccessor$3; var shared$3 = sharedExports; var sharedKey = sharedKey$4; var hiddenKeys$1 = hiddenKeys$6; var uid$1 = uid$4; var wellKnownSymbol$7 = wellKnownSymbol$l; var wrappedWellKnownSymbolModule = wellKnownSymbolWrapped; var defineWellKnownSymbol$l = wellKnownSymbolDefine; var defineSymbolToPrimitive$1 = symbolDefineToPrimitive; var setToStringTag$3 = setToStringTag$6; var InternalStateModule$3 = internalState; var $forEach$1 = arrayIteration.forEach; var HIDDEN = sharedKey('hidden'); var SYMBOL = 'Symbol'; var PROTOTYPE = 'prototype'; var setInternalState$3 = InternalStateModule$3.set; var getInternalState = InternalStateModule$3.getterFor(SYMBOL); var ObjectPrototype$1 = Object[PROTOTYPE]; var $Symbol = global$a.Symbol; var SymbolPrototype = $Symbol && $Symbol[PROTOTYPE]; var TypeError$1 = global$a.TypeError; var QObject = global$a.QObject; var nativeGetOwnPropertyDescriptor$1 = getOwnPropertyDescriptorModule$2.f; var nativeDefineProperty = definePropertyModule.f; var nativeGetOwnPropertyNames = getOwnPropertyNamesExternal.f; var nativePropertyIsEnumerable = propertyIsEnumerableModule.f; var push$4 = uncurryThis$h([].push); var AllSymbols = shared$3('symbols'); var ObjectPrototypeSymbols = shared$3('op-symbols'); var WellKnownSymbolsStore$1 = shared$3('wks'); // Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173 var USE_SETTER = !QObject || !QObject[PROTOTYPE] || !QObject[PROTOTYPE].findChild; // fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687 var setSymbolDescriptor = DESCRIPTORS$a && fails$k(function () { return nativeObjectCreate(nativeDefineProperty({}, 'a', { get: function () { return nativeDefineProperty(this, 'a', { value: 7 }).a; } })).a != 7; }) ? function (O, P, Attributes) { var ObjectPrototypeDescriptor = nativeGetOwnPropertyDescriptor$1(ObjectPrototype$1, P); if (ObjectPrototypeDescriptor) delete ObjectPrototype$1[P]; nativeDefineProperty(O, P, Attributes); if (ObjectPrototypeDescriptor && O !== ObjectPrototype$1) { nativeDefineProperty(ObjectPrototype$1, P, ObjectPrototypeDescriptor); } } : nativeDefineProperty; var wrap = function (tag, description) { var symbol = AllSymbols[tag] = nativeObjectCreate(SymbolPrototype); setInternalState$3(symbol, { type: SYMBOL, tag: tag, description: description }); if (!DESCRIPTORS$a) symbol.description = description; return symbol; }; var $defineProperty = function defineProperty(O, P, Attributes) { if (O === ObjectPrototype$1) $defineProperty(ObjectPrototypeSymbols, P, Attributes); anObject$5(O); var key = toPropertyKey(P); anObject$5(Attributes); if (hasOwn$7(AllSymbols, key)) { if (!Attributes.enumerable) { if (!hasOwn$7(O, HIDDEN)) nativeDefineProperty(O, HIDDEN, createPropertyDescriptor(1, {})); O[HIDDEN][key] = true; } else { if (hasOwn$7(O, HIDDEN) && O[HIDDEN][key]) O[HIDDEN][key] = false; Attributes = nativeObjectCreate(Attributes, { enumerable: createPropertyDescriptor(0, false) }); } return setSymbolDescriptor(O, key, Attributes); } return nativeDefineProperty(O, key, Attributes); }; var $defineProperties = function defineProperties(O, Properties) { anObject$5(O); var properties = toIndexedObject$4(Properties); var keys = objectKeys$1(properties).concat($getOwnPropertySymbols(properties)); $forEach$1(keys, function (key) { if (!DESCRIPTORS$a || call$3($propertyIsEnumerable$1, properties, key)) $defineProperty(O, key, properties[key]); }); return O; }; var $create = function create(O, Properties) { return Properties === undefined ? nativeObjectCreate(O) : $defineProperties(nativeObjectCreate(O), Properties); }; var $propertyIsEnumerable$1 = function propertyIsEnumerable(V) { var P = toPropertyKey(V); var enumerable = call$3(nativePropertyIsEnumerable, this, P); if (this === ObjectPrototype$1 && hasOwn$7(AllSymbols, P) && !hasOwn$7(ObjectPrototypeSymbols, P)) return false; return enumerable || !hasOwn$7(this, P) || !hasOwn$7(AllSymbols, P) || hasOwn$7(this, HIDDEN) && this[HIDDEN][P] ? enumerable : true; }; var $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(O, P) { var it = toIndexedObject$4(O); var key = toPropertyKey(P); if (it === ObjectPrototype$1 && hasOwn$7(AllSymbols, key) && !hasOwn$7(ObjectPrototypeSymbols, key)) return; var descriptor = nativeGetOwnPropertyDescriptor$1(it, key); if (descriptor && hasOwn$7(AllSymbols, key) && !(hasOwn$7(it, HIDDEN) && it[HIDDEN][key])) { descriptor.enumerable = true; } return descriptor; }; var $getOwnPropertyNames = function getOwnPropertyNames(O) { var names = nativeGetOwnPropertyNames(toIndexedObject$4(O)); var result = []; $forEach$1(names, function (key) { if (!hasOwn$7(AllSymbols, key) && !hasOwn$7(hiddenKeys$1, key)) push$4(result, key); }); return result; }; var $getOwnPropertySymbols = function (O) { var IS_OBJECT_PROTOTYPE = O === ObjectPrototype$1; var names = nativeGetOwnPropertyNames(IS_OBJECT_PROTOTYPE ? ObjectPrototypeSymbols : toIndexedObject$4(O)); var result = []; $forEach$1(names, function (key) { if (hasOwn$7(AllSymbols, key) && (!IS_OBJECT_PROTOTYPE || hasOwn$7(ObjectPrototype$1, key))) { push$4(result, AllSymbols[key]); } }); return result; }; // `Symbol` constructor // https://tc39.es/ecma262/#sec-symbol-constructor if (!NATIVE_SYMBOL$3) { $Symbol = function Symbol() { if (isPrototypeOf$i(SymbolPrototype, this)) throw TypeError$1('Symbol is not a constructor'); var description = !arguments.length || arguments[0] === undefined ? undefined : $toString(arguments[0]); var tag = uid$1(description); var setter = function (value) { if (this === ObjectPrototype$1) call$3(setter, ObjectPrototypeSymbols, value); if (hasOwn$7(this, HIDDEN) && hasOwn$7(this[HIDDEN], tag)) this[HIDDEN][tag] = false; setSymbolDescriptor(this, tag, createPropertyDescriptor(1, value)); }; if (DESCRIPTORS$a && USE_SETTER) setSymbolDescriptor(ObjectPrototype$1, tag, { configurable: true, set: setter }); return wrap(tag, description); }; SymbolPrototype = $Symbol[PROTOTYPE]; defineBuiltIn$1(SymbolPrototype, 'toString', function toString() { return getInternalState(this).tag; }); defineBuiltIn$1($Symbol, 'withoutSetter', function (description) { return wrap(uid$1(description), description); }); propertyIsEnumerableModule.f = $propertyIsEnumerable$1; definePropertyModule.f = $defineProperty; definePropertiesModule.f = $defineProperties; getOwnPropertyDescriptorModule$2.f = $getOwnPropertyDescriptor; getOwnPropertyNamesModule$2.f = getOwnPropertyNamesExternal.f = $getOwnPropertyNames; getOwnPropertySymbolsModule$2.f = $getOwnPropertySymbols; wrappedWellKnownSymbolModule.f = function (name) { return wrap(wellKnownSymbol$7(name), name); }; if (DESCRIPTORS$a) { // https://github.com/tc39/proposal-Symbol-description defineBuiltInAccessor$2(SymbolPrototype, 'description', { configurable: true, get: function description() { return getInternalState(this).description; } }); } } $$K({ global: true, constructor: true, wrap: true, forced: !NATIVE_SYMBOL$3, sham: !NATIVE_SYMBOL$3 }, { Symbol: $Symbol }); $forEach$1(objectKeys$1(WellKnownSymbolsStore$1), function (name) { defineWellKnownSymbol$l(name); }); $$K({ target: SYMBOL, stat: true, forced: !NATIVE_SYMBOL$3 }, { useSetter: function () { USE_SETTER = true; }, useSimple: function () { USE_SETTER = false; } }); $$K({ target: 'Object', stat: true, forced: !NATIVE_SYMBOL$3, sham: !DESCRIPTORS$a }, { // `Object.create` method // https://tc39.es/ecma262/#sec-object.create create: $create, // `Object.defineProperty` method // https://tc39.es/ecma262/#sec-object.defineproperty defineProperty: $defineProperty, // `Object.defineProperties` method // https://tc39.es/ecma262/#sec-object.defineproperties defineProperties: $defineProperties, // `Object.getOwnPropertyDescriptor` method // https://tc39.es/ecma262/#sec-object.getownpropertydescriptors getOwnPropertyDescriptor: $getOwnPropertyDescriptor }); $$K({ target: 'Object', stat: true, forced: !NATIVE_SYMBOL$3 }, { // `Object.getOwnPropertyNames` method // https://tc39.es/ecma262/#sec-object.getownpropertynames getOwnPropertyNames: $getOwnPropertyNames }); // `Symbol.prototype[@@toPrimitive]` method // https://tc39.es/ecma262/#sec-symbol.prototype-@@toprimitive defineSymbolToPrimitive$1(); // `Symbol.prototype[@@toStringTag]` property // https://tc39.es/ecma262/#sec-symbol.prototype-@@tostringtag setToStringTag$3($Symbol, SYMBOL); hiddenKeys$1[HIDDEN] = true; var NATIVE_SYMBOL$2 = symbolConstructorDetection; /* eslint-disable es/no-symbol -- safe */ var symbolRegistryDetection = NATIVE_SYMBOL$2 && !!Symbol['for'] && !!Symbol.keyFor; var $$J = _export; var getBuiltIn$7 = getBuiltIn$c; var hasOwn$6 = hasOwnProperty_1; var toString$6 = toString$a; var shared$2 = sharedExports; var NATIVE_SYMBOL_REGISTRY$1 = symbolRegistryDetection; var StringToSymbolRegistry = shared$2('string-to-symbol-registry'); var SymbolToStringRegistry$1 = shared$2('symbol-to-string-registry'); // `Symbol.for` method // https://tc39.es/ecma262/#sec-symbol.for $$J({ target: 'Symbol', stat: true, forced: !NATIVE_SYMBOL_REGISTRY$1 }, { 'for': function (key) { var string = toString$6(key); if (hasOwn$6(StringToSymbolRegistry, string)) return StringToSymbolRegistry[string]; var symbol = getBuiltIn$7('Symbol')(string); StringToSymbolRegistry[string] = symbol; SymbolToStringRegistry$1[symbol] = string; return symbol; } }); var $$I = _export; var hasOwn$5 = hasOwnProperty_1; var isSymbol$2 = isSymbol$5; var tryToString$3 = tryToString$6; var shared$1 = sharedExports; var NATIVE_SYMBOL_REGISTRY = symbolRegistryDetection; var SymbolToStringRegistry = shared$1('symbol-to-string-registry'); // `Symbol.keyFor` method // https://tc39.es/ecma262/#sec-symbol.keyfor $$I({ target: 'Symbol', stat: true, forced: !NATIVE_SYMBOL_REGISTRY }, { keyFor: function keyFor(sym) { if (!isSymbol$2(sym)) throw TypeError(tryToString$3(sym) + ' is not a symbol'); if (hasOwn$5(SymbolToStringRegistry, sym)) return SymbolToStringRegistry[sym]; } }); var uncurryThis$g = functionUncurryThis; var isArray$d = isArray$f; var isCallable$3 = isCallable$i; var classof$5 = classofRaw$2; var toString$5 = toString$a; var push$3 = uncurryThis$g([].push); var getJsonReplacerFunction = function (replacer) { if (isCallable$3(replacer)) return replacer; if (!isArray$d(replacer)) return; var rawLength = replacer.length; var keys = []; for (var i = 0; i < rawLength; i++) { var element = replacer[i]; if (typeof element == 'string') push$3(keys, element);else if (typeof element == 'number' || classof$5(element) == 'Number' || classof$5(element) == 'String') push$3(keys, toString$5(element)); } var keysLength = keys.length; var root = true; return function (key, value) { if (root) { root = false; return value; } if (isArray$d(this)) return value; for (var j = 0; j < keysLength; j++) if (keys[j] === key) return value; }; }; var $$H = _export; var getBuiltIn$6 = getBuiltIn$c; var apply$3 = functionApply; var call$2 = functionCall; var uncurryThis$f = functionUncurryThis; var fails$j = fails$w; var isCallable$2 = isCallable$i; var isSymbol$1 = isSymbol$5; var arraySlice$2 = arraySlice$5; var getReplacerFunction = getJsonReplacerFunction; var NATIVE_SYMBOL$1 = symbolConstructorDetection; var $String = String; var $stringify = getBuiltIn$6('JSON', 'stringify'); var exec$1 = uncurryThis$f(/./.exec); var charAt$1 = uncurryThis$f(''.charAt); var charCodeAt = uncurryThis$f(''.charCodeAt); var replace$1 = uncurryThis$f(''.replace); var numberToString = uncurryThis$f(1.0.toString); var tester = /[\uD800-\uDFFF]/g; var low = /^[\uD800-\uDBFF]$/; var hi = /^[\uDC00-\uDFFF]$/; var WRONG_SYMBOLS_CONVERSION = !NATIVE_SYMBOL$1 || fails$j(function () { var symbol = getBuiltIn$6('Symbol')(); // MS Edge converts symbol values to JSON as {} return $stringify([symbol]) != '[null]' // WebKit converts symbol values to JSON as null || $stringify({ a: symbol }) != '{}' // V8 throws on boxed symbols || $stringify(Object(symbol)) != '{}'; }); // https://github.com/tc39/proposal-well-formed-stringify var ILL_FORMED_UNICODE = fails$j(function () { return $stringify('\uDF06\uD834') !== '"\\udf06\\ud834"' || $stringify('\uDEAD') !== '"\\udead"'; }); var stringifyWithSymbolsFix = function (it, replacer) { var args = arraySlice$2(arguments); var $replacer = getReplacerFunction(replacer); if (!isCallable$2($replacer) && (it === undefined || isSymbol$1(it))) return; // IE8 returns string on undefined args[1] = function (key, value) { // some old implementations (like WebKit) could pass numbers as keys if (isCallable$2($replacer)) value = call$2($replacer, this, $String(key), value); if (!isSymbol$1(value)) return value; }; return apply$3($stringify, null, args); }; var fixIllFormed = function (match, offset, string) { var prev = charAt$1(string, offset - 1); var next = charAt$1(string, offset + 1); if (exec$1(low, match) && !exec$1(hi, next) || exec$1(hi, match) && !exec$1(low, prev)) { return '\\u' + numberToString(charCodeAt(match, 0), 16); } return match; }; if ($stringify) { // `JSON.stringify` method // https://tc39.es/ecma262/#sec-json.stringify $$H({ target: 'JSON', stat: true, arity: 3, forced: WRONG_SYMBOLS_CONVERSION || ILL_FORMED_UNICODE }, { // eslint-disable-next-line no-unused-vars -- required for `.length` stringify: function stringify(it, replacer, space) { var args = arraySlice$2(arguments); var result = apply$3(WRONG_SYMBOLS_CONVERSION ? stringifyWithSymbolsFix : $stringify, null, args); return ILL_FORMED_UNICODE && typeof result == 'string' ? replace$1(result, tester, fixIllFormed) : result; } }); } var $$G = _export; var NATIVE_SYMBOL = symbolConstructorDetection; var fails$i = fails$w; var getOwnPropertySymbolsModule$1 = objectGetOwnPropertySymbols; var toObject$7 = toObject$d; // V8 ~ Chrome 38 and 39 `Object.getOwnPropertySymbols` fails on primitives // https://bugs.chromium.org/p/v8/issues/detail?id=3443 var FORCED$9 = !NATIVE_SYMBOL || fails$i(function () { getOwnPropertySymbolsModule$1.f(1); }); // `Object.getOwnPropertySymbols` method // https://tc39.es/ecma262/#sec-object.getownpropertysymbols $$G({ target: 'Object', stat: true, forced: FORCED$9 }, { getOwnPropertySymbols: function getOwnPropertySymbols(it) { var $getOwnPropertySymbols = getOwnPropertySymbolsModule$1.f; return $getOwnPropertySymbols ? $getOwnPropertySymbols(toObject$7(it)) : []; } }); var path$r = path$y; var getOwnPropertySymbols$1 = path$r.Object.getOwnPropertySymbols; var parent$15 = getOwnPropertySymbols$1; var getOwnPropertySymbols = parent$15; (function (module) { module.exports = getOwnPropertySymbols; })(getOwnPropertySymbols$2); var _Object$getOwnPropertySymbols = /*@__PURE__*/getDefaultExportFromCjs(getOwnPropertySymbolsExports); var getOwnPropertyDescriptorExports$3 = {}; var getOwnPropertyDescriptor$8 = { get exports() { return getOwnPropertyDescriptorExports$3; }, set exports(v) { getOwnPropertyDescriptorExports$3 = v; } }; var getOwnPropertyDescriptorExports$2 = {}; var getOwnPropertyDescriptor$7 = { get exports() { return getOwnPropertyDescriptorExports$2; }, set exports(v) { getOwnPropertyDescriptorExports$2 = v; } }; var $$F = _export; var fails$h = fails$w; var toIndexedObject$3 = toIndexedObject$b; var nativeGetOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f; var DESCRIPTORS$9 = descriptors; var FORCED$8 = !DESCRIPTORS$9 || fails$h(function () { nativeGetOwnPropertyDescriptor(1); }); // `Object.getOwnPropertyDescriptor` method // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor $$F({ target: 'Object', stat: true, forced: FORCED$8, sham: !DESCRIPTORS$9 }, { getOwnPropertyDescriptor: function getOwnPropertyDescriptor(it, key) { return nativeGetOwnPropertyDescriptor(toIndexedObject$3(it), key); } }); var path$q = path$y; var Object$5 = path$q.Object; var getOwnPropertyDescriptor$6 = getOwnPropertyDescriptor$7.exports = function getOwnPropertyDescriptor(it, key) { return Object$5.getOwnPropertyDescriptor(it, key); }; if (Object$5.getOwnPropertyDescriptor.sham) getOwnPropertyDescriptor$6.sham = true; var parent$14 = getOwnPropertyDescriptorExports$2; var getOwnPropertyDescriptor$5 = parent$14; (function (module) { module.exports = getOwnPropertyDescriptor$5; })(getOwnPropertyDescriptor$8); var _Object$getOwnPropertyDescriptor$1 = /*@__PURE__*/getDefaultExportFromCjs(getOwnPropertyDescriptorExports$3); var getOwnPropertyDescriptorsExports = {}; var getOwnPropertyDescriptors$2 = { get exports() { return getOwnPropertyDescriptorsExports; }, set exports(v) { getOwnPropertyDescriptorsExports = v; } }; var getBuiltIn$5 = getBuiltIn$c; var uncurryThis$e = functionUncurryThis; var getOwnPropertyNamesModule$1 = objectGetOwnPropertyNames; var getOwnPropertySymbolsModule = objectGetOwnPropertySymbols; var anObject$4 = anObject$d; var concat$4 = uncurryThis$e([].concat); // all object keys, includes non-enumerable and symbols var ownKeys$9 = getBuiltIn$5('Reflect', 'ownKeys') || function ownKeys(it) { var keys = getOwnPropertyNamesModule$1.f(anObject$4(it)); var getOwnPropertySymbols = getOwnPropertySymbolsModule.f; return getOwnPropertySymbols ? concat$4(keys, getOwnPropertySymbols(it)) : keys; }; var $$E = _export; var DESCRIPTORS$8 = descriptors; var ownKeys$8 = ownKeys$9; var toIndexedObject$2 = toIndexedObject$b; var getOwnPropertyDescriptorModule$1 = objectGetOwnPropertyDescriptor; var createProperty$3 = createProperty$6; // `Object.getOwnPropertyDescriptors` method // https://tc39.es/ecma262/#sec-object.getownpropertydescriptors $$E({ target: 'Object', stat: true, sham: !DESCRIPTORS$8 }, { getOwnPropertyDescriptors: function getOwnPropertyDescriptors(object) { var O = toIndexedObject$2(object); var getOwnPropertyDescriptor = getOwnPropertyDescriptorModule$1.f; var keys = ownKeys$8(O); var result = {}; var index = 0; var key, descriptor; while (keys.length > index) { descriptor = getOwnPropertyDescriptor(O, key = keys[index++]); if (descriptor !== undefined) createProperty$3(result, key, descriptor); } return result; } }); var path$p = path$y; var getOwnPropertyDescriptors$1 = path$p.Object.getOwnPropertyDescriptors; var parent$13 = getOwnPropertyDescriptors$1; var getOwnPropertyDescriptors = parent$13; (function (module) { module.exports = getOwnPropertyDescriptors; })(getOwnPropertyDescriptors$2); var _Object$getOwnPropertyDescriptors = /*@__PURE__*/getDefaultExportFromCjs(getOwnPropertyDescriptorsExports); var definePropertiesExports$1 = {}; var defineProperties$4 = { get exports() { return definePropertiesExports$1; }, set exports(v) { definePropertiesExports$1 = v; } }; var definePropertiesExports = {}; var defineProperties$3 = { get exports() { return definePropertiesExports; }, set exports(v) { definePropertiesExports = v; } }; var $$D = _export; var DESCRIPTORS$7 = descriptors; var defineProperties$2 = objectDefineProperties.f; // `Object.defineProperties` method // https://tc39.es/ecma262/#sec-object.defineproperties // eslint-disable-next-line es/no-object-defineproperties -- safe $$D({ target: 'Object', stat: true, forced: Object.defineProperties !== defineProperties$2, sham: !DESCRIPTORS$7 }, { defineProperties: defineProperties$2 }); var path$o = path$y; var Object$4 = path$o.Object; var defineProperties$1 = defineProperties$3.exports = function defineProperties(T, D) { return Object$4.defineProperties(T, D); }; if (Object$4.defineProperties.sham) defineProperties$1.sham = true; var parent$12 = definePropertiesExports; var defineProperties = parent$12; (function (module) { module.exports = defineProperties; })(defineProperties$4); var _Object$defineProperties = /*@__PURE__*/getDefaultExportFromCjs(definePropertiesExports$1); var definePropertyExports$3 = {}; var defineProperty$a = { get exports() { return definePropertyExports$3; }, set exports(v) { definePropertyExports$3 = v; } }; var definePropertyExports$2 = {}; var defineProperty$9 = { get exports() { return definePropertyExports$2; }, set exports(v) { definePropertyExports$2 = v; } }; var $$C = _export; var DESCRIPTORS$6 = descriptors; var defineProperty$8 = objectDefineProperty.f; // `Object.defineProperty` method // https://tc39.es/ecma262/#sec-object.defineproperty // eslint-disable-next-line es/no-object-defineproperty -- safe $$C({ target: 'Object', stat: true, forced: Object.defineProperty !== defineProperty$8, sham: !DESCRIPTORS$6 }, { defineProperty: defineProperty$8 }); var path$n = path$y; var Object$3 = path$n.Object; var defineProperty$7 = defineProperty$9.exports = function defineProperty(it, key, desc) { return Object$3.defineProperty(it, key, desc); }; if (Object$3.defineProperty.sham) defineProperty$7.sham = true; var parent$11 = definePropertyExports$2; var defineProperty$6 = parent$11; (function (module) { module.exports = defineProperty$6; })(defineProperty$a); var _Object$defineProperty$1 = /*@__PURE__*/getDefaultExportFromCjs(definePropertyExports$3); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var definePropertyExports$1 = {}; var defineProperty$5 = { get exports() { return definePropertyExports$1; }, set exports(v) { definePropertyExports$1 = v; } }; var definePropertyExports = {}; var defineProperty$4 = { get exports() { return definePropertyExports; }, set exports(v) { definePropertyExports = v; } }; var parent$10 = defineProperty$6; var defineProperty$3 = parent$10; var parent$$ = defineProperty$3; var defineProperty$2 = parent$$; (function (module) { module.exports = defineProperty$2; })(defineProperty$4); (function (module) { module.exports = definePropertyExports; })(defineProperty$5); var _Object$defineProperty = /*@__PURE__*/getDefaultExportFromCjs(definePropertyExports$1); var symbolExports$2 = {}; var symbol$6 = { get exports() { return symbolExports$2; }, set exports(v) { symbolExports$2 = v; } }; var symbolExports$1 = {}; var symbol$5 = { get exports() { return symbolExports$1; }, set exports(v) { symbolExports$1 = v; } }; var $TypeError$8 = TypeError; var MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFF; // 2 ** 53 - 1 == 9007199254740991 var doesNotExceedSafeInteger$2 = function (it) { if (it > MAX_SAFE_INTEGER) throw $TypeError$8('Maximum allowed index exceeded'); return it; }; var fails$g = fails$w; var wellKnownSymbol$6 = wellKnownSymbol$l; var V8_VERSION$1 = engineV8Version; var SPECIES$2 = wellKnownSymbol$6('species'); var arrayMethodHasSpeciesSupport$5 = function (METHOD_NAME) { // We can't use this feature detection in V8 since it causes // deoptimization and serious performance degradation // https://github.com/zloirock/core-js/issues/677 return V8_VERSION$1 >= 51 || !fails$g(function () { var array = []; var constructor = array.constructor = {}; constructor[SPECIES$2] = function () { return { foo: 1 }; }; return array[METHOD_NAME](Boolean).foo !== 1; }); }; var $$B = _export; var fails$f = fails$w; var isArray$c = isArray$f; var isObject$a = isObject$j; var toObject$6 = toObject$d; var lengthOfArrayLike$6 = lengthOfArrayLike$b; var doesNotExceedSafeInteger$1 = doesNotExceedSafeInteger$2; var createProperty$2 = createProperty$6; var arraySpeciesCreate$1 = arraySpeciesCreate$3; var arrayMethodHasSpeciesSupport$4 = arrayMethodHasSpeciesSupport$5; var wellKnownSymbol$5 = wellKnownSymbol$l; var V8_VERSION = engineV8Version; var IS_CONCAT_SPREADABLE = wellKnownSymbol$5('isConcatSpreadable'); // We can't use this feature detection in V8 since it causes // deoptimization and serious performance degradation // https://github.com/zloirock/core-js/issues/679 var IS_CONCAT_SPREADABLE_SUPPORT = V8_VERSION >= 51 || !fails$f(function () { var array = []; array[IS_CONCAT_SPREADABLE] = false; return array.concat()[0] !== array; }); var isConcatSpreadable = function (O) { if (!isObject$a(O)) return false; var spreadable = O[IS_CONCAT_SPREADABLE]; return spreadable !== undefined ? !!spreadable : isArray$c(O); }; var FORCED$7 = !IS_CONCAT_SPREADABLE_SUPPORT || !arrayMethodHasSpeciesSupport$4('concat'); // `Array.prototype.concat` method // https://tc39.es/ecma262/#sec-array.prototype.concat // with adding support of @@isConcatSpreadable and @@species $$B({ target: 'Array', proto: true, arity: 1, forced: FORCED$7 }, { // eslint-disable-next-line no-unused-vars -- required for `.length` concat: function concat(arg) { var O = toObject$6(this); var A = arraySpeciesCreate$1(O, 0); var n = 0; var i, k, length, len, E; for (i = -1, length = arguments.length; i < length; i++) { E = i === -1 ? O : arguments[i]; if (isConcatSpreadable(E)) { len = lengthOfArrayLike$6(E); doesNotExceedSafeInteger$1(n + len); for (k = 0; k < len; k++, n++) if (k in E) createProperty$2(A, n, E[k]); } else { doesNotExceedSafeInteger$1(n + 1); createProperty$2(A, n++, E); } } A.length = n; return A; } }); var defineWellKnownSymbol$k = wellKnownSymbolDefine; // `Symbol.asyncIterator` well-known symbol // https://tc39.es/ecma262/#sec-symbol.asynciterator defineWellKnownSymbol$k('asyncIterator'); var defineWellKnownSymbol$j = wellKnownSymbolDefine; // `Symbol.hasInstance` well-known symbol // https://tc39.es/ecma262/#sec-symbol.hasinstance defineWellKnownSymbol$j('hasInstance'); var defineWellKnownSymbol$i = wellKnownSymbolDefine; // `Symbol.isConcatSpreadable` well-known symbol // https://tc39.es/ecma262/#sec-symbol.isconcatspreadable defineWellKnownSymbol$i('isConcatSpreadable'); var defineWellKnownSymbol$h = wellKnownSymbolDefine; // `Symbol.iterator` well-known symbol // https://tc39.es/ecma262/#sec-symbol.iterator defineWellKnownSymbol$h('iterator'); var defineWellKnownSymbol$g = wellKnownSymbolDefine; // `Symbol.match` well-known symbol // https://tc39.es/ecma262/#sec-symbol.match defineWellKnownSymbol$g('match'); var defineWellKnownSymbol$f = wellKnownSymbolDefine; // `Symbol.matchAll` well-known symbol // https://tc39.es/ecma262/#sec-symbol.matchall defineWellKnownSymbol$f('matchAll'); var defineWellKnownSymbol$e = wellKnownSymbolDefine; // `Symbol.replace` well-known symbol // https://tc39.es/ecma262/#sec-symbol.replace defineWellKnownSymbol$e('replace'); var defineWellKnownSymbol$d = wellKnownSymbolDefine; // `Symbol.search` well-known symbol // https://tc39.es/ecma262/#sec-symbol.search defineWellKnownSymbol$d('search'); var defineWellKnownSymbol$c = wellKnownSymbolDefine; // `Symbol.species` well-known symbol // https://tc39.es/ecma262/#sec-symbol.species defineWellKnownSymbol$c('species'); var defineWellKnownSymbol$b = wellKnownSymbolDefine; // `Symbol.split` well-known symbol // https://tc39.es/ecma262/#sec-symbol.split defineWellKnownSymbol$b('split'); var defineWellKnownSymbol$a = wellKnownSymbolDefine; var defineSymbolToPrimitive = symbolDefineToPrimitive; // `Symbol.toPrimitive` well-known symbol // https://tc39.es/ecma262/#sec-symbol.toprimitive defineWellKnownSymbol$a('toPrimitive'); // `Symbol.prototype[@@toPrimitive]` method // https://tc39.es/ecma262/#sec-symbol.prototype-@@toprimitive defineSymbolToPrimitive(); var getBuiltIn$4 = getBuiltIn$c; var defineWellKnownSymbol$9 = wellKnownSymbolDefine; var setToStringTag$2 = setToStringTag$6; // `Symbol.toStringTag` well-known symbol // https://tc39.es/ecma262/#sec-symbol.tostringtag defineWellKnownSymbol$9('toStringTag'); // `Symbol.prototype[@@toStringTag]` property // https://tc39.es/ecma262/#sec-symbol.prototype-@@tostringtag setToStringTag$2(getBuiltIn$4('Symbol'), 'Symbol'); var defineWellKnownSymbol$8 = wellKnownSymbolDefine; // `Symbol.unscopables` well-known symbol // https://tc39.es/ecma262/#sec-symbol.unscopables defineWellKnownSymbol$8('unscopables'); var global$9 = global$l; var setToStringTag$1 = setToStringTag$6; // JSON[@@toStringTag] property // https://tc39.es/ecma262/#sec-json-@@tostringtag setToStringTag$1(global$9.JSON, 'JSON', true); var path$m = path$y; var symbol$4 = path$m.Symbol; var parent$_ = symbol$4; var symbol$3 = parent$_; var defineWellKnownSymbol$7 = wellKnownSymbolDefine; // `Symbol.dispose` well-known symbol // https://github.com/tc39/proposal-explicit-resource-management defineWellKnownSymbol$7('dispose'); var parent$Z = symbol$3; var symbol$2 = parent$Z; var defineWellKnownSymbol$6 = wellKnownSymbolDefine; // `Symbol.asyncDispose` well-known symbol // https://github.com/tc39/proposal-async-explicit-resource-management defineWellKnownSymbol$6('asyncDispose'); var $$A = _export; var getBuiltIn$3 = getBuiltIn$c; var uncurryThis$d = functionUncurryThis; var Symbol$4 = getBuiltIn$3('Symbol'); var keyFor = Symbol$4.keyFor; var thisSymbolValue$1 = uncurryThis$d(Symbol$4.prototype.valueOf); // `Symbol.isRegistered` method // https://tc39.es/proposal-symbol-predicates/#sec-symbol-isregistered $$A({ target: 'Symbol', stat: true }, { isRegistered: function isRegistered(value) { try { return keyFor(thisSymbolValue$1(value)) !== undefined; } catch (error) { return false; } } }); var $$z = _export; var shared = sharedExports; var getBuiltIn$2 = getBuiltIn$c; var uncurryThis$c = functionUncurryThis; var isSymbol = isSymbol$5; var wellKnownSymbol$4 = wellKnownSymbol$l; var Symbol$3 = getBuiltIn$2('Symbol'); var $isWellKnown = Symbol$3.isWellKnown; var getOwnPropertyNames$4 = getBuiltIn$2('Object', 'getOwnPropertyNames'); var thisSymbolValue = uncurryThis$c(Symbol$3.prototype.valueOf); var WellKnownSymbolsStore = shared('wks'); for (var i = 0, symbolKeys = getOwnPropertyNames$4(Symbol$3), symbolKeysLength = symbolKeys.length; i < symbolKeysLength; i++) { // some old engines throws on access to some keys like `arguments` or `caller` try { var symbolKey = symbolKeys[i]; if (isSymbol(Symbol$3[symbolKey])) wellKnownSymbol$4(symbolKey); } catch (error) {/* empty */} } // `Symbol.isWellKnown` method // https://tc39.es/proposal-symbol-predicates/#sec-symbol-iswellknown // We should patch it for newly added well-known symbols. If it's not required, this module just will not be injected $$z({ target: 'Symbol', stat: true, forced: true }, { isWellKnown: function isWellKnown(value) { if ($isWellKnown && $isWellKnown(value)) return true; try { var symbol = thisSymbolValue(value); for (var j = 0, keys = getOwnPropertyNames$4(WellKnownSymbolsStore), keysLength = keys.length; j < keysLength; j++) { if (WellKnownSymbolsStore[keys[j]] == symbol) return true; } } catch (error) {/* empty */} return false; } }); var defineWellKnownSymbol$5 = wellKnownSymbolDefine; // `Symbol.matcher` well-known symbol // https://github.com/tc39/proposal-pattern-matching defineWellKnownSymbol$5('matcher'); var defineWellKnownSymbol$4 = wellKnownSymbolDefine; // `Symbol.metadataKey` well-known symbol // https://github.com/tc39/proposal-decorator-metadata defineWellKnownSymbol$4('metadataKey'); var defineWellKnownSymbol$3 = wellKnownSymbolDefine; // `Symbol.observable` well-known symbol // https://github.com/tc39/proposal-observable defineWellKnownSymbol$3('observable'); // TODO: Remove from `core-js@4` var defineWellKnownSymbol$2 = wellKnownSymbolDefine; // `Symbol.metadata` well-known symbol // https://github.com/tc39/proposal-decorators defineWellKnownSymbol$2('metadata'); // TODO: remove from `core-js@4` var defineWellKnownSymbol$1 = wellKnownSymbolDefine; // `Symbol.patternMatch` well-known symbol // https://github.com/tc39/proposal-pattern-matching defineWellKnownSymbol$1('patternMatch'); // TODO: remove from `core-js@4` var defineWellKnownSymbol = wellKnownSymbolDefine; defineWellKnownSymbol('replaceAll'); var parent$Y = symbol$2; // TODO: Remove from `core-js@4` var symbol$1 = parent$Y; (function (module) { module.exports = symbol$1; })(symbol$5); (function (module) { module.exports = symbolExports$1; })(symbol$6); var _Symbol$1 = /*@__PURE__*/getDefaultExportFromCjs(symbolExports$2); var iteratorExports$1 = {}; var iterator$5 = { get exports() { return iteratorExports$1; }, set exports(v) { iteratorExports$1 = v; } }; var iteratorExports = {}; var iterator$4 = { get exports() { return iteratorExports; }, set exports(v) { iteratorExports = v; } }; var WrappedWellKnownSymbolModule$1 = wellKnownSymbolWrapped; var iterator$3 = WrappedWellKnownSymbolModule$1.f('iterator'); var parent$X = iterator$3; var iterator$2 = parent$X; var parent$W = iterator$2; var iterator$1 = parent$W; var parent$V = iterator$1; var iterator = parent$V; (function (module) { module.exports = iterator; })(iterator$4); (function (module) { module.exports = iteratorExports; })(iterator$5); var _Symbol$iterator = /*@__PURE__*/getDefaultExportFromCjs(iteratorExports$1); function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof _Symbol$1 && "symbol" == typeof _Symbol$iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof _Symbol$1 && obj.constructor === _Symbol$1 && obj !== _Symbol$1.prototype ? "symbol" : typeof obj; }, _typeof(obj); } var toPrimitiveExports$1 = {}; var toPrimitive$5 = { get exports() { return toPrimitiveExports$1; }, set exports(v) { toPrimitiveExports$1 = v; } }; var toPrimitiveExports = {}; var toPrimitive$4 = { get exports() { return toPrimitiveExports; }, set exports(v) { toPrimitiveExports = v; } }; var WrappedWellKnownSymbolModule = wellKnownSymbolWrapped; var toPrimitive$3 = WrappedWellKnownSymbolModule.f('toPrimitive'); var parent$U = toPrimitive$3; var toPrimitive$2 = parent$U; var parent$T = toPrimitive$2; var toPrimitive$1 = parent$T; var parent$S = toPrimitive$1; var toPrimitive = parent$S; (function (module) { module.exports = toPrimitive; })(toPrimitive$4); (function (module) { module.exports = toPrimitiveExports; })(toPrimitive$5); var _Symbol$toPrimitive = /*@__PURE__*/getDefaultExportFromCjs(toPrimitiveExports$1); function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[_Symbol$toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; _Object$defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); _Object$defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { _Object$defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var isArrayExports$2 = {}; var isArray$b = { get exports() { return isArrayExports$2; }, set exports(v) { isArrayExports$2 = v; } }; var isArrayExports$1 = {}; var isArray$a = { get exports() { return isArrayExports$1; }, set exports(v) { isArrayExports$1 = v; } }; var $$y = _export; var isArray$9 = isArray$f; // `Array.isArray` method // https://tc39.es/ecma262/#sec-array.isarray $$y({ target: 'Array', stat: true }, { isArray: isArray$9 }); var path$l = path$y; var isArray$8 = path$l.Array.isArray; var parent$R = isArray$8; var isArray$7 = parent$R; var parent$Q = isArray$7; var isArray$6 = parent$Q; var parent$P = isArray$6; var isArray$5 = parent$P; (function (module) { module.exports = isArray$5; })(isArray$a); (function (module) { module.exports = isArrayExports$1; })(isArray$b); var _Array$isArray$1 = /*@__PURE__*/getDefaultExportFromCjs(isArrayExports$2); function _arrayWithHoles(arr) { if (_Array$isArray$1(arr)) return arr; } function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof _Symbol$1 && _getIteratorMethod(arr) || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } } var sliceExports$2 = {}; var slice$7 = { get exports() { return sliceExports$2; }, set exports(v) { sliceExports$2 = v; } }; var sliceExports$1 = {}; var slice$6 = { get exports() { return sliceExports$1; }, set exports(v) { sliceExports$1 = v; } }; var $$x = _export; var isArray$4 = isArray$f; var isConstructor$1 = isConstructor$4; var isObject$9 = isObject$j; var toAbsoluteIndex$2 = toAbsoluteIndex$5; var lengthOfArrayLike$5 = lengthOfArrayLike$b; var toIndexedObject$1 = toIndexedObject$b; var createProperty$1 = createProperty$6; var wellKnownSymbol$3 = wellKnownSymbol$l; var arrayMethodHasSpeciesSupport$3 = arrayMethodHasSpeciesSupport$5; var nativeSlice = arraySlice$5; var HAS_SPECIES_SUPPORT$3 = arrayMethodHasSpeciesSupport$3('slice'); var SPECIES$1 = wellKnownSymbol$3('species'); var $Array = Array; var max$1 = Math.max; // `Array.prototype.slice` method // https://tc39.es/ecma262/#sec-array.prototype.slice // fallback for not array-like ES3 strings and DOM objects $$x({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$3 }, { slice: function slice(start, end) { var O = toIndexedObject$1(this); var length = lengthOfArrayLike$5(O); var k = toAbsoluteIndex$2(start, length); var fin = toAbsoluteIndex$2(end === undefined ? length : end, length); // inline `ArraySpeciesCreate` for usage native `Array#slice` where it's possible var Constructor, result, n; if (isArray$4(O)) { Constructor = O.constructor; // cross-realm fallback if (isConstructor$1(Constructor) && (Constructor === $Array || isArray$4(Constructor.prototype))) { Constructor = undefined; } else if (isObject$9(Constructor)) { Constructor = Constructor[SPECIES$1]; if (Constructor === null) Constructor = undefined; } if (Constructor === $Array || Constructor === undefined) { return nativeSlice(O, k, fin); } } result = new (Constructor === undefined ? $Array : Constructor)(max$1(fin - k, 0)); for (n = 0; k < fin; k++, n++) if (k in O) createProperty$1(result, n, O[k]); result.length = n; return result; } }); var entryVirtual$g = entryVirtual$i; var slice$5 = entryVirtual$g('Array').slice; var isPrototypeOf$h = objectIsPrototypeOf; var method$e = slice$5; var ArrayPrototype$e = Array.prototype; var slice$4 = function (it) { var own = it.slice; return it === ArrayPrototype$e || isPrototypeOf$h(ArrayPrototype$e, it) && own === ArrayPrototype$e.slice ? method$e : own; }; var parent$O = slice$4; var slice$3 = parent$O; var parent$N = slice$3; var slice$2 = parent$N; var parent$M = slice$2; var slice$1 = parent$M; (function (module) { module.exports = slice$1; })(slice$6); (function (module) { module.exports = sliceExports$1; })(slice$7); var _sliceInstanceProperty$1 = /*@__PURE__*/getDefaultExportFromCjs(sliceExports$2); var fromExports$1 = {}; var from$3 = { get exports() { return fromExports$1; }, set exports(v) { fromExports$1 = v; } }; var fromExports = {}; var from$2 = { get exports() { return fromExports; }, set exports(v) { fromExports = v; } }; var parent$L = from$4; var from$1 = parent$L; var parent$K = from$1; var from = parent$K; (function (module) { module.exports = from; })(from$2); (function (module) { module.exports = fromExports; })(from$3); var _Array$from = /*@__PURE__*/getDefaultExportFromCjs(fromExports$1); function _arrayLikeToArray$7(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _unsupportedIterableToArray$7(o, minLen) { var _context; if (!o) return; if (typeof o === "string") return _arrayLikeToArray$7(o, minLen); var n = _sliceInstanceProperty$1(_context = Object.prototype.toString.call(o)).call(_context, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$7(o, minLen); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray$7(arr, i) || _nonIterableRest(); } function _arrayWithoutHoles(arr) { if (_Array$isArray$1(arr)) return _arrayLikeToArray$7(arr); } function _iterableToArray(iter) { if (typeof _Symbol$1 !== "undefined" && _getIteratorMethod(iter) != null || iter["@@iterator"] != null) return _Array$from(iter); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray$7(arr) || _nonIterableSpread(); } var symbolExports = {}; var symbol = { get exports() { return symbolExports; }, set exports(v) { symbolExports = v; } }; (function (module) { module.exports = symbol$3; })(symbol); var _Symbol = /*@__PURE__*/getDefaultExportFromCjs(symbolExports); var concatExports = {}; var concat$3 = { get exports() { return concatExports; }, set exports(v) { concatExports = v; } }; var entryVirtual$f = entryVirtual$i; var concat$2 = entryVirtual$f('Array').concat; var isPrototypeOf$g = objectIsPrototypeOf; var method$d = concat$2; var ArrayPrototype$d = Array.prototype; var concat$1 = function (it) { var own = it.concat; return it === ArrayPrototype$d || isPrototypeOf$g(ArrayPrototype$d, it) && own === ArrayPrototype$d.concat ? method$d : own; }; var parent$J = concat$1; var concat = parent$J; (function (module) { module.exports = concat; })(concat$3); var _concatInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(concatExports); var sliceExports = {}; var slice = { get exports() { return sliceExports; }, set exports(v) { sliceExports = v; } }; (function (module) { module.exports = slice$3; })(slice); var _sliceInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(sliceExports); var ownKeysExports = {}; var ownKeys$7 = { get exports() { return ownKeysExports; }, set exports(v) { ownKeysExports = v; } }; var $$w = _export; var ownKeys$6 = ownKeys$9; // `Reflect.ownKeys` method // https://tc39.es/ecma262/#sec-reflect.ownkeys $$w({ target: 'Reflect', stat: true }, { ownKeys: ownKeys$6 }); var path$k = path$y; var ownKeys$5 = path$k.Reflect.ownKeys; var parent$I = ownKeys$5; var ownKeys$4 = parent$I; (function (module) { module.exports = ownKeys$4; })(ownKeys$7); var isArrayExports = {}; var isArray$3 = { get exports() { return isArrayExports; }, set exports(v) { isArrayExports = v; } }; (function (module) { module.exports = isArray$7; })(isArray$3); var _Array$isArray = /*@__PURE__*/getDefaultExportFromCjs(isArrayExports); var mapExports$1 = {}; var map$6 = { get exports() { return mapExports$1; }, set exports(v) { mapExports$1 = v; } }; var $$v = _export; var $map = arrayIteration.map; var arrayMethodHasSpeciesSupport$2 = arrayMethodHasSpeciesSupport$5; var HAS_SPECIES_SUPPORT$2 = arrayMethodHasSpeciesSupport$2('map'); // `Array.prototype.map` method // https://tc39.es/ecma262/#sec-array.prototype.map // with adding support of @@species $$v({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$2 }, { map: function map(callbackfn /* , thisArg */) { return $map(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); } }); var entryVirtual$e = entryVirtual$i; var map$5 = entryVirtual$e('Array').map; var isPrototypeOf$f = objectIsPrototypeOf; var method$c = map$5; var ArrayPrototype$c = Array.prototype; var map$4 = function (it) { var own = it.map; return it === ArrayPrototype$c || isPrototypeOf$f(ArrayPrototype$c, it) && own === ArrayPrototype$c.map ? method$c : own; }; var parent$H = map$4; var map$3 = parent$H; (function (module) { module.exports = map$3; })(map$6); var _mapInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(mapExports$1); var keysExports = {}; var keys$2 = { get exports() { return keysExports; }, set exports(v) { keysExports = v; } }; var $$u = _export; var toObject$5 = toObject$d; var nativeKeys = objectKeys$4; var fails$e = fails$w; var FAILS_ON_PRIMITIVES$3 = fails$e(function () { nativeKeys(1); }); // `Object.keys` method // https://tc39.es/ecma262/#sec-object.keys $$u({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$3 }, { keys: function keys(it) { return nativeKeys(toObject$5(it)); } }); var path$j = path$y; var keys$1 = path$j.Object.keys; var parent$G = keys$1; var keys = parent$G; (function (module) { module.exports = keys; })(keys$2); var _Object$keys = /*@__PURE__*/getDefaultExportFromCjs(keysExports); var nowExports = {}; var now$3 = { get exports() { return nowExports; }, set exports(v) { nowExports = v; } }; // TODO: Remove from `core-js@4` var $$t = _export; var uncurryThis$b = functionUncurryThis; var $Date = Date; var thisTimeValue = uncurryThis$b($Date.prototype.getTime); // `Date.now` method // https://tc39.es/ecma262/#sec-date.now $$t({ target: 'Date', stat: true }, { now: function now() { return thisTimeValue(new $Date()); } }); var path$i = path$y; var now$2 = path$i.Date.now; var parent$F = now$2; var now$1 = parent$F; (function (module) { module.exports = now$1; })(now$3); var _Date$now = /*@__PURE__*/getDefaultExportFromCjs(nowExports); var forEachExports = {}; var forEach$6 = { get exports() { return forEachExports; }, set exports(v) { forEachExports = v; } }; var fails$d = fails$w; var arrayMethodIsStrict$6 = function (METHOD_NAME, argument) { var method = [][METHOD_NAME]; return !!method && fails$d(function () { // eslint-disable-next-line no-useless-call -- required for testing method.call(null, argument || function () { return 1; }, 1); }); }; var $forEach = arrayIteration.forEach; var arrayMethodIsStrict$5 = arrayMethodIsStrict$6; var STRICT_METHOD$3 = arrayMethodIsStrict$5('forEach'); // `Array.prototype.forEach` method implementation // https://tc39.es/ecma262/#sec-array.prototype.foreach var arrayForEach = !STRICT_METHOD$3 ? function forEach(callbackfn /* , thisArg */) { return $forEach(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); // eslint-disable-next-line es/no-array-prototype-foreach -- safe } : [].forEach; var $$s = _export; var forEach$5 = arrayForEach; // `Array.prototype.forEach` method // https://tc39.es/ecma262/#sec-array.prototype.foreach // eslint-disable-next-line es/no-array-prototype-foreach -- safe $$s({ target: 'Array', proto: true, forced: [].forEach != forEach$5 }, { forEach: forEach$5 }); var entryVirtual$d = entryVirtual$i; var forEach$4 = entryVirtual$d('Array').forEach; var parent$E = forEach$4; var forEach$3 = parent$E; var classof$4 = classof$d; var hasOwn$4 = hasOwnProperty_1; var isPrototypeOf$e = objectIsPrototypeOf; var method$b = forEach$3; var ArrayPrototype$b = Array.prototype; var DOMIterables$1 = { DOMTokenList: true, NodeList: true }; var forEach$2 = function (it) { var own = it.forEach; return it === ArrayPrototype$b || isPrototypeOf$e(ArrayPrototype$b, it) && own === ArrayPrototype$b.forEach || hasOwn$4(DOMIterables$1, classof$4(it)) ? method$b : own; }; (function (module) { module.exports = forEach$2; })(forEach$6); var _forEachInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(forEachExports); var reverseExports = {}; var reverse$3 = { get exports() { return reverseExports; }, set exports(v) { reverseExports = v; } }; var $$r = _export; var uncurryThis$a = functionUncurryThis; var isArray$2 = isArray$f; var nativeReverse = uncurryThis$a([].reverse); var test$1 = [1, 2]; // `Array.prototype.reverse` method // https://tc39.es/ecma262/#sec-array.prototype.reverse // fix for Safari 12.0 bug // https://bugs.webkit.org/show_bug.cgi?id=188794 $$r({ target: 'Array', proto: true, forced: String(test$1) === String(test$1.reverse()) }, { reverse: function reverse() { // eslint-disable-next-line no-self-assign -- dirty hack if (isArray$2(this)) this.length = this.length; return nativeReverse(this); } }); var entryVirtual$c = entryVirtual$i; var reverse$2 = entryVirtual$c('Array').reverse; var isPrototypeOf$d = objectIsPrototypeOf; var method$a = reverse$2; var ArrayPrototype$a = Array.prototype; var reverse$1 = function (it) { var own = it.reverse; return it === ArrayPrototype$a || isPrototypeOf$d(ArrayPrototype$a, it) && own === ArrayPrototype$a.reverse ? method$a : own; }; var parent$D = reverse$1; var reverse = parent$D; (function (module) { module.exports = reverse; })(reverse$3); var _reverseInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(reverseExports); var spliceExports = {}; var splice$4 = { get exports() { return spliceExports; }, set exports(v) { spliceExports = v; } }; var DESCRIPTORS$5 = descriptors; var isArray$1 = isArray$f; var $TypeError$7 = TypeError; // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe var getOwnPropertyDescriptor$4 = Object.getOwnPropertyDescriptor; // Safari < 13 does not throw an error in this case var SILENT_ON_NON_WRITABLE_LENGTH_SET = DESCRIPTORS$5 && !function () { // makes no sense without proper strict mode support if (this !== undefined) return true; try { // eslint-disable-next-line es/no-object-defineproperty -- safe Object.defineProperty([], 'length', { writable: false }).length = 1; } catch (error) { return error instanceof TypeError; } }(); var arraySetLength = SILENT_ON_NON_WRITABLE_LENGTH_SET ? function (O, length) { if (isArray$1(O) && !getOwnPropertyDescriptor$4(O, 'length').writable) { throw $TypeError$7('Cannot set read only .length'); } return O.length = length; } : function (O, length) { return O.length = length; }; var tryToString$2 = tryToString$6; var $TypeError$6 = TypeError; var deletePropertyOrThrow$2 = function (O, P) { if (!delete O[P]) throw $TypeError$6('Cannot delete property ' + tryToString$2(P) + ' of ' + tryToString$2(O)); }; var $$q = _export; var toObject$4 = toObject$d; var toAbsoluteIndex$1 = toAbsoluteIndex$5; var toIntegerOrInfinity = toIntegerOrInfinity$4; var lengthOfArrayLike$4 = lengthOfArrayLike$b; var setArrayLength = arraySetLength; var doesNotExceedSafeInteger = doesNotExceedSafeInteger$2; var arraySpeciesCreate = arraySpeciesCreate$3; var createProperty = createProperty$6; var deletePropertyOrThrow$1 = deletePropertyOrThrow$2; var arrayMethodHasSpeciesSupport$1 = arrayMethodHasSpeciesSupport$5; var HAS_SPECIES_SUPPORT$1 = arrayMethodHasSpeciesSupport$1('splice'); var max = Math.max; var min = Math.min; // `Array.prototype.splice` method // https://tc39.es/ecma262/#sec-array.prototype.splice // with adding support of @@species $$q({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT$1 }, { splice: function splice(start, deleteCount /* , ...items */) { var O = toObject$4(this); var len = lengthOfArrayLike$4(O); var actualStart = toAbsoluteIndex$1(start, len); var argumentsLength = arguments.length; var insertCount, actualDeleteCount, A, k, from, to; if (argumentsLength === 0) { insertCount = actualDeleteCount = 0; } else if (argumentsLength === 1) { insertCount = 0; actualDeleteCount = len - actualStart; } else { insertCount = argumentsLength - 2; actualDeleteCount = min(max(toIntegerOrInfinity(deleteCount), 0), len - actualStart); } doesNotExceedSafeInteger(len + insertCount - actualDeleteCount); A = arraySpeciesCreate(O, actualDeleteCount); for (k = 0; k < actualDeleteCount; k++) { from = actualStart + k; if (from in O) createProperty(A, k, O[from]); } A.length = actualDeleteCount; if (insertCount < actualDeleteCount) { for (k = actualStart; k < len - actualDeleteCount; k++) { from = k + actualDeleteCount; to = k + insertCount; if (from in O) O[to] = O[from];else deletePropertyOrThrow$1(O, to); } for (k = len; k > len - actualDeleteCount + insertCount; k--) deletePropertyOrThrow$1(O, k - 1); } else if (insertCount > actualDeleteCount) { for (k = len - actualDeleteCount; k > actualStart; k--) { from = k + actualDeleteCount - 1; to = k + insertCount - 1; if (from in O) O[to] = O[from];else deletePropertyOrThrow$1(O, to); } } for (k = 0; k < insertCount; k++) { O[k + actualStart] = arguments[k + 2]; } setArrayLength(O, len - actualDeleteCount + insertCount); return A; } }); var entryVirtual$b = entryVirtual$i; var splice$3 = entryVirtual$b('Array').splice; var isPrototypeOf$c = objectIsPrototypeOf; var method$9 = splice$3; var ArrayPrototype$9 = Array.prototype; var splice$2 = function (it) { var own = it.splice; return it === ArrayPrototype$9 || isPrototypeOf$c(ArrayPrototype$9, it) && own === ArrayPrototype$9.splice ? method$9 : own; }; var parent$C = splice$2; var splice$1 = parent$C; (function (module) { module.exports = splice$1; })(splice$4); var _spliceInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(spliceExports); var includesExports = {}; var includes$4 = { get exports() { return includesExports; }, set exports(v) { includesExports = v; } }; var $$p = _export; var $includes = arrayIncludes.includes; var fails$c = fails$w; // FF99+ bug var BROKEN_ON_SPARSE = fails$c(function () { // eslint-disable-next-line es/no-array-prototype-includes -- detection return !Array(1).includes(); }); // `Array.prototype.includes` method // https://tc39.es/ecma262/#sec-array.prototype.includes $$p({ target: 'Array', proto: true, forced: BROKEN_ON_SPARSE }, { includes: function includes(el /* , fromIndex = 0 */) { return $includes(this, el, arguments.length > 1 ? arguments[1] : undefined); } }); var entryVirtual$a = entryVirtual$i; var includes$3 = entryVirtual$a('Array').includes; var isObject$8 = isObject$j; var classof$3 = classofRaw$2; var wellKnownSymbol$2 = wellKnownSymbol$l; var MATCH$1 = wellKnownSymbol$2('match'); // `IsRegExp` abstract operation // https://tc39.es/ecma262/#sec-isregexp var isRegexp = function (it) { var isRegExp; return isObject$8(it) && ((isRegExp = it[MATCH$1]) !== undefined ? !!isRegExp : classof$3(it) == 'RegExp'); }; var isRegExp = isRegexp; var $TypeError$5 = TypeError; var notARegexp = function (it) { if (isRegExp(it)) { throw $TypeError$5("The method doesn't accept regular expressions"); } return it; }; var wellKnownSymbol$1 = wellKnownSymbol$l; var MATCH = wellKnownSymbol$1('match'); var correctIsRegexpLogic = function (METHOD_NAME) { var regexp = /./; try { '/./'[METHOD_NAME](regexp); } catch (error1) { try { regexp[MATCH] = false; return '/./'[METHOD_NAME](regexp); } catch (error2) {/* empty */} } return false; }; var $$o = _export; var uncurryThis$9 = functionUncurryThis; var notARegExp = notARegexp; var requireObjectCoercible$1 = requireObjectCoercible$5; var toString$4 = toString$a; var correctIsRegExpLogic = correctIsRegexpLogic; var stringIndexOf = uncurryThis$9(''.indexOf); // `String.prototype.includes` method // https://tc39.es/ecma262/#sec-string.prototype.includes $$o({ target: 'String', proto: true, forced: !correctIsRegExpLogic('includes') }, { includes: function includes(searchString /* , position = 0 */) { return !!~stringIndexOf(toString$4(requireObjectCoercible$1(this)), toString$4(notARegExp(searchString)), arguments.length > 1 ? arguments[1] : undefined); } }); var entryVirtual$9 = entryVirtual$i; var includes$2 = entryVirtual$9('String').includes; var isPrototypeOf$b = objectIsPrototypeOf; var arrayMethod = includes$3; var stringMethod = includes$2; var ArrayPrototype$8 = Array.prototype; var StringPrototype$1 = String.prototype; var includes$1 = function (it) { var own = it.includes; if (it === ArrayPrototype$8 || isPrototypeOf$b(ArrayPrototype$8, it) && own === ArrayPrototype$8.includes) return arrayMethod; if (typeof it == 'string' || it === StringPrototype$1 || isPrototypeOf$b(StringPrototype$1, it) && own === StringPrototype$1.includes) { return stringMethod; } return own; }; var parent$B = includes$1; var includes = parent$B; (function (module) { module.exports = includes; })(includes$4); var _includesInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(includesExports); var getPrototypeOfExports$2 = {}; var getPrototypeOf$7 = { get exports() { return getPrototypeOfExports$2; }, set exports(v) { getPrototypeOfExports$2 = v; } }; var $$n = _export; var fails$b = fails$w; var toObject$3 = toObject$d; var nativeGetPrototypeOf = objectGetPrototypeOf; var CORRECT_PROTOTYPE_GETTER = correctPrototypeGetter; var FAILS_ON_PRIMITIVES$2 = fails$b(function () { nativeGetPrototypeOf(1); }); // `Object.getPrototypeOf` method // https://tc39.es/ecma262/#sec-object.getprototypeof $$n({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES$2, sham: !CORRECT_PROTOTYPE_GETTER }, { getPrototypeOf: function getPrototypeOf(it) { return nativeGetPrototypeOf(toObject$3(it)); } }); var path$h = path$y; var getPrototypeOf$6 = path$h.Object.getPrototypeOf; var parent$A = getPrototypeOf$6; var getPrototypeOf$5 = parent$A; (function (module) { module.exports = getPrototypeOf$5; })(getPrototypeOf$7); var _Object$getPrototypeOf$1 = /*@__PURE__*/getDefaultExportFromCjs(getPrototypeOfExports$2); var filterExports = {}; var filter$3 = { get exports() { return filterExports; }, set exports(v) { filterExports = v; } }; var $$m = _export; var $filter = arrayIteration.filter; var arrayMethodHasSpeciesSupport = arrayMethodHasSpeciesSupport$5; var HAS_SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('filter'); // `Array.prototype.filter` method // https://tc39.es/ecma262/#sec-array.prototype.filter // with adding support of @@species $$m({ target: 'Array', proto: true, forced: !HAS_SPECIES_SUPPORT }, { filter: function filter(callbackfn /* , thisArg */) { return $filter(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); } }); var entryVirtual$8 = entryVirtual$i; var filter$2 = entryVirtual$8('Array').filter; var isPrototypeOf$a = objectIsPrototypeOf; var method$8 = filter$2; var ArrayPrototype$7 = Array.prototype; var filter$1 = function (it) { var own = it.filter; return it === ArrayPrototype$7 || isPrototypeOf$a(ArrayPrototype$7, it) && own === ArrayPrototype$7.filter ? method$8 : own; }; var parent$z = filter$1; var filter = parent$z; (function (module) { module.exports = filter; })(filter$3); var _filterInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(filterExports); var valuesExports$1 = {}; var values$6 = { get exports() { return valuesExports$1; }, set exports(v) { valuesExports$1 = v; } }; var DESCRIPTORS$4 = descriptors; var uncurryThis$8 = functionUncurryThis; var objectKeys = objectKeys$4; var toIndexedObject = toIndexedObject$b; var $propertyIsEnumerable = objectPropertyIsEnumerable.f; var propertyIsEnumerable = uncurryThis$8($propertyIsEnumerable); var push$2 = uncurryThis$8([].push); // `Object.{ entries, values }` methods implementation var createMethod$2 = function (TO_ENTRIES) { return function (it) { var O = toIndexedObject(it); var keys = objectKeys(O); var length = keys.length; var i = 0; var result = []; var key; while (length > i) { key = keys[i++]; if (!DESCRIPTORS$4 || propertyIsEnumerable(O, key)) { push$2(result, TO_ENTRIES ? [key, O[key]] : O[key]); } } return result; }; }; var objectToArray = { // `Object.entries` method // https://tc39.es/ecma262/#sec-object.entries entries: createMethod$2(true), // `Object.values` method // https://tc39.es/ecma262/#sec-object.values values: createMethod$2(false) }; var $$l = _export; var $values = objectToArray.values; // `Object.values` method // https://tc39.es/ecma262/#sec-object.values $$l({ target: 'Object', stat: true }, { values: function values(O) { return $values(O); } }); var path$g = path$y; var values$5 = path$g.Object.values; var parent$y = values$5; var values$4 = parent$y; (function (module) { module.exports = values$4; })(values$6); var _parseIntExports = {}; var _parseInt$3 = { get exports() { return _parseIntExports; }, set exports(v) { _parseIntExports = v; } }; // a string of all valid unicode whitespaces var whitespaces$4 = '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u2000\u2001\u2002' + '\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF'; var uncurryThis$7 = functionUncurryThis; var requireObjectCoercible = requireObjectCoercible$5; var toString$3 = toString$a; var whitespaces$3 = whitespaces$4; var replace = uncurryThis$7(''.replace); var ltrim = RegExp('^[' + whitespaces$3 + ']+'); var rtrim = RegExp('(^|[^' + whitespaces$3 + '])[' + whitespaces$3 + ']+$'); // `String.prototype.{ trim, trimStart, trimEnd, trimLeft, trimRight }` methods implementation var createMethod$1 = function (TYPE) { return function ($this) { var string = toString$3(requireObjectCoercible($this)); if (TYPE & 1) string = replace(string, ltrim, ''); if (TYPE & 2) string = replace(string, rtrim, '$1'); return string; }; }; var stringTrim = { // `String.prototype.{ trimLeft, trimStart }` methods // https://tc39.es/ecma262/#sec-string.prototype.trimstart start: createMethod$1(1), // `String.prototype.{ trimRight, trimEnd }` methods // https://tc39.es/ecma262/#sec-string.prototype.trimend end: createMethod$1(2), // `String.prototype.trim` method // https://tc39.es/ecma262/#sec-string.prototype.trim trim: createMethod$1(3) }; var global$8 = global$l; var fails$a = fails$w; var uncurryThis$6 = functionUncurryThis; var toString$2 = toString$a; var trim$5 = stringTrim.trim; var whitespaces$2 = whitespaces$4; var $parseInt$1 = global$8.parseInt; var Symbol$2 = global$8.Symbol; var ITERATOR$1 = Symbol$2 && Symbol$2.iterator; var hex = /^[+-]?0x/i; var exec = uncurryThis$6(hex.exec); var FORCED$6 = $parseInt$1(whitespaces$2 + '08') !== 8 || $parseInt$1(whitespaces$2 + '0x16') !== 22 // MS Edge 18- broken with boxed symbols || ITERATOR$1 && !fails$a(function () { $parseInt$1(Object(ITERATOR$1)); }); // `parseInt` method // https://tc39.es/ecma262/#sec-parseint-string-radix var numberParseInt = FORCED$6 ? function parseInt(string, radix) { var S = trim$5(toString$2(string)); return $parseInt$1(S, radix >>> 0 || (exec(hex, S) ? 16 : 10)); } : $parseInt$1; var $$k = _export; var $parseInt = numberParseInt; // `parseInt` method // https://tc39.es/ecma262/#sec-parseint-string-radix $$k({ global: true, forced: parseInt != $parseInt }, { parseInt: $parseInt }); var path$f = path$y; var _parseInt$2 = path$f.parseInt; var parent$x = _parseInt$2; var _parseInt$1 = parent$x; (function (module) { module.exports = _parseInt$1; })(_parseInt$3); var _parseInt = /*@__PURE__*/getDefaultExportFromCjs(_parseIntExports); var indexOfExports = {}; var indexOf$3 = { get exports() { return indexOfExports; }, set exports(v) { indexOfExports = v; } }; /* eslint-disable es/no-array-prototype-indexof -- required for testing */ var $$j = _export; var uncurryThis$5 = functionUncurryThisClause; var $indexOf = arrayIncludes.indexOf; var arrayMethodIsStrict$4 = arrayMethodIsStrict$6; var nativeIndexOf = uncurryThis$5([].indexOf); var NEGATIVE_ZERO = !!nativeIndexOf && 1 / nativeIndexOf([1], 1, -0) < 0; var FORCED$5 = NEGATIVE_ZERO || !arrayMethodIsStrict$4('indexOf'); // `Array.prototype.indexOf` method // https://tc39.es/ecma262/#sec-array.prototype.indexof $$j({ target: 'Array', proto: true, forced: FORCED$5 }, { indexOf: function indexOf(searchElement /* , fromIndex = 0 */) { var fromIndex = arguments.length > 1 ? arguments[1] : undefined; return NEGATIVE_ZERO // convert -0 to +0 ? nativeIndexOf(this, searchElement, fromIndex) || 0 : $indexOf(this, searchElement, fromIndex); } }); var entryVirtual$7 = entryVirtual$i; var indexOf$2 = entryVirtual$7('Array').indexOf; var isPrototypeOf$9 = objectIsPrototypeOf; var method$7 = indexOf$2; var ArrayPrototype$6 = Array.prototype; var indexOf$1 = function (it) { var own = it.indexOf; return it === ArrayPrototype$6 || isPrototypeOf$9(ArrayPrototype$6, it) && own === ArrayPrototype$6.indexOf ? method$7 : own; }; var parent$w = indexOf$1; var indexOf = parent$w; (function (module) { module.exports = indexOf; })(indexOf$3); var _indexOfInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(indexOfExports); var trimExports = {}; var trim$4 = { get exports() { return trimExports; }, set exports(v) { trimExports = v; } }; var PROPER_FUNCTION_NAME = functionName.PROPER; var fails$9 = fails$w; var whitespaces$1 = whitespaces$4; var non = '\u200B\u0085\u180E'; // check that a method works with the correct list // of whitespaces and has a correct name var stringTrimForced = function (METHOD_NAME) { return fails$9(function () { return !!whitespaces$1[METHOD_NAME]() || non[METHOD_NAME]() !== non || PROPER_FUNCTION_NAME && whitespaces$1[METHOD_NAME].name !== METHOD_NAME; }); }; var $$i = _export; var $trim = stringTrim.trim; var forcedStringTrimMethod = stringTrimForced; // `String.prototype.trim` method // https://tc39.es/ecma262/#sec-string.prototype.trim $$i({ target: 'String', proto: true, forced: forcedStringTrimMethod('trim') }, { trim: function trim() { return $trim(this); } }); var entryVirtual$6 = entryVirtual$i; var trim$3 = entryVirtual$6('String').trim; var isPrototypeOf$8 = objectIsPrototypeOf; var method$6 = trim$3; var StringPrototype = String.prototype; var trim$2 = function (it) { var own = it.trim; return typeof it == 'string' || it === StringPrototype || isPrototypeOf$8(StringPrototype, it) && own === StringPrototype.trim ? method$6 : own; }; var parent$v = trim$2; var trim$1 = parent$v; (function (module) { module.exports = trim$1; })(trim$4); var createExports$2 = {}; var create$9 = { get exports() { return createExports$2; }, set exports(v) { createExports$2 = v; } }; // TODO: Remove from `core-js@4` var $$h = _export; var DESCRIPTORS$3 = descriptors; var create$8 = objectCreate; // `Object.create` method // https://tc39.es/ecma262/#sec-object.create $$h({ target: 'Object', stat: true, sham: !DESCRIPTORS$3 }, { create: create$8 }); var path$e = path$y; var Object$2 = path$e.Object; var create$7 = function create(P, D) { return Object$2.create(P, D); }; var parent$u = create$7; var create$6 = parent$u; (function (module) { module.exports = create$6; })(create$9); var _Object$create$1 = /*@__PURE__*/getDefaultExportFromCjs(createExports$2); var stringifyExports = {}; var stringify$2 = { get exports() { return stringifyExports; }, set exports(v) { stringifyExports = v; } }; var path$d = path$y; var apply$2 = functionApply; // eslint-disable-next-line es/no-json -- safe if (!path$d.JSON) path$d.JSON = { stringify: JSON.stringify }; // eslint-disable-next-line no-unused-vars -- required for `.length` var stringify$1 = function stringify(it, replacer, space) { return apply$2(path$d.JSON.stringify, null, arguments); }; var parent$t = stringify$1; var stringify = parent$t; (function (module) { module.exports = stringify; })(stringify$2); var _JSON$stringify = /*@__PURE__*/getDefaultExportFromCjs(stringifyExports); var setTimeoutExports = {}; var setTimeout$3 = { get exports() { return setTimeoutExports; }, set exports(v) { setTimeoutExports = v; } }; /* global Bun -- Deno case */ var engineIsBun = typeof Bun == 'function' && Bun && typeof Bun.version == 'string'; var $TypeError$4 = TypeError; var validateArgumentsLength$1 = function (passed, required) { if (passed < required) throw $TypeError$4('Not enough arguments'); return passed; }; var global$7 = global$l; var apply$1 = functionApply; var isCallable$1 = isCallable$i; var ENGINE_IS_BUN = engineIsBun; var USER_AGENT = engineUserAgent; var arraySlice$1 = arraySlice$5; var validateArgumentsLength = validateArgumentsLength$1; var Function$1 = global$7.Function; // dirty IE9- and Bun 0.3.0- checks var WRAP = /MSIE .\./.test(USER_AGENT) || ENGINE_IS_BUN && function () { var version = global$7.Bun.version.split('.'); return version.length < 3 || version[0] == 0 && (version[1] < 3 || version[1] == 3 && version[2] == 0); }(); // IE9- / Bun 0.3.0- setTimeout / setInterval / setImmediate additional parameters fix // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers // https://github.com/oven-sh/bun/issues/1633 var schedulersFix$2 = function (scheduler, hasTimeArg) { var firstParamIndex = hasTimeArg ? 2 : 1; return WRAP ? function (handler, timeout /* , ...arguments */) { var boundArgs = validateArgumentsLength(arguments.length, 1) > firstParamIndex; var fn = isCallable$1(handler) ? handler : Function$1(handler); var params = boundArgs ? arraySlice$1(arguments, firstParamIndex) : []; var callback = boundArgs ? function () { apply$1(fn, this, params); } : fn; return hasTimeArg ? scheduler(callback, timeout) : scheduler(callback); } : scheduler; }; var $$g = _export; var global$6 = global$l; var schedulersFix$1 = schedulersFix$2; var setInterval$2 = schedulersFix$1(global$6.setInterval, true); // Bun / IE9- setInterval additional parameters fix // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-setinterval $$g({ global: true, bind: true, forced: global$6.setInterval !== setInterval$2 }, { setInterval: setInterval$2 }); var $$f = _export; var global$5 = global$l; var schedulersFix = schedulersFix$2; var setTimeout$2 = schedulersFix(global$5.setTimeout, true); // Bun / IE9- setTimeout additional parameters fix // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-settimeout $$f({ global: true, bind: true, forced: global$5.setTimeout !== setTimeout$2 }, { setTimeout: setTimeout$2 }); var path$c = path$y; var setTimeout$1 = path$c.setTimeout; (function (module) { module.exports = setTimeout$1; })(setTimeout$3); var _setTimeout = /*@__PURE__*/getDefaultExportFromCjs(setTimeoutExports); var fillExports = {}; var fill$4 = { get exports() { return fillExports; }, set exports(v) { fillExports = v; } }; var toObject$2 = toObject$d; var toAbsoluteIndex = toAbsoluteIndex$5; var lengthOfArrayLike$3 = lengthOfArrayLike$b; // `Array.prototype.fill` method implementation // https://tc39.es/ecma262/#sec-array.prototype.fill var arrayFill = function fill(value /* , start = 0, end = @length */) { var O = toObject$2(this); var length = lengthOfArrayLike$3(O); var argumentsLength = arguments.length; var index = toAbsoluteIndex(argumentsLength > 1 ? arguments[1] : undefined, length); var end = argumentsLength > 2 ? arguments[2] : undefined; var endPos = end === undefined ? length : toAbsoluteIndex(end, length); while (endPos > index) O[index++] = value; return O; }; var $$e = _export; var fill$3 = arrayFill; // `Array.prototype.fill` method // https://tc39.es/ecma262/#sec-array.prototype.fill $$e({ target: 'Array', proto: true }, { fill: fill$3 }); var entryVirtual$5 = entryVirtual$i; var fill$2 = entryVirtual$5('Array').fill; var isPrototypeOf$7 = objectIsPrototypeOf; var method$5 = fill$2; var ArrayPrototype$5 = Array.prototype; var fill$1 = function (it) { var own = it.fill; return it === ArrayPrototype$5 || isPrototypeOf$7(ArrayPrototype$5, it) && own === ArrayPrototype$5.fill ? method$5 : own; }; var parent$s = fill$1; var fill = parent$s; (function (module) { module.exports = fill; })(fill$4); var _fillInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(fillExports); /*! Hammer.JS - v2.0.17-rc - 2019-12-16 * http://naver.github.io/egjs * * Forked By Naver egjs * Copyright (c) hammerjs * Licensed under the MIT license */ function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } function _assertThisInitialized$1(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } /** * @private * extend object. * means that properties in dest will be overwritten by the ones in src. * @param {Object} target * @param {...Object} objects_to_assign * @returns {Object} target */ var assign; if (typeof Object.assign !== 'function') { assign = function assign(target) { if (target === undefined || target === null) { throw new TypeError('Cannot convert undefined or null to object'); } var output = Object(target); for (var index = 1; index < arguments.length; index++) { var source = arguments[index]; if (source !== undefined && source !== null) { for (var nextKey in source) { if (source.hasOwnProperty(nextKey)) { output[nextKey] = source[nextKey]; } } } } return output; }; } else { assign = Object.assign; } var assign$1 = assign; var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o']; var TEST_ELEMENT = typeof document === "undefined" ? { style: {} } : document.createElement('div'); var TYPE_FUNCTION = 'function'; var round = Math.round, abs$1 = Math.abs; var now = Date.now; /** * @private * get the prefixed property * @param {Object} obj * @param {String} property * @returns {String|Undefined} prefixed */ function prefixed(obj, property) { var prefix; var prop; var camelProp = property[0].toUpperCase() + property.slice(1); var i = 0; while (i < VENDOR_PREFIXES.length) { prefix = VENDOR_PREFIXES[i]; prop = prefix ? prefix + camelProp : property; if (prop in obj) { return prop; } i++; } return undefined; } /* eslint-disable no-new-func, no-nested-ternary */ var win; if (typeof window === "undefined") { // window is undefined in node.js win = {}; } else { win = window; } var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; function getTouchActionProps() { if (!NATIVE_TOUCH_ACTION) { return false; } var touchMap = {}; var cssSupports = win.CSS && win.CSS.supports; ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function (val) { // If css.supports is not supported but there is native touch-action assume it supports // all values. This is the case for IE 10 and 11. return touchMap[val] = cssSupports ? win.CSS.supports('touch-action', val) : true; }); return touchMap; } var TOUCH_ACTION_COMPUTE = 'compute'; var TOUCH_ACTION_AUTO = 'auto'; var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented var TOUCH_ACTION_NONE = 'none'; var TOUCH_ACTION_PAN_X = 'pan-x'; var TOUCH_ACTION_PAN_Y = 'pan-y'; var TOUCH_ACTION_MAP = getTouchActionProps(); var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; var SUPPORT_TOUCH = ('ontouchstart' in win); var SUPPORT_POINTER_EVENTS = prefixed(win, 'PointerEvent') !== undefined; var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); var INPUT_TYPE_TOUCH = 'touch'; var INPUT_TYPE_PEN = 'pen'; var INPUT_TYPE_MOUSE = 'mouse'; var INPUT_TYPE_KINECT = 'kinect'; var COMPUTE_INTERVAL = 25; var INPUT_START = 1; var INPUT_MOVE = 2; var INPUT_END = 4; var INPUT_CANCEL = 8; var DIRECTION_NONE = 1; var DIRECTION_LEFT = 2; var DIRECTION_RIGHT = 4; var DIRECTION_UP = 8; var DIRECTION_DOWN = 16; var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; var PROPS_XY = ['x', 'y']; var PROPS_CLIENT_XY = ['clientX', 'clientY']; /** * @private * walk objects and arrays * @param {Object} obj * @param {Function} iterator * @param {Object} context */ function each(obj, iterator, context) { var i; if (!obj) { return; } if (obj.forEach) { obj.forEach(iterator, context); } else if (obj.length !== undefined) { i = 0; while (i < obj.length) { iterator.call(context, obj[i], i, obj); i++; } } else { for (i in obj) { obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); } } } /** * @private * let a boolean value also be a function that must return a boolean * this first item in args will be used as the context * @param {Boolean|Function} val * @param {Array} [args] * @returns {Boolean} */ function boolOrFn(val, args) { if (typeof val === TYPE_FUNCTION) { return val.apply(args ? args[0] || undefined : undefined, args); } return val; } /** * @private * small indexOf wrapper * @param {String} str * @param {String} find * @returns {Boolean} found */ function inStr(str, find) { return str.indexOf(find) > -1; } /** * @private * when the touchActions are collected they are not a valid value, so we need to clean things up. * * @param {String} actions * @returns {*} */ function cleanTouchActions(actions) { // none if (inStr(actions, TOUCH_ACTION_NONE)) { return TOUCH_ACTION_NONE; } var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); // if both pan-x and pan-y are set (different recognizers // for different directions, e.g. horizontal pan but vertical swipe?) // we need none (as otherwise with pan-x pan-y combined none of these // recognizers will work, since the browser would handle all panning if (hasPanX && hasPanY) { return TOUCH_ACTION_NONE; } // pan-x OR pan-y if (hasPanX || hasPanY) { return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; } // manipulation if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { return TOUCH_ACTION_MANIPULATION; } return TOUCH_ACTION_AUTO; } /** * @private * Touch Action * sets the touchAction property or uses the js alternative * @param {Manager} manager * @param {String} value * @constructor */ var TouchAction = /*#__PURE__*/ function () { function TouchAction(manager, value) { this.manager = manager; this.set(value); } /** * @private * set the touchAction value on the element or enable the polyfill * @param {String} value */ var _proto = TouchAction.prototype; _proto.set = function set(value) { // find out the touch-action by the event handlers if (value === TOUCH_ACTION_COMPUTE) { value = this.compute(); } if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) { this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; } this.actions = value.toLowerCase().trim(); }; /** * @private * just re-set the touchAction value */ _proto.update = function update() { this.set(this.manager.options.touchAction); }; /** * @private * compute the value for the touchAction property based on the recognizer's settings * @returns {String} value */ _proto.compute = function compute() { var actions = []; each(this.manager.recognizers, function (recognizer) { if (boolOrFn(recognizer.options.enable, [recognizer])) { actions = actions.concat(recognizer.getTouchAction()); } }); return cleanTouchActions(actions.join(' ')); }; /** * @private * this method is called on each input cycle and provides the preventing of the browser behavior * @param {Object} input */ _proto.preventDefaults = function preventDefaults(input) { var srcEvent = input.srcEvent; var direction = input.offsetDirection; // if the touch action did prevented once this session if (this.manager.session.prevented) { srcEvent.preventDefault(); return; } var actions = this.actions; var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE]; var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y]; var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X]; if (hasNone) { // do not prevent defaults if this is a tap gesture var isTapPointer = input.pointers.length === 1; var isTapMovement = input.distance < 2; var isTapTouchTime = input.deltaTime < 250; if (isTapPointer && isTapMovement && isTapTouchTime) { return; } } if (hasPanX && hasPanY) { // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent return; } if (hasNone || hasPanY && direction & DIRECTION_HORIZONTAL || hasPanX && direction & DIRECTION_VERTICAL) { return this.preventSrc(srcEvent); } }; /** * @private * call preventDefault to prevent the browser's default behavior (scrolling in most cases) * @param {Object} srcEvent */ _proto.preventSrc = function preventSrc(srcEvent) { this.manager.session.prevented = true; srcEvent.preventDefault(); }; return TouchAction; }(); /** * @private * find if a node is in the given parent * @method hasParent * @param {HTMLElement} node * @param {HTMLElement} parent * @return {Boolean} found */ function hasParent(node, parent) { while (node) { if (node === parent) { return true; } node = node.parentNode; } return false; } /** * @private * get the center of all the pointers * @param {Array} pointers * @return {Object} center contains `x` and `y` properties */ function getCenter(pointers) { var pointersLength = pointers.length; // no need to loop when only one touch if (pointersLength === 1) { return { x: round(pointers[0].clientX), y: round(pointers[0].clientY) }; } var x = 0; var y = 0; var i = 0; while (i < pointersLength) { x += pointers[i].clientX; y += pointers[i].clientY; i++; } return { x: round(x / pointersLength), y: round(y / pointersLength) }; } /** * @private * create a simple clone from the input used for storage of firstInput and firstMultiple * @param {Object} input * @returns {Object} clonedInputData */ function simpleCloneInputData(input) { // make a simple copy of the pointers because we will get a reference if we don't // we only need clientXY for the calculations var pointers = []; var i = 0; while (i < input.pointers.length) { pointers[i] = { clientX: round(input.pointers[i].clientX), clientY: round(input.pointers[i].clientY) }; i++; } return { timeStamp: now(), pointers: pointers, center: getCenter(pointers), deltaX: input.deltaX, deltaY: input.deltaY }; } /** * @private * calculate the absolute distance between two points * @param {Object} p1 {x, y} * @param {Object} p2 {x, y} * @param {Array} [props] containing x and y keys * @return {Number} distance */ function getDistance(p1, p2, props) { if (!props) { props = PROPS_XY; } var x = p2[props[0]] - p1[props[0]]; var y = p2[props[1]] - p1[props[1]]; return Math.sqrt(x * x + y * y); } /** * @private * calculate the angle between two coordinates * @param {Object} p1 * @param {Object} p2 * @param {Array} [props] containing x and y keys * @return {Number} angle */ function getAngle(p1, p2, props) { if (!props) { props = PROPS_XY; } var x = p2[props[0]] - p1[props[0]]; var y = p2[props[1]] - p1[props[1]]; return Math.atan2(y, x) * 180 / Math.PI; } /** * @private * get the direction between two points * @param {Number} x * @param {Number} y * @return {Number} direction */ function getDirection(x, y) { if (x === y) { return DIRECTION_NONE; } if (abs$1(x) >= abs$1(y)) { return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; } return y < 0 ? DIRECTION_UP : DIRECTION_DOWN; } function computeDeltaXY(session, input) { var center = input.center; // let { offsetDelta:offset = {}, prevDelta = {}, prevInput = {} } = session; // jscs throwing error on defalut destructured values and without defaults tests fail var offset = session.offsetDelta || {}; var prevDelta = session.prevDelta || {}; var prevInput = session.prevInput || {}; if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { prevDelta = session.prevDelta = { x: prevInput.deltaX || 0, y: prevInput.deltaY || 0 }; offset = session.offsetDelta = { x: center.x, y: center.y }; } input.deltaX = prevDelta.x + (center.x - offset.x); input.deltaY = prevDelta.y + (center.y - offset.y); } /** * @private * calculate the velocity between two points. unit is in px per ms. * @param {Number} deltaTime * @param {Number} x * @param {Number} y * @return {Object} velocity `x` and `y` */ function getVelocity(deltaTime, x, y) { return { x: x / deltaTime || 0, y: y / deltaTime || 0 }; } /** * @private * calculate the scale factor between two pointersets * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out * @param {Array} start array of pointers * @param {Array} end array of pointers * @return {Number} scale */ function getScale(start, end) { return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); } /** * @private * calculate the rotation degrees between two pointersets * @param {Array} start array of pointers * @param {Array} end array of pointers * @return {Number} rotation */ function getRotation(start, end) { return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY); } /** * @private * velocity is calculated every x ms * @param {Object} session * @param {Object} input */ function computeIntervalInputData(session, input) { var last = session.lastInterval || input; var deltaTime = input.timeStamp - last.timeStamp; var velocity; var velocityX; var velocityY; var direction; if (input.eventType !== INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { var deltaX = input.deltaX - last.deltaX; var deltaY = input.deltaY - last.deltaY; var v = getVelocity(deltaTime, deltaX, deltaY); velocityX = v.x; velocityY = v.y; velocity = abs$1(v.x) > abs$1(v.y) ? v.x : v.y; direction = getDirection(deltaX, deltaY); session.lastInterval = input; } else { // use latest velocity info if it doesn't overtake a minimum period velocity = last.velocity; velocityX = last.velocityX; velocityY = last.velocityY; direction = last.direction; } input.velocity = velocity; input.velocityX = velocityX; input.velocityY = velocityY; input.direction = direction; } /** * @private * extend the data with some usable properties like scale, rotate, velocity etc * @param {Object} manager * @param {Object} input */ function computeInputData(manager, input) { var session = manager.session; var pointers = input.pointers; var pointersLength = pointers.length; // store the first input to calculate the distance and direction if (!session.firstInput) { session.firstInput = simpleCloneInputData(input); } // to compute scale and rotation we need to store the multiple touches if (pointersLength > 1 && !session.firstMultiple) { session.firstMultiple = simpleCloneInputData(input); } else if (pointersLength === 1) { session.firstMultiple = false; } var firstInput = session.firstInput, firstMultiple = session.firstMultiple; var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; var center = input.center = getCenter(pointers); input.timeStamp = now(); input.deltaTime = input.timeStamp - firstInput.timeStamp; input.angle = getAngle(offsetCenter, center); input.distance = getDistance(offsetCenter, center); computeDeltaXY(session, input); input.offsetDirection = getDirection(input.deltaX, input.deltaY); var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY); input.overallVelocityX = overallVelocity.x; input.overallVelocityY = overallVelocity.y; input.overallVelocity = abs$1(overallVelocity.x) > abs$1(overallVelocity.y) ? overallVelocity.x : overallVelocity.y; input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; input.maxPointers = !session.prevInput ? input.pointers.length : input.pointers.length > session.prevInput.maxPointers ? input.pointers.length : session.prevInput.maxPointers; computeIntervalInputData(session, input); // find the correct target var target = manager.element; var srcEvent = input.srcEvent; var srcEventTarget; if (srcEvent.composedPath) { srcEventTarget = srcEvent.composedPath()[0]; } else if (srcEvent.path) { srcEventTarget = srcEvent.path[0]; } else { srcEventTarget = srcEvent.target; } if (hasParent(srcEventTarget, target)) { target = srcEventTarget; } input.target = target; } /** * @private * handle input events * @param {Manager} manager * @param {String} eventType * @param {Object} input */ function inputHandler(manager, eventType, input) { var pointersLen = input.pointers.length; var changedPointersLen = input.changedPointers.length; var isFirst = eventType & INPUT_START && pointersLen - changedPointersLen === 0; var isFinal = eventType & (INPUT_END | INPUT_CANCEL) && pointersLen - changedPointersLen === 0; input.isFirst = !!isFirst; input.isFinal = !!isFinal; if (isFirst) { manager.session = {}; } // source event is the normalized value of the domEvents // like 'touchstart, mouseup, pointerdown' input.eventType = eventType; // compute scale, rotation etc computeInputData(manager, input); // emit secret event manager.emit('hammer.input', input); manager.recognize(input); manager.session.prevInput = input; } /** * @private * split string on whitespace * @param {String} str * @returns {Array} words */ function splitStr(str) { return str.trim().split(/\s+/g); } /** * @private * addEventListener with multiple events at once * @param {EventTarget} target * @param {String} types * @param {Function} handler */ function addEventListeners(target, types, handler) { each(splitStr(types), function (type) { target.addEventListener(type, handler, false); }); } /** * @private * removeEventListener with multiple events at once * @param {EventTarget} target * @param {String} types * @param {Function} handler */ function removeEventListeners(target, types, handler) { each(splitStr(types), function (type) { target.removeEventListener(type, handler, false); }); } /** * @private * get the window object of an element * @param {HTMLElement} element * @returns {DocumentView|Window} */ function getWindowForElement(element) { var doc = element.ownerDocument || element; return doc.defaultView || doc.parentWindow || window; } /** * @private * create new input type manager * @param {Manager} manager * @param {Function} callback * @returns {Input} * @constructor */ var Input = /*#__PURE__*/ function () { function Input(manager, callback) { var self = this; this.manager = manager; this.callback = callback; this.element = manager.element; this.target = manager.options.inputTarget; // smaller wrapper around the handler, for the scope and the enabled state of the manager, // so when disabled the input events are completely bypassed. this.domHandler = function (ev) { if (boolOrFn(manager.options.enable, [manager])) { self.handler(ev); } }; this.init(); } /** * @private * should handle the inputEvent data and trigger the callback * @virtual */ var _proto = Input.prototype; _proto.handler = function handler() {}; /** * @private * bind the events */ _proto.init = function init() { this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); }; /** * @private * unbind the events */ _proto.destroy = function destroy() { this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); }; return Input; }(); /** * @private * find if a array contains the object using indexOf or a simple polyFill * @param {Array} src * @param {String} find * @param {String} [findByKey] * @return {Boolean|Number} false when not found, or the index */ function inArray(src, find, findByKey) { if (src.indexOf && !findByKey) { return src.indexOf(find); } else { var i = 0; while (i < src.length) { if (findByKey && src[i][findByKey] == find || !findByKey && src[i] === find) { // do not use === here, test fails return i; } i++; } return -1; } } var POINTER_INPUT_MAP = { pointerdown: INPUT_START, pointermove: INPUT_MOVE, pointerup: INPUT_END, pointercancel: INPUT_CANCEL, pointerout: INPUT_CANCEL }; // in IE10 the pointer types is defined as an enum var IE10_POINTER_TYPE_ENUM = { 2: INPUT_TYPE_TOUCH, 3: INPUT_TYPE_PEN, 4: INPUT_TYPE_MOUSE, 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 }; var POINTER_ELEMENT_EVENTS = 'pointerdown'; var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; // IE10 has prefixed support, and case-sensitive if (win.MSPointerEvent && !win.PointerEvent) { POINTER_ELEMENT_EVENTS = 'MSPointerDown'; POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; } /** * @private * Pointer events input * @constructor * @extends Input */ var PointerEventInput = /*#__PURE__*/ function (_Input) { _inheritsLoose(PointerEventInput, _Input); function PointerEventInput() { var _this; var proto = PointerEventInput.prototype; proto.evEl = POINTER_ELEMENT_EVENTS; proto.evWin = POINTER_WINDOW_EVENTS; _this = _Input.apply(this, arguments) || this; _this.store = _this.manager.session.pointerEvents = []; return _this; } /** * @private * handle mouse events * @param {Object} ev */ var _proto = PointerEventInput.prototype; _proto.handler = function handler(ev) { var store = this.store; var removePointer = false; var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; var isTouch = pointerType === INPUT_TYPE_TOUCH; // get index of the event in the store var storeIndex = inArray(store, ev.pointerId, 'pointerId'); // start and mouse must be down if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { if (storeIndex < 0) { store.push(ev); storeIndex = store.length - 1; } } else if (eventType & (INPUT_END | INPUT_CANCEL)) { removePointer = true; } // it not found, so the pointer hasn't been down (so it's probably a hover) if (storeIndex < 0) { return; } // update the event in the store store[storeIndex] = ev; this.callback(this.manager, eventType, { pointers: store, changedPointers: [ev], pointerType: pointerType, srcEvent: ev }); if (removePointer) { // remove from the store store.splice(storeIndex, 1); } }; return PointerEventInput; }(Input); /** * @private * convert array-like objects to real arrays * @param {Object} obj * @returns {Array} */ function toArray(obj) { return Array.prototype.slice.call(obj, 0); } /** * @private * unique array with objects based on a key (like 'id') or just by the array's value * @param {Array} src [{id:1},{id:2},{id:1}] * @param {String} [key] * @param {Boolean} [sort=False] * @returns {Array} [{id:1},{id:2}] */ function uniqueArray(src, key, sort) { var results = []; var values = []; var i = 0; while (i < src.length) { var val = key ? src[i][key] : src[i]; if (inArray(values, val) < 0) { results.push(src[i]); } values[i] = val; i++; } if (sort) { if (!key) { results = results.sort(); } else { results = results.sort(function (a, b) { return a[key] > b[key]; }); } } return results; } var TOUCH_INPUT_MAP = { touchstart: INPUT_START, touchmove: INPUT_MOVE, touchend: INPUT_END, touchcancel: INPUT_CANCEL }; var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; /** * @private * Multi-user touch events input * @constructor * @extends Input */ var TouchInput = /*#__PURE__*/ function (_Input) { _inheritsLoose(TouchInput, _Input); function TouchInput() { var _this; TouchInput.prototype.evTarget = TOUCH_TARGET_EVENTS; _this = _Input.apply(this, arguments) || this; _this.targetIds = {}; // this.evTarget = TOUCH_TARGET_EVENTS; return _this; } var _proto = TouchInput.prototype; _proto.handler = function handler(ev) { var type = TOUCH_INPUT_MAP[ev.type]; var touches = getTouches.call(this, ev, type); if (!touches) { return; } this.callback(this.manager, type, { pointers: touches[0], changedPointers: touches[1], pointerType: INPUT_TYPE_TOUCH, srcEvent: ev }); }; return TouchInput; }(Input); function getTouches(ev, type) { var allTouches = toArray(ev.touches); var targetIds = this.targetIds; // when there is only one touch, the process can be simplified if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { targetIds[allTouches[0].identifier] = true; return [allTouches, allTouches]; } var i; var targetTouches; var changedTouches = toArray(ev.changedTouches); var changedTargetTouches = []; var target = this.target; // get target touches from touches targetTouches = allTouches.filter(function (touch) { return hasParent(touch.target, target); }); // collect touches if (type === INPUT_START) { i = 0; while (i < targetTouches.length) { targetIds[targetTouches[i].identifier] = true; i++; } } // filter changed touches to only contain touches that exist in the collected target ids i = 0; while (i < changedTouches.length) { if (targetIds[changedTouches[i].identifier]) { changedTargetTouches.push(changedTouches[i]); } // cleanup removed touches if (type & (INPUT_END | INPUT_CANCEL)) { delete targetIds[changedTouches[i].identifier]; } i++; } if (!changedTargetTouches.length) { return; } return [ // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), changedTargetTouches]; } var MOUSE_INPUT_MAP = { mousedown: INPUT_START, mousemove: INPUT_MOVE, mouseup: INPUT_END }; var MOUSE_ELEMENT_EVENTS = 'mousedown'; var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; /** * @private * Mouse events input * @constructor * @extends Input */ var MouseInput = /*#__PURE__*/ function (_Input) { _inheritsLoose(MouseInput, _Input); function MouseInput() { var _this; var proto = MouseInput.prototype; proto.evEl = MOUSE_ELEMENT_EVENTS; proto.evWin = MOUSE_WINDOW_EVENTS; _this = _Input.apply(this, arguments) || this; _this.pressed = false; // mousedown state return _this; } /** * @private * handle mouse events * @param {Object} ev */ var _proto = MouseInput.prototype; _proto.handler = function handler(ev) { var eventType = MOUSE_INPUT_MAP[ev.type]; // on start we want to have the left mouse button down if (eventType & INPUT_START && ev.button === 0) { this.pressed = true; } if (eventType & INPUT_MOVE && ev.which !== 1) { eventType = INPUT_END; } // mouse must be down if (!this.pressed) { return; } if (eventType & INPUT_END) { this.pressed = false; } this.callback(this.manager, eventType, { pointers: [ev], changedPointers: [ev], pointerType: INPUT_TYPE_MOUSE, srcEvent: ev }); }; return MouseInput; }(Input); /** * @private * Combined touch and mouse input * * Touch has a higher priority then mouse, and while touching no mouse events are allowed. * This because touch devices also emit mouse events while doing a touch. * * @constructor * @extends Input */ var DEDUP_TIMEOUT = 2500; var DEDUP_DISTANCE = 25; function setLastTouch(eventData) { var _eventData$changedPoi = eventData.changedPointers, touch = _eventData$changedPoi[0]; if (touch.identifier === this.primaryTouch) { var lastTouch = { x: touch.clientX, y: touch.clientY }; var lts = this.lastTouches; this.lastTouches.push(lastTouch); var removeLastTouch = function removeLastTouch() { var i = lts.indexOf(lastTouch); if (i > -1) { lts.splice(i, 1); } }; setTimeout(removeLastTouch, DEDUP_TIMEOUT); } } function recordTouches(eventType, eventData) { if (eventType & INPUT_START) { this.primaryTouch = eventData.changedPointers[0].identifier; setLastTouch.call(this, eventData); } else if (eventType & (INPUT_END | INPUT_CANCEL)) { setLastTouch.call(this, eventData); } } function isSyntheticEvent(eventData) { var x = eventData.srcEvent.clientX; var y = eventData.srcEvent.clientY; for (var i = 0; i < this.lastTouches.length; i++) { var t = this.lastTouches[i]; var dx = Math.abs(x - t.x); var dy = Math.abs(y - t.y); if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) { return true; } } return false; } var TouchMouseInput = /*#__PURE__*/ function () { var TouchMouseInput = /*#__PURE__*/ function (_Input) { _inheritsLoose(TouchMouseInput, _Input); function TouchMouseInput(_manager, callback) { var _this; _this = _Input.call(this, _manager, callback) || this; _this.handler = function (manager, inputEvent, inputData) { var isTouch = inputData.pointerType === INPUT_TYPE_TOUCH; var isMouse = inputData.pointerType === INPUT_TYPE_MOUSE; if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) { return; } // when we're in a touch event, record touches to de-dupe synthetic mouse event if (isTouch) { recordTouches.call(_assertThisInitialized$1(_assertThisInitialized$1(_this)), inputEvent, inputData); } else if (isMouse && isSyntheticEvent.call(_assertThisInitialized$1(_assertThisInitialized$1(_this)), inputData)) { return; } _this.callback(manager, inputEvent, inputData); }; _this.touch = new TouchInput(_this.manager, _this.handler); _this.mouse = new MouseInput(_this.manager, _this.handler); _this.primaryTouch = null; _this.lastTouches = []; return _this; } /** * @private * handle mouse and touch events * @param {Hammer} manager * @param {String} inputEvent * @param {Object} inputData */ var _proto = TouchMouseInput.prototype; /** * @private * remove the event listeners */ _proto.destroy = function destroy() { this.touch.destroy(); this.mouse.destroy(); }; return TouchMouseInput; }(Input); return TouchMouseInput; }(); /** * @private * create new input type manager * called by the Manager constructor * @param {Hammer} manager * @returns {Input} */ function createInputInstance(manager) { var Type; // let inputClass = manager.options.inputClass; var inputClass = manager.options.inputClass; if (inputClass) { Type = inputClass; } else if (SUPPORT_POINTER_EVENTS) { Type = PointerEventInput; } else if (SUPPORT_ONLY_TOUCH) { Type = TouchInput; } else if (!SUPPORT_TOUCH) { Type = MouseInput; } else { Type = TouchMouseInput; } return new Type(manager, inputHandler); } /** * @private * if the argument is an array, we want to execute the fn on each entry * if it aint an array we don't want to do a thing. * this is used by all the methods that accept a single and array argument. * @param {*|Array} arg * @param {String} fn * @param {Object} [context] * @returns {Boolean} */ function invokeArrayArg(arg, fn, context) { if (Array.isArray(arg)) { each(arg, context[fn], context); return true; } return false; } var STATE_POSSIBLE = 1; var STATE_BEGAN = 2; var STATE_CHANGED = 4; var STATE_ENDED = 8; var STATE_RECOGNIZED = STATE_ENDED; var STATE_CANCELLED = 16; var STATE_FAILED = 32; /** * @private * get a unique id * @returns {number} uniqueId */ var _uniqueId = 1; function uniqueId() { return _uniqueId++; } /** * @private * get a recognizer by name if it is bound to a manager * @param {Recognizer|String} otherRecognizer * @param {Recognizer} recognizer * @returns {Recognizer} */ function getRecognizerByNameIfManager(otherRecognizer, recognizer) { var manager = recognizer.manager; if (manager) { return manager.get(otherRecognizer); } return otherRecognizer; } /** * @private * get a usable string, used as event postfix * @param {constant} state * @returns {String} state */ function stateStr(state) { if (state & STATE_CANCELLED) { return 'cancel'; } else if (state & STATE_ENDED) { return 'end'; } else if (state & STATE_CHANGED) { return 'move'; } else if (state & STATE_BEGAN) { return 'start'; } return ''; } /** * @private * Recognizer flow explained; * * All recognizers have the initial state of POSSIBLE when a input session starts. * The definition of a input session is from the first input until the last input, with all it's movement in it. * * Example session for mouse-input: mousedown -> mousemove -> mouseup * * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed * which determines with state it should be. * * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to * POSSIBLE to give it another change on the next cycle. * * Possible * | * +-----+---------------+ * | | * +-----+-----+ | * | | | * Failed Cancelled | * +-------+------+ * | | * Recognized Began * | * Changed * | * Ended/Recognized */ /** * @private * Recognizer * Every recognizer needs to extend from this class. * @constructor * @param {Object} options */ var Recognizer = /*#__PURE__*/ function () { function Recognizer(options) { if (options === void 0) { options = {}; } this.options = _extends({ enable: true }, options); this.id = uniqueId(); this.manager = null; // default is enable true this.state = STATE_POSSIBLE; this.simultaneous = {}; this.requireFail = []; } /** * @private * set options * @param {Object} options * @return {Recognizer} */ var _proto = Recognizer.prototype; _proto.set = function set(options) { assign$1(this.options, options); // also update the touchAction, in case something changed about the directions/enabled state this.manager && this.manager.touchAction.update(); return this; }; /** * @private * recognize simultaneous with an other recognizer. * @param {Recognizer} otherRecognizer * @returns {Recognizer} this */ _proto.recognizeWith = function recognizeWith(otherRecognizer) { if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { return this; } var simultaneous = this.simultaneous; otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); if (!simultaneous[otherRecognizer.id]) { simultaneous[otherRecognizer.id] = otherRecognizer; otherRecognizer.recognizeWith(this); } return this; }; /** * @private * drop the simultaneous link. it doesnt remove the link on the other recognizer. * @param {Recognizer} otherRecognizer * @returns {Recognizer} this */ _proto.dropRecognizeWith = function dropRecognizeWith(otherRecognizer) { if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { return this; } otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); delete this.simultaneous[otherRecognizer.id]; return this; }; /** * @private * recognizer can only run when an other is failing * @param {Recognizer} otherRecognizer * @returns {Recognizer} this */ _proto.requireFailure = function requireFailure(otherRecognizer) { if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { return this; } var requireFail = this.requireFail; otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); if (inArray(requireFail, otherRecognizer) === -1) { requireFail.push(otherRecognizer); otherRecognizer.requireFailure(this); } return this; }; /** * @private * drop the requireFailure link. it does not remove the link on the other recognizer. * @param {Recognizer} otherRecognizer * @returns {Recognizer} this */ _proto.dropRequireFailure = function dropRequireFailure(otherRecognizer) { if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { return this; } otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); var index = inArray(this.requireFail, otherRecognizer); if (index > -1) { this.requireFail.splice(index, 1); } return this; }; /** * @private * has require failures boolean * @returns {boolean} */ _proto.hasRequireFailures = function hasRequireFailures() { return this.requireFail.length > 0; }; /** * @private * if the recognizer can recognize simultaneous with an other recognizer * @param {Recognizer} otherRecognizer * @returns {Boolean} */ _proto.canRecognizeWith = function canRecognizeWith(otherRecognizer) { return !!this.simultaneous[otherRecognizer.id]; }; /** * @private * You should use `tryEmit` instead of `emit` directly to check * that all the needed recognizers has failed before emitting. * @param {Object} input */ _proto.emit = function emit(input) { var self = this; var state = this.state; function emit(event) { self.manager.emit(event, input); } // 'panstart' and 'panmove' if (state < STATE_ENDED) { emit(self.options.event + stateStr(state)); } emit(self.options.event); // simple 'eventName' events if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...) emit(input.additionalEvent); } // panend and pancancel if (state >= STATE_ENDED) { emit(self.options.event + stateStr(state)); } }; /** * @private * Check that all the require failure recognizers has failed, * if true, it emits a gesture event, * otherwise, setup the state to FAILED. * @param {Object} input */ _proto.tryEmit = function tryEmit(input) { if (this.canEmit()) { return this.emit(input); } // it's failing anyway this.state = STATE_FAILED; }; /** * @private * can we emit? * @returns {boolean} */ _proto.canEmit = function canEmit() { var i = 0; while (i < this.requireFail.length) { if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { return false; } i++; } return true; }; /** * @private * update the recognizer * @param {Object} inputData */ _proto.recognize = function recognize(inputData) { // make a new copy of the inputData // so we can change the inputData without messing up the other recognizers var inputDataClone = assign$1({}, inputData); // is is enabled and allow recognizing? if (!boolOrFn(this.options.enable, [this, inputDataClone])) { this.reset(); this.state = STATE_FAILED; return; } // reset when we've reached the end if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { this.state = STATE_POSSIBLE; } this.state = this.process(inputDataClone); // the recognizer has recognized a gesture // so trigger an event if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { this.tryEmit(inputDataClone); } }; /** * @private * return the state of the recognizer * the actual recognizing happens in this method * @virtual * @param {Object} inputData * @returns {constant} STATE */ /* jshint ignore:start */ _proto.process = function process(inputData) {}; /* jshint ignore:end */ /** * @private * return the preferred touch-action * @virtual * @returns {Array} */ _proto.getTouchAction = function getTouchAction() {}; /** * @private * called when the gesture isn't allowed to recognize * like when another is being recognized or it is disabled * @virtual */ _proto.reset = function reset() {}; return Recognizer; }(); /** * @private * A tap is recognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur * between the given interval and position. The delay option can be used to recognize multi-taps without firing * a single tap. * * The eventData from the emitted event contains the property `tapCount`, which contains the amount of * multi-taps being recognized. * @constructor * @extends Recognizer */ var TapRecognizer = /*#__PURE__*/ function (_Recognizer) { _inheritsLoose(TapRecognizer, _Recognizer); function TapRecognizer(options) { var _this; if (options === void 0) { options = {}; } _this = _Recognizer.call(this, _extends({ event: 'tap', pointers: 1, taps: 1, interval: 300, // max time between the multi-tap taps time: 250, // max time of the pointer to be down (like finger on the screen) threshold: 9, // a minimal movement is ok, but keep it low posThreshold: 10 }, options)) || this; // previous time and center, // used for tap counting _this.pTime = false; _this.pCenter = false; _this._timer = null; _this._input = null; _this.count = 0; return _this; } var _proto = TapRecognizer.prototype; _proto.getTouchAction = function getTouchAction() { return [TOUCH_ACTION_MANIPULATION]; }; _proto.process = function process(input) { var _this2 = this; var options = this.options; var validPointers = input.pointers.length === options.pointers; var validMovement = input.distance < options.threshold; var validTouchTime = input.deltaTime < options.time; this.reset(); if (input.eventType & INPUT_START && this.count === 0) { return this.failTimeout(); } // we only allow little movement // and we've reached an end event, so a tap is possible if (validMovement && validTouchTime && validPointers) { if (input.eventType !== INPUT_END) { return this.failTimeout(); } var validInterval = this.pTime ? input.timeStamp - this.pTime < options.interval : true; var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; this.pTime = input.timeStamp; this.pCenter = input.center; if (!validMultiTap || !validInterval) { this.count = 1; } else { this.count += 1; } this._input = input; // if tap count matches we have recognized it, // else it has began recognizing... var tapCount = this.count % options.taps; if (tapCount === 0) { // no failing requirements, immediately trigger the tap event // or wait as long as the multitap interval to trigger if (!this.hasRequireFailures()) { return STATE_RECOGNIZED; } else { this._timer = setTimeout(function () { _this2.state = STATE_RECOGNIZED; _this2.tryEmit(); }, options.interval); return STATE_BEGAN; } } } return STATE_FAILED; }; _proto.failTimeout = function failTimeout() { var _this3 = this; this._timer = setTimeout(function () { _this3.state = STATE_FAILED; }, this.options.interval); return STATE_FAILED; }; _proto.reset = function reset() { clearTimeout(this._timer); }; _proto.emit = function emit() { if (this.state === STATE_RECOGNIZED) { this._input.tapCount = this.count; this.manager.emit(this.options.event, this._input); } }; return TapRecognizer; }(Recognizer); /** * @private * This recognizer is just used as a base for the simple attribute recognizers. * @constructor * @extends Recognizer */ var AttrRecognizer = /*#__PURE__*/ function (_Recognizer) { _inheritsLoose(AttrRecognizer, _Recognizer); function AttrRecognizer(options) { if (options === void 0) { options = {}; } return _Recognizer.call(this, _extends({ pointers: 1 }, options)) || this; } /** * @private * Used to check if it the recognizer receives valid input, like input.distance > 10. * @memberof AttrRecognizer * @param {Object} input * @returns {Boolean} recognized */ var _proto = AttrRecognizer.prototype; _proto.attrTest = function attrTest(input) { var optionPointers = this.options.pointers; return optionPointers === 0 || input.pointers.length === optionPointers; }; /** * @private * Process the input and return the state for the recognizer * @memberof AttrRecognizer * @param {Object} input * @returns {*} State */ _proto.process = function process(input) { var state = this.state; var eventType = input.eventType; var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); var isValid = this.attrTest(input); // on cancel input and we've recognized before, return STATE_CANCELLED if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { return state | STATE_CANCELLED; } else if (isRecognized || isValid) { if (eventType & INPUT_END) { return state | STATE_ENDED; } else if (!(state & STATE_BEGAN)) { return STATE_BEGAN; } return state | STATE_CHANGED; } return STATE_FAILED; }; return AttrRecognizer; }(Recognizer); /** * @private * direction cons to string * @param {constant} direction * @returns {String} */ function directionStr(direction) { if (direction === DIRECTION_DOWN) { return 'down'; } else if (direction === DIRECTION_UP) { return 'up'; } else if (direction === DIRECTION_LEFT) { return 'left'; } else if (direction === DIRECTION_RIGHT) { return 'right'; } return ''; } /** * @private * Pan * Recognized when the pointer is down and moved in the allowed direction. * @constructor * @extends AttrRecognizer */ var PanRecognizer = /*#__PURE__*/ function (_AttrRecognizer) { _inheritsLoose(PanRecognizer, _AttrRecognizer); function PanRecognizer(options) { var _this; if (options === void 0) { options = {}; } _this = _AttrRecognizer.call(this, _extends({ event: 'pan', threshold: 10, pointers: 1, direction: DIRECTION_ALL }, options)) || this; _this.pX = null; _this.pY = null; return _this; } var _proto = PanRecognizer.prototype; _proto.getTouchAction = function getTouchAction() { var direction = this.options.direction; var actions = []; if (direction & DIRECTION_HORIZONTAL) { actions.push(TOUCH_ACTION_PAN_Y); } if (direction & DIRECTION_VERTICAL) { actions.push(TOUCH_ACTION_PAN_X); } return actions; }; _proto.directionTest = function directionTest(input) { var options = this.options; var hasMoved = true; var distance = input.distance; var direction = input.direction; var x = input.deltaX; var y = input.deltaY; // lock to axis? if (!(direction & options.direction)) { if (options.direction & DIRECTION_HORIZONTAL) { direction = x === 0 ? DIRECTION_NONE : x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; hasMoved = x !== this.pX; distance = Math.abs(input.deltaX); } else { direction = y === 0 ? DIRECTION_NONE : y < 0 ? DIRECTION_UP : DIRECTION_DOWN; hasMoved = y !== this.pY; distance = Math.abs(input.deltaY); } } input.direction = direction; return hasMoved && distance > options.threshold && direction & options.direction; }; _proto.attrTest = function attrTest(input) { return AttrRecognizer.prototype.attrTest.call(this, input) && ( // replace with a super call this.state & STATE_BEGAN || !(this.state & STATE_BEGAN) && this.directionTest(input)); }; _proto.emit = function emit(input) { this.pX = input.deltaX; this.pY = input.deltaY; var direction = directionStr(input.direction); if (direction) { input.additionalEvent = this.options.event + direction; } _AttrRecognizer.prototype.emit.call(this, input); }; return PanRecognizer; }(AttrRecognizer); /** * @private * Swipe * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. * @constructor * @extends AttrRecognizer */ var SwipeRecognizer = /*#__PURE__*/ function (_AttrRecognizer) { _inheritsLoose(SwipeRecognizer, _AttrRecognizer); function SwipeRecognizer(options) { if (options === void 0) { options = {}; } return _AttrRecognizer.call(this, _extends({ event: 'swipe', threshold: 10, velocity: 0.3, direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, pointers: 1 }, options)) || this; } var _proto = SwipeRecognizer.prototype; _proto.getTouchAction = function getTouchAction() { return PanRecognizer.prototype.getTouchAction.call(this); }; _proto.attrTest = function attrTest(input) { var direction = this.options.direction; var velocity; if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { velocity = input.overallVelocity; } else if (direction & DIRECTION_HORIZONTAL) { velocity = input.overallVelocityX; } else if (direction & DIRECTION_VERTICAL) { velocity = input.overallVelocityY; } return _AttrRecognizer.prototype.attrTest.call(this, input) && direction & input.offsetDirection && input.distance > this.options.threshold && input.maxPointers === this.options.pointers && abs$1(velocity) > this.options.velocity && input.eventType & INPUT_END; }; _proto.emit = function emit(input) { var direction = directionStr(input.offsetDirection); if (direction) { this.manager.emit(this.options.event + direction, input); } this.manager.emit(this.options.event, input); }; return SwipeRecognizer; }(AttrRecognizer); /** * @private * Pinch * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). * @constructor * @extends AttrRecognizer */ var PinchRecognizer = /*#__PURE__*/ function (_AttrRecognizer) { _inheritsLoose(PinchRecognizer, _AttrRecognizer); function PinchRecognizer(options) { if (options === void 0) { options = {}; } return _AttrRecognizer.call(this, _extends({ event: 'pinch', threshold: 0, pointers: 2 }, options)) || this; } var _proto = PinchRecognizer.prototype; _proto.getTouchAction = function getTouchAction() { return [TOUCH_ACTION_NONE]; }; _proto.attrTest = function attrTest(input) { return _AttrRecognizer.prototype.attrTest.call(this, input) && (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); }; _proto.emit = function emit(input) { if (input.scale !== 1) { var inOut = input.scale < 1 ? 'in' : 'out'; input.additionalEvent = this.options.event + inOut; } _AttrRecognizer.prototype.emit.call(this, input); }; return PinchRecognizer; }(AttrRecognizer); /** * @private * Rotate * Recognized when two or more pointer are moving in a circular motion. * @constructor * @extends AttrRecognizer */ var RotateRecognizer = /*#__PURE__*/ function (_AttrRecognizer) { _inheritsLoose(RotateRecognizer, _AttrRecognizer); function RotateRecognizer(options) { if (options === void 0) { options = {}; } return _AttrRecognizer.call(this, _extends({ event: 'rotate', threshold: 0, pointers: 2 }, options)) || this; } var _proto = RotateRecognizer.prototype; _proto.getTouchAction = function getTouchAction() { return [TOUCH_ACTION_NONE]; }; _proto.attrTest = function attrTest(input) { return _AttrRecognizer.prototype.attrTest.call(this, input) && (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); }; return RotateRecognizer; }(AttrRecognizer); /** * @private * Press * Recognized when the pointer is down for x ms without any movement. * @constructor * @extends Recognizer */ var PressRecognizer = /*#__PURE__*/ function (_Recognizer) { _inheritsLoose(PressRecognizer, _Recognizer); function PressRecognizer(options) { var _this; if (options === void 0) { options = {}; } _this = _Recognizer.call(this, _extends({ event: 'press', pointers: 1, time: 251, // minimal time of the pointer to be pressed threshold: 9 }, options)) || this; _this._timer = null; _this._input = null; return _this; } var _proto = PressRecognizer.prototype; _proto.getTouchAction = function getTouchAction() { return [TOUCH_ACTION_AUTO]; }; _proto.process = function process(input) { var _this2 = this; var options = this.options; var validPointers = input.pointers.length === options.pointers; var validMovement = input.distance < options.threshold; var validTime = input.deltaTime > options.time; this._input = input; // we only allow little movement // and we've reached an end event, so a tap is possible if (!validMovement || !validPointers || input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime) { this.reset(); } else if (input.eventType & INPUT_START) { this.reset(); this._timer = setTimeout(function () { _this2.state = STATE_RECOGNIZED; _this2.tryEmit(); }, options.time); } else if (input.eventType & INPUT_END) { return STATE_RECOGNIZED; } return STATE_FAILED; }; _proto.reset = function reset() { clearTimeout(this._timer); }; _proto.emit = function emit(input) { if (this.state !== STATE_RECOGNIZED) { return; } if (input && input.eventType & INPUT_END) { this.manager.emit(this.options.event + "up", input); } else { this._input.timeStamp = now(); this.manager.emit(this.options.event, this._input); } }; return PressRecognizer; }(Recognizer); var defaults = { /** * @private * set if DOM events are being triggered. * But this is slower and unused by simple implementations, so disabled by default. * @type {Boolean} * @default false */ domEvents: false, /** * @private * The value for the touchAction property/fallback. * When set to `compute` it will magically set the correct value based on the added recognizers. * @type {String} * @default compute */ touchAction: TOUCH_ACTION_COMPUTE, /** * @private * @type {Boolean} * @default true */ enable: true, /** * @private * EXPERIMENTAL FEATURE -- can be removed/changed * Change the parent input target element. * If Null, then it is being set the to main element. * @type {Null|EventTarget} * @default null */ inputTarget: null, /** * @private * force an input class * @type {Null|Function} * @default null */ inputClass: null, /** * @private * Some CSS properties can be used to improve the working of Hammer. * Add them to this method and they will be set when creating a new Manager. * @namespace */ cssProps: { /** * @private * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. * @type {String} * @default 'none' */ userSelect: "none", /** * @private * Disable the Windows Phone grippers when pressing an element. * @type {String} * @default 'none' */ touchSelect: "none", /** * @private * Disables the default callout shown when you touch and hold a touch target. * On iOS, when you touch and hold a touch target such as a link, Safari displays * a callout containing information about the link. This property allows you to disable that callout. * @type {String} * @default 'none' */ touchCallout: "none", /** * @private * Specifies whether zooming is enabled. Used by IE10> * @type {String} * @default 'none' */ contentZooming: "none", /** * @private * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. * @type {String} * @default 'none' */ userDrag: "none", /** * @private * Overrides the highlight color shown when the user taps a link or a JavaScript * clickable element in iOS. This property obeys the alpha value, if specified. * @type {String} * @default 'rgba(0,0,0,0)' */ tapHighlightColor: "rgba(0,0,0,0)" } }; /** * @private * Default recognizer setup when calling `Hammer()` * When creating a new Manager these will be skipped. * This is separated with other defaults because of tree-shaking. * @type {Array} */ var preset = [[RotateRecognizer, { enable: false }], [PinchRecognizer, { enable: false }, ['rotate']], [SwipeRecognizer, { direction: DIRECTION_HORIZONTAL }], [PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']], [TapRecognizer], [TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']], [PressRecognizer]]; var STOP = 1; var FORCED_STOP = 2; /** * @private * add/remove the css properties as defined in manager.options.cssProps * @param {Manager} manager * @param {Boolean} add */ function toggleCssProps(manager, add) { var element = manager.element; if (!element.style) { return; } var prop; each(manager.options.cssProps, function (value, name) { prop = prefixed(element.style, name); if (add) { manager.oldCssProps[prop] = element.style[prop]; element.style[prop] = value; } else { element.style[prop] = manager.oldCssProps[prop] || ""; } }); if (!add) { manager.oldCssProps = {}; } } /** * @private * trigger dom event * @param {String} event * @param {Object} data */ function triggerDomEvent(event, data) { var gestureEvent = document.createEvent("Event"); gestureEvent.initEvent(event, true, true); gestureEvent.gesture = data; data.target.dispatchEvent(gestureEvent); } /** * @private * Manager * @param {HTMLElement} element * @param {Object} [options] * @constructor */ var Manager = /*#__PURE__*/ function () { function Manager(element, options) { var _this = this; this.options = assign$1({}, defaults, options || {}); this.options.inputTarget = this.options.inputTarget || element; this.handlers = {}; this.session = {}; this.recognizers = []; this.oldCssProps = {}; this.element = element; this.input = createInputInstance(this); this.touchAction = new TouchAction(this, this.options.touchAction); toggleCssProps(this, true); each(this.options.recognizers, function (item) { var recognizer = _this.add(new item[0](item[1])); item[2] && recognizer.recognizeWith(item[2]); item[3] && recognizer.requireFailure(item[3]); }, this); } /** * @private * set options * @param {Object} options * @returns {Manager} */ var _proto = Manager.prototype; _proto.set = function set(options) { assign$1(this.options, options); // Options that need a little more setup if (options.touchAction) { this.touchAction.update(); } if (options.inputTarget) { // Clean up existing event listeners and reinitialize this.input.destroy(); this.input.target = options.inputTarget; this.input.init(); } return this; }; /** * @private * stop recognizing for this session. * This session will be discarded, when a new [input]start event is fired. * When forced, the recognizer cycle is stopped immediately. * @param {Boolean} [force] */ _proto.stop = function stop(force) { this.session.stopped = force ? FORCED_STOP : STOP; }; /** * @private * run the recognizers! * called by the inputHandler function on every movement of the pointers (touches) * it walks through all the recognizers and tries to detect the gesture that is being made * @param {Object} inputData */ _proto.recognize = function recognize(inputData) { var session = this.session; if (session.stopped) { return; } // run the touch-action polyfill this.touchAction.preventDefaults(inputData); var recognizer; var recognizers = this.recognizers; // this holds the recognizer that is being recognized. // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED // if no recognizer is detecting a thing, it is set to `null` var curRecognizer = session.curRecognizer; // reset when the last recognizer is recognized // or when we're in a new session if (!curRecognizer || curRecognizer && curRecognizer.state & STATE_RECOGNIZED) { session.curRecognizer = null; curRecognizer = null; } var i = 0; while (i < recognizers.length) { recognizer = recognizers[i]; // find out if we are allowed try to recognize the input for this one. // 1. allow if the session is NOT forced stopped (see the .stop() method) // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one // that is being recognized. // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. // this can be setup with the `recognizeWith()` method on the recognizer. if (session.stopped !== FORCED_STOP && ( // 1 !curRecognizer || recognizer === curRecognizer || // 2 recognizer.canRecognizeWith(curRecognizer))) { // 3 recognizer.recognize(inputData); } else { recognizer.reset(); } // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the // current active recognizer. but only if we don't already have an active recognizer if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { session.curRecognizer = recognizer; curRecognizer = recognizer; } i++; } }; /** * @private * get a recognizer by its event name. * @param {Recognizer|String} recognizer * @returns {Recognizer|Null} */ _proto.get = function get(recognizer) { if (recognizer instanceof Recognizer) { return recognizer; } var recognizers = this.recognizers; for (var i = 0; i < recognizers.length; i++) { if (recognizers[i].options.event === recognizer) { return recognizers[i]; } } return null; }; /** * @private add a recognizer to the manager * existing recognizers with the same event name will be removed * @param {Recognizer} recognizer * @returns {Recognizer|Manager} */ _proto.add = function add(recognizer) { if (invokeArrayArg(recognizer, "add", this)) { return this; } // remove existing var existing = this.get(recognizer.options.event); if (existing) { this.remove(existing); } this.recognizers.push(recognizer); recognizer.manager = this; this.touchAction.update(); return recognizer; }; /** * @private * remove a recognizer by name or instance * @param {Recognizer|String} recognizer * @returns {Manager} */ _proto.remove = function remove(recognizer) { if (invokeArrayArg(recognizer, "remove", this)) { return this; } var targetRecognizer = this.get(recognizer); // let's make sure this recognizer exists if (recognizer) { var recognizers = this.recognizers; var index = inArray(recognizers, targetRecognizer); if (index !== -1) { recognizers.splice(index, 1); this.touchAction.update(); } } return this; }; /** * @private * bind event * @param {String} events * @param {Function} handler * @returns {EventEmitter} this */ _proto.on = function on(events, handler) { if (events === undefined || handler === undefined) { return this; } var handlers = this.handlers; each(splitStr(events), function (event) { handlers[event] = handlers[event] || []; handlers[event].push(handler); }); return this; }; /** * @private unbind event, leave emit blank to remove all handlers * @param {String} events * @param {Function} [handler] * @returns {EventEmitter} this */ _proto.off = function off(events, handler) { if (events === undefined) { return this; } var handlers = this.handlers; each(splitStr(events), function (event) { if (!handler) { delete handlers[event]; } else { handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1); } }); return this; }; /** * @private emit event to the listeners * @param {String} event * @param {Object} data */ _proto.emit = function emit(event, data) { // we also want to trigger dom events if (this.options.domEvents) { triggerDomEvent(event, data); } // no handlers, so skip it all var handlers = this.handlers[event] && this.handlers[event].slice(); if (!handlers || !handlers.length) { return; } data.type = event; data.preventDefault = function () { data.srcEvent.preventDefault(); }; var i = 0; while (i < handlers.length) { handlers[i](data); i++; } }; /** * @private * destroy the manager and unbinds all events * it doesn't unbind dom events, that is the user own responsibility */ _proto.destroy = function destroy() { this.element && toggleCssProps(this, false); this.handlers = {}; this.session = {}; this.input.destroy(); this.element = null; }; return Manager; }(); var SINGLE_TOUCH_INPUT_MAP = { touchstart: INPUT_START, touchmove: INPUT_MOVE, touchend: INPUT_END, touchcancel: INPUT_CANCEL }; var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; /** * @private * Touch events input * @constructor * @extends Input */ var SingleTouchInput = /*#__PURE__*/ function (_Input) { _inheritsLoose(SingleTouchInput, _Input); function SingleTouchInput() { var _this; var proto = SingleTouchInput.prototype; proto.evTarget = SINGLE_TOUCH_TARGET_EVENTS; proto.evWin = SINGLE_TOUCH_WINDOW_EVENTS; _this = _Input.apply(this, arguments) || this; _this.started = false; return _this; } var _proto = SingleTouchInput.prototype; _proto.handler = function handler(ev) { var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; // should we handle the touch events? if (type === INPUT_START) { this.started = true; } if (!this.started) { return; } var touches = normalizeSingleTouches.call(this, ev, type); // when done, reset the started state if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { this.started = false; } this.callback(this.manager, type, { pointers: touches[0], changedPointers: touches[1], pointerType: INPUT_TYPE_TOUCH, srcEvent: ev }); }; return SingleTouchInput; }(Input); function normalizeSingleTouches(ev, type) { var all = toArray(ev.touches); var changed = toArray(ev.changedTouches); if (type & (INPUT_END | INPUT_CANCEL)) { all = uniqueArray(all.concat(changed), 'identifier', true); } return [all, changed]; } /** * @private * wrap a method with a deprecation warning and stack trace * @param {Function} method * @param {String} name * @param {String} message * @returns {Function} A new function wrapping the supplied method. */ function deprecate(method, name, message) { var deprecationMessage = "DEPRECATED METHOD: " + name + "\n" + message + " AT \n"; return function () { var e = new Error('get-stack-trace'); var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '').replace(/^\s+at\s+/gm, '').replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace'; var log = window.console && (window.console.warn || window.console.log); if (log) { log.call(window.console, deprecationMessage, stack); } return method.apply(this, arguments); }; } /** * @private * extend object. * means that properties in dest will be overwritten by the ones in src. * @param {Object} dest * @param {Object} src * @param {Boolean} [merge=false] * @returns {Object} dest */ var extend = deprecate(function (dest, src, merge) { var keys = Object.keys(src); var i = 0; while (i < keys.length) { if (!merge || merge && dest[keys[i]] === undefined) { dest[keys[i]] = src[keys[i]]; } i++; } return dest; }, 'extend', 'Use `assign`.'); /** * @private * merge the values from src in the dest. * means that properties that exist in dest will not be overwritten by src * @param {Object} dest * @param {Object} src * @returns {Object} dest */ var merge$2 = deprecate(function (dest, src) { return extend(dest, src, true); }, 'merge', 'Use `assign`.'); /** * @private * simple class inheritance * @param {Function} child * @param {Function} base * @param {Object} [properties] */ function inherit(child, base, properties) { var baseP = base.prototype; var childP; childP = child.prototype = Object.create(baseP); childP.constructor = child; childP._super = baseP; if (properties) { assign$1(childP, properties); } } /** * @private * simple function bind * @param {Function} fn * @param {Object} context * @returns {Function} */ function bindFn(fn, context) { return function boundFn() { return fn.apply(context, arguments); }; } /** * @private * Simple way to create a manager with a default set of recognizers. * @param {HTMLElement} element * @param {Object} [options] * @constructor */ var Hammer$2 = /*#__PURE__*/ function () { var Hammer = /** * @private * @const {string} */ function Hammer(element, options) { if (options === void 0) { options = {}; } return new Manager(element, _extends({ recognizers: preset.concat() }, options)); }; Hammer.VERSION = "2.0.17-rc"; Hammer.DIRECTION_ALL = DIRECTION_ALL; Hammer.DIRECTION_DOWN = DIRECTION_DOWN; Hammer.DIRECTION_LEFT = DIRECTION_LEFT; Hammer.DIRECTION_RIGHT = DIRECTION_RIGHT; Hammer.DIRECTION_UP = DIRECTION_UP; Hammer.DIRECTION_HORIZONTAL = DIRECTION_HORIZONTAL; Hammer.DIRECTION_VERTICAL = DIRECTION_VERTICAL; Hammer.DIRECTION_NONE = DIRECTION_NONE; Hammer.DIRECTION_DOWN = DIRECTION_DOWN; Hammer.INPUT_START = INPUT_START; Hammer.INPUT_MOVE = INPUT_MOVE; Hammer.INPUT_END = INPUT_END; Hammer.INPUT_CANCEL = INPUT_CANCEL; Hammer.STATE_POSSIBLE = STATE_POSSIBLE; Hammer.STATE_BEGAN = STATE_BEGAN; Hammer.STATE_CHANGED = STATE_CHANGED; Hammer.STATE_ENDED = STATE_ENDED; Hammer.STATE_RECOGNIZED = STATE_RECOGNIZED; Hammer.STATE_CANCELLED = STATE_CANCELLED; Hammer.STATE_FAILED = STATE_FAILED; Hammer.Manager = Manager; Hammer.Input = Input; Hammer.TouchAction = TouchAction; Hammer.TouchInput = TouchInput; Hammer.MouseInput = MouseInput; Hammer.PointerEventInput = PointerEventInput; Hammer.TouchMouseInput = TouchMouseInput; Hammer.SingleTouchInput = SingleTouchInput; Hammer.Recognizer = Recognizer; Hammer.AttrRecognizer = AttrRecognizer; Hammer.Tap = TapRecognizer; Hammer.Pan = PanRecognizer; Hammer.Swipe = SwipeRecognizer; Hammer.Pinch = PinchRecognizer; Hammer.Rotate = RotateRecognizer; Hammer.Press = PressRecognizer; Hammer.on = addEventListeners; Hammer.off = removeEventListeners; Hammer.each = each; Hammer.merge = merge$2; Hammer.extend = extend; Hammer.bindFn = bindFn; Hammer.assign = assign$1; Hammer.inherit = inherit; Hammer.bindFn = bindFn; Hammer.prefixed = prefixed; Hammer.toArray = toArray; Hammer.inArray = inArray; Hammer.uniqueArray = uniqueArray; Hammer.splitStr = splitStr; Hammer.boolOrFn = boolOrFn; Hammer.hasParent = hasParent; Hammer.addEventListeners = addEventListeners; Hammer.removeEventListeners = removeEventListeners; Hammer.defaults = assign$1({}, defaults, { preset: preset }); return Hammer; }(); // style loader but by script tag, not by the loader. Hammer$2.defaults; var RealHammer = Hammer$2; function _createForOfIteratorHelper$6(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray$6(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray$6(o, minLen) { var _context21; if (!o) return; if (typeof o === "string") return _arrayLikeToArray$6(o, minLen); var n = _sliceInstanceProperty(_context21 = Object.prototype.toString.call(o)).call(_context21, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from$1(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$6(o, minLen); } function _arrayLikeToArray$6(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } /** * Use this symbol to delete properies in deepObjectAssign. */ _Symbol("DELETE"); /** * Seedable, fast and reasonably good (not crypto but more than okay for our * needs) random number generator. * * @remarks * Adapted from {@link https://web.archive.org/web/20110429100736/http://baagoe.com:80/en/RandomMusings/javascript}. * Original algorithm created by Johannes Baagøe \<baagoe\@baagoe.com\> in 2010. */ /** * Create a seeded pseudo random generator based on Alea by Johannes Baagøe. * * @param seed - All supplied arguments will be used as a seed. In case nothing * is supplied the current time will be used to seed the generator. * @returns A ready to use seeded generator. */ function Alea() { for (var _len3 = arguments.length, seed = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { seed[_key3] = arguments[_key3]; } return AleaImplementation(seed.length ? seed : [_Date$now()]); } /** * An implementation of [[Alea]] without user input validation. * * @param seed - The data that will be used to seed the generator. * @returns A ready to use seeded generator. */ function AleaImplementation(seed) { var _mashSeed = mashSeed(seed), _mashSeed2 = _slicedToArray(_mashSeed, 3), s0 = _mashSeed2[0], s1 = _mashSeed2[1], s2 = _mashSeed2[2]; var c = 1; var random = function random() { var t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32 s0 = s1; s1 = s2; return s2 = t - (c = t | 0); }; random.uint32 = function () { return random() * 0x100000000; }; // 2^32 random.fract53 = function () { return random() + (random() * 0x200000 | 0) * 1.1102230246251565e-16; }; // 2^-53 random.algorithm = "Alea"; random.seed = seed; random.version = "0.9"; return random; } /** * Turn arbitrary data into values [[AleaImplementation]] can use to generate * random numbers. * * @param seed - Arbitrary data that will be used as the seed. * @returns Three numbers to use as initial values for [[AleaImplementation]]. */ function mashSeed() { var mash = Mash(); var s0 = mash(" "); var s1 = mash(" "); var s2 = mash(" "); for (var i = 0; i < arguments.length; i++) { s0 -= mash(i < 0 || arguments.length <= i ? undefined : arguments[i]); if (s0 < 0) { s0 += 1; } s1 -= mash(i < 0 || arguments.length <= i ? undefined : arguments[i]); if (s1 < 0) { s1 += 1; } s2 -= mash(i < 0 || arguments.length <= i ? undefined : arguments[i]); if (s2 < 0) { s2 += 1; } } return [s0, s1, s2]; } /** * Create a new mash function. * * @returns A nonpure function that takes arbitrary [[Mashable]] data and turns * them into numbers. */ function Mash() { var n = 0xefc8249d; return function (data) { var string = data.toString(); for (var i = 0; i < string.length; i++) { n += string.charCodeAt(i); var h = 0.02519603282416938 * n; n = h >>> 0; h -= n; h *= n; n = h >>> 0; h -= n; n += h * 0x100000000; // 2^32 } return (n >>> 0) * 2.3283064365386963e-10; // 2^-32 }; } /** * Setup a mock hammer.js object, for unit testing. * * Inspiration: https://github.com/uber/deck.gl/pull/658 * * @returns {{on: noop, off: noop, destroy: noop, emit: noop, get: get}} */ function hammerMock() { var noop = function noop() {}; return { on: noop, off: noop, destroy: noop, emit: noop, get: function get() { return { set: noop }; } }; } var Hammer$1 = typeof window !== "undefined" ? window.Hammer || RealHammer : function () { // hammer.js is only available in a browser, not in node.js. Replacing it with a mock object. return hammerMock(); }; /** * Turn an element into an clickToUse element. * When not active, the element has a transparent overlay. When the overlay is * clicked, the mode is changed to active. * When active, the element is displayed with a blue border around it, and * the interactive contents of the element can be used. When clicked outside * the element, the elements mode is changed to inactive. * * @param {Element} container * @class Activator */ function Activator$1(container) { var _this = this, _context3; this._cleanupQueue = []; this.active = false; this._dom = { container: container, overlay: document.createElement("div") }; this._dom.overlay.classList.add("vis-overlay"); this._dom.container.appendChild(this._dom.overlay); this._cleanupQueue.push(function () { _this._dom.overlay.parentNode.removeChild(_this._dom.overlay); }); var hammer = Hammer$1(this._dom.overlay); hammer.on("tap", _bindInstanceProperty$1(_context3 = this._onTapOverlay).call(_context3, this)); this._cleanupQueue.push(function () { hammer.destroy(); // FIXME: cleaning up hammer instances doesn't work (Timeline not removed // from memory) }); // block all touch events (except tap) var events = ["tap", "doubletap", "press", "pinch", "pan", "panstart", "panmove", "panend"]; _forEachInstanceProperty(events).call(events, function (event) { hammer.on(event, function (event) { event.srcEvent.stopPropagation(); }); }); // attach a click event to the window, in order to deactivate when clicking outside the timeline if (document && document.body) { this._onClick = function (event) { if (!_hasParent(event.target, container)) { _this.deactivate(); } }; document.body.addEventListener("click", this._onClick); this._cleanupQueue.push(function () { document.body.removeEventListener("click", _this._onClick); }); } // prepare escape key listener for deactivating when active this._escListener = function (event) { if ("key" in event ? event.key === "Escape" : event.keyCode === 27 /* the keyCode is for IE11 */) { _this.deactivate(); } }; } // turn into an event emitter Emitter(Activator$1.prototype); // The currently active activator Activator$1.current = null; /** * Destroy the activator. Cleans up all created DOM and event listeners */ Activator$1.prototype.destroy = function () { var _context4, _context5; this.deactivate(); var _iterator2 = _createForOfIteratorHelper$6(_reverseInstanceProperty(_context4 = _spliceInstanceProperty(_context5 = this._cleanupQueue).call(_context5, 0)).call(_context4)), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var callback = _step2.value; callback(); } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } }; /** * Activate the element * Overlay is hidden, element is decorated with a blue shadow border */ Activator$1.prototype.activate = function () { // we allow only one active activator at a time if (Activator$1.current) { Activator$1.current.deactivate(); } Activator$1.current = this; this.active = true; this._dom.overlay.style.display = "none"; this._dom.container.classList.add("vis-active"); this.emit("change"); this.emit("activate"); // ugly hack: bind ESC after emitting the events, as the Network rebinds all // keyboard events on a 'change' event document.body.addEventListener("keydown", this._escListener); }; /** * Deactivate the element * Overlay is displayed on top of the element */ Activator$1.prototype.deactivate = function () { this.active = false; this._dom.overlay.style.display = "block"; this._dom.container.classList.remove("vis-active"); document.body.removeEventListener("keydown", this._escListener); this.emit("change"); this.emit("deactivate"); }; /** * Handle a tap event: activate the container * * @param {Event} event The event * @private */ Activator$1.prototype._onTapOverlay = function (event) { // activate the container this.activate(); event.srcEvent.stopPropagation(); }; /** * Test whether the element has the requested parent element somewhere in * its chain of parent nodes. * * @param {HTMLElement} element * @param {HTMLElement} parent * @returns {boolean} Returns true when the parent is found somewhere in the * chain of parent nodes. * @private */ function _hasParent(element, parent) { while (element) { if (element === parent) { return true; } element = element.parentNode; } return false; } // Color REs var fullHexRE = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i; var shortHexRE = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; var rgbRE = /^rgb\( *(1?\d{1,2}|2[0-4]\d|25[0-5]) *, *(1?\d{1,2}|2[0-4]\d|25[0-5]) *, *(1?\d{1,2}|2[0-4]\d|25[0-5]) *\)$/i; var rgbaRE = /^rgba\( *(1?\d{1,2}|2[0-4]\d|25[0-5]) *, *(1?\d{1,2}|2[0-4]\d|25[0-5]) *, *(1?\d{1,2}|2[0-4]\d|25[0-5]) *, *([01]|0?\.\d+) *\)$/i; /** * Remove everything in the DOM object. * * @param DOMobject - Node whose child nodes will be recursively deleted. */ function recursiveDOMDelete(DOMobject) { if (DOMobject) { while (DOMobject.hasChildNodes() === true) { var child = DOMobject.firstChild; if (child) { recursiveDOMDelete(child); DOMobject.removeChild(child); } } } } /** * Test whether given object is a string. * * @param value - Input value of unknown type. * @returns True if string, false otherwise. */ function isString(value) { return value instanceof String || typeof value === "string"; } /** * Test whether given object is a object (not primitive or null). * * @param value - Input value of unknown type. * @returns True if not null object, false otherwise. */ function isObject$7(value) { return _typeof(value) === "object" && value !== null; } /** * Copy property from b to a if property present in a. * If property in b explicitly set to null, delete it if `allowDeletion` set. * * Internal helper routine, should not be exported. Not added to `exports` for that reason. * * @param a - Target object. * @param b - Source object. * @param prop - Name of property to copy from b to a. * @param allowDeletion - If true, delete property in a if explicitly set to null in b. */ function copyOrDelete(a, b, prop, allowDeletion) { var doDeletion = false; if (allowDeletion === true) { doDeletion = b[prop] === null && a[prop] !== undefined; } if (doDeletion) { delete a[prop]; } else { a[prop] = b[prop]; // Remember, this is a reference copy! } } /** * Fill an object with a possibly partially defined other object. * * Only copies values for the properties already present in a. * That means an object is not created on a property if only the b object has it. * * @param a - The object that will have it's properties updated. * @param b - The object with property updates. * @param allowDeletion - If true, delete properties in a that are explicitly set to null in b. */ function fillIfDefined(a, b) { var allowDeletion = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; // NOTE: iteration of properties of a // NOTE: prototype properties iterated over as well for (var prop in a) { if (b[prop] !== undefined) { if (b[prop] === null || _typeof(b[prop]) !== "object") { // Note: typeof null === 'object' copyOrDelete(a, b, prop, allowDeletion); } else { var aProp = a[prop]; var bProp = b[prop]; if (isObject$7(aProp) && isObject$7(bProp)) { fillIfDefined(aProp, bProp, allowDeletion); } } } } } /** * Extend object a with selected properties of object b. * Only properties with defined values are copied. * * @remarks * Previous version of this routine implied that multiple source objects could * be used; however, the implementation was **wrong**. Since multiple (\>1) * sources weren't used anywhere in the `vis.js` code, this has been removed * @param props - Names of first-level properties to copy over. * @param a - Target object. * @param b - Source object. * @param allowDeletion - If true, delete property in a if explicitly set to null in b. * @returns Argument a. */ function selectiveDeepExtend(props, a, b) { var allowDeletion = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; // TODO: add support for Arrays to deepExtend if (_Array$isArray(b)) { throw new TypeError("Arrays are not supported by deepExtend"); } for (var p = 0; p < props.length; p++) { var prop = props[p]; if (Object.prototype.hasOwnProperty.call(b, prop)) { if (b[prop] && b[prop].constructor === Object) { if (a[prop] === undefined) { a[prop] = {}; } if (a[prop].constructor === Object) { deepExtend(a[prop], b[prop], false, allowDeletion); } else { copyOrDelete(a, b, prop, allowDeletion); } } else if (_Array$isArray(b[prop])) { throw new TypeError("Arrays are not supported by deepExtend"); } else { copyOrDelete(a, b, prop, allowDeletion); } } } return a; } /** * Extend object `a` with properties of object `b`, ignoring properties which * are explicitly specified to be excluded. * * @remarks * The properties of `b` are considered for copying. Properties which are * themselves objects are are also extended. Only properties with defined * values are copied. * @param propsToExclude - Names of properties which should *not* be copied. * @param a - Object to extend. * @param b - Object to take properties from for extension. * @param allowDeletion - If true, delete properties in a that are explicitly * set to null in b. * @returns Argument a. */ function selectiveNotDeepExtend(propsToExclude, a, b) { var allowDeletion = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; // TODO: add support for Arrays to deepExtend // NOTE: array properties have an else-below; apparently, there is a problem here. if (_Array$isArray(b)) { throw new TypeError("Arrays are not supported by deepExtend"); } for (var prop in b) { if (!Object.prototype.hasOwnProperty.call(b, prop)) { continue; } // Handle local properties only if (_includesInstanceProperty(propsToExclude).call(propsToExclude, prop)) { continue; } // In exclusion list, skip if (b[prop] && b[prop].constructor === Object) { if (a[prop] === undefined) { a[prop] = {}; } if (a[prop].constructor === Object) { deepExtend(a[prop], b[prop]); // NOTE: allowDeletion not propagated! } else { copyOrDelete(a, b, prop, allowDeletion); } } else if (_Array$isArray(b[prop])) { a[prop] = []; for (var i = 0; i < b[prop].length; i++) { a[prop].push(b[prop][i]); } } else { copyOrDelete(a, b, prop, allowDeletion); } } return a; } /** * Deep extend an object a with the properties of object b. * * @param a - Target object. * @param b - Source object. * @param protoExtend - If true, the prototype values will also be extended. * (That is the options objects that inherit from others will also get the * inherited options). * @param allowDeletion - If true, the values of fields that are null will be deleted. * @returns Argument a. */ function deepExtend(a, b) { var protoExtend = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var allowDeletion = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; for (var prop in b) { if (Object.prototype.hasOwnProperty.call(b, prop) || protoExtend === true) { if (_typeof(b[prop]) === "object" && b[prop] !== null && _Object$getPrototypeOf$1(b[prop]) === Object.prototype) { if (a[prop] === undefined) { a[prop] = deepExtend({}, b[prop], protoExtend); // NOTE: allowDeletion not propagated! } else if (_typeof(a[prop]) === "object" && a[prop] !== null && _Object$getPrototypeOf$1(a[prop]) === Object.prototype) { deepExtend(a[prop], b[prop], protoExtend); // NOTE: allowDeletion not propagated! } else { copyOrDelete(a, b, prop, allowDeletion); } } else if (_Array$isArray(b[prop])) { var _context6; a[prop] = _sliceInstanceProperty(_context6 = b[prop]).call(_context6); } else { copyOrDelete(a, b, prop, allowDeletion); } } } return a; } /** * Used to extend an array and copy it. This is used to propagate paths recursively. * * @param arr - First part. * @param newValue - The value to be aadded into the array. * @returns A new array with all items from arr and newValue (which is last). */ function copyAndExtendArray(arr, newValue) { var _context7; return _concatInstanceProperty(_context7 = []).call(_context7, _toConsumableArray(arr), [newValue]); } /** * Used to extend an array and copy it. This is used to propagate paths recursively. * * @param arr - The array to be copied. * @returns Shallow copy of arr. */ function copyArray(arr) { return _sliceInstanceProperty(arr).call(arr); } /** * Retrieve the absolute left value of a DOM element. * * @param elem - A dom element, for example a div. * @returns The absolute left position of this element in the browser page. */ function getAbsoluteLeft(elem) { return elem.getBoundingClientRect().left; } /** * Retrieve the absolute top value of a DOM element. * * @param elem - A dom element, for example a div. * @returns The absolute top position of this element in the browser page. */ function getAbsoluteTop(elem) { return elem.getBoundingClientRect().top; } /** * For each method for both arrays and objects. * In case of an array, the built-in Array.forEach() is applied (**No, it's not!**). * In case of an Object, the method loops over all properties of the object. * * @param object - An Object or Array to be iterated over. * @param callback - Array.forEach-like callback. */ function forEach$1(object, callback) { if (_Array$isArray(object)) { // array var len = object.length; for (var i = 0; i < len; i++) { callback(object[i], i, object); } } else { // object for (var key in object) { if (Object.prototype.hasOwnProperty.call(object, key)) { callback(object[key], key, object); } } } } /** * Add and event listener. Works for all browsers. * * @param element - The element to bind the event listener to. * @param action - Same as Element.addEventListener(action, —, —). * @param listener - Same as Element.addEventListener(—, listener, —). * @param useCapture - Same as Element.addEventListener(—, —, useCapture). */ function addEventListener(element, action, listener, useCapture) { if (element.addEventListener) { var _context8; if (useCapture === undefined) { useCapture = false; } if (action === "mousewheel" && _includesInstanceProperty(_context8 = navigator.userAgent).call(_context8, "Firefox")) { action = "DOMMouseScroll"; // For Firefox } element.addEventListener(action, listener, useCapture); } else { // @TODO: IE types? Does anyone care? element.attachEvent("on" + action, listener); // IE browsers } } /** * Remove an event listener from an element. * * @param element - The element to bind the event listener to. * @param action - Same as Element.removeEventListener(action, —, —). * @param listener - Same as Element.removeEventListener(—, listener, —). * @param useCapture - Same as Element.removeEventListener(—, —, useCapture). */ function removeEventListener(element, action, listener, useCapture) { if (element.removeEventListener) { var _context9; // non-IE browsers if (useCapture === undefined) { useCapture = false; } if (action === "mousewheel" && _includesInstanceProperty(_context9 = navigator.userAgent).call(_context9, "Firefox")) { action = "DOMMouseScroll"; // For Firefox } element.removeEventListener(action, listener, useCapture); } else { // @TODO: IE types? Does anyone care? element.detachEvent("on" + action, listener); // IE browsers } } /** * Convert hex color string into RGB color object. * * @remarks * {@link http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb} * @param hex - Hex color string (3 or 6 digits, with or without #). * @returns RGB color object. */ function hexToRGB(hex) { var result; switch (hex.length) { case 3: case 4: result = shortHexRE.exec(hex); return result ? { r: _parseInt(result[1] + result[1], 16), g: _parseInt(result[2] + result[2], 16), b: _parseInt(result[3] + result[3], 16) } : null; case 6: case 7: result = fullHexRE.exec(hex); return result ? { r: _parseInt(result[1], 16), g: _parseInt(result[2], 16), b: _parseInt(result[3], 16) } : null; default: return null; } } /** * This function takes string color in hex or RGB format and adds the opacity, RGBA is passed through unchanged. * * @param color - The color string (hex, RGB, RGBA). * @param opacity - The new opacity. * @returns RGBA string, for example 'rgba(255, 0, 127, 0.3)'. */ function overrideOpacity(color, opacity) { if (_includesInstanceProperty(color).call(color, "rgba")) { return color; } else if (_includesInstanceProperty(color).call(color, "rgb")) { var rgb = color.substr(_indexOfInstanceProperty(color).call(color, "(") + 1).replace(")", "").split(","); return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + opacity + ")"; } else { var _rgb = hexToRGB(color); if (_rgb == null) { return color; } else { return "rgba(" + _rgb.r + "," + _rgb.g + "," + _rgb.b + "," + opacity + ")"; } } } /** * Convert RGB \<0, 255\> into hex color string. * * @param red - Red channel. * @param green - Green channel. * @param blue - Blue channel. * @returns Hex color string (for example: '#0acdc0'). */ function RGBToHex(red, green, blue) { var _context10; return "#" + _sliceInstanceProperty(_context10 = ((1 << 24) + (red << 16) + (green << 8) + blue).toString(16)).call(_context10, 1); } /** * Parse a color property into an object with border, background, and highlight colors. * * @param inputColor - Shorthand color string or input color object. * @param defaultColor - Full color object to fill in missing values in inputColor. * @returns Color object. */ function parseColor(inputColor, defaultColor) { if (isString(inputColor)) { var colorStr = inputColor; if (isValidRGB(colorStr)) { var _context11; var rgb = _mapInstanceProperty(_context11 = colorStr.substr(4).substr(0, colorStr.length - 5).split(",")).call(_context11, function (value) { return _parseInt(value); }); colorStr = RGBToHex(rgb[0], rgb[1], rgb[2]); } if (isValidHex(colorStr) === true) { var hsv = hexToHSV(colorStr); var lighterColorHSV = { h: hsv.h, s: hsv.s * 0.8, v: Math.min(1, hsv.v * 1.02) }; var darkerColorHSV = { h: hsv.h, s: Math.min(1, hsv.s * 1.25), v: hsv.v * 0.8 }; var darkerColorHex = HSVToHex(darkerColorHSV.h, darkerColorHSV.s, darkerColorHSV.v); var lighterColorHex = HSVToHex(lighterColorHSV.h, lighterColorHSV.s, lighterColorHSV.v); return { background: colorStr, border: darkerColorHex, highlight: { background: lighterColorHex, border: darkerColorHex }, hover: { background: lighterColorHex, border: darkerColorHex } }; } else { return { background: colorStr, border: colorStr, highlight: { background: colorStr, border: colorStr }, hover: { background: colorStr, border: colorStr } }; } } else { if (defaultColor) { var color = { background: inputColor.background || defaultColor.background, border: inputColor.border || defaultColor.border, highlight: isString(inputColor.highlight) ? { border: inputColor.highlight, background: inputColor.highlight } : { background: inputColor.highlight && inputColor.highlight.background || defaultColor.highlight.background, border: inputColor.highlight && inputColor.highlight.border || defaultColor.highlight.border }, hover: isString(inputColor.hover) ? { border: inputColor.hover, background: inputColor.hover } : { border: inputColor.hover && inputColor.hover.border || defaultColor.hover.border, background: inputColor.hover && inputColor.hover.background || defaultColor.hover.background } }; return color; } else { var _color = { background: inputColor.background || undefined, border: inputColor.border || undefined, highlight: isString(inputColor.highlight) ? { border: inputColor.highlight, background: inputColor.highlight } : { background: inputColor.highlight && inputColor.highlight.background || undefined, border: inputColor.highlight && inputColor.highlight.border || undefined }, hover: isString(inputColor.hover) ? { border: inputColor.hover, background: inputColor.hover } : { border: inputColor.hover && inputColor.hover.border || undefined, background: inputColor.hover && inputColor.hover.background || undefined } }; return _color; } } } /** * Convert RGB \<0, 255\> into HSV object. * * @remarks * {@link http://www.javascripter.net/faq/rgb2hsv.htm} * @param red - Red channel. * @param green - Green channel. * @param blue - Blue channel. * @returns HSV color object. */ function RGBToHSV(red, green, blue) { red = red / 255; green = green / 255; blue = blue / 255; var minRGB = Math.min(red, Math.min(green, blue)); var maxRGB = Math.max(red, Math.max(green, blue)); // Black-gray-white if (minRGB === maxRGB) { return { h: 0, s: 0, v: minRGB }; } // Colors other than black-gray-white: var d = red === minRGB ? green - blue : blue === minRGB ? red - green : blue - red; var h = red === minRGB ? 3 : blue === minRGB ? 1 : 5; var hue = 60 * (h - d / (maxRGB - minRGB)) / 360; var saturation = (maxRGB - minRGB) / maxRGB; var value = maxRGB; return { h: hue, s: saturation, v: value }; } /** * Convert HSV \<0, 1\> into RGB color object. * * @remarks * {@link https://gist.github.com/mjijackson/5311256} * @param h - Hue. * @param s - Saturation. * @param v - Value. * @returns RGB color object. */ function HSVToRGB(h, s, v) { var r; var g; var b; var i = Math.floor(h * 6); var f = h * 6 - i; var p = v * (1 - s); var q = v * (1 - f * s); var t = v * (1 - (1 - f) * s); switch (i % 6) { case 0: r = v, g = t, b = p; break; case 1: r = q, g = v, b = p; break; case 2: r = p, g = v, b = t; break; case 3: r = p, g = q, b = v; break; case 4: r = t, g = p, b = v; break; case 5: r = v, g = p, b = q; break; } return { r: Math.floor(r * 255), g: Math.floor(g * 255), b: Math.floor(b * 255) }; } /** * Convert HSV \<0, 1\> into hex color string. * * @param h - Hue. * @param s - Saturation. * @param v - Value. * @returns Hex color string. */ function HSVToHex(h, s, v) { var rgb = HSVToRGB(h, s, v); return RGBToHex(rgb.r, rgb.g, rgb.b); } /** * Convert hex color string into HSV \<0, 1\>. * * @param hex - Hex color string. * @returns HSV color object. */ function hexToHSV(hex) { var rgb = hexToRGB(hex); if (!rgb) { throw new TypeError("'".concat(hex, "' is not a valid color.")); } return RGBToHSV(rgb.r, rgb.g, rgb.b); } /** * Validate hex color string. * * @param hex - Unknown string that may contain a color. * @returns True if the string is valid, false otherwise. */ function isValidHex(hex) { var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex); return isOk; } /** * Validate RGB color string. * * @param rgb - Unknown string that may contain a color. * @returns True if the string is valid, false otherwise. */ function isValidRGB(rgb) { return rgbRE.test(rgb); } /** * Validate RGBA color string. * * @param rgba - Unknown string that may contain a color. * @returns True if the string is valid, false otherwise. */ function isValidRGBA(rgba) { return rgbaRE.test(rgba); } /** * This recursively redirects the prototype of JSON objects to the referenceObject. * This is used for default options. * * @param referenceObject - The original object. * @returns The Element if the referenceObject is an Element, or a new object inheriting from the referenceObject. */ function bridgeObject(referenceObject) { if (referenceObject === null || _typeof(referenceObject) !== "object") { return null; } if (referenceObject instanceof Element) { // Avoid bridging DOM objects return referenceObject; } var objectTo = _Object$create$1(referenceObject); for (var i in referenceObject) { if (Object.prototype.hasOwnProperty.call(referenceObject, i)) { if (_typeof(referenceObject[i]) == "object") { objectTo[i] = bridgeObject(referenceObject[i]); } } } return objectTo; } /** * This is used to set the options of subobjects in the options object. * * A requirement of these subobjects is that they have an 'enabled' element * which is optional for the user but mandatory for the program. * * The added value here of the merge is that option 'enabled' is set as required. * * @param mergeTarget - Either this.options or the options used for the groups. * @param options - Options. * @param option - Option key in the options argument. * @param globalOptions - Global options, passed in to determine value of option 'enabled'. */ function mergeOptions(mergeTarget, options, option) { var globalOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; // Local helpers var isPresent = function isPresent(obj) { return obj !== null && obj !== undefined; }; var isObject = function isObject(obj) { return obj !== null && _typeof(obj) === "object"; }; // https://stackoverflow.com/a/34491287/1223531 var isEmpty = function isEmpty(obj) { for (var x in obj) { if (Object.prototype.hasOwnProperty.call(obj, x)) { return false; } } return true; }; // Guards if (!isObject(mergeTarget)) { throw new Error("Parameter mergeTarget must be an object"); } if (!isObject(options)) { throw new Error("Parameter options must be an object"); } if (!isPresent(option)) { throw new Error("Parameter option must have a value"); } if (!isObject(globalOptions)) { throw new Error("Parameter globalOptions must be an object"); } // // Actual merge routine, separated from main logic // Only a single level of options is merged. Deeper levels are ref'd. This may actually be an issue. // var doMerge = function doMerge(target, options, option) { if (!isObject(target[option])) { target[option] = {}; } var src = options[option]; var dst = target[option]; for (var prop in src) { if (Object.prototype.hasOwnProperty.call(src, prop)) { dst[prop] = src[prop]; } } }; // Local initialization var srcOption = options[option]; var globalPassed = isObject(globalOptions) && !isEmpty(globalOptions); var globalOption = globalPassed ? globalOptions[option] : undefined; var globalEnabled = globalOption ? globalOption.enabled : undefined; ///////////////////////////////////////// // Main routine ///////////////////////////////////////// if (srcOption === undefined) { return; // Nothing to do } if (typeof srcOption === "boolean") { if (!isObject(mergeTarget[option])) { mergeTarget[option] = {}; } mergeTarget[option].enabled = srcOption; return; } if (srcOption === null && !isObject(mergeTarget[option])) { // If possible, explicit copy from globals if (isPresent(globalOption)) { mergeTarget[option] = _Object$create$1(globalOption); } else { return; // Nothing to do } } if (!isObject(srcOption)) { return; } // // Ensure that 'enabled' is properly set. It is required internally // Note that the value from options will always overwrite the existing value // var enabled = true; // default value if (srcOption.enabled !== undefined) { enabled = srcOption.enabled; } else { // Take from globals, if present if (globalEnabled !== undefined) { enabled = globalOption.enabled; } } doMerge(mergeTarget, options, option); mergeTarget[option].enabled = enabled; } /* * Easing Functions. * Only considering the t value for the range [0, 1] => [0, 1]. * * Inspiration: from http://gizma.com/easing/ * https://gist.github.com/gre/1650294 */ var easingFunctions = { /** * Provides no easing and no acceleration. * * @param t - Time. * @returns Value at time t. */ linear: function linear(t) { return t; }, /** * Accelerate from zero velocity. * * @param t - Time. * @returns Value at time t. */ easeInQuad: function easeInQuad(t) { return t * t; }, /** * Decelerate to zero velocity. * * @param t - Time. * @returns Value at time t. */ easeOutQuad: function easeOutQuad(t) { return t * (2 - t); }, /** * Accelerate until halfway, then decelerate. * * @param t - Time. * @returns Value at time t. */ easeInOutQuad: function easeInOutQuad(t) { return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; }, /** * Accelerate from zero velocity. * * @param t - Time. * @returns Value at time t. */ easeInCubic: function easeInCubic(t) { return t * t * t; }, /** * Decelerate to zero velocity. * * @param t - Time. * @returns Value at time t. */ easeOutCubic: function easeOutCubic(t) { return --t * t * t + 1; }, /** * Accelerate until halfway, then decelerate. * * @param t - Time. * @returns Value at time t. */ easeInOutCubic: function easeInOutCubic(t) { return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1; }, /** * Accelerate from zero velocity. * * @param t - Time. * @returns Value at time t. */ easeInQuart: function easeInQuart(t) { return t * t * t * t; }, /** * Decelerate to zero velocity. * * @param t - Time. * @returns Value at time t. */ easeOutQuart: function easeOutQuart(t) { return 1 - --t * t * t * t; }, /** * Accelerate until halfway, then decelerate. * * @param t - Time. * @returns Value at time t. */ easeInOutQuart: function easeInOutQuart(t) { return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t; }, /** * Accelerate from zero velocity. * * @param t - Time. * @returns Value at time t. */ easeInQuint: function easeInQuint(t) { return t * t * t * t * t; }, /** * Decelerate to zero velocity. * * @param t - Time. * @returns Value at time t. */ easeOutQuint: function easeOutQuint(t) { return 1 + --t * t * t * t * t; }, /** * Accelerate until halfway, then decelerate. * * @param t - Time. * @returns Value at time t. */ easeInOutQuint: function easeInOutQuint(t) { return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t; } }; // @TODO: This doesn't work properly. // It works only for single property objects, // otherwise it combines all of the types in a union. // export function topMost<K1 extends string, V1> ( // pile: Record<K1, undefined | V1>[], // accessors: K1 | [K1] // ): undefined | V1 // export function topMost<K1 extends string, K2 extends string, V1, V2> ( // pile: Record<K1, undefined | V1 | Record<K2, undefined | V2>>[], // accessors: [K1, K2] // ): undefined | V1 | V2 // export function topMost<K1 extends string, K2 extends string, K3 extends string, V1, V2, V3> ( // pile: Record<K1, undefined | V1 | Record<K2, undefined | V2 | Record<K3, undefined | V3>>>[], // accessors: [K1, K2, K3] // ): undefined | V1 | V2 | V3 /** * Get the top most property value from a pile of objects. * * @param pile - Array of objects, no required format. * @param accessors - Array of property names. * For example `object['foo']['bar']` → `['foo', 'bar']`. * @returns Value of the property with given accessors path from the first pile item where it's not undefined. */ function topMost(pile, accessors) { var candidate; if (!_Array$isArray(accessors)) { accessors = [accessors]; } var _iterator3 = _createForOfIteratorHelper$6(pile), _step3; try { for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { var member = _step3.value; if (member) { candidate = member[accessors[0]]; for (var i = 1; i < accessors.length; i++) { if (candidate) { candidate = candidate[accessors[i]]; } } if (typeof candidate !== "undefined") { break; } } } } catch (err) { _iterator3.e(err); } finally { _iterator3.f(); } return candidate; } var htmlColors = { black: "#000000", navy: "#000080", darkblue: "#00008B", mediumblue: "#0000CD", blue: "#0000FF", darkgreen: "#006400", green: "#008000", teal: "#008080", darkcyan: "#008B8B", deepskyblue: "#00BFFF", darkturquoise: "#00CED1", mediumspringgreen: "#00FA9A", lime: "#00FF00", springgreen: "#00FF7F", aqua: "#00FFFF", cyan: "#00FFFF", midnightblue: "#191970", dodgerblue: "#1E90FF", lightseagreen: "#20B2AA", forestgreen: "#228B22", seagreen: "#2E8B57", darkslategray: "#2F4F4F", limegreen: "#32CD32", mediumseagreen: "#3CB371", turquoise: "#40E0D0", royalblue: "#4169E1", steelblue: "#4682B4", darkslateblue: "#483D8B", mediumturquoise: "#48D1CC", indigo: "#4B0082", darkolivegreen: "#556B2F", cadetblue: "#5F9EA0", cornflowerblue: "#6495ED", mediumaquamarine: "#66CDAA", dimgray: "#696969", slateblue: "#6A5ACD", olivedrab: "#6B8E23", slategray: "#708090", lightslategray: "#778899", mediumslateblue: "#7B68EE", lawngreen: "#7CFC00", chartreuse: "#7FFF00", aquamarine: "#7FFFD4", maroon: "#800000", purple: "#800080", olive: "#808000", gray: "#808080", skyblue: "#87CEEB", lightskyblue: "#87CEFA", blueviolet: "#8A2BE2", darkred: "#8B0000", darkmagenta: "#8B008B", saddlebrown: "#8B4513", darkseagreen: "#8FBC8F", lightgreen: "#90EE90", mediumpurple: "#9370D8", darkviolet: "#9400D3", palegreen: "#98FB98", darkorchid: "#9932CC", yellowgreen: "#9ACD32", sienna: "#A0522D", brown: "#A52A2A", darkgray: "#A9A9A9", lightblue: "#ADD8E6", greenyellow: "#ADFF2F", paleturquoise: "#AFEEEE", lightsteelblue: "#B0C4DE", powderblue: "#B0E0E6", firebrick: "#B22222", darkgoldenrod: "#B8860B", mediumorchid: "#BA55D3", rosybrown: "#BC8F8F", darkkhaki: "#BDB76B", silver: "#C0C0C0", mediumvioletred: "#C71585", indianred: "#CD5C5C", peru: "#CD853F", chocolate: "#D2691E", tan: "#D2B48C", lightgrey: "#D3D3D3", palevioletred: "#D87093", thistle: "#D8BFD8", orchid: "#DA70D6", goldenrod: "#DAA520", crimson: "#DC143C", gainsboro: "#DCDCDC", plum: "#DDA0DD", burlywood: "#DEB887", lightcyan: "#E0FFFF", lavender: "#E6E6FA", darksalmon: "#E9967A", violet: "#EE82EE", palegoldenrod: "#EEE8AA", lightcoral: "#F08080", khaki: "#F0E68C", aliceblue: "#F0F8FF", honeydew: "#F0FFF0", azure: "#F0FFFF", sandybrown: "#F4A460", wheat: "#F5DEB3", beige: "#F5F5DC", whitesmoke: "#F5F5F5", mintcream: "#F5FFFA", ghostwhite: "#F8F8FF", salmon: "#FA8072", antiquewhite: "#FAEBD7", linen: "#FAF0E6", lightgoldenrodyellow: "#FAFAD2", oldlace: "#FDF5E6", red: "#FF0000", fuchsia: "#FF00FF", magenta: "#FF00FF", deeppink: "#FF1493", orangered: "#FF4500", tomato: "#FF6347", hotpink: "#FF69B4", coral: "#FF7F50", darkorange: "#FF8C00", lightsalmon: "#FFA07A", orange: "#FFA500", lightpink: "#FFB6C1", pink: "#FFC0CB", gold: "#FFD700", peachpuff: "#FFDAB9", navajowhite: "#FFDEAD", moccasin: "#FFE4B5", bisque: "#FFE4C4", mistyrose: "#FFE4E1", blanchedalmond: "#FFEBCD", papayawhip: "#FFEFD5", lavenderblush: "#FFF0F5", seashell: "#FFF5EE", cornsilk: "#FFF8DC", lemonchiffon: "#FFFACD", floralwhite: "#FFFAF0", snow: "#FFFAFA", yellow: "#FFFF00", lightyellow: "#FFFFE0", ivory: "#FFFFF0", white: "#FFFFFF" }; /** * @param {number} [pixelRatio=1] */ var ColorPicker$1 = /*#__PURE__*/function () { /** * @param {number} [pixelRatio=1] */ function ColorPicker$1() { var pixelRatio = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; _classCallCheck(this, ColorPicker$1); this.pixelRatio = pixelRatio; this.generated = false; this.centerCoordinates = { x: 289 / 2, y: 289 / 2 }; this.r = 289 * 0.49; this.color = { r: 255, g: 255, b: 255, a: 1.0 }; this.hueCircle = undefined; this.initialColor = { r: 255, g: 255, b: 255, a: 1.0 }; this.previousColor = undefined; this.applied = false; // bound by this.updateCallback = function () {}; this.closeCallback = function () {}; // create all DOM elements this._create(); } /** * this inserts the colorPicker into a div from the DOM * * @param {Element} container */ _createClass(ColorPicker$1, [{ key: "insertTo", value: function insertTo(container) { if (this.hammer !== undefined) { this.hammer.destroy(); this.hammer = undefined; } this.container = container; this.container.appendChild(this.frame); this._bindHammer(); this._setSize(); } /** * the callback is executed on apply and save. Bind it to the application * * @param {Function} callback */ }, { key: "setUpdateCallback", value: function setUpdateCallback(callback) { if (typeof callback === "function") { this.updateCallback = callback; } else { throw new Error("Function attempted to set as colorPicker update callback is not a function."); } } /** * the callback is executed on apply and save. Bind it to the application * * @param {Function} callback */ }, { key: "setCloseCallback", value: function setCloseCallback(callback) { if (typeof callback === "function") { this.closeCallback = callback; } else { throw new Error("Function attempted to set as colorPicker closing callback is not a function."); } } /** * * @param {string} color * @returns {string} * @private */ }, { key: "_isColorString", value: function _isColorString(color) { if (typeof color === "string") { return htmlColors[color]; } } /** * Set the color of the colorPicker * Supported formats: * 'red' --> HTML color string * '#ffffff' --> hex string * 'rgb(255,255,255)' --> rgb string * 'rgba(255,255,255,1.0)' --> rgba string * {r:255,g:255,b:255} --> rgb object * {r:255,g:255,b:255,a:1.0} --> rgba object * * @param {string | object} color * @param {boolean} [setInitial=true] */ }, { key: "setColor", value: function setColor(color) { var setInitial = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; if (color === "none") { return; } var rgba; // if a html color shorthand is used, convert to hex var htmlColor = this._isColorString(color); if (htmlColor !== undefined) { color = htmlColor; } // check format if (isString(color) === true) { if (isValidRGB(color) === true) { var rgbaArray = color.substr(4).substr(0, color.length - 5).split(","); rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: 1.0 }; } else if (isValidRGBA(color) === true) { var _rgbaArray = color.substr(5).substr(0, color.length - 6).split(","); rgba = { r: _rgbaArray[0], g: _rgbaArray[1], b: _rgbaArray[2], a: _rgbaArray[3] }; } else if (isValidHex(color) === true) { var rgbObj = hexToRGB(color); rgba = { r: rgbObj.r, g: rgbObj.g, b: rgbObj.b, a: 1.0 }; } } else { if (color instanceof Object) { if (color.r !== undefined && color.g !== undefined && color.b !== undefined) { var alpha = color.a !== undefined ? color.a : "1.0"; rgba = { r: color.r, g: color.g, b: color.b, a: alpha }; } } } // set color if (rgba === undefined) { throw new Error("Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: " + _JSON$stringify(color)); } else { this._setColor(rgba, setInitial); } } /** * this shows the color picker. * The hue circle is constructed once and stored. */ }, { key: "show", value: function show() { if (this.closeCallback !== undefined) { this.closeCallback(); this.closeCallback = undefined; } this.applied = false; this.frame.style.display = "block"; this._generateHueCircle(); } // ------------------------------------------ PRIVATE ----------------------------- // /** * Hide the picker. Is called by the cancel button. * Optional boolean to store the previous color for easy access later on. * * @param {boolean} [storePrevious=true] * @private */ }, { key: "_hide", value: function _hide() { var _this2 = this; var storePrevious = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; // store the previous color for next time; if (storePrevious === true) { this.previousColor = _Object$assign({}, this.color); } if (this.applied === true) { this.updateCallback(this.initialColor); } this.frame.style.display = "none"; // call the closing callback, restoring the onclick method. // this is in a setTimeout because it will trigger the show again before the click is done. _setTimeout(function () { if (_this2.closeCallback !== undefined) { _this2.closeCallback(); _this2.closeCallback = undefined; } }, 0); } /** * bound to the save button. Saves and hides. * * @private */ }, { key: "_save", value: function _save() { this.updateCallback(this.color); this.applied = false; this._hide(); } /** * Bound to apply button. Saves but does not close. Is undone by the cancel button. * * @private */ }, { key: "_apply", value: function _apply() { this.applied = true; this.updateCallback(this.color); this._updatePicker(this.color); } /** * load the color from the previous session. * * @private */ }, { key: "_loadLast", value: function _loadLast() { if (this.previousColor !== undefined) { this.setColor(this.previousColor, false); } else { alert("There is no last color to load..."); } } /** * set the color, place the picker * * @param {object} rgba * @param {boolean} [setInitial=true] * @private */ }, { key: "_setColor", value: function _setColor(rgba) { var setInitial = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; // store the initial color if (setInitial === true) { this.initialColor = _Object$assign({}, rgba); } this.color = rgba; var hsv = RGBToHSV(rgba.r, rgba.g, rgba.b); var angleConvert = 2 * Math.PI; var radius = this.r * hsv.s; var x = this.centerCoordinates.x + radius * Math.sin(angleConvert * hsv.h); var y = this.centerCoordinates.y + radius * Math.cos(angleConvert * hsv.h); this.colorPickerSelector.style.left = x - 0.5 * this.colorPickerSelector.clientWidth + "px"; this.colorPickerSelector.style.top = y - 0.5 * this.colorPickerSelector.clientHeight + "px"; this._updatePicker(rgba); } /** * bound to opacity control * * @param {number} value * @private */ }, { key: "_setOpacity", value: function _setOpacity(value) { this.color.a = value / 100; this._updatePicker(this.color); } /** * bound to brightness control * * @param {number} value * @private */ }, { key: "_setBrightness", value: function _setBrightness(value) { var hsv = RGBToHSV(this.color.r, this.color.g, this.color.b); hsv.v = value / 100; var rgba = HSVToRGB(hsv.h, hsv.s, hsv.v); rgba["a"] = this.color.a; this.color = rgba; this._updatePicker(); } /** * update the color picker. A black circle overlays the hue circle to mimic the brightness decreasing. * * @param {object} rgba * @private */ }, { key: "_updatePicker", value: function _updatePicker() { var rgba = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.color; var hsv = RGBToHSV(rgba.r, rgba.g, rgba.b); var ctx = this.colorPickerCanvas.getContext("2d"); if (this.pixelRation === undefined) { this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); } ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); // clear the canvas var w = this.colorPickerCanvas.clientWidth; var h = this.colorPickerCanvas.clientHeight; ctx.clearRect(0, 0, w, h); ctx.putImageData(this.hueCircle, 0, 0); ctx.fillStyle = "rgba(0,0,0," + (1 - hsv.v) + ")"; ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); _fillInstanceProperty(ctx).call(ctx); this.brightnessRange.value = 100 * hsv.v; this.opacityRange.value = 100 * rgba.a; this.initialColorDiv.style.backgroundColor = "rgba(" + this.initialColor.r + "," + this.initialColor.g + "," + this.initialColor.b + "," + this.initialColor.a + ")"; this.newColorDiv.style.backgroundColor = "rgba(" + this.color.r + "," + this.color.g + "," + this.color.b + "," + this.color.a + ")"; } /** * used by create to set the size of the canvas. * * @private */ }, { key: "_setSize", value: function _setSize() { this.colorPickerCanvas.style.width = "100%"; this.colorPickerCanvas.style.height = "100%"; this.colorPickerCanvas.width = 289 * this.pixelRatio; this.colorPickerCanvas.height = 289 * this.pixelRatio; } /** * create all dom elements * TODO: cleanup, lots of similar dom elements * * @private */ }, { key: "_create", value: function _create() { var _context16, _context17, _context18, _context19; this.frame = document.createElement("div"); this.frame.className = "vis-color-picker"; this.colorPickerDiv = document.createElement("div"); this.colorPickerSelector = document.createElement("div"); this.colorPickerSelector.className = "vis-selector"; this.colorPickerDiv.appendChild(this.colorPickerSelector); this.colorPickerCanvas = document.createElement("canvas"); this.colorPickerDiv.appendChild(this.colorPickerCanvas); if (!this.colorPickerCanvas.getContext) { var noCanvas = document.createElement("DIV"); noCanvas.style.color = "red"; noCanvas.style.fontWeight = "bold"; noCanvas.style.padding = "10px"; noCanvas.innerText = "Error: your browser does not support HTML canvas"; this.colorPickerCanvas.appendChild(noCanvas); } else { var ctx = this.colorPickerCanvas.getContext("2d"); this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); this.colorPickerCanvas.getContext("2d").setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); } this.colorPickerDiv.className = "vis-color"; this.opacityDiv = document.createElement("div"); this.opacityDiv.className = "vis-opacity"; this.brightnessDiv = document.createElement("div"); this.brightnessDiv.className = "vis-brightness"; this.arrowDiv = document.createElement("div"); this.arrowDiv.className = "vis-arrow"; this.opacityRange = document.createElement("input"); try { this.opacityRange.type = "range"; // Not supported on IE9 this.opacityRange.min = "0"; this.opacityRange.max = "100"; } catch (err) { // TODO: Add some error handling. } this.opacityRange.value = "100"; this.opacityRange.className = "vis-range"; this.brightnessRange = document.createElement("input"); try { this.brightnessRange.type = "range"; // Not supported on IE9 this.brightnessRange.min = "0"; this.brightnessRange.max = "100"; } catch (err) { // TODO: Add some error handling. } this.brightnessRange.value = "100"; this.brightnessRange.className = "vis-range"; this.opacityDiv.appendChild(this.opacityRange); this.brightnessDiv.appendChild(this.brightnessRange); var me = this; this.opacityRange.onchange = function () { me._setOpacity(this.value); }; this.opacityRange.oninput = function () { me._setOpacity(this.value); }; this.brightnessRange.onchange = function () { me._setBrightness(this.value); }; this.brightnessRange.oninput = function () { me._setBrightness(this.value); }; this.brightnessLabel = document.createElement("div"); this.brightnessLabel.className = "vis-label vis-brightness"; this.brightnessLabel.innerText = "brightness:"; this.opacityLabel = document.createElement("div"); this.opacityLabel.className = "vis-label vis-opacity"; this.opacityLabel.innerText = "opacity:"; this.newColorDiv = document.createElement("div"); this.newColorDiv.className = "vis-new-color"; this.newColorDiv.innerText = "new"; this.initialColorDiv = document.createElement("div"); this.initialColorDiv.className = "vis-initial-color"; this.initialColorDiv.innerText = "initial"; this.cancelButton = document.createElement("div"); this.cancelButton.className = "vis-button vis-cancel"; this.cancelButton.innerText = "cancel"; this.cancelButton.onclick = _bindInstanceProperty$1(_context16 = this._hide).call(_context16, this, false); this.applyButton = document.createElement("div"); this.applyButton.className = "vis-button vis-apply"; this.applyButton.innerText = "apply"; this.applyButton.onclick = _bindInstanceProperty$1(_context17 = this._apply).call(_context17, this); this.saveButton = document.createElement("div"); this.saveButton.className = "vis-button vis-save"; this.saveButton.innerText = "save"; this.saveButton.onclick = _bindInstanceProperty$1(_context18 = this._save).call(_context18, this); this.loadButton = document.createElement("div"); this.loadButton.className = "vis-button vis-load"; this.loadButton.innerText = "load last"; this.loadButton.onclick = _bindInstanceProperty$1(_context19 = this._loadLast).call(_context19, this); this.frame.appendChild(this.colorPickerDiv); this.frame.appendChild(this.arrowDiv); this.frame.appendChild(this.brightnessLabel); this.frame.appendChild(this.brightnessDiv); this.frame.appendChild(this.opacityLabel); this.frame.appendChild(this.opacityDiv); this.frame.appendChild(this.newColorDiv); this.frame.appendChild(this.initialColorDiv); this.frame.appendChild(this.cancelButton); this.frame.appendChild(this.applyButton); this.frame.appendChild(this.saveButton); this.frame.appendChild(this.loadButton); } /** * bind hammer to the color picker * * @private */ }, { key: "_bindHammer", value: function _bindHammer() { var _this3 = this; this.drag = {}; this.pinch = {}; this.hammer = new Hammer$1(this.colorPickerCanvas); this.hammer.get("pinch").set({ enable: true }); this.hammer.on("hammer.input", function (event) { if (event.isFirst) { _this3._moveSelector(event); } }); this.hammer.on("tap", function (event) { _this3._moveSelector(event); }); this.hammer.on("panstart", function (event) { _this3._moveSelector(event); }); this.hammer.on("panmove", function (event) { _this3._moveSelector(event); }); this.hammer.on("panend", function (event) { _this3._moveSelector(event); }); } /** * generate the hue circle. This is relatively heavy (200ms) and is done only once on the first time it is shown. * * @private */ }, { key: "_generateHueCircle", value: function _generateHueCircle() { if (this.generated === false) { var ctx = this.colorPickerCanvas.getContext("2d"); if (this.pixelRation === undefined) { this.pixelRatio = (window.devicePixelRatio || 1) / (ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1); } ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); // clear the canvas var w = this.colorPickerCanvas.clientWidth; var h = this.colorPickerCanvas.clientHeight; ctx.clearRect(0, 0, w, h); // draw hue circle var x, y, hue, sat; this.centerCoordinates = { x: w * 0.5, y: h * 0.5 }; this.r = 0.49 * w; var angleConvert = 2 * Math.PI / 360; var hfac = 1 / 360; var sfac = 1 / this.r; var rgb; for (hue = 0; hue < 360; hue++) { for (sat = 0; sat < this.r; sat++) { x = this.centerCoordinates.x + sat * Math.sin(angleConvert * hue); y = this.centerCoordinates.y + sat * Math.cos(angleConvert * hue); rgb = HSVToRGB(hue * hfac, sat * sfac, 1); ctx.fillStyle = "rgb(" + rgb.r + "," + rgb.g + "," + rgb.b + ")"; ctx.fillRect(x - 0.5, y - 0.5, 2, 2); } } ctx.strokeStyle = "rgba(0,0,0,1)"; ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r); ctx.stroke(); this.hueCircle = ctx.getImageData(0, 0, w, h); } this.generated = true; } /** * move the selector. This is called by hammer functions. * * @param {Event} event The event * @private */ }, { key: "_moveSelector", value: function _moveSelector(event) { var rect = this.colorPickerDiv.getBoundingClientRect(); var left = event.center.x - rect.left; var top = event.center.y - rect.top; var centerY = 0.5 * this.colorPickerDiv.clientHeight; var centerX = 0.5 * this.colorPickerDiv.clientWidth; var x = left - centerX; var y = top - centerY; var angle = Math.atan2(x, y); var radius = 0.98 * Math.min(Math.sqrt(x * x + y * y), centerX); var newTop = Math.cos(angle) * radius + centerY; var newLeft = Math.sin(angle) * radius + centerX; this.colorPickerSelector.style.top = newTop - 0.5 * this.colorPickerSelector.clientHeight + "px"; this.colorPickerSelector.style.left = newLeft - 0.5 * this.colorPickerSelector.clientWidth + "px"; // set color var h = angle / (2 * Math.PI); h = h < 0 ? h + 1 : h; var s = radius / this.r; var hsv = RGBToHSV(this.color.r, this.color.g, this.color.b); hsv.h = h; hsv.s = s; var rgba = HSVToRGB(hsv.h, hsv.s, hsv.v); rgba["a"] = this.color.a; this.color = rgba; // update previews this.initialColorDiv.style.backgroundColor = "rgba(" + this.initialColor.r + "," + this.initialColor.g + "," + this.initialColor.b + "," + this.initialColor.a + ")"; this.newColorDiv.style.backgroundColor = "rgba(" + this.color.r + "," + this.color.g + "," + this.color.b + "," + this.color.a + ")"; } }]); return ColorPicker$1; }(); /** * Wrap given text (last argument) in HTML elements (all preceding arguments). * * @param {...any} rest - List of tag names followed by inner text. * @returns An element or a text node. */ function wrapInTag() { for (var _len5 = arguments.length, rest = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { rest[_key5] = arguments[_key5]; } if (rest.length < 1) { throw new TypeError("Invalid arguments."); } else if (rest.length === 1) { return document.createTextNode(rest[0]); } else { var element = document.createElement(rest[0]); element.appendChild(wrapInTag.apply(void 0, _toConsumableArray(_sliceInstanceProperty(rest).call(rest, 1)))); return element; } } /** * The way this works is for all properties of this.possible options, you can supply the property name in any form to list the options. * Boolean options are recognised as Boolean * Number options should be written as array: [default value, min value, max value, stepsize] * Colors should be written as array: ['color', '#ffffff'] * Strings with should be written as array: [option1, option2, option3, ..] * * The options are matched with their counterparts in each of the modules and the values used in the configuration are */ var Configurator$1 = /*#__PURE__*/function () { /** * @param {object} parentModule | the location where parentModule.setOptions() can be called * @param {object} defaultContainer | the default container of the module * @param {object} configureOptions | the fully configured and predefined options set found in allOptions.js * @param {number} pixelRatio | canvas pixel ratio * @param {Function} hideOption | custom logic to dynamically hide options */ function Configurator$1(parentModule, defaultContainer, configureOptions) { var pixelRatio = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1; var hideOption = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : function () { return false; }; _classCallCheck(this, Configurator$1); this.parent = parentModule; this.changedOptions = []; this.container = defaultContainer; this.allowCreation = false; this.hideOption = hideOption; this.options = {}; this.initialized = false; this.popupCounter = 0; this.defaultOptions = { enabled: false, filter: true, container: undefined, showButton: true }; _Object$assign(this.options, this.defaultOptions); this.configureOptions = configureOptions; this.moduleOptions = {}; this.domElements = []; this.popupDiv = {}; this.popupLimit = 5; this.popupHistory = {}; this.colorPicker = new ColorPicker$1(pixelRatio); this.wrapper = undefined; } /** * refresh all options. * Because all modules parse their options by themselves, we just use their options. We copy them here. * * @param {object} options */ _createClass(Configurator$1, [{ key: "setOptions", value: function setOptions(options) { if (options !== undefined) { // reset the popup history because the indices may have been changed. this.popupHistory = {}; this._removePopup(); var enabled = true; if (typeof options === "string") { this.options.filter = options; } else if (_Array$isArray(options)) { this.options.filter = options.join(); } else if (_typeof(options) === "object") { if (options == null) { throw new TypeError("options cannot be null"); } if (options.container !== undefined) { this.options.container = options.container; } if (_filterInstanceProperty(options) !== undefined) { this.options.filter = _filterInstanceProperty(options); } if (options.showButton !== undefined) { this.options.showButton = options.showButton; } if (options.enabled !== undefined) { enabled = options.enabled; } } else if (typeof options === "boolean") { this.options.filter = true; enabled = options; } else if (typeof options === "function") { this.options.filter = options; enabled = true; } if (_filterInstanceProperty(this.options) === false) { enabled = false; } this.options.enabled = enabled; } this._clean(); } /** * * @param {object} moduleOptions */ }, { key: "setModuleOptions", value: function setModuleOptions(moduleOptions) { this.moduleOptions = moduleOptions; if (this.options.enabled === true) { this._clean(); if (this.options.container !== undefined) { this.container = this.options.container; } this._create(); } } /** * Create all DOM elements * * @private */ }, { key: "_create", value: function _create() { this._clean(); this.changedOptions = []; var filter = _filterInstanceProperty(this.options); var counter = 0; var show = false; for (var _option in this.configureOptions) { if (Object.prototype.hasOwnProperty.call(this.configureOptions, _option)) { this.allowCreation = false; show = false; if (typeof filter === "function") { show = filter(_option, []); show = show || this._handleObject(this.configureOptions[_option], [_option], true); } else if (filter === true || _indexOfInstanceProperty(filter).call(filter, _option) !== -1) { show = true; } if (show !== false) { this.allowCreation = true; // linebreak between categories if (counter > 0) { this._makeItem([]); } // a header for the category this._makeHeader(_option); // get the sub options this._handleObject(this.configureOptions[_option], [_option]); } counter++; } } this._makeButton(); this._push(); //~ this.colorPicker.insertTo(this.container); } /** * draw all DOM elements on the screen * * @private */ }, { key: "_push", value: function _push() { this.wrapper = document.createElement("div"); this.wrapper.className = "vis-configuration-wrapper"; this.container.appendChild(this.wrapper); for (var i = 0; i < this.domElements.length; i++) { this.wrapper.appendChild(this.domElements[i]); } this._showPopupIfNeeded(); } /** * delete all DOM elements * * @private */ }, { key: "_clean", value: function _clean() { for (var i = 0; i < this.domElements.length; i++) { this.wrapper.removeChild(this.domElements[i]); } if (this.wrapper !== undefined) { this.container.removeChild(this.wrapper); this.wrapper = undefined; } this.domElements = []; this._removePopup(); } /** * get the value from the actualOptions if it exists * * @param {Array} path | where to look for the actual option * @returns {*} * @private */ }, { key: "_getValue", value: function _getValue(path) { var base = this.moduleOptions; for (var i = 0; i < path.length; i++) { if (base[path[i]] !== undefined) { base = base[path[i]]; } else { base = undefined; break; } } return base; } /** * all option elements are wrapped in an item * * @param {Array} path | where to look for the actual option * @param {Array.<Element>} domElements * @returns {number} * @private */ }, { key: "_makeItem", value: function _makeItem(path) { if (this.allowCreation === true) { var item = document.createElement("div"); item.className = "vis-configuration vis-config-item vis-config-s" + path.length; for (var _len6 = arguments.length, domElements = new Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) { domElements[_key6 - 1] = arguments[_key6]; } _forEachInstanceProperty(domElements).call(domElements, function (element) { item.appendChild(element); }); this.domElements.push(item); return this.domElements.length; } return 0; } /** * header for major subjects * * @param {string} name * @private */ }, { key: "_makeHeader", value: function _makeHeader(name) { var div = document.createElement("div"); div.className = "vis-configuration vis-config-header"; div.innerText = name; this._makeItem([], div); } /** * make a label, if it is an object label, it gets different styling. * * @param {string} name * @param {Array} path | where to look for the actual option * @param {string} objectLabel * @returns {HTMLElement} * @private */ }, { key: "_makeLabel", value: function _makeLabel(name, path) { var objectLabel = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var div = document.createElement("div"); div.className = "vis-configuration vis-config-label vis-config-s" + path.length; if (objectLabel === true) { while (div.firstChild) { div.removeChild(div.firstChild); } div.appendChild(wrapInTag("i", "b", name)); } else { div.innerText = name + ":"; } return div; } /** * make a dropdown list for multiple possible string optoins * * @param {Array.<number>} arr * @param {number} value * @param {Array} path | where to look for the actual option * @private */ }, { key: "_makeDropdown", value: function _makeDropdown(arr, value, path) { var select = document.createElement("select"); select.className = "vis-configuration vis-config-select"; var selectedValue = 0; if (value !== undefined) { if (_indexOfInstanceProperty(arr).call(arr, value) !== -1) { selectedValue = _indexOfInstanceProperty(arr).call(arr, value); } } for (var i = 0; i < arr.length; i++) { var _option2 = document.createElement("option"); _option2.value = arr[i]; if (i === selectedValue) { _option2.selected = "selected"; } _option2.innerText = arr[i]; select.appendChild(_option2); } var me = this; select.onchange = function () { me._update(this.value, path); }; var label = this._makeLabel(path[path.length - 1], path); this._makeItem(path, label, select); } /** * make a range object for numeric options * * @param {Array.<number>} arr * @param {number} value * @param {Array} path | where to look for the actual option * @private */ }, { key: "_makeRange", value: function _makeRange(arr, value, path) { var defaultValue = arr[0]; var min = arr[1]; var max = arr[2]; var step = arr[3]; var range = document.createElement("input"); range.className = "vis-configuration vis-config-range"; try { range.type = "range"; // not supported on IE9 range.min = min; range.max = max; } catch (err) { // TODO: Add some error handling. } range.step = step; // set up the popup settings in case they are needed. var popupString = ""; var popupValue = 0; if (value !== undefined) { var factor = 1.2; if (value < 0 && value * factor < min) { range.min = Math.ceil(value * factor); popupValue = range.min; popupString = "range increased"; } else if (value / factor < min) { range.min = Math.ceil(value / factor); popupValue = range.min; popupString = "range increased"; } if (value * factor > max && max !== 1) { range.max = Math.ceil(value * factor); popupValue = range.max; popupString = "range increased"; } range.value = value; } else { range.value = defaultValue; } var input = document.createElement("input"); input.className = "vis-configuration vis-config-rangeinput"; input.value = range.value; var me = this; range.onchange = function () { input.value = this.value; me._update(Number(this.value), path); }; range.oninput = function () { input.value = this.value; }; var label = this._makeLabel(path[path.length - 1], path); var itemIndex = this._makeItem(path, label, range, input); // if a popup is needed AND it has not been shown for this value, show it. if (popupString !== "" && this.popupHistory[itemIndex] !== popupValue) { this.popupHistory[itemIndex] = popupValue; this._setupPopup(popupString, itemIndex); } } /** * make a button object * * @private */ }, { key: "_makeButton", value: function _makeButton() { var _this4 = this; if (this.options.showButton === true) { var generateButton = document.createElement("div"); generateButton.className = "vis-configuration vis-config-button"; generateButton.innerText = "generate options"; generateButton.onclick = function () { _this4._printOptions(); }; generateButton.onmouseover = function () { generateButton.className = "vis-configuration vis-config-button hover"; }; generateButton.onmouseout = function () { generateButton.className = "vis-configuration vis-config-button"; }; this.optionsContainer = document.createElement("div"); this.optionsContainer.className = "vis-configuration vis-config-option-container"; this.domElements.push(this.optionsContainer); this.domElements.push(generateButton); } } /** * prepare the popup * * @param {string} string * @param {number} index * @private */ }, { key: "_setupPopup", value: function _setupPopup(string, index) { var _this5 = this; if (this.initialized === true && this.allowCreation === true && this.popupCounter < this.popupLimit) { var div = document.createElement("div"); div.id = "vis-configuration-popup"; div.className = "vis-configuration-popup"; div.innerText = string; div.onclick = function () { _this5._removePopup(); }; this.popupCounter += 1; this.popupDiv = { html: div, index: index }; } } /** * remove the popup from the dom * * @private */ }, { key: "_removePopup", value: function _removePopup() { if (this.popupDiv.html !== undefined) { this.popupDiv.html.parentNode.removeChild(this.popupDiv.html); clearTimeout(this.popupDiv.hideTimeout); clearTimeout(this.popupDiv.deleteTimeout); this.popupDiv = {}; } } /** * Show the popup if it is needed. * * @private */ }, { key: "_showPopupIfNeeded", value: function _showPopupIfNeeded() { var _this6 = this; if (this.popupDiv.html !== undefined) { var correspondingElement = this.domElements[this.popupDiv.index]; var rect = correspondingElement.getBoundingClientRect(); this.popupDiv.html.style.left = rect.left + "px"; this.popupDiv.html.style.top = rect.top - 30 + "px"; // 30 is the height; document.body.appendChild(this.popupDiv.html); this.popupDiv.hideTimeout = _setTimeout(function () { _this6.popupDiv.html.style.opacity = 0; }, 1500); this.popupDiv.deleteTimeout = _setTimeout(function () { _this6._removePopup(); }, 1800); } } /** * make a checkbox for boolean options. * * @param {number} defaultValue * @param {number} value * @param {Array} path | where to look for the actual option * @private */ }, { key: "_makeCheckbox", value: function _makeCheckbox(defaultValue, value, path) { var checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.className = "vis-configuration vis-config-checkbox"; checkbox.checked = defaultValue; if (value !== undefined) { checkbox.checked = value; if (value !== defaultValue) { if (_typeof(defaultValue) === "object") { if (value !== defaultValue.enabled) { this.changedOptions.push({ path: path, value: value }); } } else { this.changedOptions.push({ path: path, value: value }); } } } var me = this; checkbox.onchange = function () { me._update(this.checked, path); }; var label = this._makeLabel(path[path.length - 1], path); this._makeItem(path, label, checkbox); } /** * make a text input field for string options. * * @param {number} defaultValue * @param {number} value * @param {Array} path | where to look for the actual option * @private */ }, { key: "_makeTextInput", value: function _makeTextInput(defaultValue, value, path) { var checkbox = document.createElement("input"); checkbox.type = "text"; checkbox.className = "vis-configuration vis-config-text"; checkbox.value = value; if (value !== defaultValue) { this.changedOptions.push({ path: path, value: value }); } var me = this; checkbox.onchange = function () { me._update(this.value, path); }; var label = this._makeLabel(path[path.length - 1], path); this._makeItem(path, label, checkbox); } /** * make a color field with a color picker for color fields * * @param {Array.<number>} arr * @param {number} value * @param {Array} path | where to look for the actual option * @private */ }, { key: "_makeColorField", value: function _makeColorField(arr, value, path) { var _this7 = this; var defaultColor = arr[1]; var div = document.createElement("div"); value = value === undefined ? defaultColor : value; if (value !== "none") { div.className = "vis-configuration vis-config-colorBlock"; div.style.backgroundColor = value; } else { div.className = "vis-configuration vis-config-colorBlock none"; } value = value === undefined ? defaultColor : value; div.onclick = function () { _this7._showColorPicker(value, div, path); }; var label = this._makeLabel(path[path.length - 1], path); this._makeItem(path, label, div); } /** * used by the color buttons to call the color picker. * * @param {number} value * @param {HTMLElement} div * @param {Array} path | where to look for the actual option * @private */ }, { key: "_showColorPicker", value: function _showColorPicker(value, div, path) { var _this8 = this; // clear the callback from this div div.onclick = function () {}; this.colorPicker.insertTo(div); this.colorPicker.show(); this.colorPicker.setColor(value); this.colorPicker.setUpdateCallback(function (color) { var colorString = "rgba(" + color.r + "," + color.g + "," + color.b + "," + color.a + ")"; div.style.backgroundColor = colorString; _this8._update(colorString, path); }); // on close of the colorpicker, restore the callback. this.colorPicker.setCloseCallback(function () { div.onclick = function () { _this8._showColorPicker(value, div, path); }; }); } /** * parse an object and draw the correct items * * @param {object} obj * @param {Array} [path=[]] | where to look for the actual option * @param {boolean} [checkOnly=false] * @returns {boolean} * @private */ }, { key: "_handleObject", value: function _handleObject(obj) { var path = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; var checkOnly = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var show = false; var filter = _filterInstanceProperty(this.options); var visibleInSet = false; for (var subObj in obj) { if (Object.prototype.hasOwnProperty.call(obj, subObj)) { show = true; var item = obj[subObj]; var newPath = copyAndExtendArray(path, subObj); if (typeof filter === "function") { show = filter(subObj, path); // if needed we must go deeper into the object. if (show === false) { if (!_Array$isArray(item) && typeof item !== "string" && typeof item !== "boolean" && item instanceof Object) { this.allowCreation = false; show = this._handleObject(item, newPath, true); this.allowCreation = checkOnly === false; } } } if (show !== false) { visibleInSet = true; var value = this._getValue(newPath); if (_Array$isArray(item)) { this._handleArray(item, value, newPath); } else if (typeof item === "string") { this._makeTextInput(item, value, newPath); } else if (typeof item === "boolean") { this._makeCheckbox(item, value, newPath); } else if (item instanceof Object) { // skip the options that are not enabled if (!this.hideOption(path, subObj, this.moduleOptions)) { // initially collapse options with an disabled enabled option. if (item.enabled !== undefined) { var enabledPath = copyAndExtendArray(newPath, "enabled"); var enabledValue = this._getValue(enabledPath); if (enabledValue === true) { var label = this._makeLabel(subObj, newPath, true); this._makeItem(newPath, label); visibleInSet = this._handleObject(item, newPath) || visibleInSet; } else { this._makeCheckbox(item, enabledValue, newPath); } } else { var _label = this._makeLabel(subObj, newPath, true); this._makeItem(newPath, _label); visibleInSet = this._handleObject(item, newPath) || visibleInSet; } } } else { console.error("dont know how to handle", item, subObj, newPath); } } } } return visibleInSet; } /** * handle the array type of option * * @param {Array.<number>} arr * @param {number} value * @param {Array} path | where to look for the actual option * @private */ }, { key: "_handleArray", value: function _handleArray(arr, value, path) { if (typeof arr[0] === "string" && arr[0] === "color") { this._makeColorField(arr, value, path); if (arr[1] !== value) { this.changedOptions.push({ path: path, value: value }); } } else if (typeof arr[0] === "string") { this._makeDropdown(arr, value, path); if (arr[0] !== value) { this.changedOptions.push({ path: path, value: value }); } } else if (typeof arr[0] === "number") { this._makeRange(arr, value, path); if (arr[0] !== value) { this.changedOptions.push({ path: path, value: Number(value) }); } } } /** * called to update the network with the new settings. * * @param {number} value * @param {Array} path | where to look for the actual option * @private */ }, { key: "_update", value: function _update(value, path) { var options = this._constructOptions(value, path); if (this.parent.body && this.parent.body.emitter && this.parent.body.emitter.emit) { this.parent.body.emitter.emit("configChange", options); } this.initialized = true; this.parent.setOptions(options); } /** * * @param {string | boolean} value * @param {Array.<string>} path * @param {{}} optionsObj * @returns {{}} * @private */ }, { key: "_constructOptions", value: function _constructOptions(value, path) { var optionsObj = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var pointer = optionsObj; // when dropdown boxes can be string or boolean, we typecast it into correct types value = value === "true" ? true : value; value = value === "false" ? false : value; for (var i = 0; i < path.length; i++) { if (path[i] !== "global") { if (pointer[path[i]] === undefined) { pointer[path[i]] = {}; } if (i !== path.length - 1) { pointer = pointer[path[i]]; } else { pointer[path[i]] = value; } } } return optionsObj; } /** * @private */ }, { key: "_printOptions", value: function _printOptions() { var options = this.getOptions(); while (this.optionsContainer.firstChild) { this.optionsContainer.removeChild(this.optionsContainer.firstChild); } this.optionsContainer.appendChild(wrapInTag("pre", "const options = " + _JSON$stringify(options, null, 2))); } /** * * @returns {{}} options */ }, { key: "getOptions", value: function getOptions() { var options = {}; for (var i = 0; i < this.changedOptions.length; i++) { this._constructOptions(this.changedOptions[i].value, this.changedOptions[i].path, options); } return options; } }]); return Configurator$1; }(); /** * Popup is a class to create a popup window with some text */ var Popup$1 = /*#__PURE__*/function () { /** * @param {Element} container The container object. * @param {string} overflowMethod How the popup should act to overflowing ('flip' or 'cap') */ function Popup$1(container, overflowMethod) { _classCallCheck(this, Popup$1); this.container = container; this.overflowMethod = overflowMethod || "cap"; this.x = 0; this.y = 0; this.padding = 5; this.hidden = false; // create the frame this.frame = document.createElement("div"); this.frame.className = "vis-tooltip"; this.container.appendChild(this.frame); } /** * @param {number} x Horizontal position of the popup window * @param {number} y Vertical position of the popup window */ _createClass(Popup$1, [{ key: "setPosition", value: function setPosition(x, y) { this.x = _parseInt(x); this.y = _parseInt(y); } /** * Set the content for the popup window. This can be HTML code or text. * * @param {string | Element} content */ }, { key: "setText", value: function setText(content) { if (content instanceof Element) { while (this.frame.firstChild) { this.frame.removeChild(this.frame.firstChild); } this.frame.appendChild(content); } else { // String containing literal text, element has to be used for HTML due to // XSS risks associated with innerHTML (i.e. prevent XSS by accident). this.frame.innerText = content; } } /** * Show the popup window * * @param {boolean} [doShow] Show or hide the window */ }, { key: "show", value: function show(doShow) { if (doShow === undefined) { doShow = true; } if (doShow === true) { var height = this.frame.clientHeight; var width = this.frame.clientWidth; var maxHeight = this.frame.parentNode.clientHeight; var maxWidth = this.frame.parentNode.clientWidth; var left = 0, top = 0; if (this.overflowMethod == "flip") { var isLeft = false, isTop = true; // Where around the position it's located if (this.y - height < this.padding) { isTop = false; } if (this.x + width > maxWidth - this.padding) { isLeft = true; } if (isLeft) { left = this.x - width; } else { left = this.x; } if (isTop) { top = this.y - height; } else { top = this.y; } } else { top = this.y - height; if (top + height + this.padding > maxHeight) { top = maxHeight - height - this.padding; } if (top < this.padding) { top = this.padding; } left = this.x; if (left + width + this.padding > maxWidth) { left = maxWidth - width - this.padding; } if (left < this.padding) { left = this.padding; } } this.frame.style.left = left + "px"; this.frame.style.top = top + "px"; this.frame.style.visibility = "visible"; this.hidden = false; } else { this.hide(); } } /** * Hide the popup window */ }, { key: "hide", value: function hide() { this.hidden = true; this.frame.style.left = "0"; this.frame.style.top = "0"; this.frame.style.visibility = "hidden"; } /** * Remove the popup window */ }, { key: "destroy", value: function destroy() { this.frame.parentNode.removeChild(this.frame); // Remove element from DOM } }]); return Popup$1; }(); var errorFound = false; var allOptions$1; var VALIDATOR_PRINT_STYLE$1 = "background: #FFeeee; color: #dd0000"; /** * Used to validate options. */ var Validator$1 = /*#__PURE__*/function () { function Validator$1() { _classCallCheck(this, Validator$1); } _createClass(Validator$1, null, [{ key: "validate", value: /** * Main function to be called * * @param {object} options * @param {object} referenceOptions * @param {object} subObject * @returns {boolean} * @static */ function validate(options, referenceOptions, subObject) { errorFound = false; allOptions$1 = referenceOptions; var usedOptions = referenceOptions; if (subObject !== undefined) { usedOptions = referenceOptions[subObject]; } Validator$1.parse(options, usedOptions, []); return errorFound; } /** * Will traverse an object recursively and check every value * * @param {object} options * @param {object} referenceOptions * @param {Array} path | where to look for the actual option * @static */ }, { key: "parse", value: function parse(options, referenceOptions, path) { for (var _option3 in options) { if (Object.prototype.hasOwnProperty.call(options, _option3)) { Validator$1.check(_option3, options, referenceOptions, path); } } } /** * Check every value. If the value is an object, call the parse function on that object. * * @param {string} option * @param {object} options * @param {object} referenceOptions * @param {Array} path | where to look for the actual option * @static */ }, { key: "check", value: function check(option, options, referenceOptions, path) { if (referenceOptions[option] === undefined && referenceOptions.__any__ === undefined) { Validator$1.getSuggestion(option, referenceOptions, path); return; } var referenceOption = option; var is_object = true; if (referenceOptions[option] === undefined && referenceOptions.__any__ !== undefined) { // NOTE: This only triggers if the __any__ is in the top level of the options object. // THAT'S A REALLY BAD PLACE TO ALLOW IT!!!! // TODO: Examine if needed, remove if possible // __any__ is a wildcard. Any value is accepted and will be further analysed by reference. referenceOption = "__any__"; // if the any-subgroup is not a predefined object in the configurator, // we do not look deeper into the object. is_object = Validator$1.getType(options[option]) === "object"; } var refOptionObj = referenceOptions[referenceOption]; if (is_object && refOptionObj.__type__ !== undefined) { refOptionObj = refOptionObj.__type__; } Validator$1.checkFields(option, options, referenceOptions, referenceOption, refOptionObj, path); } /** * * @param {string} option | the option property * @param {object} options | The supplied options object * @param {object} referenceOptions | The reference options containing all options and their allowed formats * @param {string} referenceOption | Usually this is the same as option, except when handling an __any__ tag. * @param {string} refOptionObj | This is the type object from the reference options * @param {Array} path | where in the object is the option * @static */ }, { key: "checkFields", value: function checkFields(option, options, referenceOptions, referenceOption, refOptionObj, path) { var log = function log(message) { console.error("%c" + message + Validator$1.printLocation(path, option), VALIDATOR_PRINT_STYLE$1); }; var optionType = Validator$1.getType(options[option]); var refOptionType = refOptionObj[optionType]; if (refOptionType !== undefined) { // if the type is correct, we check if it is supposed to be one of a few select values if (Validator$1.getType(refOptionType) === "array" && _indexOfInstanceProperty(refOptionType).call(refOptionType, options[option]) === -1) { log('Invalid option detected in "' + option + '".' + " Allowed values are:" + Validator$1.print(refOptionType) + ' not "' + options[option] + '". '); errorFound = true; } else if (optionType === "object" && referenceOption !== "__any__") { path = copyAndExtendArray(path, option); Validator$1.parse(options[option], referenceOptions[referenceOption], path); } } else if (refOptionObj["any"] === undefined) { // type of the field is incorrect and the field cannot be any log('Invalid type received for "' + option + '". Expected: ' + Validator$1.print(_Object$keys(refOptionObj)) + ". Received [" + optionType + '] "' + options[option] + '"'); errorFound = true; } } /** * * @param {object | boolean | number | string | Array.<number> | Date | Node | Moment | undefined | null} object * @returns {string} * @static */ }, { key: "getType", value: function getType(object) { var type = _typeof(object); if (type === "object") { if (object === null) { return "null"; } if (object instanceof Boolean) { return "boolean"; } if (object instanceof Number) { return "number"; } if (object instanceof String) { return "string"; } if (_Array$isArray(object)) { return "array"; } if (object instanceof Date) { return "date"; } if (object.nodeType !== undefined) { return "dom"; } if (object._isAMomentObject === true) { return "moment"; } return "object"; } else if (type === "number") { return "number"; } else if (type === "boolean") { return "boolean"; } else if (type === "string") { return "string"; } else if (type === undefined) { return "undefined"; } return type; } /** * @param {string} option * @param {object} options * @param {Array.<string>} path * @static */ }, { key: "getSuggestion", value: function getSuggestion(option, options, path) { var localSearch = Validator$1.findInOptions(option, options, path, false); var globalSearch = Validator$1.findInOptions(option, allOptions$1, [], true); var localSearchThreshold = 8; var globalSearchThreshold = 4; var msg; if (localSearch.indexMatch !== undefined) { msg = " in " + Validator$1.printLocation(localSearch.path, option, "") + 'Perhaps it was incomplete? Did you mean: "' + localSearch.indexMatch + '"?\n\n'; } else if (globalSearch.distance <= globalSearchThreshold && localSearch.distance > globalSearch.distance) { msg = " in " + Validator$1.printLocation(localSearch.path, option, "") + "Perhaps it was misplaced? Matching option found at: " + Validator$1.printLocation(globalSearch.path, globalSearch.closestMatch, ""); } else if (localSearch.distance <= localSearchThreshold) { msg = '. Did you mean "' + localSearch.closestMatch + '"?' + Validator$1.printLocation(localSearch.path, option); } else { msg = ". Did you mean one of these: " + Validator$1.print(_Object$keys(options)) + Validator$1.printLocation(path, option); } console.error('%cUnknown option detected: "' + option + '"' + msg, VALIDATOR_PRINT_STYLE$1); errorFound = true; } /** * traverse the options in search for a match. * * @param {string} option * @param {object} options * @param {Array} path | where to look for the actual option * @param {boolean} [recursive=false] * @returns {{closestMatch: string, path: Array, distance: number}} * @static */ }, { key: "findInOptions", value: function findInOptions(option, options, path) { var recursive = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; var min = 1e9; var closestMatch = ""; var closestMatchPath = []; var lowerCaseOption = option.toLowerCase(); var indexMatch = undefined; for (var op in options) { var distance = void 0; if (options[op].__type__ !== undefined && recursive === true) { var result = Validator$1.findInOptions(option, options[op], copyAndExtendArray(path, op)); if (min > result.distance) { closestMatch = result.closestMatch; closestMatchPath = result.path; min = result.distance; indexMatch = result.indexMatch; } } else { var _context20; if (_indexOfInstanceProperty(_context20 = op.toLowerCase()).call(_context20, lowerCaseOption) !== -1) { indexMatch = op; } distance = Validator$1.levenshteinDistance(option, op); if (min > distance) { closestMatch = op; closestMatchPath = copyArray(path); min = distance; } } } return { closestMatch: closestMatch, path: closestMatchPath, distance: min, indexMatch: indexMatch }; } /** * @param {Array.<string>} path * @param {object} option * @param {string} prefix * @returns {string} * @static */ }, { key: "printLocation", value: function printLocation(path, option) { var prefix = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "Problem value found at: \n"; var str = "\n\n" + prefix + "options = {\n"; for (var i = 0; i < path.length; i++) { for (var j = 0; j < i + 1; j++) { str += " "; } str += path[i] + ": {\n"; } for (var _j = 0; _j < path.length + 1; _j++) { str += " "; } str += option + "\n"; for (var _i3 = 0; _i3 < path.length + 1; _i3++) { for (var _j2 = 0; _j2 < path.length - _i3; _j2++) { str += " "; } str += "}\n"; } return str + "\n\n"; } /** * @param {object} options * @returns {string} * @static */ }, { key: "print", value: function print(options) { return _JSON$stringify(options).replace(/(")|(\[)|(\])|(,"__type__")/g, "").replace(/(,)/g, ", "); } /** * Compute the edit distance between the two given strings * http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript * * Copyright (c) 2011 Andrei Mackenzie * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * @param {string} a * @param {string} b * @returns {Array.<Array.<number>>}} * @static */ }, { key: "levenshteinDistance", value: function levenshteinDistance(a, b) { if (a.length === 0) return b.length; if (b.length === 0) return a.length; var matrix = []; // increment along the first column of each row var i; for (i = 0; i <= b.length; i++) { matrix[i] = [i]; } // increment each column in the first row var j; for (j = 0; j <= a.length; j++) { matrix[0][j] = j; } // Fill in the rest of the matrix for (i = 1; i <= b.length; i++) { for (j = 1; j <= a.length; j++) { if (b.charAt(i - 1) == a.charAt(j - 1)) { matrix[i][j] = matrix[i - 1][j - 1]; } else { matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution Math.min(matrix[i][j - 1] + 1, // insertion matrix[i - 1][j] + 1)); // deletion } } } return matrix[b.length][a.length]; } }]); return Validator$1; }(); var Activator = Activator$1; var Configurator = Configurator$1; var Hammer = Hammer$1; var Popup = Popup$1; var VALIDATOR_PRINT_STYLE = VALIDATOR_PRINT_STYLE$1; var Validator = Validator$1; /* eslint-disable no-prototype-builtins */ /* eslint-disable no-unused-vars */ /* eslint-disable no-var */ /** * Parse a text source containing data in DOT language into a JSON object. * The object contains two lists: one with nodes and one with edges. * * DOT language reference: http://www.graphviz.org/doc/info/lang.html * * DOT language attributes: http://graphviz.org/content/attrs * * @param {string} data Text containing a graph in DOT-notation * @returns {object} graph An object containing two parameters: * {Object[]} nodes * {Object[]} edges * * ------------------------------------------- * TODO * ==== * * For label handling, this is an incomplete implementation. From docs (quote #3015): * * > the escape sequences "\n", "\l" and "\r" divide the label into lines, centered, * > left-justified, and right-justified, respectively. * * Source: http://www.graphviz.org/content/attrs#kescString * * > As another aid for readability, dot allows double-quoted strings to span multiple physical * > lines using the standard C convention of a backslash immediately preceding a newline * > character * > In addition, double-quoted strings can be concatenated using a '+' operator. * > As HTML strings can contain newline characters, which are used solely for formatting, * > the language does not allow escaped newlines or concatenation operators to be used * > within them. * * - Currently, only '\\n' is handled * - Note that text explicitly says 'labels'; the dot parser currently handles escape * sequences in **all** strings. */ function parseDOT(data) { dot = data; return parseGraph(); } // mapping of attributes from DOT (the keys) to vis.js (the values) var NODE_ATTR_MAPPING = { fontsize: "font.size", fontcolor: "font.color", labelfontcolor: "font.color", fontname: "font.face", color: ["color.border", "color.background"], fillcolor: "color.background", tooltip: "title", labeltooltip: "title" }; var EDGE_ATTR_MAPPING = _Object$create$1(NODE_ATTR_MAPPING); EDGE_ATTR_MAPPING.color = "color.color"; EDGE_ATTR_MAPPING.style = "dashes"; // token types enumeration var TOKENTYPE = { NULL: 0, DELIMITER: 1, IDENTIFIER: 2, UNKNOWN: 3 }; // map with all delimiters var DELIMITERS = { "{": true, "}": true, "[": true, "]": true, ";": true, "=": true, ",": true, "->": true, "--": true }; var dot = ""; // current dot file var index = 0; // current index in dot file var c = ""; // current token character in expr var token = ""; // current token var tokenType = TOKENTYPE.NULL; // type of the token /** * Get the first character from the dot file. * The character is stored into the char c. If the end of the dot file is * reached, the function puts an empty string in c. */ function first() { index = 0; c = dot.charAt(0); } /** * Get the next character from the dot file. * The character is stored into the char c. If the end of the dot file is * reached, the function puts an empty string in c. */ function next() { index++; c = dot.charAt(index); } /** * Preview the next character from the dot file. * * @returns {string} cNext */ function nextPreview() { return dot.charAt(index + 1); } /** * Test whether given character is alphabetic or numeric ( a-zA-Z_0-9.:# ) * * @param {string} c * @returns {boolean} isAlphaNumeric */ function isAlphaNumeric(c) { var charCode = c.charCodeAt(0); if (charCode < 47) { // #. return charCode === 35 || charCode === 46; } if (charCode < 59) { // 0-9 and : return charCode > 47; } if (charCode < 91) { // A-Z return charCode > 64; } if (charCode < 96) { // _ return charCode === 95; } if (charCode < 123) { // a-z return charCode > 96; } return false; } /** * Merge all options of object b into object b * * @param {object} a * @param {object} b * @returns {object} a */ function merge$1(a, b) { if (!a) { a = {}; } if (b) { for (var name in b) { if (b.hasOwnProperty(name)) { a[name] = b[name]; } } } return a; } /** * Set a value in an object, where the provided parameter name can be a * path with nested parameters. For example: * * var obj = {a: 2}; * setValue(obj, 'b.c', 3); // obj = {a: 2, b: {c: 3}} * * @param {object} obj * @param {string} path A parameter name or dot-separated parameter path, * like "color.highlight.border". * @param {*} value */ function setValue(obj, path, value) { var keys = path.split("."); var o = obj; while (keys.length) { var key = keys.shift(); if (keys.length) { // this isn't the end point if (!o[key]) { o[key] = {}; } o = o[key]; } else { // this is the end point o[key] = value; } } } /** * Add a node to a graph object. If there is already a node with * the same id, their attributes will be merged. * * @param {object} graph * @param {object} node */ function addNode(graph, node) { var i, len; var current = null; // find root graph (in case of subgraph) var graphs = [graph]; // list with all graphs from current graph to root graph var root = graph; while (root.parent) { graphs.push(root.parent); root = root.parent; } // find existing node (at root level) by its id if (root.nodes) { for (i = 0, len = root.nodes.length; i < len; i++) { if (node.id === root.nodes[i].id) { current = root.nodes[i]; break; } } } if (!current) { // this is a new node current = { id: node.id }; if (graph.node) { // clone default attributes current.attr = merge$1(current.attr, graph.node); } } // add node to this (sub)graph and all its parent graphs for (i = graphs.length - 1; i >= 0; i--) { var _context; var g = graphs[i]; if (!g.nodes) { g.nodes = []; } if (_indexOfInstanceProperty(_context = g.nodes).call(_context, current) === -1) { g.nodes.push(current); } } // merge attributes if (node.attr) { current.attr = merge$1(current.attr, node.attr); } } /** * Add an edge to a graph object * * @param {object} graph * @param {object} edge */ function addEdge(graph, edge) { if (!graph.edges) { graph.edges = []; } graph.edges.push(edge); if (graph.edge) { var attr = merge$1({}, graph.edge); // clone default attributes edge.attr = merge$1(attr, edge.attr); // merge attributes } } /** * Create an edge to a graph object * * @param {object} graph * @param {string | number | object} from * @param {string | number | object} to * @param {string} type * @param {object | null} attr * @returns {object} edge */ function createEdge(graph, from, to, type, attr) { var edge = { from: from, to: to, type: type }; if (graph.edge) { edge.attr = merge$1({}, graph.edge); // clone default attributes } edge.attr = merge$1(edge.attr || {}, attr); // merge attributes // Move arrows attribute from attr to edge temporally created in // parseAttributeList(). if (attr != null) { if (attr.hasOwnProperty("arrows") && attr["arrows"] != null) { edge["arrows"] = { to: { enabled: true, type: attr.arrows.type } }; attr["arrows"] = null; } } return edge; } /** * Get next token in the current dot file. * The token and token type are available as token and tokenType */ function getToken() { tokenType = TOKENTYPE.NULL; token = ""; // skip over whitespaces while (c === " " || c === "\t" || c === "\n" || c === "\r") { // space, tab, enter next(); } do { var isComment = false; // skip comment if (c === "#") { // find the previous non-space character var i = index - 1; while (dot.charAt(i) === " " || dot.charAt(i) === "\t") { i--; } if (dot.charAt(i) === "\n" || dot.charAt(i) === "") { // the # is at the start of a line, this is indeed a line comment while (c != "" && c != "\n") { next(); } isComment = true; } } if (c === "/" && nextPreview() === "/") { // skip line comment while (c != "" && c != "\n") { next(); } isComment = true; } if (c === "/" && nextPreview() === "*") { // skip block comment while (c != "") { if (c === "*" && nextPreview() === "/") { // end of block comment found. skip these last two characters next(); next(); break; } else { next(); } } isComment = true; } // skip over whitespaces while (c === " " || c === "\t" || c === "\n" || c === "\r") { // space, tab, enter next(); } } while (isComment); // check for end of dot file if (c === "") { // token is still empty tokenType = TOKENTYPE.DELIMITER; return; } // check for delimiters consisting of 2 characters var c2 = c + nextPreview(); if (DELIMITERS[c2]) { tokenType = TOKENTYPE.DELIMITER; token = c2; next(); next(); return; } // check for delimiters consisting of 1 character if (DELIMITERS[c]) { tokenType = TOKENTYPE.DELIMITER; token = c; next(); return; } // check for an identifier (number or string) // TODO: more precise parsing of numbers/strings (and the port separator ':') if (isAlphaNumeric(c) || c === "-") { token += c; next(); while (isAlphaNumeric(c)) { token += c; next(); } if (token === "false") { token = false; // convert to boolean } else if (token === "true") { token = true; // convert to boolean } else if (!isNaN(Number(token))) { token = Number(token); // convert to number } tokenType = TOKENTYPE.IDENTIFIER; return; } // check for a string enclosed by double quotes if (c === '"') { next(); while (c != "" && (c != '"' || c === '"' && nextPreview() === '"')) { if (c === '"') { // skip the escape character token += c; next(); } else if (c === "\\" && nextPreview() === "n") { // Honor a newline escape sequence token += "\n"; next(); } else { token += c; } next(); } if (c != '"') { throw newSyntaxError('End of string " expected'); } next(); tokenType = TOKENTYPE.IDENTIFIER; return; } // something unknown is found, wrong characters, a syntax error tokenType = TOKENTYPE.UNKNOWN; while (c != "") { token += c; next(); } throw new SyntaxError('Syntax error in part "' + chop(token, 30) + '"'); } /** * Parse a graph. * * @returns {object} graph */ function parseGraph() { var graph = {}; first(); getToken(); // optional strict keyword if (token === "strict") { graph.strict = true; getToken(); } // graph or digraph keyword if (token === "graph" || token === "digraph") { graph.type = token; getToken(); } // optional graph id if (tokenType === TOKENTYPE.IDENTIFIER) { graph.id = token; getToken(); } // open angle bracket if (token != "{") { throw newSyntaxError("Angle bracket { expected"); } getToken(); // statements parseStatements(graph); // close angle bracket if (token != "}") { throw newSyntaxError("Angle bracket } expected"); } getToken(); // end of file if (token !== "") { throw newSyntaxError("End of file expected"); } getToken(); // remove temporary default options delete graph.node; delete graph.edge; delete graph.graph; return graph; } /** * Parse a list with statements. * * @param {object} graph */ function parseStatements(graph) { while (token !== "" && token != "}") { parseStatement(graph); if (token === ";") { getToken(); } } } /** * Parse a single statement. Can be a an attribute statement, node * statement, a series of node statements and edge statements, or a * parameter. * * @param {object} graph */ function parseStatement(graph) { // parse subgraph var subgraph = parseSubgraph(graph); if (subgraph) { // edge statements parseEdge(graph, subgraph); return; } // parse an attribute statement var attr = parseAttributeStatement(graph); if (attr) { return; } // parse node if (tokenType != TOKENTYPE.IDENTIFIER) { throw newSyntaxError("Identifier expected"); } var id = token; // id can be a string or a number getToken(); if (token === "=") { // id statement getToken(); if (tokenType != TOKENTYPE.IDENTIFIER) { throw newSyntaxError("Identifier expected"); } graph[id] = token; getToken(); // TODO: implement comma separated list with "a_list: ID=ID [','] [a_list] " } else { parseNodeStatement(graph, id); } } /** * Parse a subgraph * * @param {object} graph parent graph object * @returns {object | null} subgraph */ function parseSubgraph(graph) { var subgraph = null; // optional subgraph keyword if (token === "subgraph") { subgraph = {}; subgraph.type = "subgraph"; getToken(); // optional graph id if (tokenType === TOKENTYPE.IDENTIFIER) { subgraph.id = token; getToken(); } } // open angle bracket if (token === "{") { getToken(); if (!subgraph) { subgraph = {}; } subgraph.parent = graph; subgraph.node = graph.node; subgraph.edge = graph.edge; subgraph.graph = graph.graph; // statements parseStatements(subgraph); // close angle bracket if (token != "}") { throw newSyntaxError("Angle bracket } expected"); } getToken(); // remove temporary default options delete subgraph.node; delete subgraph.edge; delete subgraph.graph; delete subgraph.parent; // register at the parent graph if (!graph.subgraphs) { graph.subgraphs = []; } graph.subgraphs.push(subgraph); } return subgraph; } /** * parse an attribute statement like "node [shape=circle fontSize=16]". * Available keywords are 'node', 'edge', 'graph'. * The previous list with default attributes will be replaced * * @param {object} graph * @returns {string | null} keyword Returns the name of the parsed attribute * (node, edge, graph), or null if nothing * is parsed. */ function parseAttributeStatement(graph) { // attribute statements if (token === "node") { getToken(); // node attributes graph.node = parseAttributeList(); return "node"; } else if (token === "edge") { getToken(); // edge attributes graph.edge = parseAttributeList(); return "edge"; } else if (token === "graph") { getToken(); // graph attributes graph.graph = parseAttributeList(); return "graph"; } return null; } /** * parse a node statement * * @param {object} graph * @param {string | number} id */ function parseNodeStatement(graph, id) { // node statement var node = { id: id }; var attr = parseAttributeList(); if (attr) { node.attr = attr; } addNode(graph, node); // edge statements parseEdge(graph, id); } /** * Parse an edge or a series of edges * * @param {object} graph * @param {string | number} from Id of the from node */ function parseEdge(graph, from) { while (token === "->" || token === "--") { var to; var type = token; getToken(); var subgraph = parseSubgraph(graph); if (subgraph) { to = subgraph; } else { if (tokenType != TOKENTYPE.IDENTIFIER) { throw newSyntaxError("Identifier or subgraph expected"); } to = token; addNode(graph, { id: to }); getToken(); } // parse edge attributes var attr = parseAttributeList(); // create edge var edge = createEdge(graph, from, to, type, attr); addEdge(graph, edge); from = to; } } /** * Parse a set with attributes, * for example [label="1.000", shape=solid] * * @returns {object | null} attr */ function parseAttributeList() { var i; var attr = null; // edge styles of dot and vis var edgeStyles = { dashed: true, solid: false, dotted: [1, 5] }; /** * Define arrow types. * vis currently supports types defined in 'arrowTypes'. * Details of arrow shapes are described in * http://www.graphviz.org/content/arrow-shapes */ var arrowTypes = { dot: "circle", box: "box", crow: "crow", curve: "curve", icurve: "inv_curve", normal: "triangle", inv: "inv_triangle", diamond: "diamond", tee: "bar", vee: "vee" }; /** * 'attr_list' contains attributes for checking if some of them are affected * later. For instance, both of 'arrowhead' and 'dir' (edge style defined * in DOT) make changes to 'arrows' attribute in vis. */ var attr_list = new Array(); var attr_names = new Array(); // used for checking the case. // parse attributes while (token === "[") { getToken(); attr = {}; while (token !== "" && token != "]") { if (tokenType != TOKENTYPE.IDENTIFIER) { throw newSyntaxError("Attribute name expected"); } var name = token; getToken(); if (token != "=") { throw newSyntaxError("Equal sign = expected"); } getToken(); if (tokenType != TOKENTYPE.IDENTIFIER) { throw newSyntaxError("Attribute value expected"); } var value = token; // convert from dot style to vis if (name === "style") { value = edgeStyles[value]; } var arrowType; if (name === "arrowhead") { arrowType = arrowTypes[value]; name = "arrows"; value = { to: { enabled: true, type: arrowType } }; } if (name === "arrowtail") { arrowType = arrowTypes[value]; name = "arrows"; value = { from: { enabled: true, type: arrowType } }; } attr_list.push({ attr: attr, name: name, value: value }); attr_names.push(name); getToken(); if (token == ",") { getToken(); } } if (token != "]") { throw newSyntaxError("Bracket ] expected"); } getToken(); } /** * As explained in [1], graphviz has limitations for combination of * arrow[head|tail] and dir. If attribute list includes 'dir', * following cases just be supported. * 1. both or none + arrowhead, arrowtail * 2. forward + arrowhead (arrowtail is not affedted) * 3. back + arrowtail (arrowhead is not affected) * [1] https://www.graphviz.org/doc/info/attrs.html#h:undir_note */ if (_includesInstanceProperty(attr_names).call(attr_names, "dir")) { var idx = {}; // get index of 'arrows' and 'dir' idx.arrows = {}; for (i = 0; i < attr_list.length; i++) { if (attr_list[i].name === "arrows") { if (attr_list[i].value.to != null) { idx.arrows.to = i; } else if (attr_list[i].value.from != null) { idx.arrows.from = i; } else { throw newSyntaxError("Invalid value of arrows"); } } else if (attr_list[i].name === "dir") { idx.dir = i; } } // first, add default arrow shape if it is not assigned to avoid error var dir_type = attr_list[idx.dir].value; if (!_includesInstanceProperty(attr_names).call(attr_names, "arrows")) { if (dir_type === "both") { attr_list.push({ attr: attr_list[idx.dir].attr, name: "arrows", value: { to: { enabled: true } } }); idx.arrows.to = attr_list.length - 1; attr_list.push({ attr: attr_list[idx.dir].attr, name: "arrows", value: { from: { enabled: true } } }); idx.arrows.from = attr_list.length - 1; } else if (dir_type === "forward") { attr_list.push({ attr: attr_list[idx.dir].attr, name: "arrows", value: { to: { enabled: true } } }); idx.arrows.to = attr_list.length - 1; } else if (dir_type === "back") { attr_list.push({ attr: attr_list[idx.dir].attr, name: "arrows", value: { from: { enabled: true } } }); idx.arrows.from = attr_list.length - 1; } else if (dir_type === "none") { attr_list.push({ attr: attr_list[idx.dir].attr, name: "arrows", value: "" }); idx.arrows.to = attr_list.length - 1; } else { throw newSyntaxError('Invalid dir type "' + dir_type + '"'); } } var from_type; var to_type; // update 'arrows' attribute from 'dir'. if (dir_type === "both") { // both of shapes of 'from' and 'to' are given if (idx.arrows.to && idx.arrows.from) { to_type = attr_list[idx.arrows.to].value.to.type; from_type = attr_list[idx.arrows.from].value.from.type; attr_list[idx.arrows.to] = { attr: attr_list[idx.arrows.to].attr, name: attr_list[idx.arrows.to].name, value: { to: { enabled: true, type: to_type }, from: { enabled: true, type: from_type } } }; _spliceInstanceProperty(attr_list).call(attr_list, idx.arrows.from, 1); // shape of 'to' is assigned and use default to 'from' } else if (idx.arrows.to) { to_type = attr_list[idx.arrows.to].value.to.type; from_type = "arrow"; attr_list[idx.arrows.to] = { attr: attr_list[idx.arrows.to].attr, name: attr_list[idx.arrows.to].name, value: { to: { enabled: true, type: to_type }, from: { enabled: true, type: from_type } } }; // only shape of 'from' is assigned and use default for 'to' } else if (idx.arrows.from) { to_type = "arrow"; from_type = attr_list[idx.arrows.from].value.from.type; attr_list[idx.arrows.from] = { attr: attr_list[idx.arrows.from].attr, name: attr_list[idx.arrows.from].name, value: { to: { enabled: true, type: to_type }, from: { enabled: true, type: from_type } } }; } } else if (dir_type === "back") { // given both of shapes, but use only 'from' if (idx.arrows.to && idx.arrows.from) { to_type = ""; from_type = attr_list[idx.arrows.from].value.from.type; attr_list[idx.arrows.from] = { attr: attr_list[idx.arrows.from].attr, name: attr_list[idx.arrows.from].name, value: { to: { enabled: true, type: to_type }, from: { enabled: true, type: from_type } } }; // given shape of 'to', but does not use it } else if (idx.arrows.to) { to_type = ""; from_type = "arrow"; idx.arrows.from = idx.arrows.to; attr_list[idx.arrows.from] = { attr: attr_list[idx.arrows.from].attr, name: attr_list[idx.arrows.from].name, value: { to: { enabled: true, type: to_type }, from: { enabled: true, type: from_type } } }; // assign given 'from' shape } else if (idx.arrows.from) { to_type = ""; from_type = attr_list[idx.arrows.from].value.from.type; attr_list[idx.arrows.to] = { attr: attr_list[idx.arrows.from].attr, name: attr_list[idx.arrows.from].name, value: { to: { enabled: true, type: to_type }, from: { enabled: true, type: from_type } } }; } attr_list[idx.arrows.from] = { attr: attr_list[idx.arrows.from].attr, name: attr_list[idx.arrows.from].name, value: { from: { enabled: true, type: attr_list[idx.arrows.from].value.from.type } } }; } else if (dir_type === "none") { var idx_arrow; if (idx.arrows.to) { idx_arrow = idx.arrows.to; } else { idx_arrow = idx.arrows.from; } attr_list[idx_arrow] = { attr: attr_list[idx_arrow].attr, name: attr_list[idx_arrow].name, value: "" }; } else if (dir_type === "forward") { // given both of shapes, but use only 'to' if (idx.arrows.to && idx.arrows.from) { to_type = attr_list[idx.arrows.to].value.to.type; from_type = ""; attr_list[idx.arrows.to] = { attr: attr_list[idx.arrows.to].attr, name: attr_list[idx.arrows.to].name, value: { to: { enabled: true, type: to_type }, from: { enabled: true, type: from_type } } }; // assign given 'to' shape } else if (idx.arrows.to) { to_type = attr_list[idx.arrows.to].value.to.type; from_type = ""; attr_list[idx.arrows.to] = { attr: attr_list[idx.arrows.to].attr, name: attr_list[idx.arrows.to].name, value: { to: { enabled: true, type: to_type }, from: { enabled: true, type: from_type } } }; // given shape of 'from', but does not use it } else if (idx.arrows.from) { to_type = "arrow"; from_type = ""; idx.arrows.to = idx.arrows.from; attr_list[idx.arrows.to] = { attr: attr_list[idx.arrows.to].attr, name: attr_list[idx.arrows.to].name, value: { to: { enabled: true, type: to_type }, from: { enabled: true, type: from_type } } }; } attr_list[idx.arrows.to] = { attr: attr_list[idx.arrows.to].attr, name: attr_list[idx.arrows.to].name, value: { to: { enabled: true, type: attr_list[idx.arrows.to].value.to.type } } }; } else { throw newSyntaxError('Invalid dir type "' + dir_type + '"'); } // remove 'dir' attribute no need anymore _spliceInstanceProperty(attr_list).call(attr_list, idx.dir, 1); } // parse 'penwidth' var nof_attr_list; if (_includesInstanceProperty(attr_names).call(attr_names, "penwidth")) { var tmp_attr_list = []; nof_attr_list = attr_list.length; for (i = 0; i < nof_attr_list; i++) { // exclude 'width' from attr_list if 'penwidth' exists if (attr_list[i].name !== "width") { if (attr_list[i].name === "penwidth") { attr_list[i].name = "width"; } tmp_attr_list.push(attr_list[i]); } } attr_list = tmp_attr_list; } nof_attr_list = attr_list.length; for (i = 0; i < nof_attr_list; i++) { setValue(attr_list[i].attr, attr_list[i].name, attr_list[i].value); } return attr; } /** * Create a syntax error with extra information on current token and index. * * @param {string} message * @returns {SyntaxError} err */ function newSyntaxError(message) { return new SyntaxError(message + ', got "' + chop(token, 30) + '" (char ' + index + ")"); } /** * Chop off text after a maximum length * * @param {string} text * @param {number} maxLength * @returns {string} */ function chop(text, maxLength) { return text.length <= maxLength ? text : text.substr(0, 27) + "..."; } /** * Execute a function fn for each pair of elements in two arrays * * @param {Array | *} array1 * @param {Array | *} array2 * @param {Function} fn */ function forEach2(array1, array2, fn) { if (_Array$isArray(array1)) { _forEachInstanceProperty(array1).call(array1, function (elem1) { if (_Array$isArray(array2)) { _forEachInstanceProperty(array2).call(array2, function (elem2) { fn(elem1, elem2); }); } else { fn(elem1, array2); } }); } else { if (_Array$isArray(array2)) { _forEachInstanceProperty(array2).call(array2, function (elem2) { fn(array1, elem2); }); } else { fn(array1, array2); } } } /** * Set a nested property on an object * When nested objects are missing, they will be created. * For example setProp({}, 'font.color', 'red') will return {font: {color: 'red'}} * * @param {object} object * @param {string} path A dot separated string like 'font.color' * @param {*} value Value for the property * @returns {object} Returns the original object, allows for chaining. */ function setProp(object, path, value) { var names = path.split("."); var prop = names.pop(); // traverse over the nested objects var obj = object; for (var i = 0; i < names.length; i++) { var name = names[i]; if (!(name in obj)) { obj[name] = {}; } obj = obj[name]; } // set the property value obj[prop] = value; return object; } /** * Convert an object with DOT attributes to their vis.js equivalents. * * @param {object} attr Object with DOT attributes * @param {object} mapping * @returns {object} Returns an object with vis.js attributes */ function convertAttr(attr, mapping) { var converted = {}; for (var prop in attr) { if (attr.hasOwnProperty(prop)) { var visProp = mapping[prop]; if (_Array$isArray(visProp)) { _forEachInstanceProperty(visProp).call(visProp, function (visPropI) { setProp(converted, visPropI, attr[prop]); }); } else if (typeof visProp === "string") { setProp(converted, visProp, attr[prop]); } else { setProp(converted, prop, attr[prop]); } } } return converted; } /** * Convert a string containing a graph in DOT language into a map containing * with nodes and edges in the format of graph. * * @param {string} data Text containing a graph in DOT-notation * @returns {object} graphData */ function DOTToGraph(data) { // parse the DOT file var dotData = parseDOT(data); var graphData = { nodes: [], edges: [], options: {} }; // copy the nodes if (dotData.nodes) { var _context2; _forEachInstanceProperty(_context2 = dotData.nodes).call(_context2, function (dotNode) { var graphNode = { id: dotNode.id, label: String(dotNode.label || dotNode.id) }; merge$1(graphNode, convertAttr(dotNode.attr, NODE_ATTR_MAPPING)); if (graphNode.image) { graphNode.shape = "image"; } graphData.nodes.push(graphNode); }); } // copy the edges if (dotData.edges) { var _context3; /** * Convert an edge in DOT format to an edge with VisGraph format * * @param {object} dotEdge * @returns {object} graphEdge */ var convertEdge = function convertEdge(dotEdge) { var graphEdge = { from: dotEdge.from, to: dotEdge.to }; merge$1(graphEdge, convertAttr(dotEdge.attr, EDGE_ATTR_MAPPING)); // Add arrows attribute to default styled arrow. // The reason why default style is not added in parseAttributeList() is // because only default is cleared before here. if (graphEdge.arrows == null && dotEdge.type === "->") { graphEdge.arrows = "to"; } return graphEdge; }; _forEachInstanceProperty(_context3 = dotData.edges).call(_context3, function (dotEdge) { var from, to; if (dotEdge.from instanceof Object) { from = dotEdge.from.nodes; } else { from = { id: dotEdge.from }; } if (dotEdge.to instanceof Object) { to = dotEdge.to.nodes; } else { to = { id: dotEdge.to }; } if (dotEdge.from instanceof Object && dotEdge.from.edges) { var _context4; _forEachInstanceProperty(_context4 = dotEdge.from.edges).call(_context4, function (subEdge) { var graphEdge = convertEdge(subEdge); graphData.edges.push(graphEdge); }); } forEach2(from, to, function (from, to) { var subEdge = createEdge(graphData, from.id, to.id, dotEdge.type, dotEdge.attr); var graphEdge = convertEdge(subEdge); graphData.edges.push(graphEdge); }); if (dotEdge.to instanceof Object && dotEdge.to.edges) { var _context5; _forEachInstanceProperty(_context5 = dotEdge.to.edges).call(_context5, function (subEdge) { var graphEdge = convertEdge(subEdge); graphData.edges.push(graphEdge); }); } }); } // copy the options if (dotData.attr) { graphData.options = dotData.attr; } return graphData; } /* eslint-enable no-var */ /* eslint-enable no-unused-vars */ /* eslint-enable no-prototype-builtins */ var dotparser = /*#__PURE__*/Object.freeze({ __proto__: null, DOTToGraph: DOTToGraph, parseDOT: parseDOT }); /** * Convert Gephi to Vis. * * @param gephiJSON - The parsed JSON data in Gephi format. * @param optionsObj - Additional options. * @returns The converted data ready to be used in Vis. */ function parseGephi(gephiJSON, optionsObj) { var _context; var options = { edges: { inheritColor: false }, nodes: { fixed: false, parseColor: false } }; if (optionsObj != null) { if (optionsObj.fixed != null) { options.nodes.fixed = optionsObj.fixed; } if (optionsObj.parseColor != null) { options.nodes.parseColor = optionsObj.parseColor; } if (optionsObj.inheritColor != null) { options.edges.inheritColor = optionsObj.inheritColor; } } var gEdges = gephiJSON.edges; var vEdges = _mapInstanceProperty(gEdges).call(gEdges, function (gEdge) { var vEdge = { from: gEdge.source, id: gEdge.id, to: gEdge.target }; if (gEdge.attributes != null) { vEdge.attributes = gEdge.attributes; } if (gEdge.label != null) { vEdge.label = gEdge.label; } if (gEdge.attributes != null && gEdge.attributes.title != null) { vEdge.title = gEdge.attributes.title; } if (gEdge.type === "Directed") { vEdge.arrows = "to"; } // edge['value'] = gEdge.attributes != null ? gEdge.attributes.Weight : undefined; // edge['width'] = edge['value'] != null ? undefined : edgegEdge.size; if (gEdge.color && options.edges.inheritColor === false) { vEdge.color = gEdge.color; } return vEdge; }); var vNodes = _mapInstanceProperty(_context = gephiJSON.nodes).call(_context, function (gNode) { var vNode = { id: gNode.id, fixed: options.nodes.fixed && gNode.x != null && gNode.y != null }; if (gNode.attributes != null) { vNode.attributes = gNode.attributes; } if (gNode.label != null) { vNode.label = gNode.label; } if (gNode.size != null) { vNode.size = gNode.size; } if (gNode.attributes != null && gNode.attributes.title != null) { vNode.title = gNode.attributes.title; } if (gNode.title != null) { vNode.title = gNode.title; } if (gNode.x != null) { vNode.x = gNode.x; } if (gNode.y != null) { vNode.y = gNode.y; } if (gNode.color != null) { if (options.nodes.parseColor === true) { vNode.color = gNode.color; } else { vNode.color = { background: gNode.color, border: gNode.color, highlight: { background: gNode.color, border: gNode.color }, hover: { background: gNode.color, border: gNode.color } }; } } return vNode; }); return { nodes: vNodes, edges: vEdges }; } var gephiParser = /*#__PURE__*/Object.freeze({ __proto__: null, parseGephi: parseGephi }); // English var en = { addDescription: "Click in an empty space to place a new node.", addEdge: "Add Edge", addNode: "Add Node", back: "Back", close: "Close", createEdgeError: "Cannot link edges to a cluster.", del: "Delete selected", deleteClusterError: "Clusters cannot be deleted.", edgeDescription: "Click on a node and drag the edge to another node to connect them.", edit: "Edit", editClusterError: "Clusters cannot be edited.", editEdge: "Edit Edge", editEdgeDescription: "Click on the control points and drag them to a node to connect to it.", editNode: "Edit Node" }; // German var de = { addDescription: "Klicke auf eine freie Stelle, um einen neuen Knoten zu plazieren.", addEdge: "Kante hinzuf\xFCgen", addNode: "Knoten hinzuf\xFCgen", back: "Zur\xFCck", close: "Schließen", createEdgeError: "Es ist nicht m\xF6glich, Kanten mit Clustern zu verbinden.", del: "L\xF6sche Auswahl", deleteClusterError: "Cluster k\xF6nnen nicht gel\xF6scht werden.", edgeDescription: "Klicke auf einen Knoten und ziehe die Kante zu einem anderen Knoten, um diese zu verbinden.", edit: "Editieren", editClusterError: "Cluster k\xF6nnen nicht editiert werden.", editEdge: "Kante editieren", editEdgeDescription: "Klicke auf die Verbindungspunkte und ziehe diese auf einen Knoten, um sie zu verbinden.", editNode: "Knoten editieren" }; // Spanish var es = { addDescription: "Haga clic en un lugar vac\xEDo para colocar un nuevo nodo.", addEdge: "A\xF1adir arista", addNode: "A\xF1adir nodo", back: "Atr\xE1s", close: "Cerrar", createEdgeError: "No se puede conectar una arista a un grupo.", del: "Eliminar selecci\xF3n", deleteClusterError: "No es posible eliminar grupos.", edgeDescription: "Haga clic en un nodo y arrastre la arista hacia otro nodo para conectarlos.", edit: "Editar", editClusterError: "No es posible editar grupos.", editEdge: "Editar arista", editEdgeDescription: "Haga clic en un punto de control y arrastrelo a un nodo para conectarlo.", editNode: "Editar nodo" }; //Italiano var it = { addDescription: "Clicca per aggiungere un nuovo nodo", addEdge: "Aggiungi un vertice", addNode: "Aggiungi un nodo", back: "Indietro", close: "Chiudere", createEdgeError: "Non si possono collegare vertici ad un cluster", del: "Cancella la selezione", deleteClusterError: "I cluster non possono essere cancellati", edgeDescription: "Clicca su un nodo e trascinalo ad un altro nodo per connetterli.", edit: "Modifica", editClusterError: "I clusters non possono essere modificati.", editEdge: "Modifica il vertice", editEdgeDescription: "Clicca sui Punti di controllo e trascinali ad un nodo per connetterli.", editNode: "Modifica il nodo" }; // Dutch var nl = { addDescription: "Klik op een leeg gebied om een nieuwe node te maken.", addEdge: "Link toevoegen", addNode: "Node toevoegen", back: "Terug", close: "Sluiten", createEdgeError: "Kan geen link maken naar een cluster.", del: "Selectie verwijderen", deleteClusterError: "Clusters kunnen niet worden verwijderd.", edgeDescription: "Klik op een node en sleep de link naar een andere node om ze te verbinden.", edit: "Wijzigen", editClusterError: "Clusters kunnen niet worden aangepast.", editEdge: "Link wijzigen", editEdgeDescription: "Klik op de verbindingspunten en sleep ze naar een node om daarmee te verbinden.", editNode: "Node wijzigen" }; // Portuguese Brazil var pt = { addDescription: "Clique em um espaço em branco para adicionar um novo nó", addEdge: "Adicionar aresta", addNode: "Adicionar nó", back: "Voltar", close: "Fechar", createEdgeError: "Não foi possível linkar arestas a um cluster.", del: "Remover selecionado", deleteClusterError: "Clusters não puderam ser removidos.", edgeDescription: "Clique em um nó e arraste a aresta até outro nó para conectá-los", edit: "Editar", editClusterError: "Clusters não puderam ser editados.", editEdge: "Editar aresta", editEdgeDescription: "Clique nos pontos de controle e os arraste para um nó para conectá-los", editNode: "Editar nó" }; // Russian var ru = { addDescription: "Кликните в свободное место, чтобы добавить новый узел.", addEdge: "Добавить ребро", addNode: "Добавить узел", back: "Назад", close: "Закрывать", createEdgeError: "Невозможно соединить ребра в кластер.", del: "Удалить выбранное", deleteClusterError: "Кластеры не могут быть удалены", edgeDescription: "Кликните на узел и протяните ребро к другому узлу, чтобы соединить их.", edit: "Редактировать", editClusterError: "Кластеры недоступны для редактирования.", editEdge: "Редактировать ребро", editEdgeDescription: "Кликните на контрольные точки и перетащите их в узел, чтобы подключиться к нему.", editNode: "Редактировать узел" }; // Chinese var cn = { addDescription: "单击空白处放置新节点。", addEdge: "添加连接线", addNode: "添加节点", back: "返回", close: "關閉", createEdgeError: "无法将连接线连接到群集。", del: "删除选定", deleteClusterError: "无法删除群集。", edgeDescription: "单击某个节点并将该连接线拖动到另一个节点以连接它们。", edit: "编辑", editClusterError: "无法编辑群集。", editEdge: "编辑连接线", editEdgeDescription: "单击控制节点并将它们拖到节点上连接。", editNode: "编辑节点" }; // Ukrainian var uk = { addDescription: "Kлікніть на вільне місце, щоб додати новий вузол.", addEdge: "Додати край", addNode: "Додати вузол", back: "Назад", close: "Закрити", createEdgeError: "Не можливо об'єднати краї в групу.", del: "Видалити обране", deleteClusterError: "Групи не можуть бути видалені.", edgeDescription: "Клікніть на вузол і перетягніть край до іншого вузла, щоб їх з'єднати.", edit: "Редагувати", editClusterError: "Групи недоступні для редагування.", editEdge: "Редагувати край", editEdgeDescription: "Клікніть на контрольні точки і перетягніть їх у вузол, щоб підключитися до нього.", editNode: "Редагувати вузол" }; // French var fr = { addDescription: "Cliquez dans un endroit vide pour placer un nœud.", addEdge: "Ajouter un lien", addNode: "Ajouter un nœud", back: "Retour", close: "Fermer", createEdgeError: "Impossible de créer un lien vers un cluster.", del: "Effacer la sélection", deleteClusterError: "Les clusters ne peuvent pas être effacés.", edgeDescription: "Cliquez sur un nœud et glissez le lien vers un autre nœud pour les connecter.", edit: "Éditer", editClusterError: "Les clusters ne peuvent pas être édités.", editEdge: "Éditer le lien", editEdgeDescription: "Cliquez sur les points de contrôle et glissez-les pour connecter un nœud.", editNode: "Éditer le nœud" }; // Czech var cs = { addDescription: "Kluknutím do prázdného prostoru můžete přidat nový vrchol.", addEdge: "Přidat hranu", addNode: "Přidat vrchol", back: "Zpět", close: "Zavřít", createEdgeError: "Nelze připojit hranu ke shluku.", del: "Smazat výběr", deleteClusterError: "Nelze mazat shluky.", edgeDescription: "Přetažením z jednoho vrcholu do druhého můžete spojit tyto vrcholy novou hranou.", edit: "Upravit", editClusterError: "Nelze upravovat shluky.", editEdge: "Upravit hranu", editEdgeDescription: "Přetažením kontrolního vrcholu hrany ji můžete připojit k jinému vrcholu.", editNode: "Upravit vrchol" }; var locales = /*#__PURE__*/Object.freeze({ __proto__: null, cn: cn, cs: cs, de: de, en: en, es: es, fr: fr, it: it, nl: nl, pt: pt, ru: ru, uk: uk }); /** * Normalizes language code into the format used internally. * * @param locales - All the available locales. * @param rawCode - The original code as supplied by the user. * @returns Language code in the format language-COUNTRY or language, eventually * fallbacks to en. */ function normalizeLanguageCode(locales, rawCode) { try { var _rawCode$split = rawCode.split(/[-_ /]/, 2), _rawCode$split2 = _slicedToArray(_rawCode$split, 2), rawLanguage = _rawCode$split2[0], rawCountry = _rawCode$split2[1]; var language = rawLanguage != null ? rawLanguage.toLowerCase() : null; var country = rawCountry != null ? rawCountry.toUpperCase() : null; if (language && country) { var code = language + "-" + country; if (Object.prototype.hasOwnProperty.call(locales, code)) { return code; } else { var _context; console.warn(_concatInstanceProperty(_context = "Unknown variant ".concat(country, " of language ")).call(_context, language, ".")); } } if (language) { var _code = language; if (Object.prototype.hasOwnProperty.call(locales, _code)) { return _code; } else { console.warn("Unknown language ".concat(language)); } } console.warn("Unknown locale ".concat(rawCode, ", falling back to English.")); return "en"; } catch (error) { console.error(error); console.warn("Unexpected error while normalizing locale ".concat(rawCode, ", falling back to English.")); return "en"; } } /** * Associates a canvas to a given image, containing a number of renderings * of the image at various sizes. * * This technique is known as 'mipmapping'. * * NOTE: Images can also be of type 'data:svg+xml`. This code also works * for svg, but the mipmapping may not be necessary. * * @param {Image} image */ var CachedImage = /*#__PURE__*/function () { /** * @ignore */ function CachedImage() { _classCallCheck(this, CachedImage); this.NUM_ITERATIONS = 4; // Number of items in the coordinates array this.image = new Image(); this.canvas = document.createElement("canvas"); } /** * Called when the image has been successfully loaded. */ _createClass(CachedImage, [{ key: "init", value: function init() { if (this.initialized()) return; this.src = this.image.src; // For same interface with Image var w = this.image.width; var h = this.image.height; // Ease external access this.width = w; this.height = h; var h2 = Math.floor(h / 2); var h4 = Math.floor(h / 4); var h8 = Math.floor(h / 8); var h16 = Math.floor(h / 16); var w2 = Math.floor(w / 2); var w4 = Math.floor(w / 4); var w8 = Math.floor(w / 8); var w16 = Math.floor(w / 16); // Make canvas as small as possible this.canvas.width = 3 * w4; this.canvas.height = h2; // Coordinates and sizes of images contained in the canvas // Values per row: [top x, left y, width, height] this.coordinates = [[0, 0, w2, h2], [w2, 0, w4, h4], [w2, h4, w8, h8], [5 * w8, h4, w16, h16]]; this._fillMipMap(); } /** * @returns {boolean} true if init() has been called, false otherwise. */ }, { key: "initialized", value: function initialized() { return this.coordinates !== undefined; } /** * Redraw main image in various sizes to the context. * * The rationale behind this is to reduce artefacts due to interpolation * at differing zoom levels. * * Source: http://stackoverflow.com/q/18761404/1223531 * * This methods takes the resizing out of the drawing loop, in order to * reduce performance overhead. * * TODO: The code assumes that a 2D context can always be gotten. This is * not necessarily true! OTOH, if not true then usage of this class * is senseless. * * @private */ }, { key: "_fillMipMap", value: function _fillMipMap() { var ctx = this.canvas.getContext("2d"); // First zoom-level comes from the image var to = this.coordinates[0]; ctx.drawImage(this.image, to[0], to[1], to[2], to[3]); // The rest are copy actions internal to the canvas/context for (var iterations = 1; iterations < this.NUM_ITERATIONS; iterations++) { var from = this.coordinates[iterations - 1]; var _to = this.coordinates[iterations]; ctx.drawImage(this.canvas, from[0], from[1], from[2], from[3], _to[0], _to[1], _to[2], _to[3]); } } /** * Draw the image, using the mipmap if necessary. * * MipMap is only used if param factor > 2; otherwise, original bitmap * is resized. This is also used to skip mipmap usage, e.g. by setting factor = 1 * * Credits to 'Alex de Mulder' for original implementation. * * @param {CanvasRenderingContext2D} ctx context on which to draw zoomed image * @param {Float} factor scale factor at which to draw * @param {number} left * @param {number} top * @param {number} width * @param {number} height */ }, { key: "drawImageAtPosition", value: function drawImageAtPosition(ctx, factor, left, top, width, height) { if (!this.initialized()) return; //can't draw image yet not intialized if (factor > 2) { // Determine which zoomed image to use factor *= 0.5; var iterations = 0; while (factor > 2 && iterations < this.NUM_ITERATIONS) { factor *= 0.5; iterations += 1; } if (iterations >= this.NUM_ITERATIONS) { iterations = this.NUM_ITERATIONS - 1; } //console.log("iterations: " + iterations); var from = this.coordinates[iterations]; ctx.drawImage(this.canvas, from[0], from[1], from[2], from[3], left, top, width, height); } else { // Draw image directly ctx.drawImage(this.image, left, top, width, height); } } }]); return CachedImage; }(); /** * This callback is a callback that accepts an Image. * * @callback ImageCallback * @param {Image} image */ /** * This class loads images and keeps them stored. * * @param {ImageCallback} callback */ var Images = /*#__PURE__*/function () { /** * @param {ImageCallback} callback */ function Images(callback) { _classCallCheck(this, Images); this.images = {}; this.imageBroken = {}; this.callback = callback; } /** * @param {string} url The original Url that failed to load, if the broken image is successfully loaded it will be added to the cache using this Url as the key so that subsequent requests for this Url will return the broken image * @param {string} brokenUrl Url the broken image to try and load * @param {Image} imageToLoadBrokenUrlOn The image object */ _createClass(Images, [{ key: "_tryloadBrokenUrl", value: function _tryloadBrokenUrl(url, brokenUrl, imageToLoadBrokenUrlOn) { //If these parameters aren't specified then exit the function because nothing constructive can be done if (url === undefined || imageToLoadBrokenUrlOn === undefined) return; if (brokenUrl === undefined) { console.warn("No broken url image defined"); return; } //Clear the old subscription to the error event and put a new in place that only handle errors in loading the brokenImageUrl imageToLoadBrokenUrlOn.image.onerror = function () { console.error("Could not load brokenImage:", brokenUrl); // cache item will contain empty image, this should be OK for default }; //Set the source of the image to the brokenUrl, this is actually what kicks off the loading of the broken image imageToLoadBrokenUrlOn.image.src = brokenUrl; } /** * * @param {vis.Image} imageToRedrawWith * @private */ }, { key: "_redrawWithImage", value: function _redrawWithImage(imageToRedrawWith) { if (this.callback) { this.callback(imageToRedrawWith); } } /** * @param {string} url Url of the image * @param {string} brokenUrl Url of an image to use if the url image is not found * @returns {Image} img The image object */ }, { key: "load", value: function load(url, brokenUrl) { var _this = this; //Try and get the image from the cache, if successful then return the cached image var cachedImage = this.images[url]; if (cachedImage) return cachedImage; //Create a new image var img = new CachedImage(); // Need to add to cache here, otherwise final return will spawn different copies of the same image, // Also, there will be multiple loads of the same image. this.images[url] = img; //Subscribe to the event that is raised if the image loads successfully img.image.onload = function () { // Properly init the cached item and then request a redraw _this._fixImageCoordinates(img.image); img.init(); _this._redrawWithImage(img); }; //Subscribe to the event that is raised if the image fails to load img.image.onerror = function () { console.error("Could not load image:", url); //Try and load the image specified by the brokenUrl using _this._tryloadBrokenUrl(url, brokenUrl, img); }; //Set the source of the image to the url, this is what actually kicks off the loading of the image img.image.src = url; //Return the new image return img; } /** * IE11 fix -- thanks dponch! * * Local helper function * * @param {vis.Image} imageToCache * @private */ }, { key: "_fixImageCoordinates", value: function _fixImageCoordinates(imageToCache) { if (imageToCache.width === 0) { document.body.appendChild(imageToCache); imageToCache.width = imageToCache.offsetWidth; imageToCache.height = imageToCache.offsetHeight; document.body.removeChild(imageToCache); } } }]); return Images; }(); var mapExports = {}; var map$2 = { get exports() { return mapExports; }, set exports(v) { mapExports = v; } }; var internalMetadataExports = {}; var internalMetadata = { get exports() { return internalMetadataExports; }, set exports(v) { internalMetadataExports = v; } }; // FF26- bug: ArrayBuffers are non-extensible, but Object.isExtensible does not report it var fails$8 = fails$w; var arrayBufferNonExtensible = fails$8(function () { if (typeof ArrayBuffer == 'function') { var buffer = new ArrayBuffer(8); // eslint-disable-next-line es/no-object-isextensible, es/no-object-defineproperty -- safe if (Object.isExtensible(buffer)) Object.defineProperty(buffer, 'a', { value: 8 }); } }); var fails$7 = fails$w; var isObject$6 = isObject$j; var classof$2 = classofRaw$2; var ARRAY_BUFFER_NON_EXTENSIBLE = arrayBufferNonExtensible; // eslint-disable-next-line es/no-object-isextensible -- safe var $isExtensible = Object.isExtensible; var FAILS_ON_PRIMITIVES$1 = fails$7(function () { $isExtensible(1); }); // `Object.isExtensible` method // https://tc39.es/ecma262/#sec-object.isextensible var objectIsExtensible = FAILS_ON_PRIMITIVES$1 || ARRAY_BUFFER_NON_EXTENSIBLE ? function isExtensible(it) { if (!isObject$6(it)) return false; if (ARRAY_BUFFER_NON_EXTENSIBLE && classof$2(it) == 'ArrayBuffer') return false; return $isExtensible ? $isExtensible(it) : true; } : $isExtensible; var fails$6 = fails$w; var freezing = !fails$6(function () { // eslint-disable-next-line es/no-object-isextensible, es/no-object-preventextensions -- required for testing return Object.isExtensible(Object.preventExtensions({})); }); var $$d = _export; var uncurryThis$4 = functionUncurryThis; var hiddenKeys = hiddenKeys$6; var isObject$5 = isObject$j; var hasOwn$3 = hasOwnProperty_1; var defineProperty$1 = objectDefineProperty.f; var getOwnPropertyNamesModule = objectGetOwnPropertyNames; var getOwnPropertyNamesExternalModule = objectGetOwnPropertyNamesExternal; var isExtensible$1 = objectIsExtensible; var uid = uid$4; var FREEZING$1 = freezing; var REQUIRED = false; var METADATA = uid('meta'); var id$1 = 0; var setMetadata = function (it) { defineProperty$1(it, METADATA, { value: { objectID: 'O' + id$1++, // object ID weakData: {} // weak collections IDs } }); }; var fastKey$1 = function (it, create) { // return a primitive with prefix if (!isObject$5(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it; if (!hasOwn$3(it, METADATA)) { // can't set metadata to uncaught frozen object if (!isExtensible$1(it)) return 'F'; // not necessary to add metadata if (!create) return 'E'; // add missing metadata setMetadata(it); // return object ID } return it[METADATA].objectID; }; var getWeakData$1 = function (it, create) { if (!hasOwn$3(it, METADATA)) { // can't set metadata to uncaught frozen object if (!isExtensible$1(it)) return true; // not necessary to add metadata if (!create) return false; // add missing metadata setMetadata(it); // return the store of weak collections IDs } return it[METADATA].weakData; }; // add metadata on freeze-family methods calling var onFreeze = function (it) { if (FREEZING$1 && REQUIRED && isExtensible$1(it) && !hasOwn$3(it, METADATA)) setMetadata(it); return it; }; var enable = function () { meta.enable = function () {/* empty */}; REQUIRED = true; var getOwnPropertyNames = getOwnPropertyNamesModule.f; var splice = uncurryThis$4([].splice); var test = {}; test[METADATA] = 1; // prevent exposing of metadata key if (getOwnPropertyNames(test).length) { getOwnPropertyNamesModule.f = function (it) { var result = getOwnPropertyNames(it); for (var i = 0, length = result.length; i < length; i++) { if (result[i] === METADATA) { splice(result, i, 1); break; } } return result; }; $$d({ target: 'Object', stat: true, forced: true }, { getOwnPropertyNames: getOwnPropertyNamesExternalModule.f }); } }; var meta = internalMetadata.exports = { enable: enable, fastKey: fastKey$1, getWeakData: getWeakData$1, onFreeze: onFreeze }; hiddenKeys[METADATA] = true; var bind$6 = functionBindContext; var call$1 = functionCall; var anObject$3 = anObject$d; var tryToString$1 = tryToString$6; var isArrayIteratorMethod = isArrayIteratorMethod$2; var lengthOfArrayLike$2 = lengthOfArrayLike$b; var isPrototypeOf$6 = objectIsPrototypeOf; var getIterator = getIterator$2; var getIteratorMethod = getIteratorMethod$9; var iteratorClose = iteratorClose$2; var $TypeError$3 = TypeError; var Result = function (stopped, result) { this.stopped = stopped; this.result = result; }; var ResultPrototype = Result.prototype; var iterate$3 = function (iterable, unboundFunction, options) { var that = options && options.that; var AS_ENTRIES = !!(options && options.AS_ENTRIES); var IS_RECORD = !!(options && options.IS_RECORD); var IS_ITERATOR = !!(options && options.IS_ITERATOR); var INTERRUPTED = !!(options && options.INTERRUPTED); var fn = bind$6(unboundFunction, that); var iterator, iterFn, index, length, result, next, step; var stop = function (condition) { if (iterator) iteratorClose(iterator, 'normal', condition); return new Result(true, condition); }; var callFn = function (value) { if (AS_ENTRIES) { anObject$3(value); return INTERRUPTED ? fn(value[0], value[1], stop) : fn(value[0], value[1]); } return INTERRUPTED ? fn(value, stop) : fn(value); }; if (IS_RECORD) { iterator = iterable.iterator; } else if (IS_ITERATOR) { iterator = iterable; } else { iterFn = getIteratorMethod(iterable); if (!iterFn) throw $TypeError$3(tryToString$1(iterable) + ' is not iterable'); // optimisation for array iterators if (isArrayIteratorMethod(iterFn)) { for (index = 0, length = lengthOfArrayLike$2(iterable); length > index; index++) { result = callFn(iterable[index]); if (result && isPrototypeOf$6(ResultPrototype, result)) return result; } return new Result(false); } iterator = getIterator(iterable, iterFn); } next = IS_RECORD ? iterable.next : iterator.next; while (!(step = call$1(next, iterator)).done) { try { result = callFn(step.value); } catch (error) { iteratorClose(iterator, 'throw', error); } if (typeof result == 'object' && result && isPrototypeOf$6(ResultPrototype, result)) return result; } return new Result(false); }; var isPrototypeOf$5 = objectIsPrototypeOf; var $TypeError$2 = TypeError; var anInstance$3 = function (it, Prototype) { if (isPrototypeOf$5(Prototype, it)) return it; throw $TypeError$2('Incorrect invocation'); }; var $$c = _export; var global$4 = global$l; var InternalMetadataModule$1 = internalMetadataExports; var fails$5 = fails$w; var createNonEnumerableProperty = createNonEnumerableProperty$6; var iterate$2 = iterate$3; var anInstance$2 = anInstance$3; var isCallable = isCallable$i; var isObject$4 = isObject$j; var setToStringTag = setToStringTag$6; var defineProperty = objectDefineProperty.f; var forEach = arrayIteration.forEach; var DESCRIPTORS$2 = descriptors; var InternalStateModule$2 = internalState; var setInternalState$2 = InternalStateModule$2.set; var internalStateGetterFor$2 = InternalStateModule$2.getterFor; var collection$3 = function (CONSTRUCTOR_NAME, wrapper, common) { var IS_MAP = CONSTRUCTOR_NAME.indexOf('Map') !== -1; var IS_WEAK = CONSTRUCTOR_NAME.indexOf('Weak') !== -1; var ADDER = IS_MAP ? 'set' : 'add'; var NativeConstructor = global$4[CONSTRUCTOR_NAME]; var NativePrototype = NativeConstructor && NativeConstructor.prototype; var exported = {}; var Constructor; if (!DESCRIPTORS$2 || !isCallable(NativeConstructor) || !(IS_WEAK || NativePrototype.forEach && !fails$5(function () { new NativeConstructor().entries().next(); }))) { // create collection constructor Constructor = common.getConstructor(wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER); InternalMetadataModule$1.enable(); } else { Constructor = wrapper(function (target, iterable) { setInternalState$2(anInstance$2(target, Prototype), { type: CONSTRUCTOR_NAME, collection: new NativeConstructor() }); if (iterable != undefined) iterate$2(iterable, target[ADDER], { that: target, AS_ENTRIES: IS_MAP }); }); var Prototype = Constructor.prototype; var getInternalState = internalStateGetterFor$2(CONSTRUCTOR_NAME); forEach(['add', 'clear', 'delete', 'forEach', 'get', 'has', 'set', 'keys', 'values', 'entries'], function (KEY) { var IS_ADDER = KEY == 'add' || KEY == 'set'; if (KEY in NativePrototype && !(IS_WEAK && KEY == 'clear')) { createNonEnumerableProperty(Prototype, KEY, function (a, b) { var collection = getInternalState(this).collection; if (!IS_ADDER && IS_WEAK && !isObject$4(a)) return KEY == 'get' ? undefined : false; var result = collection[KEY](a === 0 ? 0 : a, b); return IS_ADDER ? this : result; }); } }); IS_WEAK || defineProperty(Prototype, 'size', { configurable: true, get: function () { return getInternalState(this).collection.size; } }); } setToStringTag(Constructor, CONSTRUCTOR_NAME, false, true); exported[CONSTRUCTOR_NAME] = Constructor; $$c({ global: true, forced: true }, exported); if (!IS_WEAK) common.setStrong(Constructor, CONSTRUCTOR_NAME, IS_MAP); return Constructor; }; var defineBuiltIn = defineBuiltIn$5; var defineBuiltIns$3 = function (target, src, options) { for (var key in src) { if (options && options.unsafe && target[key]) target[key] = src[key];else defineBuiltIn(target, key, src[key], options); } return target; }; var getBuiltIn$1 = getBuiltIn$c; var defineBuiltInAccessor$1 = defineBuiltInAccessor$3; var wellKnownSymbol = wellKnownSymbol$l; var DESCRIPTORS$1 = descriptors; var SPECIES = wellKnownSymbol('species'); var setSpecies$1 = function (CONSTRUCTOR_NAME) { var Constructor = getBuiltIn$1(CONSTRUCTOR_NAME); if (DESCRIPTORS$1 && Constructor && !Constructor[SPECIES]) { defineBuiltInAccessor$1(Constructor, SPECIES, { configurable: true, get: function () { return this; } }); } }; var create$5 = objectCreate; var defineBuiltInAccessor = defineBuiltInAccessor$3; var defineBuiltIns$2 = defineBuiltIns$3; var bind$5 = functionBindContext; var anInstance$1 = anInstance$3; var isNullOrUndefined$1 = isNullOrUndefined$5; var iterate$1 = iterate$3; var defineIterator = iteratorDefine; var createIterResultObject = createIterResultObject$3; var setSpecies = setSpecies$1; var DESCRIPTORS = descriptors; var fastKey = internalMetadataExports.fastKey; var InternalStateModule$1 = internalState; var setInternalState$1 = InternalStateModule$1.set; var internalStateGetterFor$1 = InternalStateModule$1.getterFor; var collectionStrong$2 = { getConstructor: function (wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER) { var Constructor = wrapper(function (that, iterable) { anInstance$1(that, Prototype); setInternalState$1(that, { type: CONSTRUCTOR_NAME, index: create$5(null), first: undefined, last: undefined, size: 0 }); if (!DESCRIPTORS) that.size = 0; if (!isNullOrUndefined$1(iterable)) iterate$1(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP }); }); var Prototype = Constructor.prototype; var getInternalState = internalStateGetterFor$1(CONSTRUCTOR_NAME); var define = function (that, key, value) { var state = getInternalState(that); var entry = getEntry(that, key); var previous, index; // change existing entry if (entry) { entry.value = value; // create new entry } else { state.last = entry = { index: index = fastKey(key, true), key: key, value: value, previous: previous = state.last, next: undefined, removed: false }; if (!state.first) state.first = entry; if (previous) previous.next = entry; if (DESCRIPTORS) state.size++;else that.size++; // add to index if (index !== 'F') state.index[index] = entry; } return that; }; var getEntry = function (that, key) { var state = getInternalState(that); // fast case var index = fastKey(key); var entry; if (index !== 'F') return state.index[index]; // frozen object case for (entry = state.first; entry; entry = entry.next) { if (entry.key == key) return entry; } }; defineBuiltIns$2(Prototype, { // `{ Map, Set }.prototype.clear()` methods // https://tc39.es/ecma262/#sec-map.prototype.clear // https://tc39.es/ecma262/#sec-set.prototype.clear clear: function clear() { var that = this; var state = getInternalState(that); var data = state.index; var entry = state.first; while (entry) { entry.removed = true; if (entry.previous) entry.previous = entry.previous.next = undefined; delete data[entry.index]; entry = entry.next; } state.first = state.last = undefined; if (DESCRIPTORS) state.size = 0;else that.size = 0; }, // `{ Map, Set }.prototype.delete(key)` methods // https://tc39.es/ecma262/#sec-map.prototype.delete // https://tc39.es/ecma262/#sec-set.prototype.delete 'delete': function (key) { var that = this; var state = getInternalState(that); var entry = getEntry(that, key); if (entry) { var next = entry.next; var prev = entry.previous; delete state.index[entry.index]; entry.removed = true; if (prev) prev.next = next; if (next) next.previous = prev; if (state.first == entry) state.first = next; if (state.last == entry) state.last = prev; if (DESCRIPTORS) state.size--;else that.size--; } return !!entry; }, // `{ Map, Set }.prototype.forEach(callbackfn, thisArg = undefined)` methods // https://tc39.es/ecma262/#sec-map.prototype.foreach // https://tc39.es/ecma262/#sec-set.prototype.foreach forEach: function forEach(callbackfn /* , that = undefined */) { var state = getInternalState(this); var boundFunction = bind$5(callbackfn, arguments.length > 1 ? arguments[1] : undefined); var entry; while (entry = entry ? entry.next : state.first) { boundFunction(entry.value, entry.key, this); // revert to the last existing entry while (entry && entry.removed) entry = entry.previous; } }, // `{ Map, Set}.prototype.has(key)` methods // https://tc39.es/ecma262/#sec-map.prototype.has // https://tc39.es/ecma262/#sec-set.prototype.has has: function has(key) { return !!getEntry(this, key); } }); defineBuiltIns$2(Prototype, IS_MAP ? { // `Map.prototype.get(key)` method // https://tc39.es/ecma262/#sec-map.prototype.get get: function get(key) { var entry = getEntry(this, key); return entry && entry.value; }, // `Map.prototype.set(key, value)` method // https://tc39.es/ecma262/#sec-map.prototype.set set: function set(key, value) { return define(this, key === 0 ? 0 : key, value); } } : { // `Set.prototype.add(value)` method // https://tc39.es/ecma262/#sec-set.prototype.add add: function add(value) { return define(this, value = value === 0 ? 0 : value, value); } }); if (DESCRIPTORS) defineBuiltInAccessor(Prototype, 'size', { configurable: true, get: function () { return getInternalState(this).size; } }); return Constructor; }, setStrong: function (Constructor, CONSTRUCTOR_NAME, IS_MAP) { var ITERATOR_NAME = CONSTRUCTOR_NAME + ' Iterator'; var getInternalCollectionState = internalStateGetterFor$1(CONSTRUCTOR_NAME); var getInternalIteratorState = internalStateGetterFor$1(ITERATOR_NAME); // `{ Map, Set }.prototype.{ keys, values, entries, @@iterator }()` methods // https://tc39.es/ecma262/#sec-map.prototype.entries // https://tc39.es/ecma262/#sec-map.prototype.keys // https://tc39.es/ecma262/#sec-map.prototype.values // https://tc39.es/ecma262/#sec-map.prototype-@@iterator // https://tc39.es/ecma262/#sec-set.prototype.entries // https://tc39.es/ecma262/#sec-set.prototype.keys // https://tc39.es/ecma262/#sec-set.prototype.values // https://tc39.es/ecma262/#sec-set.prototype-@@iterator defineIterator(Constructor, CONSTRUCTOR_NAME, function (iterated, kind) { setInternalState$1(this, { type: ITERATOR_NAME, target: iterated, state: getInternalCollectionState(iterated), kind: kind, last: undefined }); }, function () { var state = getInternalIteratorState(this); var kind = state.kind; var entry = state.last; // revert to the last existing entry while (entry && entry.removed) entry = entry.previous; // get next entry if (!state.target || !(state.last = entry = entry ? entry.next : state.state.first)) { // or finish the iteration state.target = undefined; return createIterResultObject(undefined, true); } // return step by kind if (kind == 'keys') return createIterResultObject(entry.key, false); if (kind == 'values') return createIterResultObject(entry.value, false); return createIterResultObject([entry.key, entry.value], false); }, IS_MAP ? 'entries' : 'values', !IS_MAP, true); // `{ Map, Set }.prototype[@@species]` accessors // https://tc39.es/ecma262/#sec-get-map-@@species // https://tc39.es/ecma262/#sec-get-set-@@species setSpecies(CONSTRUCTOR_NAME); } }; var collection$2 = collection$3; var collectionStrong$1 = collectionStrong$2; // `Map` constructor // https://tc39.es/ecma262/#sec-map-objects collection$2('Map', function (init) { return function Map() { return init(this, arguments.length ? arguments[0] : undefined); }; }, collectionStrong$1); var path$b = path$y; var map$1 = path$b.Map; var parent$r = map$1; var map = parent$r; (function (module) { module.exports = map; })(map$2); var _Map = /*@__PURE__*/getDefaultExportFromCjs(mapExports); /** * This class can store groups and options specific for groups. */ var Groups = /*#__PURE__*/function () { /** * @ignore */ function Groups() { _classCallCheck(this, Groups); this.clear(); this._defaultIndex = 0; this._groupIndex = 0; this._defaultGroups = [{ border: "#2B7CE9", background: "#97C2FC", highlight: { border: "#2B7CE9", background: "#D2E5FF" }, hover: { border: "#2B7CE9", background: "#D2E5FF" } }, // 0: blue { border: "#FFA500", background: "#FFFF00", highlight: { border: "#FFA500", background: "#FFFFA3" }, hover: { border: "#FFA500", background: "#FFFFA3" } }, // 1: yellow { border: "#FA0A10", background: "#FB7E81", highlight: { border: "#FA0A10", background: "#FFAFB1" }, hover: { border: "#FA0A10", background: "#FFAFB1" } }, // 2: red { border: "#41A906", background: "#7BE141", highlight: { border: "#41A906", background: "#A1EC76" }, hover: { border: "#41A906", background: "#A1EC76" } }, // 3: green { border: "#E129F0", background: "#EB7DF4", highlight: { border: "#E129F0", background: "#F0B3F5" }, hover: { border: "#E129F0", background: "#F0B3F5" } }, // 4: magenta { border: "#7C29F0", background: "#AD85E4", highlight: { border: "#7C29F0", background: "#D3BDF0" }, hover: { border: "#7C29F0", background: "#D3BDF0" } }, // 5: purple { border: "#C37F00", background: "#FFA807", highlight: { border: "#C37F00", background: "#FFCA66" }, hover: { border: "#C37F00", background: "#FFCA66" } }, // 6: orange { border: "#4220FB", background: "#6E6EFD", highlight: { border: "#4220FB", background: "#9B9BFD" }, hover: { border: "#4220FB", background: "#9B9BFD" } }, // 7: darkblue { border: "#FD5A77", background: "#FFC0CB", highlight: { border: "#FD5A77", background: "#FFD1D9" }, hover: { border: "#FD5A77", background: "#FFD1D9" } }, // 8: pink { border: "#4AD63A", background: "#C2FABC", highlight: { border: "#4AD63A", background: "#E6FFE3" }, hover: { border: "#4AD63A", background: "#E6FFE3" } }, // 9: mint { border: "#990000", background: "#EE0000", highlight: { border: "#BB0000", background: "#FF3333" }, hover: { border: "#BB0000", background: "#FF3333" } }, // 10:bright red { border: "#FF6000", background: "#FF6000", highlight: { border: "#FF6000", background: "#FF6000" }, hover: { border: "#FF6000", background: "#FF6000" } }, // 12: real orange { border: "#97C2FC", background: "#2B7CE9", highlight: { border: "#D2E5FF", background: "#2B7CE9" }, hover: { border: "#D2E5FF", background: "#2B7CE9" } }, // 13: blue { border: "#399605", background: "#255C03", highlight: { border: "#399605", background: "#255C03" }, hover: { border: "#399605", background: "#255C03" } }, // 14: green { border: "#B70054", background: "#FF007E", highlight: { border: "#B70054", background: "#FF007E" }, hover: { border: "#B70054", background: "#FF007E" } }, // 15: magenta { border: "#AD85E4", background: "#7C29F0", highlight: { border: "#D3BDF0", background: "#7C29F0" }, hover: { border: "#D3BDF0", background: "#7C29F0" } }, // 16: purple { border: "#4557FA", background: "#000EA1", highlight: { border: "#6E6EFD", background: "#000EA1" }, hover: { border: "#6E6EFD", background: "#000EA1" } }, // 17: darkblue { border: "#FFC0CB", background: "#FD5A77", highlight: { border: "#FFD1D9", background: "#FD5A77" }, hover: { border: "#FFD1D9", background: "#FD5A77" } }, // 18: pink { border: "#C2FABC", background: "#74D66A", highlight: { border: "#E6FFE3", background: "#74D66A" }, hover: { border: "#E6FFE3", background: "#74D66A" } }, // 19: mint { border: "#EE0000", background: "#990000", highlight: { border: "#FF3333", background: "#BB0000" }, hover: { border: "#FF3333", background: "#BB0000" } } // 20:bright red ]; this.options = {}; this.defaultOptions = { useDefaultGroups: true }; _Object$assign(this.options, this.defaultOptions); } /** * * @param {object} options */ _createClass(Groups, [{ key: "setOptions", value: function setOptions(options) { var optionFields = ["useDefaultGroups"]; if (options !== undefined) { for (var groupName in options) { if (Object.prototype.hasOwnProperty.call(options, groupName)) { if (_indexOfInstanceProperty(optionFields).call(optionFields, groupName) === -1) { var group = options[groupName]; this.add(groupName, group); } } } } } /** * Clear all groups */ }, { key: "clear", value: function clear() { this._groups = new _Map(); this._groupNames = []; } /** * Get group options of a groupname. * If groupname is not found, a new group may be created. * * @param {*} groupname Can be a number, string, Date, etc. * @param {boolean} [shouldCreate=true] If true, create a new group * @returns {object} The found or created group */ }, { key: "get", value: function get(groupname) { var shouldCreate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; var group = this._groups.get(groupname); if (group === undefined && shouldCreate) { if (this.options.useDefaultGroups === false && this._groupNames.length > 0) { // create new group var index = this._groupIndex % this._groupNames.length; ++this._groupIndex; group = {}; group.color = this._groups.get(this._groupNames[index]); this._groups.set(groupname, group); } else { // create new group var _index = this._defaultIndex % this._defaultGroups.length; this._defaultIndex++; group = {}; group.color = this._defaultGroups[_index]; this._groups.set(groupname, group); } } return group; } /** * Add custom group style. * * @param {string} groupName - The name of the group, a new group will be * created if a group with the same name doesn't exist, otherwise the old * groups style will be overwritten. * @param {object} style - An object containing borderColor, backgroundColor, * etc. * @returns {object} The created group object. */ }, { key: "add", value: function add(groupName, style) { // Only push group name once to prevent duplicates which would consume more // RAM and also skew the distribution towards more often updated groups, // neither of which is desirable. if (!this._groups.has(groupName)) { this._groupNames.push(groupName); } this._groups.set(groupName, style); return style; } }]); return Groups; }(); var isNanExports = {}; var isNan$2 = { get exports() { return isNanExports; }, set exports(v) { isNanExports = v; } }; var $$b = _export; // `Number.isNaN` method // https://tc39.es/ecma262/#sec-number.isnan $$b({ target: 'Number', stat: true }, { isNaN: function isNaN(number) { // eslint-disable-next-line no-self-compare -- NaN check return number != number; } }); var path$a = path$y; var isNan$1 = path$a.Number.isNaN; var parent$q = isNan$1; var isNan = parent$q; (function (module) { module.exports = isNan; })(isNan$2); var _Number$isNaN = /*@__PURE__*/getDefaultExportFromCjs(isNanExports); var _isFiniteExports = {}; var _isFinite$2 = { get exports() { return _isFiniteExports; }, set exports(v) { _isFiniteExports = v; } }; var global$3 = global$l; var globalIsFinite = global$3.isFinite; // `Number.isFinite` method // https://tc39.es/ecma262/#sec-number.isfinite // eslint-disable-next-line es/no-number-isfinite -- safe var numberIsFinite$1 = Number.isFinite || function isFinite(it) { return typeof it == 'number' && globalIsFinite(it); }; var $$a = _export; var numberIsFinite = numberIsFinite$1; // `Number.isFinite` method // https://tc39.es/ecma262/#sec-number.isfinite $$a({ target: 'Number', stat: true }, { isFinite: numberIsFinite }); var path$9 = path$y; var _isFinite$1 = path$9.Number.isFinite; var parent$p = _isFinite$1; var _isFinite = parent$p; (function (module) { module.exports = _isFinite; })(_isFinite$2); var _Number$isFinite = /*@__PURE__*/getDefaultExportFromCjs(_isFiniteExports); var someExports = {}; var some$3 = { get exports() { return someExports; }, set exports(v) { someExports = v; } }; var $$9 = _export; var $some = arrayIteration.some; var arrayMethodIsStrict$3 = arrayMethodIsStrict$6; var STRICT_METHOD$2 = arrayMethodIsStrict$3('some'); // `Array.prototype.some` method // https://tc39.es/ecma262/#sec-array.prototype.some $$9({ target: 'Array', proto: true, forced: !STRICT_METHOD$2 }, { some: function some(callbackfn /* , thisArg */) { return $some(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); } }); var entryVirtual$4 = entryVirtual$i; var some$2 = entryVirtual$4('Array').some; var isPrototypeOf$4 = objectIsPrototypeOf; var method$4 = some$2; var ArrayPrototype$4 = Array.prototype; var some$1 = function (it) { var own = it.some; return it === ArrayPrototype$4 || isPrototypeOf$4(ArrayPrototype$4, it) && own === ArrayPrototype$4.some ? method$4 : own; }; var parent$o = some$1; var some = parent$o; (function (module) { module.exports = some; })(some$3); var _someInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(someExports); var _parseFloatExports = {}; var _parseFloat$3 = { get exports() { return _parseFloatExports; }, set exports(v) { _parseFloatExports = v; } }; var global$2 = global$l; var fails$4 = fails$w; var uncurryThis$3 = functionUncurryThis; var toString$1 = toString$a; var trim = stringTrim.trim; var whitespaces = whitespaces$4; var charAt = uncurryThis$3(''.charAt); var $parseFloat$1 = global$2.parseFloat; var Symbol$1 = global$2.Symbol; var ITERATOR = Symbol$1 && Symbol$1.iterator; var FORCED$4 = 1 / $parseFloat$1(whitespaces + '-0') !== -Infinity // MS Edge 18- broken with boxed symbols || ITERATOR && !fails$4(function () { $parseFloat$1(Object(ITERATOR)); }); // `parseFloat` method // https://tc39.es/ecma262/#sec-parsefloat-string var numberParseFloat = FORCED$4 ? function parseFloat(string) { var trimmedString = trim(toString$1(string)); var result = $parseFloat$1(trimmedString); return result === 0 && charAt(trimmedString, 0) == '-' ? -0 : result; } : $parseFloat$1; var $$8 = _export; var $parseFloat = numberParseFloat; // `parseFloat` method // https://tc39.es/ecma262/#sec-parsefloat-string $$8({ global: true, forced: parseFloat != $parseFloat }, { parseFloat: $parseFloat }); var path$8 = path$y; var _parseFloat$2 = path$8.parseFloat; var parent$n = _parseFloat$2; var _parseFloat$1 = parent$n; (function (module) { module.exports = _parseFloat$1; })(_parseFloat$3); var _parseFloat = /*@__PURE__*/getDefaultExportFromCjs(_parseFloatExports); var getOwnPropertyNamesExports = {}; var getOwnPropertyNames$3 = { get exports() { return getOwnPropertyNamesExports; }, set exports(v) { getOwnPropertyNamesExports = v; } }; var $$7 = _export; var fails$3 = fails$w; var getOwnPropertyNames$2 = objectGetOwnPropertyNamesExternal.f; // eslint-disable-next-line es/no-object-getownpropertynames -- required for testing var FAILS_ON_PRIMITIVES = fails$3(function () { return !Object.getOwnPropertyNames(1); }); // `Object.getOwnPropertyNames` method // https://tc39.es/ecma262/#sec-object.getownpropertynames $$7({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES }, { getOwnPropertyNames: getOwnPropertyNames$2 }); var path$7 = path$y; var Object$1 = path$7.Object; var getOwnPropertyNames$1 = function getOwnPropertyNames(it) { return Object$1.getOwnPropertyNames(it); }; var parent$m = getOwnPropertyNames$1; var getOwnPropertyNames = parent$m; (function (module) { module.exports = getOwnPropertyNames; })(getOwnPropertyNames$3); var _Object$getOwnPropertyNames = /*@__PURE__*/getDefaultExportFromCjs(getOwnPropertyNamesExports); /** * Helper functions for components */ /** * Determine values to use for (sub)options of 'chosen'. * * This option is either a boolean or an object whose values should be examined further. * The relevant structures are: * * - chosen: <boolean value> * - chosen: { subOption: <boolean or function> } * * Where subOption is 'node', 'edge' or 'label'. * * The intention of this method appears to be to set a specific priority to the options; * Since most properties are either bridged or merged into the local options objects, there * is not much point in handling them separately. * TODO: examine if 'most' in previous sentence can be replaced with 'all'. In that case, we * should be able to get rid of this method. * * @param {string} subOption option within object 'chosen' to consider; either 'node', 'edge' or 'label' * @param {object} pile array of options objects to consider * @returns {boolean | Function} value for passed subOption of 'chosen' to use */ function choosify(subOption, pile) { // allowed values for subOption var allowed = ["node", "edge", "label"]; var value = true; var chosen = topMost(pile, "chosen"); if (typeof chosen === "boolean") { value = chosen; } else if (_typeof(chosen) === "object") { if (_indexOfInstanceProperty(allowed).call(allowed, subOption) === -1) { throw new Error("choosify: subOption '" + subOption + "' should be one of " + "'" + allowed.join("', '") + "'"); } var chosenEdge = topMost(pile, ["chosen", subOption]); if (typeof chosenEdge === "boolean" || typeof chosenEdge === "function") { value = chosenEdge; } } return value; } /** * Check if the point falls within the given rectangle. * * @param {rect} rect * @param {point} point * @param {rotationPoint} [rotationPoint] if specified, the rotation that applies to the rectangle. * @returns {boolean} true if point within rectangle, false otherwise */ function pointInRect(rect, point, rotationPoint) { if (rect.width <= 0 || rect.height <= 0) { return false; // early out } if (rotationPoint !== undefined) { // Rotate the point the same amount as the rectangle var tmp = { x: point.x - rotationPoint.x, y: point.y - rotationPoint.y }; if (rotationPoint.angle !== 0) { // In order to get the coordinates the same, you need to // rotate in the reverse direction var angle = -rotationPoint.angle; var tmp2 = { x: Math.cos(angle) * tmp.x - Math.sin(angle) * tmp.y, y: Math.sin(angle) * tmp.x + Math.cos(angle) * tmp.y }; point = tmp2; } else { point = tmp; } // Note that if a rotation is specified, the rectangle coordinates // are **not* the full canvas coordinates. They are relative to the // rotationPoint. Hence, the point coordinates need not be translated // back in this case. } var right = rect.x + rect.width; var bottom = rect.y + rect.width; return rect.left < point.x && right > point.x && rect.top < point.y && bottom > point.y; } /** * Check if given value is acceptable as a label text. * * @param {*} text value to check; can be anything at this point * @returns {boolean} true if valid label value, false otherwise */ function isValidLabel(text) { // Note that this is quite strict: types that *might* be converted to string are disallowed return typeof text === "string" && text !== ""; } /** * Returns x, y of self reference circle based on provided angle * * @param {object} ctx * @param {number} angle * @param {number} radius * @param {VisNode} node * @returns {object} x and y coordinates */ function getSelfRefCoordinates(ctx, angle, radius, node) { var x = node.x; var y = node.y; if (typeof node.distanceToBorder === "function") { //calculating opposite and adjacent //distaneToBorder becomes Hypotenuse. //Formulas sin(a) = Opposite / Hypotenuse and cos(a) = Adjacent / Hypotenuse var toBorderDist = node.distanceToBorder(ctx, angle); var yFromNodeCenter = Math.sin(angle) * toBorderDist; var xFromNodeCenter = Math.cos(angle) * toBorderDist; //xFromNodeCenter is basically x and if xFromNodeCenter equals to the distance to border then it means //that y does not need calculation because it is equal node.height / 2 or node.y //same thing with yFromNodeCenter and if yFromNodeCenter equals to the distance to border then it means //that x is equal node.width / 2 or node.x if (xFromNodeCenter === toBorderDist) { x += toBorderDist; y = node.y; } else if (yFromNodeCenter === toBorderDist) { x = node.x; y -= toBorderDist; } else { x += xFromNodeCenter; y -= yFromNodeCenter; } } else if (node.shape.width > node.shape.height) { x = node.x + node.shape.width * 0.5; y = node.y - radius; } else { x = node.x + radius; y = node.y - node.shape.height * 0.5; } return { x: x, y: y }; } var valuesExports = {}; var values$3 = { get exports() { return valuesExports; }, set exports(v) { valuesExports = v; } }; var entryVirtual$3 = entryVirtual$i; var values$2 = entryVirtual$3('Array').values; var parent$l = values$2; var values$1 = parent$l; var classof$1 = classof$d; var hasOwn$2 = hasOwnProperty_1; var isPrototypeOf$3 = objectIsPrototypeOf; var method$3 = values$1; var ArrayPrototype$3 = Array.prototype; var DOMIterables = { DOMTokenList: true, NodeList: true }; var values = function (it) { var own = it.values; return it === ArrayPrototype$3 || isPrototypeOf$3(ArrayPrototype$3, it) && own === ArrayPrototype$3.values || hasOwn$2(DOMIterables, classof$1(it)) ? method$3 : own; }; (function (module) { module.exports = values; })(values$3); var _valuesInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(valuesExports); /** * Callback to determine text dimensions, using the parent label settings. * * @callback MeasureText * @param {text} text * @param {text} mod * @returns {object} { width, values} width in pixels and font attributes */ /** * Helper class for Label which collects results of splitting labels into lines and blocks. * * @private */ var LabelAccumulator = /*#__PURE__*/function () { /** * @param {MeasureText} measureText */ function LabelAccumulator(measureText) { _classCallCheck(this, LabelAccumulator); this.measureText = measureText; this.current = 0; this.width = 0; this.height = 0; this.lines = []; } /** * Append given text to the given line. * * @param {number} l index of line to add to * @param {string} text string to append to line * @param {'bold'|'ital'|'boldital'|'mono'|'normal'} [mod='normal'] * @private */ _createClass(LabelAccumulator, [{ key: "_add", value: function _add(l, text) { var mod = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "normal"; if (this.lines[l] === undefined) { this.lines[l] = { width: 0, height: 0, blocks: [] }; } // We still need to set a block for undefined and empty texts, hence return at this point // This is necessary because we don't know at this point if we're at the // start of an empty line or not. // To compensate, empty blocks are removed in `finalize()`. // // Empty strings should still have a height var tmpText = text; if (text === undefined || text === "") tmpText = " "; // Determine width and get the font properties var result = this.measureText(tmpText, mod); var block = _Object$assign({}, _valuesInstanceProperty(result)); block.text = text; block.width = result.width; block.mod = mod; if (text === undefined || text === "") { block.width = 0; } this.lines[l].blocks.push(block); // Update the line width. We need this for determining if a string goes over max width this.lines[l].width += block.width; } /** * Returns the width in pixels of the current line. * * @returns {number} */ }, { key: "curWidth", value: function curWidth() { var line = this.lines[this.current]; if (line === undefined) return 0; return line.width; } /** * Add text in block to current line * * @param {string} text * @param {'bold'|'ital'|'boldital'|'mono'|'normal'} [mod='normal'] */ }, { key: "append", value: function append(text) { var mod = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "normal"; this._add(this.current, text, mod); } /** * Add text in block to current line and start a new line * * @param {string} text * @param {'bold'|'ital'|'boldital'|'mono'|'normal'} [mod='normal'] */ }, { key: "newLine", value: function newLine(text) { var mod = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "normal"; this._add(this.current, text, mod); this.current++; } /** * Determine and set the heights of all the lines currently contained in this instance * * Note that width has already been set. * * @private */ }, { key: "determineLineHeights", value: function determineLineHeights() { for (var k = 0; k < this.lines.length; k++) { var line = this.lines[k]; // Looking for max height of blocks in line var height = 0; if (line.blocks !== undefined) { // Can happen if text contains e.g. '\n ' for (var l = 0; l < line.blocks.length; l++) { var block = line.blocks[l]; if (height < block.height) { height = block.height; } } } line.height = height; } } /** * Determine the full size of the label text, as determined by current lines and blocks * * @private */ }, { key: "determineLabelSize", value: function determineLabelSize() { var width = 0; var height = 0; for (var k = 0; k < this.lines.length; k++) { var line = this.lines[k]; if (line.width > width) { width = line.width; } height += line.height; } this.width = width; this.height = height; } /** * Remove all empty blocks and empty lines we don't need * * This must be done after the width/height determination, * so that these are set properly for processing here. * * @returns {Array<Line>} Lines with empty blocks (and some empty lines) removed * @private */ }, { key: "removeEmptyBlocks", value: function removeEmptyBlocks() { var tmpLines = []; for (var k = 0; k < this.lines.length; k++) { var line = this.lines[k]; // Note: an empty line in between text has width zero but is still relevant to layout. // So we can't use width for testing empty line here if (line.blocks.length === 0) continue; // Discard final empty line always if (k === this.lines.length - 1) { if (line.width === 0) continue; } var tmpLine = {}; _Object$assign(tmpLine, line); tmpLine.blocks = []; var firstEmptyBlock = void 0; var tmpBlocks = []; for (var l = 0; l < line.blocks.length; l++) { var block = line.blocks[l]; if (block.width !== 0) { tmpBlocks.push(block); } else { if (firstEmptyBlock === undefined) { firstEmptyBlock = block; } } } // Ensure that there is *some* text present if (tmpBlocks.length === 0 && firstEmptyBlock !== undefined) { tmpBlocks.push(firstEmptyBlock); } tmpLine.blocks = tmpBlocks; tmpLines.push(tmpLine); } return tmpLines; } /** * Set the sizes for all lines and the whole thing. * * @returns {{width: (number|*), height: (number|*), lines: Array}} */ }, { key: "finalize", value: function finalize() { //console.log(JSON.stringify(this.lines, null, 2)); this.determineLineHeights(); this.determineLabelSize(); var tmpLines = this.removeEmptyBlocks(); // Return a simple hash object for further processing. return { width: this.width, height: this.height, lines: tmpLines }; } }]); return LabelAccumulator; }(); // Hash of prepared regexp's for tags var tagPattern = { // HTML "<b>": /<b>/, "<i>": /<i>/, "<code>": /<code>/, "</b>": /<\/b>/, "</i>": /<\/i>/, "</code>": /<\/code>/, // Markdown "*": /\*/, // bold _: /_/, // ital "`": /`/, // mono afterBold: /[^*]/, afterItal: /[^_]/, afterMono: /[^`]/ }; /** * Internal helper class for parsing the markup tags for HTML and Markdown. * * NOTE: Sequences of tabs and spaces are reduced to single space. * Scan usage of `this.spacing` within method */ var MarkupAccumulator = /*#__PURE__*/function () { /** * Create an instance * * @param {string} text text to parse for markup */ function MarkupAccumulator(text) { _classCallCheck(this, MarkupAccumulator); this.text = text; this.bold = false; this.ital = false; this.mono = false; this.spacing = false; this.position = 0; this.buffer = ""; this.modStack = []; this.blocks = []; } /** * Return the mod label currently on the top of the stack * * @returns {string} label of topmost mod * @private */ _createClass(MarkupAccumulator, [{ key: "mod", value: function mod() { return this.modStack.length === 0 ? "normal" : this.modStack[0]; } /** * Return the mod label currently active * * @returns {string} label of active mod * @private */ }, { key: "modName", value: function modName() { if (this.modStack.length === 0) return "normal";else if (this.modStack[0] === "mono") return "mono";else { if (this.bold && this.ital) { return "boldital"; } else if (this.bold) { return "bold"; } else if (this.ital) { return "ital"; } } } /** * @private */ }, { key: "emitBlock", value: function emitBlock() { if (this.spacing) { this.add(" "); this.spacing = false; } if (this.buffer.length > 0) { this.blocks.push({ text: this.buffer, mod: this.modName() }); this.buffer = ""; } } /** * Output text to buffer * * @param {string} text text to add * @private */ }, { key: "add", value: function add(text) { if (text === " ") { this.spacing = true; } if (this.spacing) { this.buffer += " "; this.spacing = false; } if (text != " ") { this.buffer += text; } } /** * Handle parsing of whitespace * * @param {string} ch the character to check * @returns {boolean} true if the character was processed as whitespace, false otherwise */ }, { key: "parseWS", value: function parseWS(ch) { if (/[ \t]/.test(ch)) { if (!this.mono) { this.spacing = true; } else { this.add(ch); } return true; } return false; } /** * @param {string} tagName label for block type to set * @private */ }, { key: "setTag", value: function setTag(tagName) { this.emitBlock(); this[tagName] = true; this.modStack.unshift(tagName); } /** * @param {string} tagName label for block type to unset * @private */ }, { key: "unsetTag", value: function unsetTag(tagName) { this.emitBlock(); this[tagName] = false; this.modStack.shift(); } /** * @param {string} tagName label for block type we are currently processing * @param {string|RegExp} tag string to match in text * @returns {boolean} true if the tag was processed, false otherwise */ }, { key: "parseStartTag", value: function parseStartTag(tagName, tag) { // Note: if 'mono' passed as tagName, there is a double check here. This is OK if (!this.mono && !this[tagName] && this.match(tag)) { this.setTag(tagName); return true; } return false; } /** * @param {string|RegExp} tag * @param {number} [advance=true] if set, advance current position in text * @returns {boolean} true if match at given position, false otherwise * @private */ }, { key: "match", value: function match(tag) { var advance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; var _this$prepareRegExp = this.prepareRegExp(tag), _this$prepareRegExp2 = _slicedToArray(_this$prepareRegExp, 2), regExp = _this$prepareRegExp2[0], length = _this$prepareRegExp2[1]; var matched = regExp.test(this.text.substr(this.position, length)); if (matched && advance) { this.position += length - 1; } return matched; } /** * @param {string} tagName label for block type we are currently processing * @param {string|RegExp} tag string to match in text * @param {RegExp} [nextTag] regular expression to match for characters *following* the current tag * @returns {boolean} true if the tag was processed, false otherwise */ }, { key: "parseEndTag", value: function parseEndTag(tagName, tag, nextTag) { var checkTag = this.mod() === tagName; if (tagName === "mono") { // special handling for 'mono' checkTag = checkTag && this.mono; } else { checkTag = checkTag && !this.mono; } if (checkTag && this.match(tag)) { if (nextTag !== undefined) { // Purpose of the following match is to prevent a direct unset/set of a given tag // E.g. '*bold **still bold*' => '*bold still bold*' if (this.position === this.text.length - 1 || this.match(nextTag, false)) { this.unsetTag(tagName); } } else { this.unsetTag(tagName); } return true; } return false; } /** * @param {string|RegExp} tag string to match in text * @param {value} value string to replace tag with, if found at current position * @returns {boolean} true if the tag was processed, false otherwise */ }, { key: "replace", value: function replace(tag, value) { if (this.match(tag)) { this.add(value); this.position += length - 1; return true; } return false; } /** * Create a regular expression for the tag if it isn't already one. * * The return value is an array `[RegExp, number]`, with exactly two value, where: * - RegExp is the regular expression to use * - number is the lenth of the input string to match * * @param {string|RegExp} tag string to match in text * @returns {Array} regular expression to use and length of input string to match * @private */ }, { key: "prepareRegExp", value: function prepareRegExp(tag) { var length; var regExp; if (tag instanceof RegExp) { regExp = tag; length = 1; // ASSUMPTION: regexp only tests one character } else { // use prepared regexp if present var prepared = tagPattern[tag]; if (prepared !== undefined) { regExp = prepared; } else { regExp = new RegExp(tag); } length = tag.length; } return [regExp, length]; } }]); return MarkupAccumulator; }(); /** * Helper class for Label which explodes the label text into lines and blocks within lines * * @private */ var LabelSplitter = /*#__PURE__*/function () { /** * @param {CanvasRenderingContext2D} ctx Canvas rendering context * @param {Label} parent reference to the Label instance using current instance * @param {boolean} selected * @param {boolean} hover */ function LabelSplitter(ctx, parent, selected, hover) { var _this = this; _classCallCheck(this, LabelSplitter); this.ctx = ctx; this.parent = parent; this.selected = selected; this.hover = hover; /** * Callback to determine text width; passed to LabelAccumulator instance * * @param {string} text string to determine width of * @param {string} mod font type to use for this text * @returns {object} { width, values} width in pixels and font attributes */ var textWidth = function textWidth(text, mod) { if (text === undefined) return 0; // TODO: This can be done more efficiently with caching // This will set the ctx.font correctly, depending on selected/hover and mod - so that ctx.measureText() will be accurate. var values = _this.parent.getFormattingValues(ctx, selected, hover, mod); var width = 0; if (text !== "") { var measure = _this.ctx.measureText(text); width = measure.width; } return { width: width, values: values }; }; this.lines = new LabelAccumulator(textWidth); } /** * Split passed text of a label into lines and blocks. * * # NOTE * * The handling of spacing is option dependent: * * - if `font.multi : false`, all spaces are retained * - if `font.multi : true`, every sequence of spaces is compressed to a single space * * This might not be the best way to do it, but this is as it has been working till now. * In order not to break existing functionality, for the time being this behaviour will * be retained in any code changes. * * @param {string} text text to split * @returns {Array<line>} */ _createClass(LabelSplitter, [{ key: "process", value: function process(text) { if (!isValidLabel(text)) { return this.lines.finalize(); } var font = this.parent.fontOptions; // Normalize the end-of-line's to a single representation - order important text = text.replace(/\r\n/g, "\n"); // Dos EOL's text = text.replace(/\r/g, "\n"); // Mac EOL's // Note that at this point, there can be no \r's in the text. // This is used later on splitStringIntoLines() to split multifont texts. var nlLines = String(text).split("\n"); var lineCount = nlLines.length; if (font.multi) { // Multi-font case: styling tags active for (var i = 0; i < lineCount; i++) { var blocks = this.splitBlocks(nlLines[i], font.multi); // Post: Sequences of tabs and spaces are reduced to single space if (blocks === undefined) continue; if (blocks.length === 0) { this.lines.newLine(""); continue; } if (font.maxWdt > 0) { // widthConstraint.maximum defined //console.log('Running widthConstraint multi, max: ' + this.fontOptions.maxWdt); for (var j = 0; j < blocks.length; j++) { var mod = blocks[j].mod; var _text = blocks[j].text; this.splitStringIntoLines(_text, mod, true); } } else { // widthConstraint.maximum NOT defined for (var _j = 0; _j < blocks.length; _j++) { var _mod = blocks[_j].mod; var _text2 = blocks[_j].text; this.lines.append(_text2, _mod); } } this.lines.newLine(); } } else { // Single-font case if (font.maxWdt > 0) { // widthConstraint.maximum defined // console.log('Running widthConstraint normal, max: ' + this.fontOptions.maxWdt); for (var _i = 0; _i < lineCount; _i++) { this.splitStringIntoLines(nlLines[_i]); } } else { // widthConstraint.maximum NOT defined for (var _i2 = 0; _i2 < lineCount; _i2++) { this.lines.newLine(nlLines[_i2]); } } } return this.lines.finalize(); } /** * normalize the markup system * * @param {boolean|'md'|'markdown'|'html'} markupSystem * @returns {string} */ }, { key: "decodeMarkupSystem", value: function decodeMarkupSystem(markupSystem) { var system = "none"; if (markupSystem === "markdown" || markupSystem === "md") { system = "markdown"; } else if (markupSystem === true || markupSystem === "html") { system = "html"; } return system; } /** * * @param {string} text * @returns {Array} */ }, { key: "splitHtmlBlocks", value: function splitHtmlBlocks(text) { var s = new MarkupAccumulator(text); var parseEntities = function parseEntities(ch) { if (/&/.test(ch)) { var parsed = s.replace(s.text, "<", "<") || s.replace(s.text, "&", "&"); if (!parsed) { s.add("&"); } return true; } return false; }; while (s.position < s.text.length) { var ch = s.text.charAt(s.position); var parsed = s.parseWS(ch) || /</.test(ch) && (s.parseStartTag("bold", "<b>") || s.parseStartTag("ital", "<i>") || s.parseStartTag("mono", "<code>") || s.parseEndTag("bold", "</b>") || s.parseEndTag("ital", "</i>") || s.parseEndTag("mono", "</code>")) || parseEntities(ch); if (!parsed) { s.add(ch); } s.position++; } s.emitBlock(); return s.blocks; } /** * * @param {string} text * @returns {Array} */ }, { key: "splitMarkdownBlocks", value: function splitMarkdownBlocks(text) { var _this2 = this; var s = new MarkupAccumulator(text); var beginable = true; var parseOverride = function parseOverride(ch) { if (/\\/.test(ch)) { if (s.position < _this2.text.length + 1) { s.position++; ch = _this2.text.charAt(s.position); if (/ \t/.test(ch)) { s.spacing = true; } else { s.add(ch); beginable = false; } } return true; } return false; }; while (s.position < s.text.length) { var ch = s.text.charAt(s.position); var parsed = s.parseWS(ch) || parseOverride(ch) || (beginable || s.spacing) && (s.parseStartTag("bold", "*") || s.parseStartTag("ital", "_") || s.parseStartTag("mono", "`")) || s.parseEndTag("bold", "*", "afterBold") || s.parseEndTag("ital", "_", "afterItal") || s.parseEndTag("mono", "`", "afterMono"); if (!parsed) { s.add(ch); beginable = false; } s.position++; } s.emitBlock(); return s.blocks; } /** * Explodes a piece of text into single-font blocks using a given markup * * @param {string} text * @param {boolean|'md'|'markdown'|'html'} markupSystem * @returns {Array.<{text: string, mod: string}>} * @private */ }, { key: "splitBlocks", value: function splitBlocks(text, markupSystem) { var system = this.decodeMarkupSystem(markupSystem); if (system === "none") { return [{ text: text, mod: "normal" }]; } else if (system === "markdown") { return this.splitMarkdownBlocks(text); } else if (system === "html") { return this.splitHtmlBlocks(text); } } /** * @param {string} text * @returns {boolean} true if text length over the current max with * @private */ }, { key: "overMaxWidth", value: function overMaxWidth(text) { var width = this.ctx.measureText(text).width; return this.lines.curWidth() + width > this.parent.fontOptions.maxWdt; } /** * Determine the longest part of the sentence which still fits in the * current max width. * * @param {Array} words Array of strings signifying a text lines * @returns {number} index of first item in string making string go over max * @private */ }, { key: "getLongestFit", value: function getLongestFit(words) { var text = ""; var w = 0; while (w < words.length) { var pre = text === "" ? "" : " "; var newText = text + pre + words[w]; if (this.overMaxWidth(newText)) break; text = newText; w++; } return w; } /** * Determine the longest part of the string which still fits in the * current max width. * * @param {Array} words Array of strings signifying a text lines * @returns {number} index of first item in string making string go over max */ }, { key: "getLongestFitWord", value: function getLongestFitWord(words) { var w = 0; while (w < words.length) { if (this.overMaxWidth(_sliceInstanceProperty(words).call(words, 0, w))) break; w++; } return w; } /** * Split the passed text into lines, according to width constraint (if any). * * The method assumes that the input string is a single line, i.e. without lines break. * * This method retains spaces, if still present (case `font.multi: false`). * A space which falls on an internal line break, will be replaced by a newline. * There is no special handling of tabs; these go along with the flow. * * @param {string} str * @param {string} [mod='normal'] * @param {boolean} [appendLast=false] * @private */ }, { key: "splitStringIntoLines", value: function splitStringIntoLines(str) { var mod = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "normal"; var appendLast = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; // Set the canvas context font, based upon the current selected/hover state // and the provided mod, so the text measurement performed by getLongestFit // will be accurate - and not just use the font of whoever last used the canvas. this.parent.getFormattingValues(this.ctx, this.selected, this.hover, mod); // Still-present spaces are relevant, retain them str = str.replace(/^( +)/g, "$1\r"); str = str.replace(/([^\r][^ ]*)( +)/g, "$1\r$2\r"); var words = str.split("\r"); while (words.length > 0) { var w = this.getLongestFit(words); if (w === 0) { // Special case: the first word is already larger than the max width. var word = words[0]; // Break the word to the largest part that fits the line var x = this.getLongestFitWord(word); this.lines.newLine(_sliceInstanceProperty(word).call(word, 0, x), mod); // Adjust the word, so that the rest will be done next iteration words[0] = _sliceInstanceProperty(word).call(word, x); } else { // skip any space that is replaced by a newline var newW = w; if (words[w - 1] === " ") { w--; } else if (words[newW] === " ") { newW++; } var text = _sliceInstanceProperty(words).call(words, 0, w).join(""); if (w == words.length && appendLast) { this.lines.append(text, mod); } else { this.lines.newLine(text, mod); } // Adjust the word, so that the rest will be done next iteration words = _sliceInstanceProperty(words).call(words, newW); } } } }]); return LabelSplitter; }(); /** * List of special styles for multi-fonts * * @private */ var multiFontStyle = ["bold", "ital", "boldital", "mono"]; /** * A Label to be used for Nodes or Edges. */ var Label = /*#__PURE__*/function () { /** * @param {object} body * @param {object} options * @param {boolean} [edgelabel=false] */ function Label(body, options) { var edgelabel = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; _classCallCheck(this, Label); this.body = body; this.pointToSelf = false; this.baseSize = undefined; this.fontOptions = {}; // instance variable containing the *instance-local* font options this.setOptions(options); this.size = { top: 0, left: 0, width: 0, height: 0, yLine: 0 }; this.isEdgeLabel = edgelabel; } /** * @param {object} options the options of the parent Node-instance */ _createClass(Label, [{ key: "setOptions", value: function setOptions(options) { this.elementOptions = options; // Reference to the options of the parent Node-instance this.initFontOptions(options.font); if (isValidLabel(options.label)) { this.labelDirty = true; } else { // Bad label! Change the option value to prevent bad stuff happening options.label = undefined; } if (options.font !== undefined && options.font !== null) { // font options can be deleted at various levels if (typeof options.font === "string") { this.baseSize = this.fontOptions.size; } else if (_typeof(options.font) === "object") { var size = options.font.size; if (size !== undefined) { this.baseSize = size; } } } } /** * Init the font Options structure. * * Member fontOptions serves as an accumulator for the current font options. * As such, it needs to be completely separated from the node options. * * @param {object} newFontOptions the new font options to process * @private */ }, { key: "initFontOptions", value: function initFontOptions(newFontOptions) { var _this = this; // Prepare the multi-font option objects. // These will be filled in propagateFonts(), if required forEach$1(multiFontStyle, function (style) { _this.fontOptions[style] = {}; }); // Handle shorthand option, if present if (Label.parseFontString(this.fontOptions, newFontOptions)) { this.fontOptions.vadjust = 0; return; } // Copy over the non-multifont options, if specified forEach$1(newFontOptions, function (prop, n) { if (prop !== undefined && prop !== null && _typeof(prop) !== "object") { _this.fontOptions[n] = prop; } }); } /** * If in-variable is a string, parse it as a font specifier. * * Note that following is not done here and have to be done after the call: * - Not all font options are set (vadjust, mod) * * @param {object} outOptions out-parameter, object in which to store the parse results (if any) * @param {object} inOptions font options to parse * @returns {boolean} true if font parsed as string, false otherwise * @static */ }, { key: "constrain", value: /** * Set the width and height constraints based on 'nearest' value * * @param {Array} pile array of option objects to consider * @returns {object} the actual constraint values to use * @private */ function constrain(pile) { // NOTE: constrainWidth and constrainHeight never set! // NOTE: for edge labels, only 'maxWdt' set // Node labels can set all the fields var fontOptions = { constrainWidth: false, maxWdt: -1, minWdt: -1, constrainHeight: false, minHgt: -1, valign: "middle" }; var widthConstraint = topMost(pile, "widthConstraint"); if (typeof widthConstraint === "number") { fontOptions.maxWdt = Number(widthConstraint); fontOptions.minWdt = Number(widthConstraint); } else if (_typeof(widthConstraint) === "object") { var widthConstraintMaximum = topMost(pile, ["widthConstraint", "maximum"]); if (typeof widthConstraintMaximum === "number") { fontOptions.maxWdt = Number(widthConstraintMaximum); } var widthConstraintMinimum = topMost(pile, ["widthConstraint", "minimum"]); if (typeof widthConstraintMinimum === "number") { fontOptions.minWdt = Number(widthConstraintMinimum); } } var heightConstraint = topMost(pile, "heightConstraint"); if (typeof heightConstraint === "number") { fontOptions.minHgt = Number(heightConstraint); } else if (_typeof(heightConstraint) === "object") { var heightConstraintMinimum = topMost(pile, ["heightConstraint", "minimum"]); if (typeof heightConstraintMinimum === "number") { fontOptions.minHgt = Number(heightConstraintMinimum); } var heightConstraintValign = topMost(pile, ["heightConstraint", "valign"]); if (typeof heightConstraintValign === "string") { if (heightConstraintValign === "top" || heightConstraintValign === "bottom") { fontOptions.valign = heightConstraintValign; } } } return fontOptions; } /** * Set options and update internal state * * @param {object} options options to set * @param {Array} pile array of option objects to consider for option 'chosen' */ }, { key: "update", value: function update(options, pile) { this.setOptions(options, true); this.propagateFonts(pile); deepExtend(this.fontOptions, this.constrain(pile)); this.fontOptions.chooser = choosify("label", pile); } /** * When margins are set in an element, adjust sizes is called to remove them * from the width/height constraints. This must be done prior to label sizing. * * @param {{top: number, right: number, bottom: number, left: number}} margins */ }, { key: "adjustSizes", value: function adjustSizes(margins) { var widthBias = margins ? margins.right + margins.left : 0; if (this.fontOptions.constrainWidth) { this.fontOptions.maxWdt -= widthBias; this.fontOptions.minWdt -= widthBias; } var heightBias = margins ? margins.top + margins.bottom : 0; if (this.fontOptions.constrainHeight) { this.fontOptions.minHgt -= heightBias; } } ///////////////////////////////////////////////////////// // Methods for handling options piles // Eventually, these will be moved to a separate class ///////////////////////////////////////////////////////// /** * Add the font members of the passed list of option objects to the pile. * * @param {Pile} dstPile pile of option objects add to * @param {Pile} srcPile pile of option objects to take font options from * @private */ }, { key: "addFontOptionsToPile", value: function addFontOptionsToPile(dstPile, srcPile) { for (var i = 0; i < srcPile.length; ++i) { this.addFontToPile(dstPile, srcPile[i]); } } /** * Add given font option object to the list of objects (the 'pile') to consider for determining * multi-font option values. * * @param {Pile} pile pile of option objects to use * @param {object} options instance to add to pile * @private */ }, { key: "addFontToPile", value: function addFontToPile(pile, options) { if (options === undefined) return; if (options.font === undefined || options.font === null) return; var item = options.font; pile.push(item); } /** * Collect all own-property values from the font pile that aren't multi-font option objectss. * * @param {Pile} pile pile of option objects to use * @returns {object} object with all current own basic font properties * @private */ }, { key: "getBasicOptions", value: function getBasicOptions(pile) { var ret = {}; // Scans the whole pile to get all options present for (var n = 0; n < pile.length; ++n) { var fontOptions = pile[n]; // Convert shorthand if necessary var tmpShorthand = {}; if (Label.parseFontString(tmpShorthand, fontOptions)) { fontOptions = tmpShorthand; } forEach$1(fontOptions, function (opt, name) { if (opt === undefined) return; // multi-font option need not be present if (Object.prototype.hasOwnProperty.call(ret, name)) return; // Keep first value we encounter if (_indexOfInstanceProperty(multiFontStyle).call(multiFontStyle, name) !== -1) { // Skip multi-font properties but we do need the structure ret[name] = {}; } else { ret[name] = opt; } }); } return ret; } /** * Return the value for given option for the given multi-font. * * All available option objects are trawled in the set order to construct the option values. * * --------------------------------------------------------------------- * ## Traversal of pile for multi-fonts * * The determination of multi-font option values is a special case, because any values not * present in the multi-font options should by definition be taken from the main font options, * i.e. from the current 'parent' object of the multi-font option. * * ### Search order for multi-fonts * * 'bold' used as example: * * - search in option group 'bold' in local properties * - search in main font option group in local properties * * --------------------------------------------------------------------- * * @param {Pile} pile pile of option objects to use * @param {MultiFontStyle} multiName sub path for the multi-font * @param {string} option the option to search for, for the given multi-font * @returns {string|number} the value for the given option * @private */ }, { key: "getFontOption", value: function getFontOption(pile, multiName, option) { var multiFont; // Search multi font in local properties for (var n = 0; n < pile.length; ++n) { var fontOptions = pile[n]; if (Object.prototype.hasOwnProperty.call(fontOptions, multiName)) { multiFont = fontOptions[multiName]; if (multiFont === undefined || multiFont === null) continue; // Convert shorthand if necessary // TODO: inefficient to do this conversion every time; find a better way. var tmpShorthand = {}; if (Label.parseFontString(tmpShorthand, multiFont)) { multiFont = tmpShorthand; } if (Object.prototype.hasOwnProperty.call(multiFont, option)) { return multiFont[option]; } } } // Option is not mentioned in the multi font options; take it from the parent font options. // These have already been converted with getBasicOptions(), so use the converted values. if (Object.prototype.hasOwnProperty.call(this.fontOptions, option)) { return this.fontOptions[option]; } // A value **must** be found; you should never get here. throw new Error("Did not find value for multi-font for property: '" + option + "'"); } /** * Return all options values for the given multi-font. * * All available option objects are trawled in the set order to construct the option values. * * @param {Pile} pile pile of option objects to use * @param {MultiFontStyle} multiName sub path for the mod-font * @returns {MultiFontOptions} * @private */ }, { key: "getFontOptions", value: function getFontOptions(pile, multiName) { var result = {}; var optionNames = ["color", "size", "face", "mod", "vadjust"]; // List of allowed options per multi-font for (var i = 0; i < optionNames.length; ++i) { var mod = optionNames[i]; result[mod] = this.getFontOption(pile, multiName, mod); } return result; } ///////////////////////////////////////////////////////// // End methods for handling options piles ///////////////////////////////////////////////////////// /** * Collapse the font options for the multi-font to single objects, from * the chain of option objects passed (the 'pile'). * * @param {Pile} pile sequence of option objects to consider. * First item in list assumed to be the newly set options. */ }, { key: "propagateFonts", value: function propagateFonts(pile) { var _this2 = this; var fontPile = []; // sequence of font objects to consider, order important // Note that this.elementOptions is not used here. this.addFontOptionsToPile(fontPile, pile); this.fontOptions = this.getBasicOptions(fontPile); // We set multifont values even if multi === false, for consistency (things break otherwise) var _loop = function _loop() { var mod = multiFontStyle[i]; var modOptions = _this2.fontOptions[mod]; var tmpMultiFontOptions = _this2.getFontOptions(fontPile, mod); // Copy over found values forEach$1(tmpMultiFontOptions, function (option, n) { modOptions[n] = option; }); modOptions.size = Number(modOptions.size); modOptions.vadjust = Number(modOptions.vadjust); }; for (var i = 0; i < multiFontStyle.length; ++i) { _loop(); } } /** * Main function. This is called from anything that wants to draw a label. * * @param {CanvasRenderingContext2D} ctx * @param {number} x * @param {number} y * @param {boolean} selected * @param {boolean} hover * @param {string} [baseline='middle'] */ }, { key: "draw", value: function draw(ctx, x, y, selected, hover) { var baseline = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : "middle"; // if no label, return if (this.elementOptions.label === undefined) return; // check if we have to render the label var viewFontSize = this.fontOptions.size * this.body.view.scale; if (this.elementOptions.label && viewFontSize < this.elementOptions.scaling.label.drawThreshold - 1) return; // This ensures that there will not be HUGE letters on screen // by setting an upper limit on the visible text size (regardless of zoomLevel) if (viewFontSize >= this.elementOptions.scaling.label.maxVisible) { viewFontSize = Number(this.elementOptions.scaling.label.maxVisible) / this.body.view.scale; } // update the size cache if required this.calculateLabelSize(ctx, selected, hover, x, y, baseline); this._drawBackground(ctx); this._drawText(ctx, x, this.size.yLine, baseline, viewFontSize); } /** * Draws the label background * * @param {CanvasRenderingContext2D} ctx * @private */ }, { key: "_drawBackground", value: function _drawBackground(ctx) { if (this.fontOptions.background !== undefined && this.fontOptions.background !== "none") { ctx.fillStyle = this.fontOptions.background; var size = this.getSize(); ctx.fillRect(size.left, size.top, size.width, size.height); } } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x * @param {number} y * @param {string} [baseline='middle'] * @param {number} viewFontSize * @private */ }, { key: "_drawText", value: function _drawText(ctx, x, y) { var baseline = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : "middle"; var viewFontSize = arguments.length > 4 ? arguments[4] : undefined; var _this$_setAlignment = this._setAlignment(ctx, x, y, baseline); var _this$_setAlignment2 = _slicedToArray(_this$_setAlignment, 2); x = _this$_setAlignment2[0]; y = _this$_setAlignment2[1]; ctx.textAlign = "left"; x = x - this.size.width / 2; // Shift label 1/2-distance to the left if (this.fontOptions.valign && this.size.height > this.size.labelHeight) { if (this.fontOptions.valign === "top") { y -= (this.size.height - this.size.labelHeight) / 2; } if (this.fontOptions.valign === "bottom") { y += (this.size.height - this.size.labelHeight) / 2; } } // draw the text for (var i = 0; i < this.lineCount; i++) { var line = this.lines[i]; if (line && line.blocks) { var width = 0; if (this.isEdgeLabel || this.fontOptions.align === "center") { width += (this.size.width - line.width) / 2; } else if (this.fontOptions.align === "right") { width += this.size.width - line.width; } for (var j = 0; j < line.blocks.length; j++) { var block = line.blocks[j]; ctx.font = block.font; var _this$_getColor = this._getColor(block.color, viewFontSize, block.strokeColor), _this$_getColor2 = _slicedToArray(_this$_getColor, 2), fontColor = _this$_getColor2[0], strokeColor = _this$_getColor2[1]; if (block.strokeWidth > 0) { ctx.lineWidth = block.strokeWidth; ctx.strokeStyle = strokeColor; ctx.lineJoin = "round"; } ctx.fillStyle = fontColor; if (block.strokeWidth > 0) { ctx.strokeText(block.text, x + width, y + block.vadjust); } ctx.fillText(block.text, x + width, y + block.vadjust); width += block.width; } y += line.height; } } } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x * @param {number} y * @param {string} baseline * @returns {Array.<number>} * @private */ }, { key: "_setAlignment", value: function _setAlignment(ctx, x, y, baseline) { // check for label alignment (for edges) // TODO: make alignment for nodes if (this.isEdgeLabel && this.fontOptions.align !== "horizontal" && this.pointToSelf === false) { x = 0; y = 0; var lineMargin = 2; if (this.fontOptions.align === "top") { ctx.textBaseline = "alphabetic"; y -= 2 * lineMargin; // distance from edge, required because we use alphabetic. Alphabetic has less difference between browsers } else if (this.fontOptions.align === "bottom") { ctx.textBaseline = "hanging"; y += 2 * lineMargin; // distance from edge, required because we use hanging. Hanging has less difference between browsers } else { ctx.textBaseline = "middle"; } } else { ctx.textBaseline = baseline; } return [x, y]; } /** * fade in when relative scale is between threshold and threshold - 1. * If the relative scale would be smaller than threshold -1 the draw function would have returned before coming here. * * @param {string} color The font color to use * @param {number} viewFontSize * @param {string} initialStrokeColor * @returns {Array.<string>} An array containing the font color and stroke color * @private */ }, { key: "_getColor", value: function _getColor(color, viewFontSize, initialStrokeColor) { var fontColor = color || "#000000"; var strokeColor = initialStrokeColor || "#ffffff"; if (viewFontSize <= this.elementOptions.scaling.label.drawThreshold) { var opacity = Math.max(0, Math.min(1, 1 - (this.elementOptions.scaling.label.drawThreshold - viewFontSize))); fontColor = overrideOpacity(fontColor, opacity); strokeColor = overrideOpacity(strokeColor, opacity); } return [fontColor, strokeColor]; } /** * * @param {CanvasRenderingContext2D} ctx * @param {boolean} selected * @param {boolean} hover * @returns {{width: number, height: number}} */ }, { key: "getTextSize", value: function getTextSize(ctx) { var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; this._processLabel(ctx, selected, hover); return { width: this.size.width, height: this.size.height, lineCount: this.lineCount }; } /** * Get the current dimensions of the label * * @returns {rect} */ }, { key: "getSize", value: function getSize() { var lineMargin = 2; var x = this.size.left; // default values which might be overridden below var y = this.size.top - 0.5 * lineMargin; // idem if (this.isEdgeLabel) { var x2 = -this.size.width * 0.5; switch (this.fontOptions.align) { case "middle": x = x2; y = -this.size.height * 0.5; break; case "top": x = x2; y = -(this.size.height + lineMargin); break; case "bottom": x = x2; y = lineMargin; break; } } var ret = { left: x, top: y, width: this.size.width, height: this.size.height }; return ret; } /** * * @param {CanvasRenderingContext2D} ctx * @param {boolean} selected * @param {boolean} hover * @param {number} [x=0] * @param {number} [y=0] * @param {'middle'|'hanging'} [baseline='middle'] */ }, { key: "calculateLabelSize", value: function calculateLabelSize(ctx, selected, hover) { var x = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; var y = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0; var baseline = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : "middle"; this._processLabel(ctx, selected, hover); this.size.left = x - this.size.width * 0.5; this.size.top = y - this.size.height * 0.5; this.size.yLine = y + (1 - this.lineCount) * 0.5 * this.fontOptions.size; if (baseline === "hanging") { this.size.top += 0.5 * this.fontOptions.size; this.size.top += 4; // distance from node, required because we use hanging. Hanging has less difference between browsers this.size.yLine += 4; // distance from node } } /** * * @param {CanvasRenderingContext2D} ctx * @param {boolean} selected * @param {boolean} hover * @param {string} mod * @returns {{color, size, face, mod, vadjust, strokeWidth: *, strokeColor: (*|string|allOptions.edges.font.strokeColor|{string}|allOptions.nodes.font.strokeColor|Array)}} */ }, { key: "getFormattingValues", value: function getFormattingValues(ctx, selected, hover, mod) { var getValue = function getValue(fontOptions, mod, option) { if (mod === "normal") { if (option === "mod") return ""; return fontOptions[option]; } if (fontOptions[mod][option] !== undefined) { // Grumbl leaving out test on undefined equals false for "" return fontOptions[mod][option]; } else { // Take from parent font option return fontOptions[option]; } }; var values = { color: getValue(this.fontOptions, mod, "color"), size: getValue(this.fontOptions, mod, "size"), face: getValue(this.fontOptions, mod, "face"), mod: getValue(this.fontOptions, mod, "mod"), vadjust: getValue(this.fontOptions, mod, "vadjust"), strokeWidth: this.fontOptions.strokeWidth, strokeColor: this.fontOptions.strokeColor }; if (selected || hover) { if (mod === "normal" && this.fontOptions.chooser === true && this.elementOptions.labelHighlightBold) { values.mod = "bold"; } else { if (typeof this.fontOptions.chooser === "function") { this.fontOptions.chooser(values, this.elementOptions.id, selected, hover); } } } var fontString = ""; if (values.mod !== undefined && values.mod !== "") { // safeguard for undefined - this happened fontString += values.mod + " "; } fontString += values.size + "px " + values.face; ctx.font = fontString.replace(/"/g, ""); values.font = ctx.font; values.height = values.size; return values; } /** * * @param {boolean} selected * @param {boolean} hover * @returns {boolean} */ }, { key: "differentState", value: function differentState(selected, hover) { return selected !== this.selectedState || hover !== this.hoverState; } /** * This explodes the passed text into lines and determines the width, height and number of lines. * * @param {CanvasRenderingContext2D} ctx * @param {boolean} selected * @param {boolean} hover * @param {string} inText the text to explode * @returns {{width, height, lines}|*} * @private */ }, { key: "_processLabelText", value: function _processLabelText(ctx, selected, hover, inText) { var splitter = new LabelSplitter(ctx, this, selected, hover); return splitter.process(inText); } /** * This explodes the label string into lines and sets the width, height and number of lines. * * @param {CanvasRenderingContext2D} ctx * @param {boolean} selected * @param {boolean} hover * @private */ }, { key: "_processLabel", value: function _processLabel(ctx, selected, hover) { if (this.labelDirty === false && !this.differentState(selected, hover)) return; var state = this._processLabelText(ctx, selected, hover, this.elementOptions.label); if (this.fontOptions.minWdt > 0 && state.width < this.fontOptions.minWdt) { state.width = this.fontOptions.minWdt; } this.size.labelHeight = state.height; if (this.fontOptions.minHgt > 0 && state.height < this.fontOptions.minHgt) { state.height = this.fontOptions.minHgt; } this.lines = state.lines; this.lineCount = state.lines.length; this.size.width = state.width; this.size.height = state.height; this.selectedState = selected; this.hoverState = hover; this.labelDirty = false; } /** * Check if this label is visible * * @returns {boolean} true if this label will be show, false otherwise */ }, { key: "visible", value: function visible() { if (this.size.width === 0 || this.size.height === 0 || this.elementOptions.label === undefined) { return false; // nothing to display } var viewFontSize = this.fontOptions.size * this.body.view.scale; if (viewFontSize < this.elementOptions.scaling.label.drawThreshold - 1) { return false; // Too small or too far away to show } return true; } }], [{ key: "parseFontString", value: function parseFontString(outOptions, inOptions) { if (!inOptions || typeof inOptions !== "string") return false; var newOptionsArray = inOptions.split(" "); outOptions.size = +newOptionsArray[0].replace("px", ""); outOptions.face = newOptionsArray[1]; outOptions.color = newOptionsArray[2]; return true; } }]); return Label; }(); var constructExports = {}; var construct$2 = { get exports() { return constructExports; }, set exports(v) { constructExports = v; } }; var isConstructor = isConstructor$4; var tryToString = tryToString$6; var $TypeError$1 = TypeError; // `Assert: IsConstructor(argument) is true` var aConstructor$1 = function (argument) { if (isConstructor(argument)) return argument; throw $TypeError$1(tryToString(argument) + ' is not a constructor'); }; var $$6 = _export; var getBuiltIn = getBuiltIn$c; var apply = functionApply; var bind$4 = functionBind; var aConstructor = aConstructor$1; var anObject$2 = anObject$d; var isObject$3 = isObject$j; var create$4 = objectCreate; var fails$2 = fails$w; var nativeConstruct = getBuiltIn('Reflect', 'construct'); var ObjectPrototype = Object.prototype; var push$1 = [].push; // `Reflect.construct` method // https://tc39.es/ecma262/#sec-reflect.construct // MS Edge supports only 2 arguments and argumentsList argument is optional // FF Nightly sets third argument as `new.target`, but does not create `this` from it var NEW_TARGET_BUG = fails$2(function () { function F() {/* empty */} return !(nativeConstruct(function () {/* empty */}, [], F) instanceof F); }); var ARGS_BUG = !fails$2(function () { nativeConstruct(function () {/* empty */}); }); var FORCED$3 = NEW_TARGET_BUG || ARGS_BUG; $$6({ target: 'Reflect', stat: true, forced: FORCED$3, sham: FORCED$3 }, { construct: function construct(Target, args /* , newTarget */) { aConstructor(Target); anObject$2(args); var newTarget = arguments.length < 3 ? Target : aConstructor(arguments[2]); if (ARGS_BUG && !NEW_TARGET_BUG) return nativeConstruct(Target, args, newTarget); if (Target == newTarget) { // w/o altered newTarget, optimization for 0-4 arguments switch (args.length) { case 0: return new Target(); case 1: return new Target(args[0]); case 2: return new Target(args[0], args[1]); case 3: return new Target(args[0], args[1], args[2]); case 4: return new Target(args[0], args[1], args[2], args[3]); } // w/o altered newTarget, lot of arguments case var $args = [null]; apply(push$1, $args, args); return new (apply(bind$4, Target, $args))(); } // with altered newTarget, not support built-in constructors var proto = newTarget.prototype; var instance = create$4(isObject$3(proto) ? proto : ObjectPrototype); var result = apply(Target, instance, args); return isObject$3(result) ? result : instance; } }); var path$6 = path$y; var construct$1 = path$6.Reflect.construct; var parent$k = construct$1; var construct = parent$k; (function (module) { module.exports = construct; })(construct$2); var _Reflect$construct = /*@__PURE__*/getDefaultExportFromCjs(constructExports); var createExports$1 = {}; var create$3 = { get exports() { return createExports$1; }, set exports(v) { createExports$1 = v; } }; var createExports = {}; var create$2 = { get exports() { return createExports; }, set exports(v) { createExports = v; } }; var parent$j = create$6; var create$1 = parent$j; var parent$i = create$1; var create = parent$i; (function (module) { module.exports = create; })(create$2); (function (module) { module.exports = createExports; })(create$3); var _Object$create = /*@__PURE__*/getDefaultExportFromCjs(createExports$1); var setPrototypeOfExports$1 = {}; var setPrototypeOf$6 = { get exports() { return setPrototypeOfExports$1; }, set exports(v) { setPrototypeOfExports$1 = v; } }; var setPrototypeOfExports = {}; var setPrototypeOf$5 = { get exports() { return setPrototypeOfExports; }, set exports(v) { setPrototypeOfExports = v; } }; var $$5 = _export; var setPrototypeOf$4 = objectSetPrototypeOf; // `Object.setPrototypeOf` method // https://tc39.es/ecma262/#sec-object.setprototypeof $$5({ target: 'Object', stat: true }, { setPrototypeOf: setPrototypeOf$4 }); var path$5 = path$y; var setPrototypeOf$3 = path$5.Object.setPrototypeOf; var parent$h = setPrototypeOf$3; var setPrototypeOf$2 = parent$h; var parent$g = setPrototypeOf$2; var setPrototypeOf$1 = parent$g; var parent$f = setPrototypeOf$1; var setPrototypeOf = parent$f; (function (module) { module.exports = setPrototypeOf; })(setPrototypeOf$5); (function (module) { module.exports = setPrototypeOfExports; })(setPrototypeOf$6); var _Object$setPrototypeOf = /*@__PURE__*/getDefaultExportFromCjs(setPrototypeOfExports$1); var bindExports$1 = {}; var bind$3 = { get exports() { return bindExports$1; }, set exports(v) { bindExports$1 = v; } }; var bindExports = {}; var bind$2 = { get exports() { return bindExports; }, set exports(v) { bindExports = v; } }; var parent$e = bind$9; var bind$1 = parent$e; var parent$d = bind$1; var bind = parent$d; (function (module) { module.exports = bind; })(bind$2); (function (module) { module.exports = bindExports; })(bind$3); var _bindInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(bindExports$1); function _setPrototypeOf(o, p) { var _context; _setPrototypeOf = _Object$setPrototypeOf ? _bindInstanceProperty(_context = _Object$setPrototypeOf).call(_context) : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = _Object$create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); _Object$defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } var getPrototypeOfExports$1 = {}; var getPrototypeOf$4 = { get exports() { return getPrototypeOfExports$1; }, set exports(v) { getPrototypeOfExports$1 = v; } }; var getPrototypeOfExports = {}; var getPrototypeOf$3 = { get exports() { return getPrototypeOfExports; }, set exports(v) { getPrototypeOfExports = v; } }; var parent$c = getPrototypeOf$5; var getPrototypeOf$2 = parent$c; var parent$b = getPrototypeOf$2; var getPrototypeOf$1 = parent$b; (function (module) { module.exports = getPrototypeOf$1; })(getPrototypeOf$3); (function (module) { module.exports = getPrototypeOfExports; })(getPrototypeOf$4); var _Object$getPrototypeOf = /*@__PURE__*/getDefaultExportFromCjs(getPrototypeOfExports$1); function _getPrototypeOf(o) { var _context; _getPrototypeOf = _Object$setPrototypeOf ? _bindInstanceProperty(_context = _Object$getPrototypeOf).call(_context) : function _getPrototypeOf(o) { return o.__proto__ || _Object$getPrototypeOf(o); }; return _getPrototypeOf(o); } /** * The Base class for all Nodes. */ var NodeBase = /*#__PURE__*/function () { /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function NodeBase(options, body, labelModule) { _classCallCheck(this, NodeBase); this.body = body; this.labelModule = labelModule; this.setOptions(options); this.top = undefined; this.left = undefined; this.height = undefined; this.width = undefined; this.radius = undefined; this.margin = undefined; this.refreshNeeded = true; this.boundingBox = { top: 0, left: 0, right: 0, bottom: 0 }; } /** * * @param {object} options */ _createClass(NodeBase, [{ key: "setOptions", value: function setOptions(options) { this.options = options; } /** * * @param {Label} labelModule * @private */ }, { key: "_setMargins", value: function _setMargins(labelModule) { this.margin = {}; if (this.options.margin) { if (_typeof(this.options.margin) == "object") { this.margin.top = this.options.margin.top; this.margin.right = this.options.margin.right; this.margin.bottom = this.options.margin.bottom; this.margin.left = this.options.margin.left; } else { this.margin.top = this.options.margin; this.margin.right = this.options.margin; this.margin.bottom = this.options.margin; this.margin.left = this.options.margin; } } labelModule.adjustSizes(this.margin); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} angle * @returns {number} * @private */ }, { key: "_distanceToBorder", value: function _distanceToBorder(ctx, angle) { var borderWidth = this.options.borderWidth; if (ctx) { this.resize(ctx); } return Math.min(Math.abs(this.width / 2 / Math.cos(angle)), Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; } /** * * @param {CanvasRenderingContext2D} ctx * @param {ArrowOptions} values */ }, { key: "enableShadow", value: function enableShadow(ctx, values) { if (values.shadow) { ctx.shadowColor = values.shadowColor; ctx.shadowBlur = values.shadowSize; ctx.shadowOffsetX = values.shadowX; ctx.shadowOffsetY = values.shadowY; } } /** * * @param {CanvasRenderingContext2D} ctx * @param {ArrowOptions} values */ }, { key: "disableShadow", value: function disableShadow(ctx, values) { if (values.shadow) { ctx.shadowColor = "rgba(0,0,0,0)"; ctx.shadowBlur = 0; ctx.shadowOffsetX = 0; ctx.shadowOffsetY = 0; } } /** * * @param {CanvasRenderingContext2D} ctx * @param {ArrowOptions} values */ }, { key: "enableBorderDashes", value: function enableBorderDashes(ctx, values) { if (values.borderDashes !== false) { if (ctx.setLineDash !== undefined) { var dashes = values.borderDashes; if (dashes === true) { dashes = [5, 15]; } ctx.setLineDash(dashes); } else { console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used."); this.options.shapeProperties.borderDashes = false; values.borderDashes = false; } } } /** * * @param {CanvasRenderingContext2D} ctx * @param {ArrowOptions} values */ }, { key: "disableBorderDashes", value: function disableBorderDashes(ctx, values) { if (values.borderDashes !== false) { if (ctx.setLineDash !== undefined) { ctx.setLineDash([0]); } else { console.warn("setLineDash is not supported in this browser. The dashed borders cannot be used."); this.options.shapeProperties.borderDashes = false; values.borderDashes = false; } } } /** * Determine if the shape of a node needs to be recalculated. * * @param {boolean} selected * @param {boolean} hover * @returns {boolean} * @protected */ }, { key: "needsRefresh", value: function needsRefresh(selected, hover) { if (this.refreshNeeded === true) { // This is probably not the best location to reset this member. // However, in the current logic, it is the most convenient one. this.refreshNeeded = false; return true; } return this.width === undefined || this.labelModule.differentState(selected, hover); } /** * * @param {CanvasRenderingContext2D} ctx * @param {ArrowOptions} values */ }, { key: "initContextForDraw", value: function initContextForDraw(ctx, values) { var borderWidth = values.borderWidth / this.body.view.scale; ctx.lineWidth = Math.min(this.width, borderWidth); ctx.strokeStyle = values.borderColor; ctx.fillStyle = values.color; } /** * * @param {CanvasRenderingContext2D} ctx * @param {ArrowOptions} values */ }, { key: "performStroke", value: function performStroke(ctx, values) { var borderWidth = values.borderWidth / this.body.view.scale; //draw dashed border if enabled, save and restore is required for firefox not to crash on unix. ctx.save(); // if borders are zero width, they will be drawn with width 1 by default. This prevents that if (borderWidth > 0) { this.enableBorderDashes(ctx, values); //draw the border ctx.stroke(); //disable dashed border for other elements this.disableBorderDashes(ctx, values); } ctx.restore(); } /** * * @param {CanvasRenderingContext2D} ctx * @param {ArrowOptions} values */ }, { key: "performFill", value: function performFill(ctx, values) { ctx.save(); ctx.fillStyle = values.color; // draw shadow if enabled this.enableShadow(ctx, values); // draw the background _fillInstanceProperty(ctx).call(ctx); // disable shadows for other elements. this.disableShadow(ctx, values); ctx.restore(); this.performStroke(ctx, values); } /** * * @param {number} margin * @private */ }, { key: "_addBoundingBoxMargin", value: function _addBoundingBoxMargin(margin) { this.boundingBox.left -= margin; this.boundingBox.top -= margin; this.boundingBox.bottom += margin; this.boundingBox.right += margin; } /** * Actual implementation of this method call. * * Doing it like this makes it easier to override * in the child classes. * * @param {number} x width * @param {number} y height * @param {CanvasRenderingContext2D} ctx * @param {boolean} selected * @param {boolean} hover * @private */ }, { key: "_updateBoundingBox", value: function _updateBoundingBox(x, y, ctx, selected, hover) { if (ctx !== undefined) { this.resize(ctx, selected, hover); } this.left = x - this.width / 2; this.top = y - this.height / 2; this.boundingBox.left = this.left; this.boundingBox.top = this.top; this.boundingBox.bottom = this.top + this.height; this.boundingBox.right = this.left + this.width; } /** * Default implementation of this method call. * This acts as a stub which can be overridden. * * @param {number} x width * @param {number} y height * @param {CanvasRenderingContext2D} ctx * @param {boolean} selected * @param {boolean} hover */ }, { key: "updateBoundingBox", value: function updateBoundingBox(x, y, ctx, selected, hover) { this._updateBoundingBox(x, y, ctx, selected, hover); } /** * Determine the dimensions to use for nodes with an internal label * * Currently, these are: Circle, Ellipse, Database, Box * The other nodes have external labels, and will not call this method * * If there is no label, decent default values are supplied. * * @param {CanvasRenderingContext2D} ctx * @param {boolean} [selected] * @param {boolean} [hover] * @returns {{width:number, height:number}} */ }, { key: "getDimensionsFromLabel", value: function getDimensionsFromLabel(ctx, selected, hover) { // NOTE: previously 'textSize' was not put in 'this' for Ellipse // TODO: examine the consequences. this.textSize = this.labelModule.getTextSize(ctx, selected, hover); var width = this.textSize.width; var height = this.textSize.height; var DEFAULT_SIZE = 14; if (width === 0) { // This happens when there is no label text set width = DEFAULT_SIZE; // use a decent default height = DEFAULT_SIZE; // if width zero, then height also always zero } return { width: width, height: height }; } }]); return NodeBase; }(); function _createSuper$s(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$s(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$s() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Box Node/Cluster shape. * * @augments NodeBase */ var Box$1 = /*#__PURE__*/function (_NodeBase) { _inherits(Box, _NodeBase); var _super = _createSuper$s(Box); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function Box(options, body, labelModule) { var _this; _classCallCheck(this, Box); _this = _super.call(this, options, body, labelModule); _this._setMargins(labelModule); return _this; } /** * * @param {CanvasRenderingContext2D} ctx * @param {boolean} [selected] * @param {boolean} [hover] */ _createClass(Box, [{ key: "resize", value: function resize(ctx) { var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected; var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover; if (this.needsRefresh(selected, hover)) { var dimensions = this.getDimensionsFromLabel(ctx, selected, hover); this.width = dimensions.width + this.margin.right + this.margin.left; this.height = dimensions.height + this.margin.top + this.margin.bottom; this.radius = this.width / 2; } } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values */ }, { key: "draw", value: function draw(ctx, x, y, selected, hover, values) { this.resize(ctx, selected, hover); this.left = x - this.width / 2; this.top = y - this.height / 2; this.initContextForDraw(ctx, values); drawRoundRect(ctx, this.left, this.top, this.width, this.height, values.borderRadius); this.performFill(ctx, values); this.updateBoundingBox(x, y, ctx, selected, hover); this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, this.top + this.textSize.height / 2 + this.margin.top, selected, hover); } /** * * @param {number} x width * @param {number} y height * @param {CanvasRenderingContext2D} ctx * @param {boolean} selected * @param {boolean} hover */ }, { key: "updateBoundingBox", value: function updateBoundingBox(x, y, ctx, selected, hover) { this._updateBoundingBox(x, y, ctx, selected, hover); var borderRadius = this.options.shapeProperties.borderRadius; // only effective for box this._addBoundingBoxMargin(borderRadius); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} angle * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx, angle) { if (ctx) { this.resize(ctx); } var borderWidth = this.options.borderWidth; return Math.min(Math.abs(this.width / 2 / Math.cos(angle)), Math.abs(this.height / 2 / Math.sin(angle))) + borderWidth; } }]); return Box; }(NodeBase); function _createSuper$r(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$r(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$r() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * NOTE: This is a bad base class * * Child classes are: * * Image - uses *only* image methods * Circle - uses *only* _drawRawCircle * CircleImage - uses all * * TODO: Refactor, move _drawRawCircle to different module, derive Circle from NodeBase * Rename this to ImageBase * Consolidate common code in Image and CircleImage to base class * * @augments NodeBase */ var CircleImageBase = /*#__PURE__*/function (_NodeBase) { _inherits(CircleImageBase, _NodeBase); var _super = _createSuper$r(CircleImageBase); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function CircleImageBase(options, body, labelModule) { var _this; _classCallCheck(this, CircleImageBase); _this = _super.call(this, options, body, labelModule); _this.labelOffset = 0; _this.selected = false; return _this; } /** * * @param {object} options * @param {object} [imageObj] * @param {object} [imageObjAlt] */ _createClass(CircleImageBase, [{ key: "setOptions", value: function setOptions(options, imageObj, imageObjAlt) { this.options = options; if (!(imageObj === undefined && imageObjAlt === undefined)) { this.setImages(imageObj, imageObjAlt); } } /** * Set the images for this node. * * The images can be updated after the initial setting of options; * therefore, this method needs to be reentrant. * * For correct working in error cases, it is necessary to properly set * field 'nodes.brokenImage' in the options. * * @param {Image} imageObj required; main image to show for this node * @param {Image|undefined} imageObjAlt optional; image to show when node is selected */ }, { key: "setImages", value: function setImages(imageObj, imageObjAlt) { if (imageObjAlt && this.selected) { this.imageObj = imageObjAlt; this.imageObjAlt = imageObj; } else { this.imageObj = imageObj; this.imageObjAlt = imageObjAlt; } } /** * Set selection and switch between the base and the selected image. * * Do the switch only if imageObjAlt exists. * * @param {boolean} selected value of new selected state for current node */ }, { key: "switchImages", value: function switchImages(selected) { var selection_changed = selected && !this.selected || !selected && this.selected; this.selected = selected; // Remember new selection if (this.imageObjAlt !== undefined && selection_changed) { var imageTmp = this.imageObj; this.imageObj = this.imageObjAlt; this.imageObjAlt = imageTmp; } } /** * Returns Image Padding from node options * * @returns {{top: number,left: number,bottom: number,right: number}} image padding inside this shape * @private */ }, { key: "_getImagePadding", value: function _getImagePadding() { var imgPadding = { top: 0, right: 0, bottom: 0, left: 0 }; if (this.options.imagePadding) { var optImgPadding = this.options.imagePadding; if (_typeof(optImgPadding) == "object") { imgPadding.top = optImgPadding.top; imgPadding.right = optImgPadding.right; imgPadding.bottom = optImgPadding.bottom; imgPadding.left = optImgPadding.left; } else { imgPadding.top = optImgPadding; imgPadding.right = optImgPadding; imgPadding.bottom = optImgPadding; imgPadding.left = optImgPadding; } } return imgPadding; } /** * Adjust the node dimensions for a loaded image. * * Pre: this.imageObj is valid */ }, { key: "_resizeImage", value: function _resizeImage() { var width, height; if (this.options.shapeProperties.useImageSize === false) { // Use the size property var ratio_width = 1; var ratio_height = 1; // Only calculate the proper ratio if both width and height not zero if (this.imageObj.width && this.imageObj.height) { if (this.imageObj.width > this.imageObj.height) { ratio_width = this.imageObj.width / this.imageObj.height; } else { ratio_height = this.imageObj.height / this.imageObj.width; } } width = this.options.size * 2 * ratio_width; height = this.options.size * 2 * ratio_height; } else { // Use the image size with image padding var imgPadding = this._getImagePadding(); width = this.imageObj.width + imgPadding.left + imgPadding.right; height = this.imageObj.height + imgPadding.top + imgPadding.bottom; } this.width = width; this.height = height; this.radius = 0.5 * this.width; } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {ArrowOptions} values * @private */ }, { key: "_drawRawCircle", value: function _drawRawCircle(ctx, x, y, values) { this.initContextForDraw(ctx, values); drawCircle(ctx, x, y, values.size); this.performFill(ctx, values); } /** * * @param {CanvasRenderingContext2D} ctx * @param {ArrowOptions} values * @private */ }, { key: "_drawImageAtPosition", value: function _drawImageAtPosition(ctx, values) { if (this.imageObj.width != 0) { // draw the image ctx.globalAlpha = values.opacity !== undefined ? values.opacity : 1; // draw shadow if enabled this.enableShadow(ctx, values); var factor = 1; if (this.options.shapeProperties.interpolation === true) { factor = this.imageObj.width / this.width / this.body.view.scale; } var imgPadding = this._getImagePadding(); var imgPosLeft = this.left + imgPadding.left; var imgPosTop = this.top + imgPadding.top; var imgWidth = this.width - imgPadding.left - imgPadding.right; var imgHeight = this.height - imgPadding.top - imgPadding.bottom; this.imageObj.drawImageAtPosition(ctx, factor, imgPosLeft, imgPosTop, imgWidth, imgHeight); // disable shadows for other elements. this.disableShadow(ctx, values); } } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @private */ }, { key: "_drawImageLabel", value: function _drawImageLabel(ctx, x, y, selected, hover) { var offset = 0; if (this.height !== undefined) { offset = this.height * 0.5; var labelDimensions = this.labelModule.getTextSize(ctx, selected, hover); if (labelDimensions.lineCount >= 1) { offset += labelDimensions.height / 2; } } var yLabel = y + offset; if (this.options.label) { this.labelOffset = offset; } this.labelModule.draw(ctx, x, yLabel, selected, hover, "hanging"); } }]); return CircleImageBase; }(NodeBase); function _createSuper$q(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$q(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$q() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Circle Node/Cluster shape. * * @augments CircleImageBase */ var Circle$1 = /*#__PURE__*/function (_CircleImageBase) { _inherits(Circle, _CircleImageBase); var _super = _createSuper$q(Circle); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function Circle(options, body, labelModule) { var _this; _classCallCheck(this, Circle); _this = _super.call(this, options, body, labelModule); _this._setMargins(labelModule); return _this; } /** * * @param {CanvasRenderingContext2D} ctx * @param {boolean} [selected] * @param {boolean} [hover] */ _createClass(Circle, [{ key: "resize", value: function resize(ctx) { var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected; var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover; if (this.needsRefresh(selected, hover)) { var dimensions = this.getDimensionsFromLabel(ctx, selected, hover); var diameter = Math.max(dimensions.width + this.margin.right + this.margin.left, dimensions.height + this.margin.top + this.margin.bottom); this.options.size = diameter / 2; // NOTE: this size field only set here, not in Ellipse, Database, Box this.width = diameter; this.height = diameter; this.radius = this.width / 2; } } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values */ }, { key: "draw", value: function draw(ctx, x, y, selected, hover, values) { this.resize(ctx, selected, hover); this.left = x - this.width / 2; this.top = y - this.height / 2; this._drawRawCircle(ctx, x, y, values); this.updateBoundingBox(x, y); this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, y, selected, hover); } /** * * @param {number} x width * @param {number} y height */ }, { key: "updateBoundingBox", value: function updateBoundingBox(x, y) { this.boundingBox.top = y - this.options.size; this.boundingBox.left = x - this.options.size; this.boundingBox.right = x + this.options.size; this.boundingBox.bottom = y + this.options.size; } /** * * @param {CanvasRenderingContext2D} ctx * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx) { if (ctx) { this.resize(ctx); } return this.width * 0.5; } }]); return Circle; }(CircleImageBase); function _createSuper$p(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$p(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$p() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A CircularImage Node/Cluster shape. * * @augments CircleImageBase */ var CircularImage = /*#__PURE__*/function (_CircleImageBase) { _inherits(CircularImage, _CircleImageBase); var _super = _createSuper$p(CircularImage); /** * @param {object} options * @param {object} body * @param {Label} labelModule * @param {Image} imageObj * @param {Image} imageObjAlt */ function CircularImage(options, body, labelModule, imageObj, imageObjAlt) { var _this; _classCallCheck(this, CircularImage); _this = _super.call(this, options, body, labelModule); _this.setImages(imageObj, imageObjAlt); return _this; } /** * * @param {CanvasRenderingContext2D} ctx * @param {boolean} [selected] * @param {boolean} [hover] */ _createClass(CircularImage, [{ key: "resize", value: function resize(ctx) { var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected; var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover; var imageAbsent = this.imageObj.src === undefined || this.imageObj.width === undefined || this.imageObj.height === undefined; if (imageAbsent) { var diameter = this.options.size * 2; this.width = diameter; this.height = diameter; this.radius = 0.5 * this.width; return; } // At this point, an image is present, i.e. this.imageObj is valid. if (this.needsRefresh(selected, hover)) { this._resizeImage(); } } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values */ }, { key: "draw", value: function draw(ctx, x, y, selected, hover, values) { this.switchImages(selected); this.resize(); var labelX = x, labelY = y; if (this.options.shapeProperties.coordinateOrigin === "top-left") { this.left = x; this.top = y; labelX += this.width / 2; labelY += this.height / 2; } else { this.left = x - this.width / 2; this.top = y - this.height / 2; } // draw the background circle. IMPORTANT: the stroke in this method is used by the clip method below. this._drawRawCircle(ctx, labelX, labelY, values); // now we draw in the circle, we save so we can revert the clip operation after drawing. ctx.save(); // clip is used to use the stroke in drawRawCircle as an area that we can draw in. ctx.clip(); // draw the image this._drawImageAtPosition(ctx, values); // restore so we can again draw on the full canvas ctx.restore(); this._drawImageLabel(ctx, labelX, labelY, selected, hover); this.updateBoundingBox(x, y); } // TODO: compare with Circle.updateBoundingBox(), consolidate? More stuff is happening here /** * * @param {number} x width * @param {number} y height */ }, { key: "updateBoundingBox", value: function updateBoundingBox(x, y) { if (this.options.shapeProperties.coordinateOrigin === "top-left") { this.boundingBox.top = y; this.boundingBox.left = x; this.boundingBox.right = x + this.options.size * 2; this.boundingBox.bottom = y + this.options.size * 2; } else { this.boundingBox.top = y - this.options.size; this.boundingBox.left = x - this.options.size; this.boundingBox.right = x + this.options.size; this.boundingBox.bottom = y + this.options.size; } // TODO: compare with Image.updateBoundingBox(), consolidate? this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); } /** * * @param {CanvasRenderingContext2D} ctx * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx) { if (ctx) { this.resize(ctx); } return this.width * 0.5; } }]); return CircularImage; }(CircleImageBase); function _createSuper$o(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$o(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$o() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * Base class for constructing Node/Cluster Shapes. * * @augments NodeBase */ var ShapeBase = /*#__PURE__*/function (_NodeBase) { _inherits(ShapeBase, _NodeBase); var _super = _createSuper$o(ShapeBase); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function ShapeBase(options, body, labelModule) { _classCallCheck(this, ShapeBase); return _super.call(this, options, body, labelModule); } /** * * @param {CanvasRenderingContext2D} ctx * @param {boolean} [selected] * @param {boolean} [hover] * @param {object} [values={size: this.options.size}] */ _createClass(ShapeBase, [{ key: "resize", value: function resize(ctx) { var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected; var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover; var values = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : { size: this.options.size }; if (this.needsRefresh(selected, hover)) { var _this$customSizeWidth, _this$customSizeHeigh; this.labelModule.getTextSize(ctx, selected, hover); var size = 2 * values.size; this.width = (_this$customSizeWidth = this.customSizeWidth) !== null && _this$customSizeWidth !== void 0 ? _this$customSizeWidth : size; this.height = (_this$customSizeHeigh = this.customSizeHeight) !== null && _this$customSizeHeigh !== void 0 ? _this$customSizeHeigh : size; this.radius = 0.5 * this.width; } } /** * * @param {CanvasRenderingContext2D} ctx * @param {string} shape * @param {number} sizeMultiplier - Unused! TODO: Remove next major release * @param {number} x * @param {number} y * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values * @private * @returns {object} Callbacks to draw later on higher layers. */ }, { key: "_drawShape", value: function _drawShape(ctx, shape, sizeMultiplier, x, y, selected, hover, values) { var _this = this; this.resize(ctx, selected, hover, values); this.left = x - this.width / 2; this.top = y - this.height / 2; this.initContextForDraw(ctx, values); getShape(shape)(ctx, x, y, values.size); this.performFill(ctx, values); if (this.options.icon !== undefined) { if (this.options.icon.code !== undefined) { ctx.font = (selected ? "bold " : "") + this.height / 2 + "px " + (this.options.icon.face || "FontAwesome"); ctx.fillStyle = this.options.icon.color || "black"; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText(this.options.icon.code, x, y); } } return { drawExternalLabel: function drawExternalLabel() { if (_this.options.label !== undefined) { // Need to call following here in order to ensure value for // `this.labelModule.size.height`. _this.labelModule.calculateLabelSize(ctx, selected, hover, x, y, "hanging"); var yLabel = y + 0.5 * _this.height + 0.5 * _this.labelModule.size.height; _this.labelModule.draw(ctx, x, yLabel, selected, hover, "hanging"); } _this.updateBoundingBox(x, y); } }; } /** * * @param {number} x * @param {number} y */ }, { key: "updateBoundingBox", value: function updateBoundingBox(x, y) { this.boundingBox.top = y - this.options.size; this.boundingBox.left = x - this.options.size; this.boundingBox.right = x + this.options.size; this.boundingBox.bottom = y + this.options.size; if (this.options.label !== undefined && this.labelModule.size.width > 0) { this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height); } } }]); return ShapeBase; }(NodeBase); function ownKeys$3(object, enumerableOnly) { var keys = _Object$keys(object); if (_Object$getOwnPropertySymbols) { var symbols = _Object$getOwnPropertySymbols(object); enumerableOnly && (symbols = _filterInstanceProperty(symbols).call(symbols, function (sym) { return _Object$getOwnPropertyDescriptor$1(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread$3(target) { for (var i = 1; i < arguments.length; i++) { var _context, _context2; var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? _forEachInstanceProperty(_context = ownKeys$3(Object(source), !0)).call(_context, function (key) { _defineProperty(target, key, source[key]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(target, _Object$getOwnPropertyDescriptors(source)) : _forEachInstanceProperty(_context2 = ownKeys$3(Object(source))).call(_context2, function (key) { _Object$defineProperty$1(target, key, _Object$getOwnPropertyDescriptor$1(source, key)); }); } return target; } function _createSuper$n(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$n(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$n() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A CustomShape Node/Cluster shape. * * @augments ShapeBase */ var CustomShape = /*#__PURE__*/function (_ShapeBase) { _inherits(CustomShape, _ShapeBase); var _super = _createSuper$n(CustomShape); /** * @param {object} options * @param {object} body * @param {Label} labelModule * @param {Function} ctxRenderer */ function CustomShape(options, body, labelModule, ctxRenderer) { var _this; _classCallCheck(this, CustomShape); _this = _super.call(this, options, body, labelModule, ctxRenderer); _this.ctxRenderer = ctxRenderer; return _this; } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values * @returns {object} Callbacks to draw later on different layers. */ _createClass(CustomShape, [{ key: "draw", value: function draw(ctx, x, y, selected, hover, values) { this.resize(ctx, selected, hover, values); this.left = x - this.width / 2; this.top = y - this.height / 2; // Guard right away because someone may just draw in the function itself. ctx.save(); var drawLater = this.ctxRenderer({ ctx: ctx, id: this.options.id, x: x, y: y, state: { selected: selected, hover: hover }, style: _objectSpread$3({}, values), label: this.options.label }); // Render the node shape bellow arrows. if (drawLater.drawNode != null) { drawLater.drawNode(); } ctx.restore(); if (drawLater.drawExternalLabel) { // Guard the external label (above arrows) drawing function. var drawExternalLabel = drawLater.drawExternalLabel; drawLater.drawExternalLabel = function () { ctx.save(); drawExternalLabel(); ctx.restore(); }; } if (drawLater.nodeDimensions) { this.customSizeWidth = drawLater.nodeDimensions.width; this.customSizeHeight = drawLater.nodeDimensions.height; } return drawLater; } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} angle * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx, angle) { return this._distanceToBorder(ctx, angle); } }]); return CustomShape; }(ShapeBase); function _createSuper$m(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$m(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$m() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Database Node/Cluster shape. * * @augments NodeBase */ var Database = /*#__PURE__*/function (_NodeBase) { _inherits(Database, _NodeBase); var _super = _createSuper$m(Database); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function Database(options, body, labelModule) { var _this; _classCallCheck(this, Database); _this = _super.call(this, options, body, labelModule); _this._setMargins(labelModule); return _this; } /** * * @param {CanvasRenderingContext2D} ctx * @param {boolean} selected * @param {boolean} hover */ _createClass(Database, [{ key: "resize", value: function resize(ctx, selected, hover) { if (this.needsRefresh(selected, hover)) { var dimensions = this.getDimensionsFromLabel(ctx, selected, hover); var size = dimensions.width + this.margin.right + this.margin.left; this.width = size; this.height = size; this.radius = this.width / 2; } } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values */ }, { key: "draw", value: function draw(ctx, x, y, selected, hover, values) { this.resize(ctx, selected, hover); this.left = x - this.width / 2; this.top = y - this.height / 2; this.initContextForDraw(ctx, values); drawDatabase(ctx, x - this.width / 2, y - this.height / 2, this.width, this.height); this.performFill(ctx, values); this.updateBoundingBox(x, y, ctx, selected, hover); this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, this.top + this.textSize.height / 2 + this.margin.top, selected, hover); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} angle * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx, angle) { return this._distanceToBorder(ctx, angle); } }]); return Database; }(NodeBase); function _createSuper$l(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$l(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$l() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Diamond Node/Cluster shape. * * @augments ShapeBase */ var Diamond$1 = /*#__PURE__*/function (_ShapeBase) { _inherits(Diamond, _ShapeBase); var _super = _createSuper$l(Diamond); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function Diamond(options, body, labelModule) { _classCallCheck(this, Diamond); return _super.call(this, options, body, labelModule); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values * @returns {object} Callbacks to draw later on higher layers. */ _createClass(Diamond, [{ key: "draw", value: function draw(ctx, x, y, selected, hover, values) { return this._drawShape(ctx, "diamond", 4, x, y, selected, hover, values); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} angle * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx, angle) { return this._distanceToBorder(ctx, angle); } }]); return Diamond; }(ShapeBase); function _createSuper$k(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$k(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$k() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Dot Node/Cluster shape. * * @augments ShapeBase */ var Dot = /*#__PURE__*/function (_ShapeBase) { _inherits(Dot, _ShapeBase); var _super = _createSuper$k(Dot); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function Dot(options, body, labelModule) { _classCallCheck(this, Dot); return _super.call(this, options, body, labelModule); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values * @returns {object} Callbacks to draw later on higher layers. */ _createClass(Dot, [{ key: "draw", value: function draw(ctx, x, y, selected, hover, values) { return this._drawShape(ctx, "circle", 2, x, y, selected, hover, values); } /** * * @param {CanvasRenderingContext2D} ctx * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx) { if (ctx) { this.resize(ctx); } return this.options.size; } }]); return Dot; }(ShapeBase); function _createSuper$j(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$j(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$j() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * Am Ellipse Node/Cluster shape. * * @augments NodeBase */ var Ellipse = /*#__PURE__*/function (_NodeBase) { _inherits(Ellipse, _NodeBase); var _super = _createSuper$j(Ellipse); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function Ellipse(options, body, labelModule) { _classCallCheck(this, Ellipse); return _super.call(this, options, body, labelModule); } /** * * @param {CanvasRenderingContext2D} ctx * @param {boolean} [selected] * @param {boolean} [hover] */ _createClass(Ellipse, [{ key: "resize", value: function resize(ctx) { var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected; var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover; if (this.needsRefresh(selected, hover)) { var dimensions = this.getDimensionsFromLabel(ctx, selected, hover); this.height = dimensions.height * 2; this.width = dimensions.width + dimensions.height; this.radius = 0.5 * this.width; } } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values */ }, { key: "draw", value: function draw(ctx, x, y, selected, hover, values) { this.resize(ctx, selected, hover); this.left = x - this.width * 0.5; this.top = y - this.height * 0.5; this.initContextForDraw(ctx, values); drawEllipse(ctx, this.left, this.top, this.width, this.height); this.performFill(ctx, values); this.updateBoundingBox(x, y, ctx, selected, hover); this.labelModule.draw(ctx, x, y, selected, hover); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} angle * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx, angle) { if (ctx) { this.resize(ctx); } var a = this.width * 0.5; var b = this.height * 0.5; var w = Math.sin(angle) * a; var h = Math.cos(angle) * b; return a * b / Math.sqrt(w * w + h * h); } }]); return Ellipse; }(NodeBase); function _createSuper$i(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$i(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$i() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * An icon replacement for the default Node shape. * * @augments NodeBase */ var Icon = /*#__PURE__*/function (_NodeBase) { _inherits(Icon, _NodeBase); var _super = _createSuper$i(Icon); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function Icon(options, body, labelModule) { var _this; _classCallCheck(this, Icon); _this = _super.call(this, options, body, labelModule); _this._setMargins(labelModule); return _this; } /** * * @param {CanvasRenderingContext2D} ctx - Unused. * @param {boolean} [selected] * @param {boolean} [hover] */ _createClass(Icon, [{ key: "resize", value: function resize(ctx, selected, hover) { if (this.needsRefresh(selected, hover)) { this.iconSize = { width: Number(this.options.icon.size), height: Number(this.options.icon.size) }; this.width = this.iconSize.width + this.margin.right + this.margin.left; this.height = this.iconSize.height + this.margin.top + this.margin.bottom; this.radius = 0.5 * this.width; } } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values * @returns {object} Callbacks to draw later on higher layers. */ }, { key: "draw", value: function draw(ctx, x, y, selected, hover, values) { var _this2 = this; this.resize(ctx, selected, hover); this.options.icon.size = this.options.icon.size || 50; this.left = x - this.width / 2; this.top = y - this.height / 2; this._icon(ctx, x, y, selected, hover, values); return { drawExternalLabel: function drawExternalLabel() { if (_this2.options.label !== undefined) { var iconTextSpacing = 5; _this2.labelModule.draw(ctx, _this2.left + _this2.iconSize.width / 2 + _this2.margin.left, y + _this2.height / 2 + iconTextSpacing, selected); } _this2.updateBoundingBox(x, y); } }; } /** * * @param {number} x * @param {number} y */ }, { key: "updateBoundingBox", value: function updateBoundingBox(x, y) { this.boundingBox.top = y - this.options.icon.size * 0.5; this.boundingBox.left = x - this.options.icon.size * 0.5; this.boundingBox.right = x + this.options.icon.size * 0.5; this.boundingBox.bottom = y + this.options.icon.size * 0.5; if (this.options.label !== undefined && this.labelModule.size.width > 0) { var iconTextSpacing = 5; this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelModule.size.height + iconTextSpacing); } } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover - Unused * @param {ArrowOptions} values */ }, { key: "_icon", value: function _icon(ctx, x, y, selected, hover, values) { var iconSize = Number(this.options.icon.size); if (this.options.icon.code !== undefined) { ctx.font = [this.options.icon.weight != null ? this.options.icon.weight : selected ? "bold" : "", // If the weight is forced (for example to make Font Awesome 5 work // properly) substitute slightly bigger size for bold font face. (this.options.icon.weight != null && selected ? 5 : 0) + iconSize + "px", this.options.icon.face].join(" "); // draw icon ctx.fillStyle = this.options.icon.color || "black"; ctx.textAlign = "center"; ctx.textBaseline = "middle"; // draw shadow if enabled this.enableShadow(ctx, values); ctx.fillText(this.options.icon.code, x, y); // disable shadows for other elements. this.disableShadow(ctx, values); } else { console.error("When using the icon shape, you need to define the code in the icon options object. This can be done per node or globally."); } } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} angle * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx, angle) { return this._distanceToBorder(ctx, angle); } }]); return Icon; }(NodeBase); function _createSuper$h(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$h(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$h() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * An image-based replacement for the default Node shape. * * @augments CircleImageBase */ var Image$2 = /*#__PURE__*/function (_CircleImageBase) { _inherits(Image, _CircleImageBase); var _super = _createSuper$h(Image); /** * @param {object} options * @param {object} body * @param {Label} labelModule * @param {Image} imageObj * @param {Image} imageObjAlt */ function Image(options, body, labelModule, imageObj, imageObjAlt) { var _this; _classCallCheck(this, Image); _this = _super.call(this, options, body, labelModule); _this.setImages(imageObj, imageObjAlt); return _this; } /** * * @param {CanvasRenderingContext2D} ctx - Unused. * @param {boolean} [selected] * @param {boolean} [hover] */ _createClass(Image, [{ key: "resize", value: function resize(ctx) { var selected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.selected; var hover = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.hover; var imageAbsent = this.imageObj.src === undefined || this.imageObj.width === undefined || this.imageObj.height === undefined; if (imageAbsent) { var side = this.options.size * 2; this.width = side; this.height = side; return; } if (this.needsRefresh(selected, hover)) { this._resizeImage(); } } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values */ }, { key: "draw", value: function draw(ctx, x, y, selected, hover, values) { ctx.save(); this.switchImages(selected); this.resize(); var labelX = x, labelY = y; if (this.options.shapeProperties.coordinateOrigin === "top-left") { this.left = x; this.top = y; labelX += this.width / 2; labelY += this.height / 2; } else { this.left = x - this.width / 2; this.top = y - this.height / 2; } if (this.options.shapeProperties.useBorderWithImage === true) { var neutralborderWidth = this.options.borderWidth; var selectionLineWidth = this.options.borderWidthSelected || 2 * this.options.borderWidth; var borderWidth = (selected ? selectionLineWidth : neutralborderWidth) / this.body.view.scale; ctx.lineWidth = Math.min(this.width, borderWidth); ctx.beginPath(); var strokeStyle = selected ? this.options.color.highlight.border : hover ? this.options.color.hover.border : this.options.color.border; var fillStyle = selected ? this.options.color.highlight.background : hover ? this.options.color.hover.background : this.options.color.background; if (values.opacity !== undefined) { strokeStyle = overrideOpacity(strokeStyle, values.opacity); fillStyle = overrideOpacity(fillStyle, values.opacity); } // setup the line properties. ctx.strokeStyle = strokeStyle; // set a fillstyle ctx.fillStyle = fillStyle; // draw a rectangle to form the border around. This rectangle is filled so the opacity of a picture (in future vis releases?) can be used to tint the image ctx.rect(this.left - 0.5 * ctx.lineWidth, this.top - 0.5 * ctx.lineWidth, this.width + ctx.lineWidth, this.height + ctx.lineWidth); _fillInstanceProperty(ctx).call(ctx); this.performStroke(ctx, values); ctx.closePath(); } this._drawImageAtPosition(ctx, values); this._drawImageLabel(ctx, labelX, labelY, selected, hover); this.updateBoundingBox(x, y); ctx.restore(); } /** * * @param {number} x * @param {number} y */ }, { key: "updateBoundingBox", value: function updateBoundingBox(x, y) { this.resize(); if (this.options.shapeProperties.coordinateOrigin === "top-left") { this.left = x; this.top = y; } else { this.left = x - this.width / 2; this.top = y - this.height / 2; } this.boundingBox.left = this.left; this.boundingBox.top = this.top; this.boundingBox.bottom = this.top + this.height; this.boundingBox.right = this.left + this.width; if (this.options.label !== undefined && this.labelModule.size.width > 0) { this.boundingBox.left = Math.min(this.boundingBox.left, this.labelModule.size.left); this.boundingBox.right = Math.max(this.boundingBox.right, this.labelModule.size.left + this.labelModule.size.width); this.boundingBox.bottom = Math.max(this.boundingBox.bottom, this.boundingBox.bottom + this.labelOffset); } } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} angle * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx, angle) { return this._distanceToBorder(ctx, angle); } }]); return Image; }(CircleImageBase); function _createSuper$g(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$g(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$g() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Square Node/Cluster shape. * * @augments ShapeBase */ var Square = /*#__PURE__*/function (_ShapeBase) { _inherits(Square, _ShapeBase); var _super = _createSuper$g(Square); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function Square(options, body, labelModule) { _classCallCheck(this, Square); return _super.call(this, options, body, labelModule); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values * @returns {object} Callbacks to draw later on higher layers. */ _createClass(Square, [{ key: "draw", value: function draw(ctx, x, y, selected, hover, values) { return this._drawShape(ctx, "square", 2, x, y, selected, hover, values); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} angle * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx, angle) { return this._distanceToBorder(ctx, angle); } }]); return Square; }(ShapeBase); function _createSuper$f(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$f(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$f() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Hexagon Node/Cluster shape. * * @augments ShapeBase */ var Hexagon = /*#__PURE__*/function (_ShapeBase) { _inherits(Hexagon, _ShapeBase); var _super = _createSuper$f(Hexagon); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function Hexagon(options, body, labelModule) { _classCallCheck(this, Hexagon); return _super.call(this, options, body, labelModule); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values * @returns {object} Callbacks to draw later on higher layers. */ _createClass(Hexagon, [{ key: "draw", value: function draw(ctx, x, y, selected, hover, values) { return this._drawShape(ctx, "hexagon", 4, x, y, selected, hover, values); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} angle * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx, angle) { return this._distanceToBorder(ctx, angle); } }]); return Hexagon; }(ShapeBase); function _createSuper$e(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$e(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$e() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Star Node/Cluster shape. * * @augments ShapeBase */ var Star = /*#__PURE__*/function (_ShapeBase) { _inherits(Star, _ShapeBase); var _super = _createSuper$e(Star); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function Star(options, body, labelModule) { _classCallCheck(this, Star); return _super.call(this, options, body, labelModule); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values * @returns {object} Callbacks to draw later on higher layers. */ _createClass(Star, [{ key: "draw", value: function draw(ctx, x, y, selected, hover, values) { return this._drawShape(ctx, "star", 4, x, y, selected, hover, values); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} angle * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx, angle) { return this._distanceToBorder(ctx, angle); } }]); return Star; }(ShapeBase); function _createSuper$d(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$d(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$d() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A text-based replacement for the default Node shape. * * @augments NodeBase */ var Text = /*#__PURE__*/function (_NodeBase) { _inherits(Text, _NodeBase); var _super = _createSuper$d(Text); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function Text(options, body, labelModule) { var _this; _classCallCheck(this, Text); _this = _super.call(this, options, body, labelModule); _this._setMargins(labelModule); return _this; } /** * * @param {CanvasRenderingContext2D} ctx * @param {boolean} selected * @param {boolean} hover */ _createClass(Text, [{ key: "resize", value: function resize(ctx, selected, hover) { if (this.needsRefresh(selected, hover)) { this.textSize = this.labelModule.getTextSize(ctx, selected, hover); this.width = this.textSize.width + this.margin.right + this.margin.left; this.height = this.textSize.height + this.margin.top + this.margin.bottom; this.radius = 0.5 * this.width; } } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x width * @param {number} y height * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values */ }, { key: "draw", value: function draw(ctx, x, y, selected, hover, values) { this.resize(ctx, selected, hover); this.left = x - this.width / 2; this.top = y - this.height / 2; // draw shadow if enabled this.enableShadow(ctx, values); this.labelModule.draw(ctx, this.left + this.textSize.width / 2 + this.margin.left, this.top + this.textSize.height / 2 + this.margin.top, selected, hover); // disable shadows for other elements. this.disableShadow(ctx, values); this.updateBoundingBox(x, y, ctx, selected, hover); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} angle * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx, angle) { return this._distanceToBorder(ctx, angle); } }]); return Text; }(NodeBase); function _createSuper$c(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$c(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$c() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Triangle Node/Cluster shape. * * @augments ShapeBase */ var Triangle$1 = /*#__PURE__*/function (_ShapeBase) { _inherits(Triangle, _ShapeBase); var _super = _createSuper$c(Triangle); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function Triangle(options, body, labelModule) { _classCallCheck(this, Triangle); return _super.call(this, options, body, labelModule); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x * @param {number} y * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values * @returns {object} Callbacks to draw later on higher layers. */ _createClass(Triangle, [{ key: "draw", value: function draw(ctx, x, y, selected, hover, values) { return this._drawShape(ctx, "triangle", 3, x, y, selected, hover, values); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} angle * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx, angle) { return this._distanceToBorder(ctx, angle); } }]); return Triangle; }(ShapeBase); function _createSuper$b(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$b(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$b() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A downward facing Triangle Node/Cluster shape. * * @augments ShapeBase */ var TriangleDown = /*#__PURE__*/function (_ShapeBase) { _inherits(TriangleDown, _ShapeBase); var _super = _createSuper$b(TriangleDown); /** * @param {object} options * @param {object} body * @param {Label} labelModule */ function TriangleDown(options, body, labelModule) { _classCallCheck(this, TriangleDown); return _super.call(this, options, body, labelModule); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} x * @param {number} y * @param {boolean} selected * @param {boolean} hover * @param {ArrowOptions} values * @returns {object} Callbacks to draw later on higher layers. */ _createClass(TriangleDown, [{ key: "draw", value: function draw(ctx, x, y, selected, hover, values) { return this._drawShape(ctx, "triangleDown", 3, x, y, selected, hover, values); } /** * * @param {CanvasRenderingContext2D} ctx * @param {number} angle * @returns {number} */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx, angle) { return this._distanceToBorder(ctx, angle); } }]); return TriangleDown; }(ShapeBase); function ownKeys$2(object, enumerableOnly) { var keys = _Object$keys(object); if (_Object$getOwnPropertySymbols) { var symbols = _Object$getOwnPropertySymbols(object); enumerableOnly && (symbols = _filterInstanceProperty(symbols).call(symbols, function (sym) { return _Object$getOwnPropertyDescriptor$1(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread$2(target) { for (var i = 1; i < arguments.length; i++) { var _context5, _context6; var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? _forEachInstanceProperty(_context5 = ownKeys$2(Object(source), !0)).call(_context5, function (key) { _defineProperty(target, key, source[key]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(target, _Object$getOwnPropertyDescriptors(source)) : _forEachInstanceProperty(_context6 = ownKeys$2(Object(source))).call(_context6, function (key) { _Object$defineProperty$1(target, key, _Object$getOwnPropertyDescriptor$1(source, key)); }); } return target; } /** * A node. A node can be connected to other nodes via one or multiple edges. */ var Node = /*#__PURE__*/function () { /** * * @param {object} options An object containing options for the node. All * options are optional, except for the id. * {number} id Id of the node. Required * {string} label Text label for the node * {number} x Horizontal position of the node * {number} y Vertical position of the node * {string} shape Node shape * {string} image An image url * {string} title A title text, can be HTML * {anytype} group A group name or number * @param {object} body Shared state of current network instance * @param {Network.Images} imagelist A list with images. Only needed when the node has an image * @param {Groups} grouplist A list with groups. Needed for retrieving group options * @param {object} globalOptions Current global node options; these serve as defaults for the node instance * @param {object} defaultOptions Global default options for nodes; note that this is also the prototype * for parameter `globalOptions`. */ function Node(options, body, imagelist, grouplist, globalOptions, defaultOptions) { _classCallCheck(this, Node); this.options = bridgeObject(globalOptions); this.globalOptions = globalOptions; this.defaultOptions = defaultOptions; this.body = body; this.edges = []; // all edges connected to this node // set defaults for the options this.id = undefined; this.imagelist = imagelist; this.grouplist = grouplist; // state options this.x = undefined; this.y = undefined; this.baseSize = this.options.size; this.baseFontSize = this.options.font.size; this.predefinedPosition = false; // used to check if initial fit should just take the range or approximate this.selected = false; this.hover = false; this.labelModule = new Label(this.body, this.options, false /* Not edge label */); this.setOptions(options); } /** * Attach a edge to the node * * @param {Edge} edge */ _createClass(Node, [{ key: "attachEdge", value: function attachEdge(edge) { var _context; if (_indexOfInstanceProperty(_context = this.edges).call(_context, edge) === -1) { this.edges.push(edge); } } /** * Detach a edge from the node * * @param {Edge} edge */ }, { key: "detachEdge", value: function detachEdge(edge) { var _context2; var index = _indexOfInstanceProperty(_context2 = this.edges).call(_context2, edge); if (index != -1) { var _context3; _spliceInstanceProperty(_context3 = this.edges).call(_context3, index, 1); } } /** * Set or overwrite options for the node * * @param {object} options an object with options * @returns {null|boolean} */ }, { key: "setOptions", value: function setOptions(options) { var currentShape = this.options.shape; if (!options) { return; // Note that the return value will be 'undefined'! This is OK. } // Save the color for later. // This is necessary in order to prevent local color from being overwritten by group color. // TODO: To prevent such workarounds the way options are handled should be rewritten from scratch. // This is not the only problem with current options handling. if (typeof options.color !== "undefined") { this._localColor = options.color; } // basic options if (options.id !== undefined) { this.id = options.id; } if (this.id === undefined) { throw new Error("Node must have an id"); } Node.checkMass(options, this.id); // set these options locally // clear x and y positions if (options.x !== undefined) { if (options.x === null) { this.x = undefined; this.predefinedPosition = false; } else { this.x = _parseInt(options.x); this.predefinedPosition = true; } } if (options.y !== undefined) { if (options.y === null) { this.y = undefined; this.predefinedPosition = false; } else { this.y = _parseInt(options.y); this.predefinedPosition = true; } } if (options.size !== undefined) { this.baseSize = options.size; } if (options.value !== undefined) { options.value = _parseFloat(options.value); } // this transforms all shorthands into fully defined options Node.parseOptions(this.options, options, true, this.globalOptions, this.grouplist); var pile = [options, this.options, this.defaultOptions]; this.chooser = choosify("node", pile); this._load_images(); this.updateLabelModule(options); // Need to set local opacity after `this.updateLabelModule(options);` because `this.updateLabelModule(options);` overrites local opacity with group opacity if (options.opacity !== undefined && Node.checkOpacity(options.opacity)) { this.options.opacity = options.opacity; } this.updateShape(currentShape); return options.hidden !== undefined || options.physics !== undefined; } /** * Load the images from the options, for the nodes that need them. * * Images are always loaded, even if they are not used in the current shape. * The user may switch to an image shape later on. * * @private */ }, { key: "_load_images", value: function _load_images() { if (this.options.shape === "circularImage" || this.options.shape === "image") { if (this.options.image === undefined) { throw new Error("Option image must be defined for node type '" + this.options.shape + "'"); } } if (this.options.image === undefined) { return; } if (this.imagelist === undefined) { throw new Error("Internal Error: No images provided"); } if (typeof this.options.image === "string") { this.imageObj = this.imagelist.load(this.options.image, this.options.brokenImage, this.id); } else { if (this.options.image.unselected === undefined) { throw new Error("No unselected image provided"); } this.imageObj = this.imagelist.load(this.options.image.unselected, this.options.brokenImage, this.id); if (this.options.image.selected !== undefined) { this.imageObjAlt = this.imagelist.load(this.options.image.selected, this.options.brokenImage, this.id); } else { this.imageObjAlt = undefined; } } } /** * Check that opacity is only between 0 and 1 * * @param {number} opacity * @returns {boolean} */ }, { key: "getFormattingValues", value: /** * * @returns {{color: *, borderWidth: *, borderColor: *, size: *, borderDashes: (boolean|Array|allOptions.nodes.shapeProperties.borderDashes|{boolean, array}), borderRadius: (number|allOptions.nodes.shapeProperties.borderRadius|{number}|Array), shadow: *, shadowColor: *, shadowSize: *, shadowX: *, shadowY: *}} */ function getFormattingValues() { var values = { color: this.options.color.background, opacity: this.options.opacity, borderWidth: this.options.borderWidth, borderColor: this.options.color.border, size: this.options.size, borderDashes: this.options.shapeProperties.borderDashes, borderRadius: this.options.shapeProperties.borderRadius, shadow: this.options.shadow.enabled, shadowColor: this.options.shadow.color, shadowSize: this.options.shadow.size, shadowX: this.options.shadow.x, shadowY: this.options.shadow.y }; if (this.selected || this.hover) { if (this.chooser === true) { if (this.selected) { if (this.options.borderWidthSelected != null) { values.borderWidth = this.options.borderWidthSelected; } else { values.borderWidth *= 2; } values.color = this.options.color.highlight.background; values.borderColor = this.options.color.highlight.border; values.shadow = this.options.shadow.enabled; } else if (this.hover) { values.color = this.options.color.hover.background; values.borderColor = this.options.color.hover.border; values.shadow = this.options.shadow.enabled; } } else if (typeof this.chooser === "function") { this.chooser(values, this.options.id, this.selected, this.hover); if (values.shadow === false) { if (values.shadowColor !== this.options.shadow.color || values.shadowSize !== this.options.shadow.size || values.shadowX !== this.options.shadow.x || values.shadowY !== this.options.shadow.y) { values.shadow = true; } } } } else { values.shadow = this.options.shadow.enabled; } if (this.options.opacity !== undefined) { var opacity = this.options.opacity; values.borderColor = overrideOpacity(values.borderColor, opacity); values.color = overrideOpacity(values.color, opacity); values.shadowColor = overrideOpacity(values.shadowColor, opacity); } return values; } /** * * @param {object} options */ }, { key: "updateLabelModule", value: function updateLabelModule(options) { if (this.options.label === undefined || this.options.label === null) { this.options.label = ""; } Node.updateGroupOptions(this.options, _objectSpread$2(_objectSpread$2({}, options), {}, { color: options && options.color || this._localColor || undefined }), this.grouplist); // // Note:The prototype chain for this.options is: // // this.options -> NodesHandler.options -> NodesHandler.defaultOptions // (also: this.globalOptions) // // Note that the prototypes are mentioned explicitly in the pile list below; // WE DON'T WANT THE ORDER OF THE PROTOTYPES!!!! At least, not for font handling of labels. // This is a good indication that the prototype usage of options is deficient. // var currentGroup = this.grouplist.get(this.options.group, false); var pile = [options, // new options this.options, // current node options, see comment above for prototype currentGroup, // group options, if any this.globalOptions, // Currently set global node options this.defaultOptions // Default global node options ]; this.labelModule.update(this.options, pile); if (this.labelModule.baseSize !== undefined) { this.baseFontSize = this.labelModule.baseSize; } } /** * * @param {string} currentShape */ }, { key: "updateShape", value: function updateShape(currentShape) { if (currentShape === this.options.shape && this.shape) { this.shape.setOptions(this.options, this.imageObj, this.imageObjAlt); } else { // choose draw method depending on the shape switch (this.options.shape) { case "box": this.shape = new Box$1(this.options, this.body, this.labelModule); break; case "circle": this.shape = new Circle$1(this.options, this.body, this.labelModule); break; case "circularImage": this.shape = new CircularImage(this.options, this.body, this.labelModule, this.imageObj, this.imageObjAlt); break; case "custom": this.shape = new CustomShape(this.options, this.body, this.labelModule, this.options.ctxRenderer); break; case "database": this.shape = new Database(this.options, this.body, this.labelModule); break; case "diamond": this.shape = new Diamond$1(this.options, this.body, this.labelModule); break; case "dot": this.shape = new Dot(this.options, this.body, this.labelModule); break; case "ellipse": this.shape = new Ellipse(this.options, this.body, this.labelModule); break; case "icon": this.shape = new Icon(this.options, this.body, this.labelModule); break; case "image": this.shape = new Image$2(this.options, this.body, this.labelModule, this.imageObj, this.imageObjAlt); break; case "square": this.shape = new Square(this.options, this.body, this.labelModule); break; case "hexagon": this.shape = new Hexagon(this.options, this.body, this.labelModule); break; case "star": this.shape = new Star(this.options, this.body, this.labelModule); break; case "text": this.shape = new Text(this.options, this.body, this.labelModule); break; case "triangle": this.shape = new Triangle$1(this.options, this.body, this.labelModule); break; case "triangleDown": this.shape = new TriangleDown(this.options, this.body, this.labelModule); break; default: this.shape = new Ellipse(this.options, this.body, this.labelModule); break; } } this.needsRefresh(); } /** * select this node */ }, { key: "select", value: function select() { this.selected = true; this.needsRefresh(); } /** * unselect this node */ }, { key: "unselect", value: function unselect() { this.selected = false; this.needsRefresh(); } /** * Reset the calculated size of the node, forces it to recalculate its size */ }, { key: "needsRefresh", value: function needsRefresh() { this.shape.refreshNeeded = true; } /** * get the title of this node. * * @returns {string} title The title of the node, or undefined when no title * has been set. */ }, { key: "getTitle", value: function getTitle() { return this.options.title; } /** * Calculate the distance to the border of the Node * * @param {CanvasRenderingContext2D} ctx * @param {number} angle Angle in radians * @returns {number} distance Distance to the border in pixels */ }, { key: "distanceToBorder", value: function distanceToBorder(ctx, angle) { return this.shape.distanceToBorder(ctx, angle); } /** * Check if this node has a fixed x and y position * * @returns {boolean} true if fixed, false if not */ }, { key: "isFixed", value: function isFixed() { return this.options.fixed.x && this.options.fixed.y; } /** * check if this node is selecte * * @returns {boolean} selected True if node is selected, else false */ }, { key: "isSelected", value: function isSelected() { return this.selected; } /** * Retrieve the value of the node. Can be undefined * * @returns {number} value */ }, { key: "getValue", value: function getValue() { return this.options.value; } /** * Get the current dimensions of the label * * @returns {rect} */ }, { key: "getLabelSize", value: function getLabelSize() { return this.labelModule.size(); } /** * Adjust the value range of the node. The node will adjust it's size * based on its value. * * @param {number} min * @param {number} max * @param {number} total */ }, { key: "setValueRange", value: function setValueRange(min, max, total) { if (this.options.value !== undefined) { var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); var sizeDiff = this.options.scaling.max - this.options.scaling.min; if (this.options.scaling.label.enabled === true) { var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min; this.options.font.size = this.options.scaling.label.min + scale * fontDiff; } this.options.size = this.options.scaling.min + scale * sizeDiff; } else { this.options.size = this.baseSize; this.options.font.size = this.baseFontSize; } this.updateLabelModule(); } /** * Draw this node in the given canvas * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); * * @param {CanvasRenderingContext2D} ctx * @returns {object} Callbacks to draw later on higher layers. */ }, { key: "draw", value: function draw(ctx) { var values = this.getFormattingValues(); return this.shape.draw(ctx, this.x, this.y, this.selected, this.hover, values) || {}; } /** * Update the bounding box of the shape * * @param {CanvasRenderingContext2D} ctx */ }, { key: "updateBoundingBox", value: function updateBoundingBox(ctx) { this.shape.updateBoundingBox(this.x, this.y, ctx); } /** * Recalculate the size of this node in the given canvas * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); * * @param {CanvasRenderingContext2D} ctx */ }, { key: "resize", value: function resize(ctx) { var values = this.getFormattingValues(); this.shape.resize(ctx, this.selected, this.hover, values); } /** * Determine all visual elements of this node instance, in which the given * point falls within the bounding shape. * * @param {point} point * @returns {Array.<nodeClickItem|nodeLabelClickItem>} list with the items which are on the point */ }, { key: "getItemsOnPoint", value: function getItemsOnPoint(point) { var ret = []; if (this.labelModule.visible()) { if (pointInRect(this.labelModule.getSize(), point)) { ret.push({ nodeId: this.id, labelId: 0 }); } } if (pointInRect(this.shape.boundingBox, point)) { ret.push({ nodeId: this.id }); } return ret; } /** * Check if this object is overlapping with the provided object * * @param {object} obj an object with parameters left, top, right, bottom * @returns {boolean} True if location is located on node */ }, { key: "isOverlappingWith", value: function isOverlappingWith(obj) { return this.shape.left < obj.right && this.shape.left + this.shape.width > obj.left && this.shape.top < obj.bottom && this.shape.top + this.shape.height > obj.top; } /** * Check if this object is overlapping with the provided object * * @param {object} obj an object with parameters left, top, right, bottom * @returns {boolean} True if location is located on node */ }, { key: "isBoundingBoxOverlappingWith", value: function isBoundingBoxOverlappingWith(obj) { return this.shape.boundingBox.left < obj.right && this.shape.boundingBox.right > obj.left && this.shape.boundingBox.top < obj.bottom && this.shape.boundingBox.bottom > obj.top; } /** * Check valid values for mass * * The mass may not be negative or zero. If it is, reset to 1 * * @param {object} options * @param {Node.id} id * @static */ }], [{ key: "checkOpacity", value: function checkOpacity(opacity) { return 0 <= opacity && opacity <= 1; } /** * Check that origin is 'center' or 'top-left' * * @param {string} origin * @returns {boolean} */ }, { key: "checkCoordinateOrigin", value: function checkCoordinateOrigin(origin) { return origin === undefined || origin === "center" || origin === "top-left"; } /** * Copy group option values into the node options. * * The group options override the global node options, so the copy of group options * must happen *after* the global node options have been set. * * This method must also be called also if the global node options have changed and the group options did not. * * @param {object} parentOptions * @param {object} newOptions new values for the options, currently only passed in for check * @param {object} groupList */ }, { key: "updateGroupOptions", value: function updateGroupOptions(parentOptions, newOptions, groupList) { var _context4; if (groupList === undefined) return; // No groups, nothing to do var group = parentOptions.group; // paranoia: the selected group is already merged into node options, check. if (newOptions !== undefined && newOptions.group !== undefined && group !== newOptions.group) { throw new Error("updateGroupOptions: group values in options don't match."); } var hasGroup = typeof group === "number" || typeof group === "string" && group != ""; if (!hasGroup) return; // current node has no group, no need to merge var groupObj = groupList.get(group); if (groupObj.opacity !== undefined && newOptions.opacity === undefined) { if (!Node.checkOpacity(groupObj.opacity)) { console.error("Invalid option for node opacity. Value must be between 0 and 1, found: " + groupObj.opacity); groupObj.opacity = undefined; } } // Skip any new option to avoid them being overridden by the group options. var skipProperties = _filterInstanceProperty(_context4 = _Object$getOwnPropertyNames(newOptions)).call(_context4, function (p) { return newOptions[p] != null; }); // Always skip merging group font options into parent; these are required to be distinct for labels skipProperties.push("font"); selectiveNotDeepExtend(skipProperties, parentOptions, groupObj); // the color object needs to be completely defined. // Since groups can partially overwrite the colors, we parse it again, just in case. parentOptions.color = parseColor(parentOptions.color); } /** * This process all possible shorthands in the new options and makes sure that the parentOptions are fully defined. * Static so it can also be used by the handler. * * @param {object} parentOptions * @param {object} newOptions * @param {boolean} [allowDeletion=false] * @param {object} [globalOptions={}] * @param {object} [groupList] * @static */ }, { key: "parseOptions", value: function parseOptions(parentOptions, newOptions) { var allowDeletion = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var globalOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; var groupList = arguments.length > 4 ? arguments[4] : undefined; var fields = ["color", "fixed", "shadow"]; selectiveNotDeepExtend(fields, parentOptions, newOptions, allowDeletion); Node.checkMass(newOptions); if (parentOptions.opacity !== undefined) { if (!Node.checkOpacity(parentOptions.opacity)) { console.error("Invalid option for node opacity. Value must be between 0 and 1, found: " + parentOptions.opacity); parentOptions.opacity = undefined; } } if (newOptions.opacity !== undefined) { if (!Node.checkOpacity(newOptions.opacity)) { console.error("Invalid option for node opacity. Value must be between 0 and 1, found: " + newOptions.opacity); newOptions.opacity = undefined; } } if (newOptions.shapeProperties && !Node.checkCoordinateOrigin(newOptions.shapeProperties.coordinateOrigin)) { console.error("Invalid option for node coordinateOrigin, found: " + newOptions.shapeProperties.coordinateOrigin); } // merge the shadow options into the parent. mergeOptions(parentOptions, newOptions, "shadow", globalOptions); // individual shape newOptions if (newOptions.color !== undefined && newOptions.color !== null) { var parsedColor = parseColor(newOptions.color); fillIfDefined(parentOptions.color, parsedColor); } else if (allowDeletion === true && newOptions.color === null) { parentOptions.color = bridgeObject(globalOptions.color); // set the object back to the global options } // handle the fixed options if (newOptions.fixed !== undefined && newOptions.fixed !== null) { if (typeof newOptions.fixed === "boolean") { parentOptions.fixed.x = newOptions.fixed; parentOptions.fixed.y = newOptions.fixed; } else { if (newOptions.fixed.x !== undefined && typeof newOptions.fixed.x === "boolean") { parentOptions.fixed.x = newOptions.fixed.x; } if (newOptions.fixed.y !== undefined && typeof newOptions.fixed.y === "boolean") { parentOptions.fixed.y = newOptions.fixed.y; } } } if (allowDeletion === true && newOptions.font === null) { parentOptions.font = bridgeObject(globalOptions.font); // set the object back to the global options } Node.updateGroupOptions(parentOptions, newOptions, groupList); // handle the scaling options, specifically the label part if (newOptions.scaling !== undefined) { mergeOptions(parentOptions.scaling, newOptions.scaling, "label", globalOptions.scaling); } } }, { key: "checkMass", value: function checkMass(options, id) { if (options.mass !== undefined && options.mass <= 0) { var strId = ""; if (id !== undefined) { strId = " in node id: " + id; } console.error("%cNegative or zero mass disallowed" + strId + ", setting mass to 1.", VALIDATOR_PRINT_STYLE); options.mass = 1; } } }]); return Node; }(); function _createForOfIteratorHelper$5(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray$5(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray$5(o, minLen) { var _context4; if (!o) return; if (typeof o === "string") return _arrayLikeToArray$5(o, minLen); var n = _sliceInstanceProperty(_context4 = Object.prototype.toString.call(o)).call(_context4, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from$1(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$5(o, minLen); } function _arrayLikeToArray$5(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } /** * Handler for Nodes */ var NodesHandler = /*#__PURE__*/function () { /** * @param {object} body * @param {Images} images * @param {Array.<Group>} groups * @param {LayoutEngine} layoutEngine */ function NodesHandler(body, images, groups, layoutEngine) { var _context, _this = this; _classCallCheck(this, NodesHandler); this.body = body; this.images = images; this.groups = groups; this.layoutEngine = layoutEngine; // create the node API in the body container this.body.functions.createNode = _bindInstanceProperty$1(_context = this.create).call(_context, this); this.nodesListeners = { add: function add(event, params) { _this.add(params.items); }, update: function update(event, params) { _this.update(params.items, params.data, params.oldData); }, remove: function remove(event, params) { _this.remove(params.items); } }; this.defaultOptions = { borderWidth: 1, borderWidthSelected: undefined, brokenImage: undefined, color: { border: "#2B7CE9", background: "#97C2FC", highlight: { border: "#2B7CE9", background: "#D2E5FF" }, hover: { border: "#2B7CE9", background: "#D2E5FF" } }, opacity: undefined, // number between 0 and 1 fixed: { x: false, y: false }, font: { color: "#343434", size: 14, // px face: "arial", background: "none", strokeWidth: 0, // px strokeColor: "#ffffff", align: "center", vadjust: 0, multi: false, bold: { mod: "bold" }, boldital: { mod: "bold italic" }, ital: { mod: "italic" }, mono: { mod: "", size: 15, // px face: "monospace", vadjust: 2 } }, group: undefined, hidden: false, icon: { face: "FontAwesome", //'FontAwesome', code: undefined, //'\uf007', size: 50, //50, color: "#2B7CE9" //'#aa00ff' }, image: undefined, // --> URL imagePadding: { // only for image shape top: 0, right: 0, bottom: 0, left: 0 }, label: undefined, labelHighlightBold: true, level: undefined, margin: { top: 5, right: 5, bottom: 5, left: 5 }, mass: 1, physics: true, scaling: { min: 10, max: 30, label: { enabled: false, min: 14, max: 30, maxVisible: 30, drawThreshold: 5 }, customScalingFunction: function customScalingFunction(min, max, total, value) { if (max === min) { return 0.5; } else { var scale = 1 / (max - min); return Math.max(0, (value - min) * scale); } } }, shadow: { enabled: false, color: "rgba(0,0,0,0.5)", size: 10, x: 5, y: 5 }, shape: "ellipse", shapeProperties: { borderDashes: false, // only for borders borderRadius: 6, // only for box shape interpolation: true, // only for image and circularImage shapes useImageSize: false, // only for image and circularImage shapes useBorderWithImage: false, // only for image shape coordinateOrigin: "center" // only for image and circularImage shapes }, size: 25, title: undefined, value: undefined, x: undefined, y: undefined }; // Protect from idiocy if (this.defaultOptions.mass <= 0) { throw "Internal error: mass in defaultOptions of NodesHandler may not be zero or negative"; } this.options = bridgeObject(this.defaultOptions); this.bindEventListeners(); } /** * Binds event listeners */ _createClass(NodesHandler, [{ key: "bindEventListeners", value: function bindEventListeners() { var _context2, _context3, _this2 = this; // refresh the nodes. Used when reverting from hierarchical layout this.body.emitter.on("refreshNodes", _bindInstanceProperty$1(_context2 = this.refresh).call(_context2, this)); this.body.emitter.on("refresh", _bindInstanceProperty$1(_context3 = this.refresh).call(_context3, this)); this.body.emitter.on("destroy", function () { forEach$1(_this2.nodesListeners, function (callback, event) { if (_this2.body.data.nodes) _this2.body.data.nodes.off(event, callback); }); delete _this2.body.functions.createNode; delete _this2.nodesListeners.add; delete _this2.nodesListeners.update; delete _this2.nodesListeners.remove; delete _this2.nodesListeners; }); } /** * * @param {object} options */ }, { key: "setOptions", value: function setOptions(options) { if (options !== undefined) { Node.parseOptions(this.options, options); // Need to set opacity here because Node.parseOptions is also used for groups, // if you set opacity in Node.parseOptions it overwrites group opacity. if (options.opacity !== undefined) { if (_Number$isNaN(options.opacity) || !_Number$isFinite(options.opacity) || options.opacity < 0 || options.opacity > 1) { console.error("Invalid option for node opacity. Value must be between 0 and 1, found: " + options.opacity); } else { this.options.opacity = options.opacity; } } // update the shape in all nodes if (options.shape !== undefined) { for (var nodeId in this.body.nodes) { if (Object.prototype.hasOwnProperty.call(this.body.nodes, nodeId)) { this.body.nodes[nodeId].updateShape(); } } } // Update the labels of nodes if any relevant options changed. if (typeof options.font !== "undefined" || typeof options.widthConstraint !== "undefined" || typeof options.heightConstraint !== "undefined") { for (var _i = 0, _Object$keys$1 = _Object$keys(this.body.nodes); _i < _Object$keys$1.length; _i++) { var _nodeId = _Object$keys$1[_i]; this.body.nodes[_nodeId].updateLabelModule(); this.body.nodes[_nodeId].needsRefresh(); } } // update the shape size in all nodes if (options.size !== undefined) { for (var _nodeId2 in this.body.nodes) { if (Object.prototype.hasOwnProperty.call(this.body.nodes, _nodeId2)) { this.body.nodes[_nodeId2].needsRefresh(); } } } // update the state of the variables if needed if (options.hidden !== undefined || options.physics !== undefined) { this.body.emitter.emit("_dataChanged"); } } } /** * Set a data set with nodes for the network * * @param {Array | DataSet | DataView} nodes The data containing the nodes. * @param {boolean} [doNotEmit=false] - Suppress data changed event. * @private */ }, { key: "setData", value: function setData(nodes) { var doNotEmit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var oldNodesData = this.body.data.nodes; if ((0,vis_data_peer_esm_vis_data_js__WEBPACK_IMPORTED_MODULE_0__.isDataViewLike)("id", nodes)) { this.body.data.nodes = nodes; } else if (_Array$isArray(nodes)) { this.body.data.nodes = new vis_data_peer_esm_vis_data_js__WEBPACK_IMPORTED_MODULE_0__.DataSet(); this.body.data.nodes.add(nodes); } else if (!nodes) { this.body.data.nodes = new vis_data_peer_esm_vis_data_js__WEBPACK_IMPORTED_MODULE_0__.DataSet(); } else { throw new TypeError("Array or DataSet expected"); } if (oldNodesData) { // unsubscribe from old dataset forEach$1(this.nodesListeners, function (callback, event) { oldNodesData.off(event, callback); }); } // remove drawn nodes this.body.nodes = {}; if (this.body.data.nodes) { // subscribe to new dataset var me = this; forEach$1(this.nodesListeners, function (callback, event) { me.body.data.nodes.on(event, callback); }); // draw all new nodes var ids = this.body.data.nodes.getIds(); this.add(ids, true); } if (doNotEmit === false) { this.body.emitter.emit("_dataChanged"); } } /** * Add nodes * * @param {number[] | string[]} ids * @param {boolean} [doNotEmit=false] * @private */ }, { key: "add", value: function add(ids) { var doNotEmit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var id; var newNodes = []; for (var i = 0; i < ids.length; i++) { id = ids[i]; var properties = this.body.data.nodes.get(id); var node = this.create(properties); newNodes.push(node); this.body.nodes[id] = node; // note: this may replace an existing node } this.layoutEngine.positionInitially(newNodes); if (doNotEmit === false) { this.body.emitter.emit("_dataChanged"); } } /** * Update existing nodes, or create them when not yet existing * * @param {number[] | string[]} ids id's of changed nodes * @param {Array} changedData array with changed data * @param {Array|undefined} oldData optional; array with previous data * @private */ }, { key: "update", value: function update(ids, changedData, oldData) { var nodes = this.body.nodes; var dataChanged = false; for (var i = 0; i < ids.length; i++) { var id = ids[i]; var node = nodes[id]; var data = changedData[i]; if (node !== undefined) { // update node if (node.setOptions(data)) { dataChanged = true; } } else { dataChanged = true; // create node node = this.create(data); nodes[id] = node; } } if (!dataChanged && oldData !== undefined) { // Check for any changes which should trigger a layout recalculation // For now, this is just 'level' for hierarchical layout // Assumption: old and new data arranged in same order; at time of writing, this holds. dataChanged = _someInstanceProperty(changedData).call(changedData, function (newValue, index) { var oldValue = oldData[index]; return oldValue && oldValue.level !== newValue.level; }); } if (dataChanged === true) { this.body.emitter.emit("_dataChanged"); } else { this.body.emitter.emit("_dataUpdated"); } } /** * Remove existing nodes. If nodes do not exist, the method will just ignore it. * * @param {number[] | string[]} ids * @private */ }, { key: "remove", value: function remove(ids) { var nodes = this.body.nodes; for (var i = 0; i < ids.length; i++) { var id = ids[i]; delete nodes[id]; } this.body.emitter.emit("_dataChanged"); } /** * create a node * * @param {object} properties * @param {class} [constructorClass=Node.default] * @returns {*} */ }, { key: "create", value: function create(properties) { var constructorClass = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Node; return new constructorClass(properties, this.body, this.images, this.groups, this.options, this.defaultOptions); } /** * * @param {boolean} [clearPositions=false] */ }, { key: "refresh", value: function refresh() { var _this3 = this; var clearPositions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; forEach$1(this.body.nodes, function (node, nodeId) { var data = _this3.body.data.nodes.get(nodeId); if (data !== undefined) { if (clearPositions === true) { node.setOptions({ x: null, y: null }); } node.setOptions({ fixed: false }); node.setOptions(data); } }); } /** * Returns the positions of the nodes. * * @param {Array.<Node.id> | string} [ids] --> optional, can be array of nodeIds, can be string * @returns {{}} */ }, { key: "getPositions", value: function getPositions(ids) { var dataArray = {}; if (ids !== undefined) { if (_Array$isArray(ids) === true) { for (var i = 0; i < ids.length; i++) { if (this.body.nodes[ids[i]] !== undefined) { var node = this.body.nodes[ids[i]]; dataArray[ids[i]] = { x: Math.round(node.x), y: Math.round(node.y) }; } } } else { if (this.body.nodes[ids] !== undefined) { var _node = this.body.nodes[ids]; dataArray[ids] = { x: Math.round(_node.x), y: Math.round(_node.y) }; } } } else { for (var _i2 = 0; _i2 < this.body.nodeIndices.length; _i2++) { var _node2 = this.body.nodes[this.body.nodeIndices[_i2]]; dataArray[this.body.nodeIndices[_i2]] = { x: Math.round(_node2.x), y: Math.round(_node2.y) }; } } return dataArray; } /** * Retrieves the x y position of a specific id. * * @param {string} id The id to retrieve. * @throws {TypeError} If no id is included. * @throws {ReferenceError} If an invalid id is provided. * @returns {{ x: number, y: number }} Returns X, Y canvas position of the node with given id. */ }, { key: "getPosition", value: function getPosition(id) { if (id == undefined) { throw new TypeError("No id was specified for getPosition method."); } else if (this.body.nodes[id] == undefined) { throw new ReferenceError("NodeId provided for getPosition does not exist. Provided: ".concat(id)); } else { return { x: Math.round(this.body.nodes[id].x), y: Math.round(this.body.nodes[id].y) }; } } /** * Load the XY positions of the nodes into the dataset. */ }, { key: "storePositions", value: function storePositions() { // todo: add support for clusters and hierarchical. var dataArray = []; var dataset = this.body.data.nodes.getDataSet(); var _iterator = _createForOfIteratorHelper$5(dataset.get()), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var dsNode = _step.value; var id = dsNode.id; var bodyNode = this.body.nodes[id]; var x = Math.round(bodyNode.x); var y = Math.round(bodyNode.y); if (dsNode.x !== x || dsNode.y !== y) { dataArray.push({ id: id, x: x, y: y }); } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } dataset.update(dataArray); } /** * get the bounding box of a node. * * @param {Node.id} nodeId * @returns {j|*} */ }, { key: "getBoundingBox", value: function getBoundingBox(nodeId) { if (this.body.nodes[nodeId] !== undefined) { return this.body.nodes[nodeId].shape.boundingBox; } } /** * Get the Ids of nodes connected to this node. * * @param {Node.id} nodeId * @param {'to'|'from'|undefined} direction values 'from' and 'to' select respectively parent and child nodes only. * Any other value returns both parent and child nodes. * @returns {Array} */ }, { key: "getConnectedNodes", value: function getConnectedNodes(nodeId, direction) { var nodeList = []; if (this.body.nodes[nodeId] !== undefined) { var node = this.body.nodes[nodeId]; var nodeObj = {}; // used to quickly check if node already exists for (var i = 0; i < node.edges.length; i++) { var edge = node.edges[i]; if (direction !== "to" && edge.toId == node.id) { // these are double equals since ids can be numeric or string if (nodeObj[edge.fromId] === undefined) { nodeList.push(edge.fromId); nodeObj[edge.fromId] = true; } } else if (direction !== "from" && edge.fromId == node.id) { // these are double equals since ids can be numeric or string if (nodeObj[edge.toId] === undefined) { nodeList.push(edge.toId); nodeObj[edge.toId] = true; } } } } return nodeList; } /** * Get the ids of the edges connected to this node. * * @param {Node.id} nodeId * @returns {*} */ }, { key: "getConnectedEdges", value: function getConnectedEdges(nodeId) { var edgeList = []; if (this.body.nodes[nodeId] !== undefined) { var node = this.body.nodes[nodeId]; for (var i = 0; i < node.edges.length; i++) { edgeList.push(node.edges[i].id); } } else { console.error("NodeId provided for getConnectedEdges does not exist. Provided: ", nodeId); } return edgeList; } /** * Move a node. * * @param {Node.id} nodeId * @param {number} x * @param {number} y */ }, { key: "moveNode", value: function moveNode(nodeId, x, y) { var _this4 = this; if (this.body.nodes[nodeId] !== undefined) { this.body.nodes[nodeId].x = Number(x); this.body.nodes[nodeId].y = Number(y); _setTimeout(function () { _this4.body.emitter.emit("startSimulation"); }, 0); } else { console.error("Node id supplied to moveNode does not exist. Provided: ", nodeId); } } }]); return NodesHandler; }(); var getExports$1 = {}; var get$6 = { get exports() { return getExports$1; }, set exports(v) { getExports$1 = v; } }; var getExports = {}; var get$5 = { get exports() { return getExports; }, set exports(v) { getExports = v; } }; var hasOwn$1 = hasOwnProperty_1; var isDataDescriptor$1 = function (descriptor) { return descriptor !== undefined && (hasOwn$1(descriptor, 'value') || hasOwn$1(descriptor, 'writable')); }; var $$4 = _export; var call = functionCall; var isObject$2 = isObject$j; var anObject$1 = anObject$d; var isDataDescriptor = isDataDescriptor$1; var getOwnPropertyDescriptorModule = objectGetOwnPropertyDescriptor; var getPrototypeOf = objectGetPrototypeOf; // `Reflect.get` method // https://tc39.es/ecma262/#sec-reflect.get function get$4(target, propertyKey /* , receiver */) { var receiver = arguments.length < 3 ? target : arguments[2]; var descriptor, prototype; if (anObject$1(target) === receiver) return target[propertyKey]; descriptor = getOwnPropertyDescriptorModule.f(target, propertyKey); if (descriptor) return isDataDescriptor(descriptor) ? descriptor.value : descriptor.get === undefined ? undefined : call(descriptor.get, receiver); if (isObject$2(prototype = getPrototypeOf(target))) return get$4(prototype, propertyKey, receiver); } $$4({ target: 'Reflect', stat: true }, { get: get$4 }); var path$4 = path$y; var get$3 = path$4.Reflect.get; var parent$a = get$3; var get$2 = parent$a; var parent$9 = get$2; var get$1 = parent$9; var parent$8 = get$1; var get = parent$8; (function (module) { module.exports = get; })(get$5); (function (module) { module.exports = getExports; })(get$6); var _Reflect$get = /*@__PURE__*/getDefaultExportFromCjs(getExports$1); var getOwnPropertyDescriptorExports$1 = {}; var getOwnPropertyDescriptor$3 = { get exports() { return getOwnPropertyDescriptorExports$1; }, set exports(v) { getOwnPropertyDescriptorExports$1 = v; } }; var getOwnPropertyDescriptorExports = {}; var getOwnPropertyDescriptor$2 = { get exports() { return getOwnPropertyDescriptorExports; }, set exports(v) { getOwnPropertyDescriptorExports = v; } }; var parent$7 = getOwnPropertyDescriptor$5; var getOwnPropertyDescriptor$1 = parent$7; var parent$6 = getOwnPropertyDescriptor$1; var getOwnPropertyDescriptor = parent$6; (function (module) { module.exports = getOwnPropertyDescriptor; })(getOwnPropertyDescriptor$2); (function (module) { module.exports = getOwnPropertyDescriptorExports; })(getOwnPropertyDescriptor$3); var _Object$getOwnPropertyDescriptor = /*@__PURE__*/getDefaultExportFromCjs(getOwnPropertyDescriptorExports$1); function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } function _get() { if (typeof Reflect !== "undefined" && _Reflect$get) { var _context; _get = _bindInstanceProperty(_context = _Reflect$get).call(_context); } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = _Object$getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); } var hypotExports = {}; var hypot$2 = { get exports() { return hypotExports; }, set exports(v) { hypotExports = v; } }; var $$3 = _export; // eslint-disable-next-line es/no-math-hypot -- required for testing var $hypot = Math.hypot; var abs = Math.abs; var sqrt = Math.sqrt; // Chrome 77 bug // https://bugs.chromium.org/p/v8/issues/detail?id=9546 var FORCED$2 = !!$hypot && $hypot(Infinity, NaN) !== Infinity; // `Math.hypot` method // https://tc39.es/ecma262/#sec-math.hypot $$3({ target: 'Math', stat: true, arity: 2, forced: FORCED$2 }, { // eslint-disable-next-line no-unused-vars -- required for `.length` hypot: function hypot(value1, value2) { var sum = 0; var i = 0; var aLen = arguments.length; var larg = 0; var arg, div; while (i < aLen) { arg = abs(arguments[i++]); if (larg < arg) { div = larg / arg; sum = sum * div * div + 1; larg = arg; } else if (arg > 0) { div = arg / larg; sum += div * div; } else sum += arg; } return larg === Infinity ? Infinity : larg * sqrt(sum); } }); var path$3 = path$y; var hypot$1 = path$3.Math.hypot; var parent$5 = hypot$1; var hypot = parent$5; (function (module) { module.exports = hypot; })(hypot$2); var _Math$hypot = /*@__PURE__*/getDefaultExportFromCjs(hypotExports); function _createSuper$a(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$a(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$a() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * Common methods for endpoints * * @class */ var EndPoint = /*#__PURE__*/function () { function EndPoint() { _classCallCheck(this, EndPoint); } _createClass(EndPoint, null, [{ key: "transform", value: /** * Apply transformation on points for display. * * The following is done: * - rotate by the specified angle * - multiply the (normalized) coordinates by the passed length * - offset by the target coordinates * * @param points - The point(s) to be transformed. * @param arrowData - The data determining the result of the transformation. */ function transform(points, arrowData) { if (!_Array$isArray(points)) { points = [points]; } var x = arrowData.point.x; var y = arrowData.point.y; var angle = arrowData.angle; var length = arrowData.length; for (var i = 0; i < points.length; ++i) { var p = points[i]; var xt = p.x * Math.cos(angle) - p.y * Math.sin(angle); var yt = p.x * Math.sin(angle) + p.y * Math.cos(angle); p.x = x + length * xt; p.y = y + length * yt; } } /** * Draw a closed path using the given real coordinates. * * @param ctx - The path will be rendered into this context. * @param points - The points of the path. */ }, { key: "drawPath", value: function drawPath(ctx, points) { ctx.beginPath(); ctx.moveTo(points[0].x, points[0].y); for (var i = 1; i < points.length; ++i) { ctx.lineTo(points[i].x, points[i].y); } ctx.closePath(); } }]); return EndPoint; }(); /** * Drawing methods for the arrow endpoint. */ var Image$1 = /*#__PURE__*/function (_EndPoint) { _inherits(Image, _EndPoint); var _super = _createSuper$a(Image); function Image() { _classCallCheck(this, Image); return _super.apply(this, arguments); } _createClass(Image, null, [{ key: "draw", value: /** * Draw this shape at the end of a line. * * @param ctx - The shape will be rendered into this context. * @param arrowData - The data determining the shape. * @returns False as there is no way to fill an image. */ function draw(ctx, arrowData) { if (arrowData.image) { ctx.save(); ctx.translate(arrowData.point.x, arrowData.point.y); ctx.rotate(Math.PI / 2 + arrowData.angle); var width = arrowData.imageWidth != null ? arrowData.imageWidth : arrowData.image.width; var height = arrowData.imageHeight != null ? arrowData.imageHeight : arrowData.image.height; arrowData.image.drawImageAtPosition(ctx, 1, // scale -width / 2, // x 0, // y width, height); ctx.restore(); } return false; } }]); return Image; }(EndPoint); /** * Drawing methods for the arrow endpoint. */ var Arrow = /*#__PURE__*/function (_EndPoint2) { _inherits(Arrow, _EndPoint2); var _super2 = _createSuper$a(Arrow); function Arrow() { _classCallCheck(this, Arrow); return _super2.apply(this, arguments); } _createClass(Arrow, null, [{ key: "draw", value: /** * Draw this shape at the end of a line. * * @param ctx - The shape will be rendered into this context. * @param arrowData - The data determining the shape. * @returns True because ctx.fill() can be used to fill the arrow. */ function draw(ctx, arrowData) { // Normalized points of closed path, in the order that they should be drawn. // (0, 0) is the attachment point, and the point around which should be rotated var points = [{ x: 0, y: 0 }, { x: -1, y: 0.3 }, { x: -0.9, y: 0 }, { x: -1, y: -0.3 }]; EndPoint.transform(points, arrowData); EndPoint.drawPath(ctx, points); return true; } }]); return Arrow; }(EndPoint); /** * Drawing methods for the crow endpoint. */ var Crow = /*#__PURE__*/function () { function Crow() { _classCallCheck(this, Crow); } _createClass(Crow, null, [{ key: "draw", value: /** * Draw this shape at the end of a line. * * @param ctx - The shape will be rendered into this context. * @param arrowData - The data determining the shape. * @returns True because ctx.fill() can be used to fill the arrow. */ function draw(ctx, arrowData) { // Normalized points of closed path, in the order that they should be drawn. // (0, 0) is the attachment point, and the point around which should be rotated var points = [{ x: -1, y: 0 }, { x: 0, y: 0.3 }, { x: -0.4, y: 0 }, { x: 0, y: -0.3 }]; EndPoint.transform(points, arrowData); EndPoint.drawPath(ctx, points); return true; } }]); return Crow; }(); /** * Drawing methods for the curve endpoint. */ var Curve = /*#__PURE__*/function () { function Curve() { _classCallCheck(this, Curve); } _createClass(Curve, null, [{ key: "draw", value: /** * Draw this shape at the end of a line. * * @param ctx - The shape will be rendered into this context. * @param arrowData - The data determining the shape. * @returns True because ctx.fill() can be used to fill the arrow. */ function draw(ctx, arrowData) { // Normalized points of closed path, in the order that they should be drawn. // (0, 0) is the attachment point, and the point around which should be rotated var point = { x: -0.4, y: 0 }; EndPoint.transform(point, arrowData); // Update endpoint style for drawing transparent arc. ctx.strokeStyle = ctx.fillStyle; ctx.fillStyle = "rgba(0, 0, 0, 0)"; // Define curve endpoint as semicircle. var pi = Math.PI; var startAngle = arrowData.angle - pi / 2; var endAngle = arrowData.angle + pi / 2; ctx.beginPath(); ctx.arc(point.x, point.y, arrowData.length * 0.4, startAngle, endAngle, false); ctx.stroke(); return true; } }]); return Curve; }(); /** * Drawing methods for the inverted curve endpoint. */ var InvertedCurve = /*#__PURE__*/function () { function InvertedCurve() { _classCallCheck(this, InvertedCurve); } _createClass(InvertedCurve, null, [{ key: "draw", value: /** * Draw this shape at the end of a line. * * @param ctx - The shape will be rendered into this context. * @param arrowData - The data determining the shape. * @returns True because ctx.fill() can be used to fill the arrow. */ function draw(ctx, arrowData) { // Normalized points of closed path, in the order that they should be drawn. // (0, 0) is the attachment point, and the point around which should be rotated var point = { x: -0.3, y: 0 }; EndPoint.transform(point, arrowData); // Update endpoint style for drawing transparent arc. ctx.strokeStyle = ctx.fillStyle; ctx.fillStyle = "rgba(0, 0, 0, 0)"; // Define inverted curve endpoint as semicircle. var pi = Math.PI; var startAngle = arrowData.angle + pi / 2; var endAngle = arrowData.angle + 3 * pi / 2; ctx.beginPath(); ctx.arc(point.x, point.y, arrowData.length * 0.4, startAngle, endAngle, false); ctx.stroke(); return true; } }]); return InvertedCurve; }(); /** * Drawing methods for the trinagle endpoint. */ var Triangle = /*#__PURE__*/function () { function Triangle() { _classCallCheck(this, Triangle); } _createClass(Triangle, null, [{ key: "draw", value: /** * Draw this shape at the end of a line. * * @param ctx - The shape will be rendered into this context. * @param arrowData - The data determining the shape. * @returns True because ctx.fill() can be used to fill the arrow. */ function draw(ctx, arrowData) { // Normalized points of closed path, in the order that they should be drawn. // (0, 0) is the attachment point, and the point around which should be rotated var points = [{ x: 0.02, y: 0 }, { x: -1, y: 0.3 }, { x: -1, y: -0.3 }]; EndPoint.transform(points, arrowData); EndPoint.drawPath(ctx, points); return true; } }]); return Triangle; }(); /** * Drawing methods for the inverted trinagle endpoint. */ var InvertedTriangle = /*#__PURE__*/function () { function InvertedTriangle() { _classCallCheck(this, InvertedTriangle); } _createClass(InvertedTriangle, null, [{ key: "draw", value: /** * Draw this shape at the end of a line. * * @param ctx - The shape will be rendered into this context. * @param arrowData - The data determining the shape. * @returns True because ctx.fill() can be used to fill the arrow. */ function draw(ctx, arrowData) { // Normalized points of closed path, in the order that they should be drawn. // (0, 0) is the attachment point, and the point around which should be rotated var points = [{ x: 0, y: 0.3 }, { x: 0, y: -0.3 }, { x: -1, y: 0 }]; EndPoint.transform(points, arrowData); EndPoint.drawPath(ctx, points); return true; } }]); return InvertedTriangle; }(); /** * Drawing methods for the circle endpoint. */ var Circle = /*#__PURE__*/function () { function Circle() { _classCallCheck(this, Circle); } _createClass(Circle, null, [{ key: "draw", value: /** * Draw this shape at the end of a line. * * @param ctx - The shape will be rendered into this context. * @param arrowData - The data determining the shape. * @returns True because ctx.fill() can be used to fill the arrow. */ function draw(ctx, arrowData) { var point = { x: -0.4, y: 0 }; EndPoint.transform(point, arrowData); drawCircle(ctx, point.x, point.y, arrowData.length * 0.4); return true; } }]); return Circle; }(); /** * Drawing methods for the bar endpoint. */ var Bar = /*#__PURE__*/function () { function Bar() { _classCallCheck(this, Bar); } _createClass(Bar, null, [{ key: "draw", value: /** * Draw this shape at the end of a line. * * @param ctx - The shape will be rendered into this context. * @param arrowData - The data determining the shape. * @returns True because ctx.fill() can be used to fill the arrow. */ function draw(ctx, arrowData) { /* var points = [ {x:0, y:0.5}, {x:0, y:-0.5} ]; EndPoint.transform(points, arrowData); ctx.beginPath(); ctx.moveTo(points[0].x, points[0].y); ctx.lineTo(points[1].x, points[1].y); ctx.stroke(); */ var points = [{ x: 0, y: 0.5 }, { x: 0, y: -0.5 }, { x: -0.15, y: -0.5 }, { x: -0.15, y: 0.5 }]; EndPoint.transform(points, arrowData); EndPoint.drawPath(ctx, points); return true; } }]); return Bar; }(); /** * Drawing methods for the box endpoint. */ var Box = /*#__PURE__*/function () { function Box() { _classCallCheck(this, Box); } _createClass(Box, null, [{ key: "draw", value: /** * Draw this shape at the end of a line. * * @param ctx - The shape will be rendered into this context. * @param arrowData - The data determining the shape. * @returns True because ctx.fill() can be used to fill the arrow. */ function draw(ctx, arrowData) { var points = [{ x: 0, y: 0.3 }, { x: 0, y: -0.3 }, { x: -0.6, y: -0.3 }, { x: -0.6, y: 0.3 }]; EndPoint.transform(points, arrowData); EndPoint.drawPath(ctx, points); return true; } }]); return Box; }(); /** * Drawing methods for the diamond endpoint. */ var Diamond = /*#__PURE__*/function () { function Diamond() { _classCallCheck(this, Diamond); } _createClass(Diamond, null, [{ key: "draw", value: /** * Draw this shape at the end of a line. * * @param ctx - The shape will be rendered into this context. * @param arrowData - The data determining the shape. * @returns True because ctx.fill() can be used to fill the arrow. */ function draw(ctx, arrowData) { var points = [{ x: 0, y: 0 }, { x: -0.5, y: -0.3 }, { x: -1, y: 0 }, { x: -0.5, y: 0.3 }]; EndPoint.transform(points, arrowData); EndPoint.drawPath(ctx, points); return true; } }]); return Diamond; }(); /** * Drawing methods for the vee endpoint. */ var Vee = /*#__PURE__*/function () { function Vee() { _classCallCheck(this, Vee); } _createClass(Vee, null, [{ key: "draw", value: /** * Draw this shape at the end of a line. * * @param ctx - The shape will be rendered into this context. * @param arrowData - The data determining the shape. * @returns True because ctx.fill() can be used to fill the arrow. */ function draw(ctx, arrowData) { // Normalized points of closed path, in the order that they should be drawn. // (0, 0) is the attachment point, and the point around which should be rotated var points = [{ x: -1, y: 0.3 }, { x: -0.5, y: 0 }, { x: -1, y: -0.3 }, { x: 0, y: 0 }]; EndPoint.transform(points, arrowData); EndPoint.drawPath(ctx, points); return true; } }]); return Vee; }(); /** * Drawing methods for the endpoints. */ var EndPoints = /*#__PURE__*/function () { function EndPoints() { _classCallCheck(this, EndPoints); } _createClass(EndPoints, null, [{ key: "draw", value: /** * Draw an endpoint. * * @param ctx - The shape will be rendered into this context. * @param arrowData - The data determining the shape. * @returns True if ctx.fill() can be used to fill the arrow, false otherwise. */ function draw(ctx, arrowData) { var type; if (arrowData.type) { type = arrowData.type.toLowerCase(); } switch (type) { case "image": return Image$1.draw(ctx, arrowData); case "circle": return Circle.draw(ctx, arrowData); case "box": return Box.draw(ctx, arrowData); case "crow": return Crow.draw(ctx, arrowData); case "curve": return Curve.draw(ctx, arrowData); case "diamond": return Diamond.draw(ctx, arrowData); case "inv_curve": return InvertedCurve.draw(ctx, arrowData); case "triangle": return Triangle.draw(ctx, arrowData); case "inv_triangle": return InvertedTriangle.draw(ctx, arrowData); case "bar": return Bar.draw(ctx, arrowData); case "vee": return Vee.draw(ctx, arrowData); case "arrow": // fall-through default: return Arrow.draw(ctx, arrowData); } } }]); return EndPoints; }(); function ownKeys$1(object, enumerableOnly) { var keys = _Object$keys(object); if (_Object$getOwnPropertySymbols) { var symbols = _Object$getOwnPropertySymbols(object); enumerableOnly && (symbols = _filterInstanceProperty(symbols).call(symbols, function (sym) { return _Object$getOwnPropertyDescriptor$1(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread$1(target) { for (var i = 1; i < arguments.length; i++) { var _context2, _context3; var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? _forEachInstanceProperty(_context2 = ownKeys$1(Object(source), !0)).call(_context2, function (key) { _defineProperty(target, key, source[key]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(target, _Object$getOwnPropertyDescriptors(source)) : _forEachInstanceProperty(_context3 = ownKeys$1(Object(source))).call(_context3, function (key) { _Object$defineProperty$1(target, key, _Object$getOwnPropertyDescriptor$1(source, key)); }); } return target; } /** * The Base Class for all edges. */ var EdgeBase = /*#__PURE__*/function () { /** * Create a new instance. * * @param options - The options object of given edge. * @param _body - The body of the network. * @param _labelModule - Label module. */ function EdgeBase(options, _body, _labelModule) { _classCallCheck(this, EdgeBase); this._body = _body; this._labelModule = _labelModule; this.color = {}; this.colorDirty = true; this.hoverWidth = 1.5; this.selectionWidth = 2; this.setOptions(options); this.fromPoint = this.from; this.toPoint = this.to; } /** @inheritDoc */ _createClass(EdgeBase, [{ key: "connect", value: function connect() { this.from = this._body.nodes[this.options.from]; this.to = this._body.nodes[this.options.to]; } /** @inheritDoc */ }, { key: "cleanup", value: function cleanup() { return false; } /** * Set new edge options. * * @param options - The new edge options object. */ }, { key: "setOptions", value: function setOptions(options) { this.options = options; this.from = this._body.nodes[this.options.from]; this.to = this._body.nodes[this.options.to]; this.id = this.options.id; } /** @inheritDoc */ }, { key: "drawLine", value: function drawLine(ctx, values, _selected, _hover) { var viaNode = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : this.getViaNode(); // set style ctx.strokeStyle = this.getColor(ctx, values); ctx.lineWidth = values.width; if (values.dashes !== false) { this._drawDashedLine(ctx, values, viaNode); } else { this._drawLine(ctx, values, viaNode); } } /** * Draw a line with given style between two nodes through supplied node(s). * * @param ctx - The context that will be used for rendering. * @param values - Formatting values like color, opacity or shadow. * @param viaNode - Additional control point(s) for the edge. * @param fromPoint - TODO: Seems ignored, remove? * @param toPoint - TODO: Seems ignored, remove? */ }, { key: "_drawLine", value: function _drawLine(ctx, values, viaNode, fromPoint, toPoint) { if (this.from != this.to) { // draw line this._line(ctx, values, viaNode, fromPoint, toPoint); } else { var _this$_getCircleData = this._getCircleData(ctx), _this$_getCircleData2 = _slicedToArray(_this$_getCircleData, 3), x = _this$_getCircleData2[0], y = _this$_getCircleData2[1], radius = _this$_getCircleData2[2]; this._circle(ctx, values, x, y, radius); } } /** * Draw a dashed line with given style between two nodes through supplied node(s). * * @param ctx - The context that will be used for rendering. * @param values - Formatting values like color, opacity or shadow. * @param viaNode - Additional control point(s) for the edge. * @param _fromPoint - Ignored (TODO: remove in the future). * @param _toPoint - Ignored (TODO: remove in the future). */ }, { key: "_drawDashedLine", value: function _drawDashedLine(ctx, values, viaNode, _fromPoint, _toPoint) { ctx.lineCap = "round"; var pattern = _Array$isArray(values.dashes) ? values.dashes : [5, 5]; // only firefox and chrome support this method, else we use the legacy one. if (ctx.setLineDash !== undefined) { ctx.save(); // set dash settings for chrome or firefox ctx.setLineDash(pattern); ctx.lineDashOffset = 0; // draw the line if (this.from != this.to) { // draw line this._line(ctx, values, viaNode); } else { var _this$_getCircleData3 = this._getCircleData(ctx), _this$_getCircleData4 = _slicedToArray(_this$_getCircleData3, 3), x = _this$_getCircleData4[0], y = _this$_getCircleData4[1], radius = _this$_getCircleData4[2]; this._circle(ctx, values, x, y, radius); } // restore the dash settings. ctx.setLineDash([0]); ctx.lineDashOffset = 0; ctx.restore(); } else { // unsupporting smooth lines if (this.from != this.to) { // draw line drawDashedLine(ctx, this.from.x, this.from.y, this.to.x, this.to.y, pattern); } else { var _this$_getCircleData5 = this._getCircleData(ctx), _this$_getCircleData6 = _slicedToArray(_this$_getCircleData5, 3), _x = _this$_getCircleData6[0], _y = _this$_getCircleData6[1], _radius = _this$_getCircleData6[2]; this._circle(ctx, values, _x, _y, _radius); } // draw shadow if enabled this.enableShadow(ctx, values); ctx.stroke(); // disable shadows for other elements. this.disableShadow(ctx, values); } } /** * Find the intersection between the border of the node and the edge. * * @param node - The node (either from or to node of the edge). * @param ctx - The context that will be used for rendering. * @param options - Additional options. * @returns Cartesian coordinates of the intersection between the border of the node and the edge. */ }, { key: "findBorderPosition", value: function findBorderPosition(node, ctx, options) { if (this.from != this.to) { return this._findBorderPosition(node, ctx, options); } else { return this._findBorderPositionCircle(node, ctx, options); } } /** @inheritDoc */ }, { key: "findBorderPositions", value: function findBorderPositions(ctx) { if (this.from != this.to) { return { from: this._findBorderPosition(this.from, ctx), to: this._findBorderPosition(this.to, ctx) }; } else { var _context; var _this$_getCircleData$ = _sliceInstanceProperty(_context = this._getCircleData(ctx)).call(_context, 0, 2), _this$_getCircleData$2 = _slicedToArray(_this$_getCircleData$, 2), x = _this$_getCircleData$2[0], y = _this$_getCircleData$2[1]; return { from: this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: 0.25, high: 0.6, direction: -1 }), to: this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: 0.6, high: 0.8, direction: 1 }) }; } } /** * Compute the center point and radius of an edge connected to the same node at both ends. * * @param ctx - The context that will be used for rendering. * @returns `[x, y, radius]` */ }, { key: "_getCircleData", value: function _getCircleData(ctx) { var radius = this.options.selfReference.size; if (ctx !== undefined) { if (this.from.shape.width === undefined) { this.from.shape.resize(ctx); } } // get circle coordinates var coordinates = getSelfRefCoordinates(ctx, this.options.selfReference.angle, radius, this.from); return [coordinates.x, coordinates.y, radius]; } /** * Get a point on a circle. * * @param x - Center of the circle on the x axis. * @param y - Center of the circle on the y axis. * @param radius - Radius of the circle. * @param position - Value between 0 (line start) and 1 (line end). * @returns Cartesian coordinates of requested point on the circle. */ }, { key: "_pointOnCircle", value: function _pointOnCircle(x, y, radius, position) { var angle = position * 2 * Math.PI; return { x: x + radius * Math.cos(angle), y: y - radius * Math.sin(angle) }; } /** * Find the intersection between the border of the node and the edge. * * @remarks * This function uses binary search to look for the point where the circle crosses the border of the node. * @param nearNode - The node (either from or to node of the edge). * @param ctx - The context that will be used for rendering. * @param options - Additional options. * @returns Cartesian coordinates of the intersection between the border of the node and the edge. */ }, { key: "_findBorderPositionCircle", value: function _findBorderPositionCircle(nearNode, ctx, options) { var x = options.x; var y = options.y; var low = options.low; var high = options.high; var direction = options.direction; var maxIterations = 10; var radius = this.options.selfReference.size; var threshold = 0.05; var pos; var middle = (low + high) * 0.5; var endPointOffset = 0; if (this.options.arrowStrikethrough === true) { if (direction === -1) { endPointOffset = this.options.endPointOffset.from; } else if (direction === 1) { endPointOffset = this.options.endPointOffset.to; } } var iteration = 0; do { middle = (low + high) * 0.5; pos = this._pointOnCircle(x, y, radius, middle); var angle = Math.atan2(nearNode.y - pos.y, nearNode.x - pos.x); var distanceToBorder = nearNode.distanceToBorder(ctx, angle) + endPointOffset; var distanceToPoint = Math.sqrt(Math.pow(pos.x - nearNode.x, 2) + Math.pow(pos.y - nearNode.y, 2)); var difference = distanceToBorder - distanceToPoint; if (Math.abs(difference) < threshold) { break; // found } else if (difference > 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. if (direction > 0) { low = middle; } else { high = middle; } } else { if (direction > 0) { high = middle; } else { low = middle; } } ++iteration; } while (low <= high && iteration < maxIterations); return _objectSpread$1(_objectSpread$1({}, pos), {}, { t: middle }); } /** * Get the line width of the edge. Depends on width and whether one of the connected nodes is selected. * * @param selected - Determines wheter the line is selected. * @param hover - Determines wheter the line is being hovered, only applies if selected is false. * @returns The width of the line. */ }, { key: "getLineWidth", value: function getLineWidth(selected, hover) { if (selected === true) { return Math.max(this.selectionWidth, 0.3 / this._body.view.scale); } else if (hover === true) { return Math.max(this.hoverWidth, 0.3 / this._body.view.scale); } else { return Math.max(this.options.width, 0.3 / this._body.view.scale); } } /** * Compute the color or gradient for given edge. * * @param ctx - The context that will be used for rendering. * @param values - Formatting values like color, opacity or shadow. * @param _selected - Ignored (TODO: remove in the future). * @param _hover - Ignored (TODO: remove in the future). * @returns Color string if single color is inherited or gradient if two. */ }, { key: "getColor", value: function getColor(ctx, values) { if (values.inheritsColor !== false) { // when this is a loop edge, just use the 'from' method if (values.inheritsColor === "both" && this.from.id !== this.to.id) { var grd = ctx.createLinearGradient(this.from.x, this.from.y, this.to.x, this.to.y); var fromColor = this.from.options.color.highlight.border; var toColor = this.to.options.color.highlight.border; if (this.from.selected === false && this.to.selected === false) { fromColor = overrideOpacity(this.from.options.color.border, values.opacity); toColor = overrideOpacity(this.to.options.color.border, values.opacity); } else if (this.from.selected === true && this.to.selected === false) { toColor = this.to.options.color.border; } else if (this.from.selected === false && this.to.selected === true) { fromColor = this.from.options.color.border; } grd.addColorStop(0, fromColor); grd.addColorStop(1, toColor); // -------------------- this returns -------------------- // return grd; } if (values.inheritsColor === "to") { return overrideOpacity(this.to.options.color.border, values.opacity); } else { // "from" return overrideOpacity(this.from.options.color.border, values.opacity); } } else { return overrideOpacity(values.color, values.opacity); } } /** * Draw a line from a node to itself, a circle. * * @param ctx - The context that will be used for rendering. * @param values - Formatting values like color, opacity or shadow. * @param x - Center of the circle on the x axis. * @param y - Center of the circle on the y axis. * @param radius - Radius of the circle. */ }, { key: "_circle", value: function _circle(ctx, values, x, y, radius) { // draw shadow if enabled this.enableShadow(ctx, values); //full circle var angleFrom = 0; var angleTo = Math.PI * 2; if (!this.options.selfReference.renderBehindTheNode) { //render only parts which are not overlaping with parent node //need to find x,y of from point and x,y to point //calculating radians var low = this.options.selfReference.angle; var high = this.options.selfReference.angle + Math.PI; var pointTFrom = this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: low, high: high, direction: -1 }); var pointTTo = this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: low, high: high, direction: 1 }); angleFrom = Math.atan2(pointTFrom.y - y, pointTFrom.x - x); angleTo = Math.atan2(pointTTo.y - y, pointTTo.x - x); } // draw a circle ctx.beginPath(); ctx.arc(x, y, radius, angleFrom, angleTo, false); ctx.stroke(); // disable shadows for other elements. this.disableShadow(ctx, values); } /** * @inheritDoc * @remarks * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment */ }, { key: "getDistanceToEdge", value: function getDistanceToEdge(x1, y1, x2, y2, x3, y3) { if (this.from != this.to) { return this._getDistanceToEdge(x1, y1, x2, y2, x3, y3); } else { var _this$_getCircleData7 = this._getCircleData(undefined), _this$_getCircleData8 = _slicedToArray(_this$_getCircleData7, 3), x = _this$_getCircleData8[0], y = _this$_getCircleData8[1], radius = _this$_getCircleData8[2]; var dx = x - x3; var dy = y - y3; return Math.abs(Math.sqrt(dx * dx + dy * dy) - radius); } } /** * Calculate the distance between a point (x3, y3) and a line segment from (x1, y1) to (x2, y2). * * @param x1 - First end of the line segment on the x axis. * @param y1 - First end of the line segment on the y axis. * @param x2 - Second end of the line segment on the x axis. * @param y2 - Second end of the line segment on the y axis. * @param x3 - Position of the point on the x axis. * @param y3 - Position of the point on the y axis. * @returns The distance between the line segment and the point. */ }, { key: "_getDistanceToLine", value: function _getDistanceToLine(x1, y1, x2, y2, x3, y3) { var px = x2 - x1; var py = y2 - y1; var something = px * px + py * py; var u = ((x3 - x1) * px + (y3 - y1) * py) / something; if (u > 1) { u = 1; } else if (u < 0) { u = 0; } var x = x1 + u * px; var y = y1 + u * py; var dx = x - x3; var dy = y - y3; //# Note: If the actual distance does not matter, //# if you only want to compare what this function //# returns to other results of this function, you //# can just return the squared distance instead //# (i.e. remove the sqrt) to gain a little performance return Math.sqrt(dx * dx + dy * dy); } /** @inheritDoc */ }, { key: "getArrowData", value: function getArrowData(ctx, position, viaNode, _selected, _hover, values) { // set lets var angle; var arrowPoint; var node1; var node2; var reversed; var scaleFactor; var type; var lineWidth = values.width; if (position === "from") { node1 = this.from; node2 = this.to; reversed = values.fromArrowScale < 0; scaleFactor = Math.abs(values.fromArrowScale); type = values.fromArrowType; } else if (position === "to") { node1 = this.to; node2 = this.from; reversed = values.toArrowScale < 0; scaleFactor = Math.abs(values.toArrowScale); type = values.toArrowType; } else { node1 = this.to; node2 = this.from; reversed = values.middleArrowScale < 0; scaleFactor = Math.abs(values.middleArrowScale); type = values.middleArrowType; } var length = 15 * scaleFactor + 3 * lineWidth; // 3* lineWidth is the width of the edge. // if not connected to itself if (node1 != node2) { var approximateEdgeLength = _Math$hypot(node1.x - node2.x, node1.y - node2.y); var relativeLength = length / approximateEdgeLength; if (position !== "middle") { // draw arrow head if (this.options.smooth.enabled === true) { var pointT = this._findBorderPosition(node1, ctx, { via: viaNode }); var guidePos = this.getPoint(pointT.t + relativeLength * (position === "from" ? 1 : -1), viaNode); angle = Math.atan2(pointT.y - guidePos.y, pointT.x - guidePos.x); arrowPoint = pointT; } else { angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); arrowPoint = this._findBorderPosition(node1, ctx); } } else { // Negative half length reverses arrow direction. var halfLength = (reversed ? -relativeLength : relativeLength) / 2; var guidePos1 = this.getPoint(0.5 + halfLength, viaNode); var guidePos2 = this.getPoint(0.5 - halfLength, viaNode); angle = Math.atan2(guidePos1.y - guidePos2.y, guidePos1.x - guidePos2.x); arrowPoint = this.getPoint(0.5, viaNode); } } else { // draw circle var _this$_getCircleData9 = this._getCircleData(ctx), _this$_getCircleData10 = _slicedToArray(_this$_getCircleData9, 3), x = _this$_getCircleData10[0], y = _this$_getCircleData10[1], radius = _this$_getCircleData10[2]; if (position === "from") { var low = this.options.selfReference.angle; var high = this.options.selfReference.angle + Math.PI; var _pointT = this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: low, high: high, direction: -1 }); angle = _pointT.t * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; arrowPoint = _pointT; } else if (position === "to") { var _low = this.options.selfReference.angle; var _high = this.options.selfReference.angle + Math.PI; var _pointT2 = this._findBorderPositionCircle(this.from, ctx, { x: x, y: y, low: _low, high: _high, direction: 1 }); angle = _pointT2.t * -2 * Math.PI + 1.5 * Math.PI - 1.1 * Math.PI; arrowPoint = _pointT2; } else { var pos = this.options.selfReference.angle / (2 * Math.PI); arrowPoint = this._pointOnCircle(x, y, radius, pos); angle = pos * -2 * Math.PI + 1.5 * Math.PI + 0.1 * Math.PI; } } var xi = arrowPoint.x - length * 0.9 * Math.cos(angle); var yi = arrowPoint.y - length * 0.9 * Math.sin(angle); var arrowCore = { x: xi, y: yi }; return { point: arrowPoint, core: arrowCore, angle: angle, length: length, type: type }; } /** @inheritDoc */ }, { key: "drawArrowHead", value: function drawArrowHead(ctx, values, _selected, _hover, arrowData) { // set style ctx.strokeStyle = this.getColor(ctx, values); ctx.fillStyle = ctx.strokeStyle; ctx.lineWidth = values.width; var canFill = EndPoints.draw(ctx, arrowData); if (canFill) { // draw shadow if enabled this.enableShadow(ctx, values); _fillInstanceProperty(ctx).call(ctx); // disable shadows for other elements. this.disableShadow(ctx, values); } } /** * Set the shadow formatting values in the context if enabled, do nothing otherwise. * * @param ctx - The context that will be used for rendering. * @param values - Formatting values for the shadow. */ }, { key: "enableShadow", value: function enableShadow(ctx, values) { if (values.shadow === true) { ctx.shadowColor = values.shadowColor; ctx.shadowBlur = values.shadowSize; ctx.shadowOffsetX = values.shadowX; ctx.shadowOffsetY = values.shadowY; } } /** * Reset the shadow formatting values in the context if enabled, do nothing otherwise. * * @param ctx - The context that will be used for rendering. * @param values - Formatting values for the shadow. */ }, { key: "disableShadow", value: function disableShadow(ctx, values) { if (values.shadow === true) { ctx.shadowColor = "rgba(0,0,0,0)"; ctx.shadowBlur = 0; ctx.shadowOffsetX = 0; ctx.shadowOffsetY = 0; } } /** * Render the background according to the formatting values. * * @param ctx - The context that will be used for rendering. * @param values - Formatting values for the background. */ }, { key: "drawBackground", value: function drawBackground(ctx, values) { if (values.background !== false) { // save original line attrs var origCtxAttr = { strokeStyle: ctx.strokeStyle, lineWidth: ctx.lineWidth, dashes: ctx.dashes }; ctx.strokeStyle = values.backgroundColor; ctx.lineWidth = values.backgroundSize; this.setStrokeDashed(ctx, values.backgroundDashes); ctx.stroke(); // restore original line attrs ctx.strokeStyle = origCtxAttr.strokeStyle; ctx.lineWidth = origCtxAttr.lineWidth; ctx.dashes = origCtxAttr.dashes; this.setStrokeDashed(ctx, values.dashes); } } /** * Set the line dash pattern if supported. Logs a warning to the console if it isn't supported. * * @param ctx - The context that will be used for rendering. * @param dashes - The pattern [line, space, line…], true for default dashed line or false for normal line. */ }, { key: "setStrokeDashed", value: function setStrokeDashed(ctx, dashes) { if (dashes !== false) { if (ctx.setLineDash !== undefined) { var pattern = _Array$isArray(dashes) ? dashes : [5, 5]; ctx.setLineDash(pattern); } else { console.warn("setLineDash is not supported in this browser. The dashed stroke cannot be used."); } } else { if (ctx.setLineDash !== undefined) { ctx.setLineDash([]); } else { console.warn("setLineDash is not supported in this browser. The dashed stroke cannot be used."); } } } }]); return EdgeBase; }(); function ownKeys(object, enumerableOnly) { var keys = _Object$keys(object); if (_Object$getOwnPropertySymbols) { var symbols = _Object$getOwnPropertySymbols(object); enumerableOnly && (symbols = _filterInstanceProperty(symbols).call(symbols, function (sym) { return _Object$getOwnPropertyDescriptor$1(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var _context, _context2; var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? _forEachInstanceProperty(_context = ownKeys(Object(source), !0)).call(_context, function (key) { _defineProperty(target, key, source[key]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(target, _Object$getOwnPropertyDescriptors(source)) : _forEachInstanceProperty(_context2 = ownKeys(Object(source))).call(_context2, function (key) { _Object$defineProperty$1(target, key, _Object$getOwnPropertyDescriptor$1(source, key)); }); } return target; } function _createSuper$9(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$9(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$9() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * The Base Class for all Bezier edges. * Bezier curves are used to model smooth gradual curves in paths between nodes. */ var BezierEdgeBase = /*#__PURE__*/function (_EdgeBase) { _inherits(BezierEdgeBase, _EdgeBase); var _super = _createSuper$9(BezierEdgeBase); /** * Create a new instance. * * @param options - The options object of given edge. * @param body - The body of the network. * @param labelModule - Label module. */ function BezierEdgeBase(options, body, labelModule) { _classCallCheck(this, BezierEdgeBase); return _super.call(this, options, body, labelModule); } /** * Find the intersection between the border of the node and the edge. * * @remarks * This function uses binary search to look for the point where the bezier curve crosses the border of the node. * @param nearNode - The node (either from or to node of the edge). * @param ctx - The context that will be used for rendering. * @param viaNode - Additional node(s) the edge passes through. * @returns Cartesian coordinates of the intersection between the border of the node and the edge. */ _createClass(BezierEdgeBase, [{ key: "_findBorderPositionBezier", value: function _findBorderPositionBezier(nearNode, ctx) { var viaNode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this._getViaCoordinates(); var maxIterations = 10; var threshold = 0.2; var from = false; var high = 1; var low = 0; var node = this.to; var pos; var middle; var endPointOffset = this.options.endPointOffset ? this.options.endPointOffset.to : 0; if (nearNode.id === this.from.id) { node = this.from; from = true; endPointOffset = this.options.endPointOffset ? this.options.endPointOffset.from : 0; } if (this.options.arrowStrikethrough === false) { endPointOffset = 0; } var iteration = 0; do { middle = (low + high) * 0.5; pos = this.getPoint(middle, viaNode); var angle = Math.atan2(node.y - pos.y, node.x - pos.x); var distanceToBorder = node.distanceToBorder(ctx, angle) + endPointOffset; var distanceToPoint = Math.sqrt(Math.pow(pos.x - node.x, 2) + Math.pow(pos.y - node.y, 2)); var difference = distanceToBorder - distanceToPoint; if (Math.abs(difference) < threshold) { break; // found } else if (difference < 0) { // distance to nodes is larger than distance to border --> t needs to be bigger if we're looking at the to node. if (from === false) { low = middle; } else { high = middle; } } else { if (from === false) { high = middle; } else { low = middle; } } ++iteration; } while (low <= high && iteration < maxIterations); return _objectSpread(_objectSpread({}, pos), {}, { t: middle }); } /** * Calculate the distance between a point (x3,y3) and a line segment from (x1,y1) to (x2,y2). * * @remarks * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment * @param x1 - First end of the line segment on the x axis. * @param y1 - First end of the line segment on the y axis. * @param x2 - Second end of the line segment on the x axis. * @param y2 - Second end of the line segment on the y axis. * @param x3 - Position of the point on the x axis. * @param y3 - Position of the point on the y axis. * @param via - The control point for the edge. * @returns The distance between the line segment and the point. */ }, { key: "_getDistanceToBezierEdge", value: function _getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, via) { // x3,y3 is the point var minDistance = 1e9; var distance; var i, t, x, y; var lastX = x1; var lastY = y1; for (i = 1; i < 10; i++) { t = 0.1 * i; x = Math.pow(1 - t, 2) * x1 + 2 * t * (1 - t) * via.x + Math.pow(t, 2) * x2; y = Math.pow(1 - t, 2) * y1 + 2 * t * (1 - t) * via.y + Math.pow(t, 2) * y2; if (i > 0) { distance = this._getDistanceToLine(lastX, lastY, x, y, x3, y3); minDistance = distance < minDistance ? distance : minDistance; } lastX = x; lastY = y; } return minDistance; } /** * Render a bezier curve between two nodes. * * @remarks * The method accepts zero, one or two control points. * Passing zero control points just draws a straight line. * @param ctx - The context that will be used for rendering. * @param values - Style options for edge drawing. * @param viaNode1 - First control point for curve drawing. * @param viaNode2 - Second control point for curve drawing. */ }, { key: "_bezierCurve", value: function _bezierCurve(ctx, values, viaNode1, viaNode2) { ctx.beginPath(); ctx.moveTo(this.fromPoint.x, this.fromPoint.y); if (viaNode1 != null && viaNode1.x != null) { if (viaNode2 != null && viaNode2.x != null) { ctx.bezierCurveTo(viaNode1.x, viaNode1.y, viaNode2.x, viaNode2.y, this.toPoint.x, this.toPoint.y); } else { ctx.quadraticCurveTo(viaNode1.x, viaNode1.y, this.toPoint.x, this.toPoint.y); } } else { // fallback to normal straight edge ctx.lineTo(this.toPoint.x, this.toPoint.y); } // draw a background this.drawBackground(ctx, values); // draw shadow if enabled this.enableShadow(ctx, values); ctx.stroke(); this.disableShadow(ctx, values); } /** @inheritDoc */ }, { key: "getViaNode", value: function getViaNode() { return this._getViaCoordinates(); } }]); return BezierEdgeBase; }(EdgeBase); function _createSuper$8(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$8(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$8() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Dynamic Bezier Edge. Bezier curves are used to model smooth gradual * curves in paths between nodes. The Dynamic piece refers to how the curve * reacts to physics changes. * * @augments BezierEdgeBase */ var BezierEdgeDynamic = /*#__PURE__*/function (_BezierEdgeBase) { _inherits(BezierEdgeDynamic, _BezierEdgeBase); var _super = _createSuper$8(BezierEdgeDynamic); /** * Create a new instance. * * @param options - The options object of given edge. * @param body - The body of the network. * @param labelModule - Label module. */ function BezierEdgeDynamic(options, body, labelModule) { var _this; _classCallCheck(this, BezierEdgeDynamic); //this.via = undefined; // Here for completeness but not allowed to defined before super() is invoked. _this = _super.call(this, options, body, labelModule); // --> this calls the setOptions below _this.via = _this.via; // constructor → super → super → setOptions → setupSupportNode _this._boundFunction = function () { _this.positionBezierNode(); }; _this._body.emitter.on("_repositionBezierNodes", _this._boundFunction); return _this; } /** @inheritDoc */ _createClass(BezierEdgeDynamic, [{ key: "setOptions", value: function setOptions(options) { _get(_getPrototypeOf(BezierEdgeDynamic.prototype), "setOptions", this).call(this, options); // check if the physics has changed. var physicsChange = false; if (this.options.physics !== options.physics) { physicsChange = true; } // set the options and the to and from nodes this.options = options; this.id = this.options.id; this.from = this._body.nodes[this.options.from]; this.to = this._body.nodes[this.options.to]; // setup the support node and connect this.setupSupportNode(); this.connect(); // when we change the physics state of the edge, we reposition the support node. if (physicsChange === true) { this.via.setOptions({ physics: this.options.physics }); this.positionBezierNode(); } } /** @inheritDoc */ }, { key: "connect", value: function connect() { this.from = this._body.nodes[this.options.from]; this.to = this._body.nodes[this.options.to]; if (this.from === undefined || this.to === undefined || this.options.physics === false) { this.via.setOptions({ physics: false }); } else { // fix weird behaviour where a self referencing node has physics enabled if (this.from.id === this.to.id) { this.via.setOptions({ physics: false }); } else { this.via.setOptions({ physics: true }); } } } /** @inheritDoc */ }, { key: "cleanup", value: function cleanup() { this._body.emitter.off("_repositionBezierNodes", this._boundFunction); if (this.via !== undefined) { delete this._body.nodes[this.via.id]; this.via = undefined; return true; } return false; } /** * Create and add a support node if not already present. * * @remarks * Bezier curves require an anchor point to calculate the smooth flow. * These points are nodes. * These nodes are invisible but are used for the force calculation. * * The changed data is not called, if needed, it is returned by the main edge constructor. */ }, { key: "setupSupportNode", value: function setupSupportNode() { if (this.via === undefined) { var nodeId = "edgeId:" + this.id; var node = this._body.functions.createNode({ id: nodeId, shape: "circle", physics: true, hidden: true }); this._body.nodes[nodeId] = node; this.via = node; this.via.parentEdgeId = this.id; this.positionBezierNode(); } } /** * Position bezier node. */ }, { key: "positionBezierNode", value: function positionBezierNode() { if (this.via !== undefined && this.from !== undefined && this.to !== undefined) { this.via.x = 0.5 * (this.from.x + this.to.x); this.via.y = 0.5 * (this.from.y + this.to.y); } else if (this.via !== undefined) { this.via.x = 0; this.via.y = 0; } } /** @inheritDoc */ }, { key: "_line", value: function _line(ctx, values, viaNode) { this._bezierCurve(ctx, values, viaNode); } /** @inheritDoc */ }, { key: "_getViaCoordinates", value: function _getViaCoordinates() { return this.via; } /** @inheritDoc */ }, { key: "getViaNode", value: function getViaNode() { return this.via; } /** @inheritDoc */ }, { key: "getPoint", value: function getPoint(position) { var viaNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.via; if (this.from === this.to) { var _this$_getCircleData = this._getCircleData(), _this$_getCircleData2 = _slicedToArray(_this$_getCircleData, 3), cx = _this$_getCircleData2[0], cy = _this$_getCircleData2[1], cr = _this$_getCircleData2[2]; var a = 2 * Math.PI * (1 - position); return { x: cx + cr * Math.sin(a), y: cy + cr - cr * (1 - Math.cos(a)) }; } else { return { x: Math.pow(1 - position, 2) * this.fromPoint.x + 2 * position * (1 - position) * viaNode.x + Math.pow(position, 2) * this.toPoint.x, y: Math.pow(1 - position, 2) * this.fromPoint.y + 2 * position * (1 - position) * viaNode.y + Math.pow(position, 2) * this.toPoint.y }; } } /** @inheritDoc */ }, { key: "_findBorderPosition", value: function _findBorderPosition(nearNode, ctx) { return this._findBorderPositionBezier(nearNode, ctx, this.via); } /** @inheritDoc */ }, { key: "_getDistanceToEdge", value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { // x3,y3 is the point return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, this.via); } }]); return BezierEdgeDynamic; }(BezierEdgeBase); function _createSuper$7(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$7(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$7() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Static Bezier Edge. Bezier curves are used to model smooth gradual curves in paths between nodes. */ var BezierEdgeStatic = /*#__PURE__*/function (_BezierEdgeBase) { _inherits(BezierEdgeStatic, _BezierEdgeBase); var _super = _createSuper$7(BezierEdgeStatic); /** * Create a new instance. * * @param options - The options object of given edge. * @param body - The body of the network. * @param labelModule - Label module. */ function BezierEdgeStatic(options, body, labelModule) { _classCallCheck(this, BezierEdgeStatic); return _super.call(this, options, body, labelModule); } /** @inheritDoc */ _createClass(BezierEdgeStatic, [{ key: "_line", value: function _line(ctx, values, viaNode) { this._bezierCurve(ctx, values, viaNode); } /** @inheritDoc */ }, { key: "getViaNode", value: function getViaNode() { return this._getViaCoordinates(); } /** * Compute the coordinates of the via node. * * @remarks * We do not use the to and fromPoints here to make the via nodes the same as edges without arrows. * @returns Cartesian coordinates of the via node. */ }, { key: "_getViaCoordinates", value: function _getViaCoordinates() { // Assumption: x/y coordinates in from/to always defined var factor = this.options.smooth.roundness; var type = this.options.smooth.type; var dx = Math.abs(this.from.x - this.to.x); var dy = Math.abs(this.from.y - this.to.y); if (type === "discrete" || type === "diagonalCross") { var stepX; var stepY; if (dx <= dy) { stepX = stepY = factor * dy; } else { stepX = stepY = factor * dx; } if (this.from.x > this.to.x) { stepX = -stepX; } if (this.from.y >= this.to.y) { stepY = -stepY; } var xVia = this.from.x + stepX; var yVia = this.from.y + stepY; if (type === "discrete") { if (dx <= dy) { xVia = dx < factor * dy ? this.from.x : xVia; } else { yVia = dy < factor * dx ? this.from.y : yVia; } } return { x: xVia, y: yVia }; } else if (type === "straightCross") { var _stepX = (1 - factor) * dx; var _stepY = (1 - factor) * dy; if (dx <= dy) { // up - down _stepX = 0; if (this.from.y < this.to.y) { _stepY = -_stepY; } } else { // left - right if (this.from.x < this.to.x) { _stepX = -_stepX; } _stepY = 0; } return { x: this.to.x + _stepX, y: this.to.y + _stepY }; } else if (type === "horizontal") { var _stepX2 = (1 - factor) * dx; if (this.from.x < this.to.x) { _stepX2 = -_stepX2; } return { x: this.to.x + _stepX2, y: this.from.y }; } else if (type === "vertical") { var _stepY2 = (1 - factor) * dy; if (this.from.y < this.to.y) { _stepY2 = -_stepY2; } return { x: this.from.x, y: this.to.y + _stepY2 }; } else if (type === "curvedCW") { dx = this.to.x - this.from.x; dy = this.from.y - this.to.y; var radius = Math.sqrt(dx * dx + dy * dy); var pi = Math.PI; var originalAngle = Math.atan2(dy, dx); var myAngle = (originalAngle + (factor * 0.5 + 0.5) * pi) % (2 * pi); return { x: this.from.x + (factor * 0.5 + 0.5) * radius * Math.sin(myAngle), y: this.from.y + (factor * 0.5 + 0.5) * radius * Math.cos(myAngle) }; } else if (type === "curvedCCW") { dx = this.to.x - this.from.x; dy = this.from.y - this.to.y; var _radius = Math.sqrt(dx * dx + dy * dy); var _pi = Math.PI; var _originalAngle = Math.atan2(dy, dx); var _myAngle = (_originalAngle + (-factor * 0.5 + 0.5) * _pi) % (2 * _pi); return { x: this.from.x + (factor * 0.5 + 0.5) * _radius * Math.sin(_myAngle), y: this.from.y + (factor * 0.5 + 0.5) * _radius * Math.cos(_myAngle) }; } else { // continuous var _stepX3; var _stepY3; if (dx <= dy) { _stepX3 = _stepY3 = factor * dy; } else { _stepX3 = _stepY3 = factor * dx; } if (this.from.x > this.to.x) { _stepX3 = -_stepX3; } if (this.from.y >= this.to.y) { _stepY3 = -_stepY3; } var _xVia = this.from.x + _stepX3; var _yVia = this.from.y + _stepY3; if (dx <= dy) { if (this.from.x <= this.to.x) { _xVia = this.to.x < _xVia ? this.to.x : _xVia; } else { _xVia = this.to.x > _xVia ? this.to.x : _xVia; } } else { if (this.from.y >= this.to.y) { _yVia = this.to.y > _yVia ? this.to.y : _yVia; } else { _yVia = this.to.y < _yVia ? this.to.y : _yVia; } } return { x: _xVia, y: _yVia }; } } /** @inheritDoc */ }, { key: "_findBorderPosition", value: function _findBorderPosition(nearNode, ctx) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; return this._findBorderPositionBezier(nearNode, ctx, options.via); } /** @inheritDoc */ }, { key: "_getDistanceToEdge", value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { var viaNode = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : this._getViaCoordinates(); // x3,y3 is the point return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, viaNode); } /** @inheritDoc */ }, { key: "getPoint", value: function getPoint(position) { var viaNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this._getViaCoordinates(); var t = position; var x = Math.pow(1 - t, 2) * this.fromPoint.x + 2 * t * (1 - t) * viaNode.x + Math.pow(t, 2) * this.toPoint.x; var y = Math.pow(1 - t, 2) * this.fromPoint.y + 2 * t * (1 - t) * viaNode.y + Math.pow(t, 2) * this.toPoint.y; return { x: x, y: y }; } }]); return BezierEdgeStatic; }(BezierEdgeBase); function _createSuper$6(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$6(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$6() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Base Class for all Cubic Bezier Edges. Bezier curves are used to model * smooth gradual curves in paths between nodes. * * @augments BezierEdgeBase */ var CubicBezierEdgeBase = /*#__PURE__*/function (_BezierEdgeBase) { _inherits(CubicBezierEdgeBase, _BezierEdgeBase); var _super = _createSuper$6(CubicBezierEdgeBase); /** * Create a new instance. * * @param options - The options object of given edge. * @param body - The body of the network. * @param labelModule - Label module. */ function CubicBezierEdgeBase(options, body, labelModule) { _classCallCheck(this, CubicBezierEdgeBase); return _super.call(this, options, body, labelModule); } /** * Calculate the distance between a point (x3,y3) and a line segment from (x1,y1) to (x2,y2). * * @remarks * http://stackoverflow.com/questions/849211/shortest-distancae-between-a-point-and-a-line-segment * https://en.wikipedia.org/wiki/B%C3%A9zier_curve * @param x1 - First end of the line segment on the x axis. * @param y1 - First end of the line segment on the y axis. * @param x2 - Second end of the line segment on the x axis. * @param y2 - Second end of the line segment on the y axis. * @param x3 - Position of the point on the x axis. * @param y3 - Position of the point on the y axis. * @param via1 - The first point this edge passes through. * @param via2 - The second point this edge passes through. * @returns The distance between the line segment and the point. */ _createClass(CubicBezierEdgeBase, [{ key: "_getDistanceToBezierEdge2", value: function _getDistanceToBezierEdge2(x1, y1, x2, y2, x3, y3, via1, via2) { // x3,y3 is the point var minDistance = 1e9; var lastX = x1; var lastY = y1; var vec = [0, 0, 0, 0]; for (var i = 1; i < 10; i++) { var t = 0.1 * i; vec[0] = Math.pow(1 - t, 3); vec[1] = 3 * t * Math.pow(1 - t, 2); vec[2] = 3 * Math.pow(t, 2) * (1 - t); vec[3] = Math.pow(t, 3); var x = vec[0] * x1 + vec[1] * via1.x + vec[2] * via2.x + vec[3] * x2; var y = vec[0] * y1 + vec[1] * via1.y + vec[2] * via2.y + vec[3] * y2; if (i > 0) { var distance = this._getDistanceToLine(lastX, lastY, x, y, x3, y3); minDistance = distance < minDistance ? distance : minDistance; } lastX = x; lastY = y; } return minDistance; } }]); return CubicBezierEdgeBase; }(BezierEdgeBase); function _createSuper$5(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$5(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$5() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Cubic Bezier Edge. Bezier curves are used to model smooth gradual curves in paths between nodes. */ var CubicBezierEdge = /*#__PURE__*/function (_CubicBezierEdgeBase) { _inherits(CubicBezierEdge, _CubicBezierEdgeBase); var _super = _createSuper$5(CubicBezierEdge); /** * Create a new instance. * * @param options - The options object of given edge. * @param body - The body of the network. * @param labelModule - Label module. */ function CubicBezierEdge(options, body, labelModule) { _classCallCheck(this, CubicBezierEdge); return _super.call(this, options, body, labelModule); } /** @inheritDoc */ _createClass(CubicBezierEdge, [{ key: "_line", value: function _line(ctx, values, viaNodes) { // get the coordinates of the support points. var via1 = viaNodes[0]; var via2 = viaNodes[1]; this._bezierCurve(ctx, values, via1, via2); } /** * Compute the additional points the edge passes through. * * @returns Cartesian coordinates of the points the edge passes through. */ }, { key: "_getViaCoordinates", value: function _getViaCoordinates() { var dx = this.from.x - this.to.x; var dy = this.from.y - this.to.y; var x1; var y1; var x2; var y2; var roundness = this.options.smooth.roundness; // horizontal if x > y or if direction is forced or if direction is horizontal if ((Math.abs(dx) > Math.abs(dy) || this.options.smooth.forceDirection === true || this.options.smooth.forceDirection === "horizontal") && this.options.smooth.forceDirection !== "vertical") { y1 = this.from.y; y2 = this.to.y; x1 = this.from.x - roundness * dx; x2 = this.to.x + roundness * dx; } else { y1 = this.from.y - roundness * dy; y2 = this.to.y + roundness * dy; x1 = this.from.x; x2 = this.to.x; } return [{ x: x1, y: y1 }, { x: x2, y: y2 }]; } /** @inheritDoc */ }, { key: "getViaNode", value: function getViaNode() { return this._getViaCoordinates(); } /** @inheritDoc */ }, { key: "_findBorderPosition", value: function _findBorderPosition(nearNode, ctx) { return this._findBorderPositionBezier(nearNode, ctx); } /** @inheritDoc */ }, { key: "_getDistanceToEdge", value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { var _ref = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : this._getViaCoordinates(), _ref2 = _slicedToArray(_ref, 2), via1 = _ref2[0], via2 = _ref2[1]; // x3,y3 is the point return this._getDistanceToBezierEdge2(x1, y1, x2, y2, x3, y3, via1, via2); } /** @inheritDoc */ }, { key: "getPoint", value: function getPoint(position) { var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this._getViaCoordinates(), _ref4 = _slicedToArray(_ref3, 2), via1 = _ref4[0], via2 = _ref4[1]; var t = position; var vec = [Math.pow(1 - t, 3), 3 * t * Math.pow(1 - t, 2), 3 * Math.pow(t, 2) * (1 - t), Math.pow(t, 3)]; var x = vec[0] * this.fromPoint.x + vec[1] * via1.x + vec[2] * via2.x + vec[3] * this.toPoint.x; var y = vec[0] * this.fromPoint.y + vec[1] * via1.y + vec[2] * via2.y + vec[3] * this.toPoint.y; return { x: x, y: y }; } }]); return CubicBezierEdge; }(CubicBezierEdgeBase); function _createSuper$4(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$4(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$4() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Straight Edge. */ var StraightEdge = /*#__PURE__*/function (_EdgeBase) { _inherits(StraightEdge, _EdgeBase); var _super = _createSuper$4(StraightEdge); /** * Create a new instance. * * @param options - The options object of given edge. * @param body - The body of the network. * @param labelModule - Label module. */ function StraightEdge(options, body, labelModule) { _classCallCheck(this, StraightEdge); return _super.call(this, options, body, labelModule); } /** @inheritDoc */ _createClass(StraightEdge, [{ key: "_line", value: function _line(ctx, values) { // draw a straight line ctx.beginPath(); ctx.moveTo(this.fromPoint.x, this.fromPoint.y); ctx.lineTo(this.toPoint.x, this.toPoint.y); // draw shadow if enabled this.enableShadow(ctx, values); ctx.stroke(); this.disableShadow(ctx, values); } /** @inheritDoc */ }, { key: "getViaNode", value: function getViaNode() { return undefined; } /** @inheritDoc */ }, { key: "getPoint", value: function getPoint(position) { return { x: (1 - position) * this.fromPoint.x + position * this.toPoint.x, y: (1 - position) * this.fromPoint.y + position * this.toPoint.y }; } /** @inheritDoc */ }, { key: "_findBorderPosition", value: function _findBorderPosition(nearNode, ctx) { var node1 = this.to; var node2 = this.from; if (nearNode.id === this.from.id) { node1 = this.from; node2 = this.to; } var angle = Math.atan2(node1.y - node2.y, node1.x - node2.x); var dx = node1.x - node2.x; var dy = node1.y - node2.y; var edgeSegmentLength = Math.sqrt(dx * dx + dy * dy); var toBorderDist = nearNode.distanceToBorder(ctx, angle); var toBorderPoint = (edgeSegmentLength - toBorderDist) / edgeSegmentLength; return { x: (1 - toBorderPoint) * node2.x + toBorderPoint * node1.x, y: (1 - toBorderPoint) * node2.y + toBorderPoint * node1.y, t: 0 }; } /** @inheritDoc */ }, { key: "_getDistanceToEdge", value: function _getDistanceToEdge(x1, y1, x2, y2, x3, y3) { // x3,y3 is the point return this._getDistanceToLine(x1, y1, x2, y2, x3, y3); } }]); return StraightEdge; }(EdgeBase); /** * An edge connects two nodes and has a specific direction. */ var Edge = /*#__PURE__*/function () { /** * @param {object} options values specific to this edge, must contain at least 'from' and 'to' * @param {object} body shared state from Network instance * @param {Network.Images} imagelist A list with images. Only needed when the edge has image arrows. * @param {object} globalOptions options from the EdgesHandler instance * @param {object} defaultOptions default options from the EdgeHandler instance. Value and reference are constant */ function Edge(options, body, imagelist, globalOptions, defaultOptions) { _classCallCheck(this, Edge); if (body === undefined) { throw new Error("No body provided"); } // Since globalOptions is constant in values as well as reference, // Following needs to be done only once. this.options = bridgeObject(globalOptions); this.globalOptions = globalOptions; this.defaultOptions = defaultOptions; this.body = body; this.imagelist = imagelist; // initialize variables this.id = undefined; this.fromId = undefined; this.toId = undefined; this.selected = false; this.hover = false; this.labelDirty = true; this.baseWidth = this.options.width; this.baseFontSize = this.options.font.size; this.from = undefined; // a node this.to = undefined; // a node this.edgeType = undefined; this.connected = false; this.labelModule = new Label(this.body, this.options, true /* It's an edge label */); this.setOptions(options); } /** * Set or overwrite options for the edge * * @param {object} options an object with options * @returns {undefined|boolean} undefined if no options, true if layout affecting data changed, false otherwise. */ _createClass(Edge, [{ key: "setOptions", value: function setOptions(options) { if (!options) { return; } // Following options if changed affect the layout. var affectsLayout = typeof options.physics !== "undefined" && this.options.physics !== options.physics || typeof options.hidden !== "undefined" && (this.options.hidden || false) !== (options.hidden || false) || typeof options.from !== "undefined" && this.options.from !== options.from || typeof options.to !== "undefined" && this.options.to !== options.to; Edge.parseOptions(this.options, options, true, this.globalOptions); if (options.id !== undefined) { this.id = options.id; } if (options.from !== undefined) { this.fromId = options.from; } if (options.to !== undefined) { this.toId = options.to; } if (options.title !== undefined) { this.title = options.title; } if (options.value !== undefined) { options.value = _parseFloat(options.value); } var pile = [options, this.options, this.defaultOptions]; this.chooser = choosify("edge", pile); // update label Module this.updateLabelModule(options); // Update edge type, this if changed affects the layout. affectsLayout = this.updateEdgeType() || affectsLayout; // if anything has been updates, reset the selection width and the hover width this._setInteractionWidths(); // A node is connected when it has a from and to node that both exist in the network.body.nodes. this.connect(); return affectsLayout; } /** * * @param {object} parentOptions * @param {object} newOptions * @param {boolean} [allowDeletion=false] * @param {object} [globalOptions={}] * @param {boolean} [copyFromGlobals=false] */ }, { key: "getFormattingValues", value: /** * * @returns {ArrowOptions} */ function getFormattingValues() { var toArrow = this.options.arrows.to === true || this.options.arrows.to.enabled === true; var fromArrow = this.options.arrows.from === true || this.options.arrows.from.enabled === true; var middleArrow = this.options.arrows.middle === true || this.options.arrows.middle.enabled === true; var inheritsColor = this.options.color.inherit; var values = { toArrow: toArrow, toArrowScale: this.options.arrows.to.scaleFactor, toArrowType: this.options.arrows.to.type, toArrowSrc: this.options.arrows.to.src, toArrowImageWidth: this.options.arrows.to.imageWidth, toArrowImageHeight: this.options.arrows.to.imageHeight, middleArrow: middleArrow, middleArrowScale: this.options.arrows.middle.scaleFactor, middleArrowType: this.options.arrows.middle.type, middleArrowSrc: this.options.arrows.middle.src, middleArrowImageWidth: this.options.arrows.middle.imageWidth, middleArrowImageHeight: this.options.arrows.middle.imageHeight, fromArrow: fromArrow, fromArrowScale: this.options.arrows.from.scaleFactor, fromArrowType: this.options.arrows.from.type, fromArrowSrc: this.options.arrows.from.src, fromArrowImageWidth: this.options.arrows.from.imageWidth, fromArrowImageHeight: this.options.arrows.from.imageHeight, arrowStrikethrough: this.options.arrowStrikethrough, color: inheritsColor ? undefined : this.options.color.color, inheritsColor: inheritsColor, opacity: this.options.color.opacity, hidden: this.options.hidden, length: this.options.length, shadow: this.options.shadow.enabled, shadowColor: this.options.shadow.color, shadowSize: this.options.shadow.size, shadowX: this.options.shadow.x, shadowY: this.options.shadow.y, dashes: this.options.dashes, width: this.options.width, background: this.options.background.enabled, backgroundColor: this.options.background.color, backgroundSize: this.options.background.size, backgroundDashes: this.options.background.dashes }; if (this.selected || this.hover) { if (this.chooser === true) { if (this.selected) { var selectedWidth = this.options.selectionWidth; if (typeof selectedWidth === "function") { values.width = selectedWidth(values.width); } else if (typeof selectedWidth === "number") { values.width += selectedWidth; } values.width = Math.max(values.width, 0.3 / this.body.view.scale); values.color = this.options.color.highlight; values.shadow = this.options.shadow.enabled; } else if (this.hover) { var hoverWidth = this.options.hoverWidth; if (typeof hoverWidth === "function") { values.width = hoverWidth(values.width); } else if (typeof hoverWidth === "number") { values.width += hoverWidth; } values.width = Math.max(values.width, 0.3 / this.body.view.scale); values.color = this.options.color.hover; values.shadow = this.options.shadow.enabled; } } else if (typeof this.chooser === "function") { this.chooser(values, this.options.id, this.selected, this.hover); if (values.color !== undefined) { values.inheritsColor = false; } if (values.shadow === false) { if (values.shadowColor !== this.options.shadow.color || values.shadowSize !== this.options.shadow.size || values.shadowX !== this.options.shadow.x || values.shadowY !== this.options.shadow.y) { values.shadow = true; } } } } else { values.shadow = this.options.shadow.enabled; values.width = Math.max(values.width, 0.3 / this.body.view.scale); } return values; } /** * update the options in the label module * * @param {object} options */ }, { key: "updateLabelModule", value: function updateLabelModule(options) { var pile = [options, this.options, this.globalOptions, // Currently set global edge options this.defaultOptions]; this.labelModule.update(this.options, pile); if (this.labelModule.baseSize !== undefined) { this.baseFontSize = this.labelModule.baseSize; } } /** * update the edge type, set the options * * @returns {boolean} */ }, { key: "updateEdgeType", value: function updateEdgeType() { var smooth = this.options.smooth; var dataChanged = false; var changeInType = true; if (this.edgeType !== undefined) { if (this.edgeType instanceof BezierEdgeDynamic && smooth.enabled === true && smooth.type === "dynamic" || this.edgeType instanceof CubicBezierEdge && smooth.enabled === true && smooth.type === "cubicBezier" || this.edgeType instanceof BezierEdgeStatic && smooth.enabled === true && smooth.type !== "dynamic" && smooth.type !== "cubicBezier" || this.edgeType instanceof StraightEdge && smooth.type.enabled === false) { changeInType = false; } if (changeInType === true) { dataChanged = this.cleanup(); } } if (changeInType === true) { if (smooth.enabled === true) { if (smooth.type === "dynamic") { dataChanged = true; this.edgeType = new BezierEdgeDynamic(this.options, this.body, this.labelModule); } else if (smooth.type === "cubicBezier") { this.edgeType = new CubicBezierEdge(this.options, this.body, this.labelModule); } else { this.edgeType = new BezierEdgeStatic(this.options, this.body, this.labelModule); } } else { this.edgeType = new StraightEdge(this.options, this.body, this.labelModule); } } else { // if nothing changes, we just set the options. this.edgeType.setOptions(this.options); } return dataChanged; } /** * Connect an edge to its nodes */ }, { key: "connect", value: function connect() { this.disconnect(); this.from = this.body.nodes[this.fromId] || undefined; this.to = this.body.nodes[this.toId] || undefined; this.connected = this.from !== undefined && this.to !== undefined; if (this.connected === true) { this.from.attachEdge(this); this.to.attachEdge(this); } else { if (this.from) { this.from.detachEdge(this); } if (this.to) { this.to.detachEdge(this); } } this.edgeType.connect(); } /** * Disconnect an edge from its nodes */ }, { key: "disconnect", value: function disconnect() { if (this.from) { this.from.detachEdge(this); this.from = undefined; } if (this.to) { this.to.detachEdge(this); this.to = undefined; } this.connected = false; } /** * get the title of this edge. * * @returns {string} title The title of the edge, or undefined when no title * has been set. */ }, { key: "getTitle", value: function getTitle() { return this.title; } /** * check if this node is selecte * * @returns {boolean} selected True if node is selected, else false */ }, { key: "isSelected", value: function isSelected() { return this.selected; } /** * Retrieve the value of the edge. Can be undefined * * @returns {number} value */ }, { key: "getValue", value: function getValue() { return this.options.value; } /** * Adjust the value range of the edge. The edge will adjust it's width * based on its value. * * @param {number} min * @param {number} max * @param {number} total */ }, { key: "setValueRange", value: function setValueRange(min, max, total) { if (this.options.value !== undefined) { var scale = this.options.scaling.customScalingFunction(min, max, total, this.options.value); var widthDiff = this.options.scaling.max - this.options.scaling.min; if (this.options.scaling.label.enabled === true) { var fontDiff = this.options.scaling.label.max - this.options.scaling.label.min; this.options.font.size = this.options.scaling.label.min + scale * fontDiff; } this.options.width = this.options.scaling.min + scale * widthDiff; } else { this.options.width = this.baseWidth; this.options.font.size = this.baseFontSize; } this._setInteractionWidths(); this.updateLabelModule(); } /** * * @private */ }, { key: "_setInteractionWidths", value: function _setInteractionWidths() { if (typeof this.options.hoverWidth === "function") { this.edgeType.hoverWidth = this.options.hoverWidth(this.options.width); } else { this.edgeType.hoverWidth = this.options.hoverWidth + this.options.width; } if (typeof this.options.selectionWidth === "function") { this.edgeType.selectionWidth = this.options.selectionWidth(this.options.width); } else { this.edgeType.selectionWidth = this.options.selectionWidth + this.options.width; } } /** * Redraw a edge * Draw this edge in the given canvas * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); * * @param {CanvasRenderingContext2D} ctx */ }, { key: "draw", value: function draw(ctx) { var values = this.getFormattingValues(); if (values.hidden) { return; } // get the via node from the edge type var viaNode = this.edgeType.getViaNode(); // draw line and label this.edgeType.drawLine(ctx, values, this.selected, this.hover, viaNode); this.drawLabel(ctx, viaNode); } /** * Redraw arrows * Draw this arrows in the given canvas * The 2d context of a HTML canvas can be retrieved by canvas.getContext("2d"); * * @param {CanvasRenderingContext2D} ctx */ }, { key: "drawArrows", value: function drawArrows(ctx) { var values = this.getFormattingValues(); if (values.hidden) { return; } // get the via node from the edge type var viaNode = this.edgeType.getViaNode(); var arrowData = {}; // restore edge targets to defaults this.edgeType.fromPoint = this.edgeType.from; this.edgeType.toPoint = this.edgeType.to; // from and to arrows give a different end point for edges. we set them here if (values.fromArrow) { arrowData.from = this.edgeType.getArrowData(ctx, "from", viaNode, this.selected, this.hover, values); if (values.arrowStrikethrough === false) this.edgeType.fromPoint = arrowData.from.core; if (values.fromArrowSrc) { arrowData.from.image = this.imagelist.load(values.fromArrowSrc); } if (values.fromArrowImageWidth) { arrowData.from.imageWidth = values.fromArrowImageWidth; } if (values.fromArrowImageHeight) { arrowData.from.imageHeight = values.fromArrowImageHeight; } } if (values.toArrow) { arrowData.to = this.edgeType.getArrowData(ctx, "to", viaNode, this.selected, this.hover, values); if (values.arrowStrikethrough === false) this.edgeType.toPoint = arrowData.to.core; if (values.toArrowSrc) { arrowData.to.image = this.imagelist.load(values.toArrowSrc); } if (values.toArrowImageWidth) { arrowData.to.imageWidth = values.toArrowImageWidth; } if (values.toArrowImageHeight) { arrowData.to.imageHeight = values.toArrowImageHeight; } } // the middle arrow depends on the line, which can depend on the to and from arrows so we do this one lastly. if (values.middleArrow) { arrowData.middle = this.edgeType.getArrowData(ctx, "middle", viaNode, this.selected, this.hover, values); if (values.middleArrowSrc) { arrowData.middle.image = this.imagelist.load(values.middleArrowSrc); } if (values.middleArrowImageWidth) { arrowData.middle.imageWidth = values.middleArrowImageWidth; } if (values.middleArrowImageHeight) { arrowData.middle.imageHeight = values.middleArrowImageHeight; } } if (values.fromArrow) { this.edgeType.drawArrowHead(ctx, values, this.selected, this.hover, arrowData.from); } if (values.middleArrow) { this.edgeType.drawArrowHead(ctx, values, this.selected, this.hover, arrowData.middle); } if (values.toArrow) { this.edgeType.drawArrowHead(ctx, values, this.selected, this.hover, arrowData.to); } } /** * * @param {CanvasRenderingContext2D} ctx * @param {Node} viaNode */ }, { key: "drawLabel", value: function drawLabel(ctx, viaNode) { if (this.options.label !== undefined) { // set style var node1 = this.from; var node2 = this.to; if (this.labelModule.differentState(this.selected, this.hover)) { this.labelModule.getTextSize(ctx, this.selected, this.hover); } var point; if (node1.id != node2.id) { this.labelModule.pointToSelf = false; point = this.edgeType.getPoint(0.5, viaNode); ctx.save(); var rotationPoint = this._getRotation(ctx); if (rotationPoint.angle != 0) { ctx.translate(rotationPoint.x, rotationPoint.y); ctx.rotate(rotationPoint.angle); } // draw the label this.labelModule.draw(ctx, point.x, point.y, this.selected, this.hover); /* // Useful debug code: draw a border around the label // This should **not** be enabled in production! var size = this.labelModule.getSize();; // ;; intentional so lint catches it ctx.strokeStyle = "#ff0000"; ctx.strokeRect(size.left, size.top, size.width, size.height); // End debug code */ ctx.restore(); } else { // Ignore the orientations. this.labelModule.pointToSelf = true; // get circle coordinates var coordinates = getSelfRefCoordinates(ctx, this.options.selfReference.angle, this.options.selfReference.size, node1); point = this._pointOnCircle(coordinates.x, coordinates.y, this.options.selfReference.size, this.options.selfReference.angle); this.labelModule.draw(ctx, point.x, point.y, this.selected, this.hover); } } } /** * Determine all visual elements of this edge instance, in which the given * point falls within the bounding shape. * * @param {point} point * @returns {Array.<edgeClickItem|edgeLabelClickItem>} list with the items which are on the point */ }, { key: "getItemsOnPoint", value: function getItemsOnPoint(point) { var ret = []; if (this.labelModule.visible()) { var rotationPoint = this._getRotation(); if (pointInRect(this.labelModule.getSize(), point, rotationPoint)) { ret.push({ edgeId: this.id, labelId: 0 }); } } var obj = { left: point.x, top: point.y }; if (this.isOverlappingWith(obj)) { ret.push({ edgeId: this.id }); } return ret; } /** * Check if this object is overlapping with the provided object * * @param {object} obj an object with parameters left, top * @returns {boolean} True if location is located on the edge */ }, { key: "isOverlappingWith", value: function isOverlappingWith(obj) { if (this.connected) { var distMax = 10; var xFrom = this.from.x; var yFrom = this.from.y; var xTo = this.to.x; var yTo = this.to.y; var xObj = obj.left; var yObj = obj.top; var dist = this.edgeType.getDistanceToEdge(xFrom, yFrom, xTo, yTo, xObj, yObj); return dist < distMax; } else { return false; } } /** * Determine the rotation point, if any. * * @param {CanvasRenderingContext2D} [ctx] if passed, do a recalculation of the label size * @returns {rotationPoint} the point to rotate around and the angle in radians to rotate * @private */ }, { key: "_getRotation", value: function _getRotation(ctx) { var viaNode = this.edgeType.getViaNode(); var point = this.edgeType.getPoint(0.5, viaNode); if (ctx !== undefined) { this.labelModule.calculateLabelSize(ctx, this.selected, this.hover, point.x, point.y); } var ret = { x: point.x, y: this.labelModule.size.yLine, angle: 0 }; if (!this.labelModule.visible()) { return ret; // Don't even bother doing the atan2, there's nothing to draw } if (this.options.font.align === "horizontal") { return ret; // No need to calculate angle } var dy = this.from.y - this.to.y; var dx = this.from.x - this.to.x; var angle = Math.atan2(dy, dx); // radians // rotate so that label is readable if (angle < -1 && dx < 0 || angle > 0 && dx < 0) { angle += Math.PI; } ret.angle = angle; return ret; } /** * Get a point on a circle * * @param {number} x * @param {number} y * @param {number} radius * @param {number} angle * @returns {object} point * @private */ }, { key: "_pointOnCircle", value: function _pointOnCircle(x, y, radius, angle) { return { x: x + radius * Math.cos(angle), y: y - radius * Math.sin(angle) }; } /** * Sets selected state to true */ }, { key: "select", value: function select() { this.selected = true; } /** * Sets selected state to false */ }, { key: "unselect", value: function unselect() { this.selected = false; } /** * cleans all required things on delete * * @returns {*} */ }, { key: "cleanup", value: function cleanup() { return this.edgeType.cleanup(); } /** * Remove edge from the list and perform necessary cleanup. */ }, { key: "remove", value: function remove() { this.cleanup(); this.disconnect(); delete this.body.edges[this.id]; } /** * Check if both connecting nodes exist * * @returns {boolean} */ }, { key: "endPointsValid", value: function endPointsValid() { return this.body.nodes[this.fromId] !== undefined && this.body.nodes[this.toId] !== undefined; } }], [{ key: "parseOptions", value: function parseOptions(parentOptions, newOptions) { var allowDeletion = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var globalOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; var copyFromGlobals = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; var fields = ["endPointOffset", "arrowStrikethrough", "id", "from", "hidden", "hoverWidth", "labelHighlightBold", "length", "line", "opacity", "physics", "scaling", "selectionWidth", "selfReferenceSize", "selfReference", "to", "title", "value", "width", "font", "chosen", "widthConstraint"]; // only deep extend the items in the field array. These do not have shorthand. selectiveDeepExtend(fields, parentOptions, newOptions, allowDeletion); // Only use endPointOffset values (from and to) if it's valid values if (newOptions.endPointOffset !== undefined && newOptions.endPointOffset.from !== undefined) { if (_Number$isFinite(newOptions.endPointOffset.from)) { parentOptions.endPointOffset.from = newOptions.endPointOffset.from; } else { parentOptions.endPointOffset.from = globalOptions.endPointOffset.from !== undefined ? globalOptions.endPointOffset.from : 0; console.error("endPointOffset.from is not a valid number"); } } if (newOptions.endPointOffset !== undefined && newOptions.endPointOffset.to !== undefined) { if (_Number$isFinite(newOptions.endPointOffset.to)) { parentOptions.endPointOffset.to = newOptions.endPointOffset.to; } else { parentOptions.endPointOffset.to = globalOptions.endPointOffset.to !== undefined ? globalOptions.endPointOffset.to : 0; console.error("endPointOffset.to is not a valid number"); } } // Only copy label if it's a legal value. if (isValidLabel(newOptions.label)) { parentOptions.label = newOptions.label; } else if (!isValidLabel(parentOptions.label)) { parentOptions.label = undefined; } mergeOptions(parentOptions, newOptions, "smooth", globalOptions); mergeOptions(parentOptions, newOptions, "shadow", globalOptions); mergeOptions(parentOptions, newOptions, "background", globalOptions); if (newOptions.dashes !== undefined && newOptions.dashes !== null) { parentOptions.dashes = newOptions.dashes; } else if (allowDeletion === true && newOptions.dashes === null) { parentOptions.dashes = _Object$create$1(globalOptions.dashes); // this sets the pointer of the option back to the global option. } // set the scaling newOptions if (newOptions.scaling !== undefined && newOptions.scaling !== null) { if (newOptions.scaling.min !== undefined) { parentOptions.scaling.min = newOptions.scaling.min; } if (newOptions.scaling.max !== undefined) { parentOptions.scaling.max = newOptions.scaling.max; } mergeOptions(parentOptions.scaling, newOptions.scaling, "label", globalOptions.scaling); } else if (allowDeletion === true && newOptions.scaling === null) { parentOptions.scaling = _Object$create$1(globalOptions.scaling); // this sets the pointer of the option back to the global option. } // handle multiple input cases for arrows if (newOptions.arrows !== undefined && newOptions.arrows !== null) { if (typeof newOptions.arrows === "string") { var arrows = newOptions.arrows.toLowerCase(); parentOptions.arrows.to.enabled = _indexOfInstanceProperty(arrows).call(arrows, "to") != -1; parentOptions.arrows.middle.enabled = _indexOfInstanceProperty(arrows).call(arrows, "middle") != -1; parentOptions.arrows.from.enabled = _indexOfInstanceProperty(arrows).call(arrows, "from") != -1; } else if (_typeof(newOptions.arrows) === "object") { mergeOptions(parentOptions.arrows, newOptions.arrows, "to", globalOptions.arrows); mergeOptions(parentOptions.arrows, newOptions.arrows, "middle", globalOptions.arrows); mergeOptions(parentOptions.arrows, newOptions.arrows, "from", globalOptions.arrows); } else { throw new Error("The arrow newOptions can only be an object or a string. Refer to the documentation. You used:" + _JSON$stringify(newOptions.arrows)); } } else if (allowDeletion === true && newOptions.arrows === null) { parentOptions.arrows = _Object$create$1(globalOptions.arrows); // this sets the pointer of the option back to the global option. } // handle multiple input cases for color if (newOptions.color !== undefined && newOptions.color !== null) { var fromColor = isString(newOptions.color) ? { color: newOptions.color, highlight: newOptions.color, hover: newOptions.color, inherit: false, opacity: 1 } : newOptions.color; var toColor = parentOptions.color; // If passed, fill in values from default options - required in the case of no prototype bridging if (copyFromGlobals) { deepExtend(toColor, globalOptions.color, false, allowDeletion); } else { // Clear local properties - need to do it like this in order to retain prototype bridges for (var i in toColor) { if (Object.prototype.hasOwnProperty.call(toColor, i)) { delete toColor[i]; } } } if (isString(toColor)) { toColor.color = toColor; toColor.highlight = toColor; toColor.hover = toColor; toColor.inherit = false; if (fromColor.opacity === undefined) { toColor.opacity = 1.0; // set default } } else { var colorsDefined = false; if (fromColor.color !== undefined) { toColor.color = fromColor.color; colorsDefined = true; } if (fromColor.highlight !== undefined) { toColor.highlight = fromColor.highlight; colorsDefined = true; } if (fromColor.hover !== undefined) { toColor.hover = fromColor.hover; colorsDefined = true; } if (fromColor.inherit !== undefined) { toColor.inherit = fromColor.inherit; } if (fromColor.opacity !== undefined) { toColor.opacity = Math.min(1, Math.max(0, fromColor.opacity)); } if (colorsDefined === true) { toColor.inherit = false; } else { if (toColor.inherit === undefined) { toColor.inherit = "from"; // Set default } } } } else if (allowDeletion === true && newOptions.color === null) { parentOptions.color = bridgeObject(globalOptions.color); // set the object back to the global options } if (allowDeletion === true && newOptions.font === null) { parentOptions.font = bridgeObject(globalOptions.font); // set the object back to the global options } if (Object.prototype.hasOwnProperty.call(newOptions, "selfReferenceSize")) { console.warn("The selfReferenceSize property has been deprecated. Please use selfReference property instead. The selfReference can be set like thise selfReference:{size:30, angle:Math.PI / 4}"); parentOptions.selfReference.size = newOptions.selfReferenceSize; } } }]); return Edge; }(); /** * Handler for Edges */ var EdgesHandler = /*#__PURE__*/function () { /** * @param {object} body * @param {Array.<Image>} images * @param {Array.<Group>} groups */ function EdgesHandler(body, images, groups) { var _context, _this = this; _classCallCheck(this, EdgesHandler); this.body = body; this.images = images; this.groups = groups; // create the edge API in the body container this.body.functions.createEdge = _bindInstanceProperty$1(_context = this.create).call(_context, this); this.edgesListeners = { add: function add(event, params) { _this.add(params.items); }, update: function update(event, params) { _this.update(params.items); }, remove: function remove(event, params) { _this.remove(params.items); } }; this.options = {}; this.defaultOptions = { arrows: { to: { enabled: false, scaleFactor: 1, type: "arrow" }, // boolean / {arrowScaleFactor:1} / {enabled: false, arrowScaleFactor:1} middle: { enabled: false, scaleFactor: 1, type: "arrow" }, from: { enabled: false, scaleFactor: 1, type: "arrow" } }, endPointOffset: { from: 0, to: 0 }, arrowStrikethrough: true, color: { color: "#848484", highlight: "#848484", hover: "#848484", inherit: "from", opacity: 1.0 }, dashes: false, font: { color: "#343434", size: 14, // px face: "arial", background: "none", strokeWidth: 2, // px strokeColor: "#ffffff", align: "horizontal", multi: false, vadjust: 0, bold: { mod: "bold" }, boldital: { mod: "bold italic" }, ital: { mod: "italic" }, mono: { mod: "", size: 15, // px face: "courier new", vadjust: 2 } }, hidden: false, hoverWidth: 1.5, label: undefined, labelHighlightBold: true, length: undefined, physics: true, scaling: { min: 1, max: 15, label: { enabled: true, min: 14, max: 30, maxVisible: 30, drawThreshold: 5 }, customScalingFunction: function customScalingFunction(min, max, total, value) { if (max === min) { return 0.5; } else { var scale = 1 / (max - min); return Math.max(0, (value - min) * scale); } } }, selectionWidth: 1.5, selfReference: { size: 20, angle: Math.PI / 4, renderBehindTheNode: true }, shadow: { enabled: false, color: "rgba(0,0,0,0.5)", size: 10, x: 5, y: 5 }, background: { enabled: false, color: "rgba(111,111,111,1)", size: 10, dashes: false }, smooth: { enabled: true, type: "dynamic", forceDirection: "none", roundness: 0.5 }, title: undefined, width: 1, value: undefined }; deepExtend(this.options, this.defaultOptions); this.bindEventListeners(); } /** * Binds event listeners */ _createClass(EdgesHandler, [{ key: "bindEventListeners", value: function bindEventListeners() { var _this2 = this, _context2, _context3; // this allows external modules to force all dynamic curves to turn static. this.body.emitter.on("_forceDisableDynamicCurves", function (type) { var emit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; if (type === "dynamic") { type = "continuous"; } var dataChanged = false; for (var edgeId in _this2.body.edges) { if (Object.prototype.hasOwnProperty.call(_this2.body.edges, edgeId)) { var edge = _this2.body.edges[edgeId]; var edgeData = _this2.body.data.edges.get(edgeId); // only forcibly remove the smooth curve if the data has been set of the edge has the smooth curves defined. // this is because a change in the global would not affect these curves. if (edgeData != null) { var smoothOptions = edgeData.smooth; if (smoothOptions !== undefined) { if (smoothOptions.enabled === true && smoothOptions.type === "dynamic") { if (type === undefined) { edge.setOptions({ smooth: false }); } else { edge.setOptions({ smooth: { type: type } }); } dataChanged = true; } } } } } if (emit === true && dataChanged === true) { _this2.body.emitter.emit("_dataChanged"); } }); // this is called when options of EXISTING nodes or edges have changed. // // NOTE: Not true, called when options have NOT changed, for both existing as well as new nodes. // See update() for logic. // TODO: Verify and examine the consequences of this. It might still trigger when // non-option fields have changed, but then reconnecting edges is still useless. // Alternatively, it might also be called when edges are removed. // this.body.emitter.on("_dataUpdated", function () { _this2.reconnectEdges(); }); // refresh the edges. Used when reverting from hierarchical layout this.body.emitter.on("refreshEdges", _bindInstanceProperty$1(_context2 = this.refresh).call(_context2, this)); this.body.emitter.on("refresh", _bindInstanceProperty$1(_context3 = this.refresh).call(_context3, this)); this.body.emitter.on("destroy", function () { forEach$1(_this2.edgesListeners, function (callback, event) { if (_this2.body.data.edges) _this2.body.data.edges.off(event, callback); }); delete _this2.body.functions.createEdge; delete _this2.edgesListeners.add; delete _this2.edgesListeners.update; delete _this2.edgesListeners.remove; delete _this2.edgesListeners; }); } /** * * @param {object} options */ }, { key: "setOptions", value: function setOptions(options) { if (options !== undefined) { // use the parser from the Edge class to fill in all shorthand notations Edge.parseOptions(this.options, options, true, this.defaultOptions, true); // update smooth settings in all edges var dataChanged = false; if (options.smooth !== undefined) { for (var edgeId in this.body.edges) { if (Object.prototype.hasOwnProperty.call(this.body.edges, edgeId)) { dataChanged = this.body.edges[edgeId].updateEdgeType() || dataChanged; } } } // update fonts in all edges if (options.font !== undefined) { for (var _edgeId in this.body.edges) { if (Object.prototype.hasOwnProperty.call(this.body.edges, _edgeId)) { this.body.edges[_edgeId].updateLabelModule(); } } } // update the state of the variables if needed if (options.hidden !== undefined || options.physics !== undefined || dataChanged === true) { this.body.emitter.emit("_dataChanged"); } } } /** * Load edges by reading the data table * * @param {Array | DataSet | DataView} edges The data containing the edges. * @param {boolean} [doNotEmit=false] - Suppress data changed event. * @private */ }, { key: "setData", value: function setData(edges) { var _this3 = this; var doNotEmit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var oldEdgesData = this.body.data.edges; if ((0,vis_data_peer_esm_vis_data_js__WEBPACK_IMPORTED_MODULE_0__.isDataViewLike)("id", edges)) { this.body.data.edges = edges; } else if (_Array$isArray(edges)) { this.body.data.edges = new vis_data_peer_esm_vis_data_js__WEBPACK_IMPORTED_MODULE_0__.DataSet(); this.body.data.edges.add(edges); } else if (!edges) { this.body.data.edges = new vis_data_peer_esm_vis_data_js__WEBPACK_IMPORTED_MODULE_0__.DataSet(); } else { throw new TypeError("Array or DataSet expected"); } // TODO: is this null or undefined or false? if (oldEdgesData) { // unsubscribe from old dataset forEach$1(this.edgesListeners, function (callback, event) { oldEdgesData.off(event, callback); }); } // remove drawn edges this.body.edges = {}; // TODO: is this null or undefined or false? if (this.body.data.edges) { // subscribe to new dataset forEach$1(this.edgesListeners, function (callback, event) { _this3.body.data.edges.on(event, callback); }); // draw all new nodes var ids = this.body.data.edges.getIds(); this.add(ids, true); } this.body.emitter.emit("_adjustEdgesForHierarchicalLayout"); if (doNotEmit === false) { this.body.emitter.emit("_dataChanged"); } } /** * Add edges * * @param {number[] | string[]} ids * @param {boolean} [doNotEmit=false] * @private */ }, { key: "add", value: function add(ids) { var doNotEmit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var edges = this.body.edges; var edgesData = this.body.data.edges; for (var i = 0; i < ids.length; i++) { var id = ids[i]; var oldEdge = edges[id]; if (oldEdge) { oldEdge.disconnect(); } var data = edgesData.get(id, { showInternalIds: true }); edges[id] = this.create(data); } this.body.emitter.emit("_adjustEdgesForHierarchicalLayout"); if (doNotEmit === false) { this.body.emitter.emit("_dataChanged"); } } /** * Update existing edges, or create them when not yet existing * * @param {number[] | string[]} ids * @private */ }, { key: "update", value: function update(ids) { var edges = this.body.edges; var edgesData = this.body.data.edges; var dataChanged = false; for (var i = 0; i < ids.length; i++) { var id = ids[i]; var data = edgesData.get(id); var edge = edges[id]; if (edge !== undefined) { // update edge edge.disconnect(); dataChanged = edge.setOptions(data) || dataChanged; // if a support node is added, data can be changed. edge.connect(); } else { // create edge this.body.edges[id] = this.create(data); dataChanged = true; } } if (dataChanged === true) { this.body.emitter.emit("_adjustEdgesForHierarchicalLayout"); this.body.emitter.emit("_dataChanged"); } else { this.body.emitter.emit("_dataUpdated"); } } /** * Remove existing edges. Non existing ids will be ignored * * @param {number[] | string[]} ids * @param {boolean} [emit=true] * @private */ }, { key: "remove", value: function remove(ids) { var emit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; if (ids.length === 0) return; // early out var edges = this.body.edges; forEach$1(ids, function (id) { var edge = edges[id]; if (edge !== undefined) { edge.remove(); } }); if (emit) { this.body.emitter.emit("_dataChanged"); } } /** * Refreshes Edge Handler */ }, { key: "refresh", value: function refresh() { var _this4 = this; forEach$1(this.body.edges, function (edge, edgeId) { var data = _this4.body.data.edges.get(edgeId); if (data !== undefined) { edge.setOptions(data); } }); } /** * * @param {object} properties * @returns {Edge} */ }, { key: "create", value: function create(properties) { return new Edge(properties, this.body, this.images, this.options, this.defaultOptions); } /** * Reconnect all edges * * @private */ }, { key: "reconnectEdges", value: function reconnectEdges() { var id; var nodes = this.body.nodes; var edges = this.body.edges; for (id in nodes) { if (Object.prototype.hasOwnProperty.call(nodes, id)) { nodes[id].edges = []; } } for (id in edges) { if (Object.prototype.hasOwnProperty.call(edges, id)) { var edge = edges[id]; edge.from = null; edge.to = null; edge.connect(); } } } /** * * @param {Edge.id} edgeId * @returns {Array} */ }, { key: "getConnectedNodes", value: function getConnectedNodes(edgeId) { var nodeList = []; if (this.body.edges[edgeId] !== undefined) { var edge = this.body.edges[edgeId]; if (edge.fromId !== undefined) { nodeList.push(edge.fromId); } if (edge.toId !== undefined) { nodeList.push(edge.toId); } } return nodeList; } /** * There is no direct relation between the nodes and the edges DataSet, * so the right place to do call this is in the handler for event `_dataUpdated`. */ }, { key: "_updateState", value: function _updateState() { this._addMissingEdges(); this._removeInvalidEdges(); } /** * Scan for missing nodes and remove corresponding edges, if any. * * @private */ }, { key: "_removeInvalidEdges", value: function _removeInvalidEdges() { var _this5 = this; var edgesToDelete = []; forEach$1(this.body.edges, function (edge, id) { var toNode = _this5.body.nodes[edge.toId]; var fromNode = _this5.body.nodes[edge.fromId]; // Skip clustering edges here, let the Clustering module handle those if (toNode !== undefined && toNode.isCluster === true || fromNode !== undefined && fromNode.isCluster === true) { return; } if (toNode === undefined || fromNode === undefined) { edgesToDelete.push(id); } }); this.remove(edgesToDelete, false); } /** * add all edges from dataset that are not in the cached state * * @private */ }, { key: "_addMissingEdges", value: function _addMissingEdges() { var edgesData = this.body.data.edges; if (edgesData === undefined || edgesData === null) { return; // No edges DataSet yet; can happen on startup } var edges = this.body.edges; var addIds = []; _forEachInstanceProperty(edgesData).call(edgesData, function (edgeData, edgeId) { var edge = edges[edgeId]; if (edge === undefined) { addIds.push(edgeId); } }); this.add(addIds, true); } }]); return EdgesHandler; }(); /** * Barnes Hut Solver */ var BarnesHutSolver = /*#__PURE__*/function () { /** * @param {object} body * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody * @param {object} options */ function BarnesHutSolver(body, physicsBody, options) { _classCallCheck(this, BarnesHutSolver); this.body = body; this.physicsBody = physicsBody; this.barnesHutTree; this.setOptions(options); this._rng = Alea("BARNES HUT SOLVER"); // debug: show grid // this.body.emitter.on("afterDrawing", (ctx) => {this._debug(ctx,'#ff0000')}) } /** * * @param {object} options */ _createClass(BarnesHutSolver, [{ key: "setOptions", value: function setOptions(options) { this.options = options; this.thetaInversed = 1 / this.options.theta; // if 1 then min distance = 0.5, if 0.5 then min distance = 0.5 + 0.5*node.shape.radius this.overlapAvoidanceFactor = 1 - Math.max(0, Math.min(1, this.options.avoidOverlap)); } /** * This function calculates the forces the nodes apply on each other based on a gravitational model. * The Barnes Hut method is used to speed up this N-body simulation. * * @private */ }, { key: "solve", value: function solve() { if (this.options.gravitationalConstant !== 0 && this.physicsBody.physicsNodeIndices.length > 0) { var node; var nodes = this.body.nodes; var nodeIndices = this.physicsBody.physicsNodeIndices; var nodeCount = nodeIndices.length; // create the tree var barnesHutTree = this._formBarnesHutTree(nodes, nodeIndices); // for debugging this.barnesHutTree = barnesHutTree; // place the nodes one by one recursively for (var i = 0; i < nodeCount; i++) { node = nodes[nodeIndices[i]]; if (node.options.mass > 0) { // starting with root is irrelevant, it never passes the BarnesHutSolver condition this._getForceContributions(barnesHutTree.root, node); } } } } /** * @param {object} parentBranch * @param {Node} node * @private */ }, { key: "_getForceContributions", value: function _getForceContributions(parentBranch, node) { this._getForceContribution(parentBranch.children.NW, node); this._getForceContribution(parentBranch.children.NE, node); this._getForceContribution(parentBranch.children.SW, node); this._getForceContribution(parentBranch.children.SE, node); } /** * This function traverses the barnesHutTree. It checks when it can approximate distant nodes with their center of mass. * If a region contains a single node, we check if it is not itself, then we apply the force. * * @param {object} parentBranch * @param {Node} node * @private */ }, { key: "_getForceContribution", value: function _getForceContribution(parentBranch, node) { // we get no force contribution from an empty region if (parentBranch.childrenCount > 0) { // get the distance from the center of mass to the node. var dx = parentBranch.centerOfMass.x - node.x; var dy = parentBranch.centerOfMass.y - node.y; var distance = Math.sqrt(dx * dx + dy * dy); // BarnesHutSolver condition // original condition : s/d < theta = passed === d/s > 1/theta = passed // calcSize = 1/s --> d * 1/s > 1/theta = passed if (distance * parentBranch.calcSize > this.thetaInversed) { this._calculateForces(distance, dx, dy, node, parentBranch); } else { // Did not pass the condition, go into children if available if (parentBranch.childrenCount === 4) { this._getForceContributions(parentBranch, node); } else { // parentBranch must have only one node, if it was empty we wouldnt be here if (parentBranch.children.data.id != node.id) { // if it is not self this._calculateForces(distance, dx, dy, node, parentBranch); } } } } } /** * Calculate the forces based on the distance. * * @param {number} distance * @param {number} dx * @param {number} dy * @param {Node} node * @param {object} parentBranch * @private */ }, { key: "_calculateForces", value: function _calculateForces(distance, dx, dy, node, parentBranch) { if (distance === 0) { distance = 0.1; dx = distance; } if (this.overlapAvoidanceFactor < 1 && node.shape.radius) { distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius); } // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass / Math.pow(distance, 3); var fx = dx * gravityForce; var fy = dy * gravityForce; this.physicsBody.forces[node.id].x += fx; this.physicsBody.forces[node.id].y += fy; } /** * This function constructs the barnesHut tree recursively. It creates the root, splits it and starts placing the nodes. * * @param {Array.<Node>} nodes * @param {Array.<number>} nodeIndices * @returns {{root: {centerOfMass: {x: number, y: number}, mass: number, range: {minX: number, maxX: number, minY: number, maxY: number}, size: number, calcSize: number, children: {data: null}, maxWidth: number, level: number, childrenCount: number}}} BarnesHutTree * @private */ }, { key: "_formBarnesHutTree", value: function _formBarnesHutTree(nodes, nodeIndices) { var node; var nodeCount = nodeIndices.length; var minX = nodes[nodeIndices[0]].x; var minY = nodes[nodeIndices[0]].y; var maxX = nodes[nodeIndices[0]].x; var maxY = nodes[nodeIndices[0]].y; // get the range of the nodes for (var i = 1; i < nodeCount; i++) { var _node = nodes[nodeIndices[i]]; var x = _node.x; var y = _node.y; if (_node.options.mass > 0) { if (x < minX) { minX = x; } if (x > maxX) { maxX = x; } if (y < minY) { minY = y; } if (y > maxY) { maxY = y; } } } // make the range a square var sizeDiff = Math.abs(maxX - minX) - Math.abs(maxY - minY); // difference between X and Y if (sizeDiff > 0) { minY -= 0.5 * sizeDiff; maxY += 0.5 * sizeDiff; } // xSize > ySize else { minX += 0.5 * sizeDiff; maxX -= 0.5 * sizeDiff; } // xSize < ySize var minimumTreeSize = 1e-5; var rootSize = Math.max(minimumTreeSize, Math.abs(maxX - minX)); var halfRootSize = 0.5 * rootSize; var centerX = 0.5 * (minX + maxX), centerY = 0.5 * (minY + maxY); // construct the barnesHutTree var barnesHutTree = { root: { centerOfMass: { x: 0, y: 0 }, mass: 0, range: { minX: centerX - halfRootSize, maxX: centerX + halfRootSize, minY: centerY - halfRootSize, maxY: centerY + halfRootSize }, size: rootSize, calcSize: 1 / rootSize, children: { data: null }, maxWidth: 0, level: 0, childrenCount: 4 } }; this._splitBranch(barnesHutTree.root); // place the nodes one by one recursively for (var _i = 0; _i < nodeCount; _i++) { node = nodes[nodeIndices[_i]]; if (node.options.mass > 0) { this._placeInTree(barnesHutTree.root, node); } } // make global return barnesHutTree; } /** * this updates the mass of a branch. this is increased by adding a node. * * @param {object} parentBranch * @param {Node} node * @private */ }, { key: "_updateBranchMass", value: function _updateBranchMass(parentBranch, node) { var centerOfMass = parentBranch.centerOfMass; var totalMass = parentBranch.mass + node.options.mass; var totalMassInv = 1 / totalMass; centerOfMass.x = centerOfMass.x * parentBranch.mass + node.x * node.options.mass; centerOfMass.x *= totalMassInv; centerOfMass.y = centerOfMass.y * parentBranch.mass + node.y * node.options.mass; centerOfMass.y *= totalMassInv; parentBranch.mass = totalMass; var biggestSize = Math.max(Math.max(node.height, node.radius), node.width); parentBranch.maxWidth = parentBranch.maxWidth < biggestSize ? biggestSize : parentBranch.maxWidth; } /** * determine in which branch the node will be placed. * * @param {object} parentBranch * @param {Node} node * @param {boolean} skipMassUpdate * @private */ }, { key: "_placeInTree", value: function _placeInTree(parentBranch, node, skipMassUpdate) { if (skipMassUpdate != true || skipMassUpdate === undefined) { // update the mass of the branch. this._updateBranchMass(parentBranch, node); } var range = parentBranch.children.NW.range; var region; if (range.maxX > node.x) { // in NW or SW if (range.maxY > node.y) { region = "NW"; } else { region = "SW"; } } else { // in NE or SE if (range.maxY > node.y) { region = "NE"; } else { region = "SE"; } } this._placeInRegion(parentBranch, node, region); } /** * actually place the node in a region (or branch) * * @param {object} parentBranch * @param {Node} node * @param {'NW'| 'NE' | 'SW' | 'SE'} region * @private */ }, { key: "_placeInRegion", value: function _placeInRegion(parentBranch, node, region) { var children = parentBranch.children[region]; switch (children.childrenCount) { case 0: // place node here children.children.data = node; children.childrenCount = 1; this._updateBranchMass(children, node); break; case 1: // convert into children // if there are two nodes exactly overlapping (on init, on opening of cluster etc.) // we move one node a little bit and we do not put it in the tree. if (children.children.data.x === node.x && children.children.data.y === node.y) { node.x += this._rng(); node.y += this._rng(); } else { this._splitBranch(children); this._placeInTree(children, node); } break; case 4: // place in branch this._placeInTree(children, node); break; } } /** * this function splits a branch into 4 sub branches. If the branch contained a node, we place it in the subbranch * after the split is complete. * * @param {object} parentBranch * @private */ }, { key: "_splitBranch", value: function _splitBranch(parentBranch) { // if the branch is shaded with a node, replace the node in the new subset. var containedNode = null; if (parentBranch.childrenCount === 1) { containedNode = parentBranch.children.data; parentBranch.mass = 0; parentBranch.centerOfMass.x = 0; parentBranch.centerOfMass.y = 0; } parentBranch.childrenCount = 4; parentBranch.children.data = null; this._insertRegion(parentBranch, "NW"); this._insertRegion(parentBranch, "NE"); this._insertRegion(parentBranch, "SW"); this._insertRegion(parentBranch, "SE"); if (containedNode != null) { this._placeInTree(parentBranch, containedNode); } } /** * This function subdivides the region into four new segments. * Specifically, this inserts a single new segment. * It fills the children section of the parentBranch * * @param {object} parentBranch * @param {'NW'| 'NE' | 'SW' | 'SE'} region * @private */ }, { key: "_insertRegion", value: function _insertRegion(parentBranch, region) { var minX, maxX, minY, maxY; var childSize = 0.5 * parentBranch.size; switch (region) { case "NW": minX = parentBranch.range.minX; maxX = parentBranch.range.minX + childSize; minY = parentBranch.range.minY; maxY = parentBranch.range.minY + childSize; break; case "NE": minX = parentBranch.range.minX + childSize; maxX = parentBranch.range.maxX; minY = parentBranch.range.minY; maxY = parentBranch.range.minY + childSize; break; case "SW": minX = parentBranch.range.minX; maxX = parentBranch.range.minX + childSize; minY = parentBranch.range.minY + childSize; maxY = parentBranch.range.maxY; break; case "SE": minX = parentBranch.range.minX + childSize; maxX = parentBranch.range.maxX; minY = parentBranch.range.minY + childSize; maxY = parentBranch.range.maxY; break; } parentBranch.children[region] = { centerOfMass: { x: 0, y: 0 }, mass: 0, range: { minX: minX, maxX: maxX, minY: minY, maxY: maxY }, size: 0.5 * parentBranch.size, calcSize: 2 * parentBranch.calcSize, children: { data: null }, maxWidth: 0, level: parentBranch.level + 1, childrenCount: 0 }; } //--------------------------- DEBUGGING BELOW ---------------------------// /** * This function is for debugging purposed, it draws the tree. * * @param {CanvasRenderingContext2D} ctx * @param {string} color * @private */ }, { key: "_debug", value: function _debug(ctx, color) { if (this.barnesHutTree !== undefined) { ctx.lineWidth = 1; this._drawBranch(this.barnesHutTree.root, ctx, color); } } /** * This function is for debugging purposes. It draws the branches recursively. * * @param {object} branch * @param {CanvasRenderingContext2D} ctx * @param {string} color * @private */ }, { key: "_drawBranch", value: function _drawBranch(branch, ctx, color) { if (color === undefined) { color = "#FF0000"; } if (branch.childrenCount === 4) { this._drawBranch(branch.children.NW, ctx); this._drawBranch(branch.children.NE, ctx); this._drawBranch(branch.children.SE, ctx); this._drawBranch(branch.children.SW, ctx); } ctx.strokeStyle = color; ctx.beginPath(); ctx.moveTo(branch.range.minX, branch.range.minY); ctx.lineTo(branch.range.maxX, branch.range.minY); ctx.stroke(); ctx.beginPath(); ctx.moveTo(branch.range.maxX, branch.range.minY); ctx.lineTo(branch.range.maxX, branch.range.maxY); ctx.stroke(); ctx.beginPath(); ctx.moveTo(branch.range.maxX, branch.range.maxY); ctx.lineTo(branch.range.minX, branch.range.maxY); ctx.stroke(); ctx.beginPath(); ctx.moveTo(branch.range.minX, branch.range.maxY); ctx.lineTo(branch.range.minX, branch.range.minY); ctx.stroke(); /* if (branch.mass > 0) { ctx.circle(branch.centerOfMass.x, branch.centerOfMass.y, 3*branch.mass); ctx.stroke(); } */ } }]); return BarnesHutSolver; }(); /** * Repulsion Solver */ var RepulsionSolver = /*#__PURE__*/function () { /** * @param {object} body * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody * @param {object} options */ function RepulsionSolver(body, physicsBody, options) { _classCallCheck(this, RepulsionSolver); this._rng = Alea("REPULSION SOLVER"); this.body = body; this.physicsBody = physicsBody; this.setOptions(options); } /** * * @param {object} options */ _createClass(RepulsionSolver, [{ key: "setOptions", value: function setOptions(options) { this.options = options; } /** * Calculate the forces the nodes apply on each other based on a repulsion field. * This field is linearly approximated. * * @private */ }, { key: "solve", value: function solve() { var dx, dy, distance, fx, fy, repulsingForce, node1, node2; var nodes = this.body.nodes; var nodeIndices = this.physicsBody.physicsNodeIndices; var forces = this.physicsBody.forces; // repulsing forces between nodes var nodeDistance = this.options.nodeDistance; // approximation constants var a = -2 / 3 / nodeDistance; var b = 4 / 3; // we loop from i over all but the last entree in the array // j loops from i+1 to the last. This way we do not double count any of the indices, nor i === j for (var i = 0; i < nodeIndices.length - 1; i++) { node1 = nodes[nodeIndices[i]]; for (var j = i + 1; j < nodeIndices.length; j++) { node2 = nodes[nodeIndices[j]]; dx = node2.x - node1.x; dy = node2.y - node1.y; distance = Math.sqrt(dx * dx + dy * dy); // same condition as BarnesHutSolver, making sure nodes are never 100% overlapping. if (distance === 0) { distance = 0.1 * this._rng(); dx = distance; } if (distance < 2 * nodeDistance) { if (distance < 0.5 * nodeDistance) { repulsingForce = 1.0; } else { repulsingForce = a * distance + b; // linear approx of 1 / (1 + Math.exp((distance / nodeDistance - 1) * steepness)) } repulsingForce = repulsingForce / distance; fx = dx * repulsingForce; fy = dy * repulsingForce; forces[node1.id].x -= fx; forces[node1.id].y -= fy; forces[node2.id].x += fx; forces[node2.id].y += fy; } } } } }]); return RepulsionSolver; }(); /** * Hierarchical Repulsion Solver */ var HierarchicalRepulsionSolver = /*#__PURE__*/function () { /** * @param {object} body * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody * @param {object} options */ function HierarchicalRepulsionSolver(body, physicsBody, options) { _classCallCheck(this, HierarchicalRepulsionSolver); this.body = body; this.physicsBody = physicsBody; this.setOptions(options); } /** * * @param {object} options */ _createClass(HierarchicalRepulsionSolver, [{ key: "setOptions", value: function setOptions(options) { this.options = options; this.overlapAvoidanceFactor = Math.max(0, Math.min(1, this.options.avoidOverlap || 0)); } /** * Calculate the forces the nodes apply on each other based on a repulsion field. * This field is linearly approximated. * * @private */ }, { key: "solve", value: function solve() { var nodes = this.body.nodes; var nodeIndices = this.physicsBody.physicsNodeIndices; var forces = this.physicsBody.forces; // repulsing forces between nodes var nodeDistance = this.options.nodeDistance; // we loop from i over all but the last entree in the array // j loops from i+1 to the last. This way we do not double count any of the indices, nor i === j for (var i = 0; i < nodeIndices.length - 1; i++) { var node1 = nodes[nodeIndices[i]]; for (var j = i + 1; j < nodeIndices.length; j++) { var node2 = nodes[nodeIndices[j]]; // nodes only affect nodes on their level if (node1.level === node2.level) { var theseNodesDistance = nodeDistance + this.overlapAvoidanceFactor * ((node1.shape.radius || 0) / 2 + (node2.shape.radius || 0) / 2); var dx = node2.x - node1.x; var dy = node2.y - node1.y; var distance = Math.sqrt(dx * dx + dy * dy); var steepness = 0.05; var repulsingForce = void 0; if (distance < theseNodesDistance) { repulsingForce = -Math.pow(steepness * distance, 2) + Math.pow(steepness * theseNodesDistance, 2); } else { repulsingForce = 0; } // normalize force with if (distance !== 0) { repulsingForce = repulsingForce / distance; } var fx = dx * repulsingForce; var fy = dy * repulsingForce; forces[node1.id].x -= fx; forces[node1.id].y -= fy; forces[node2.id].x += fx; forces[node2.id].y += fy; } } } } }]); return HierarchicalRepulsionSolver; }(); /** * Spring Solver */ var SpringSolver = /*#__PURE__*/function () { /** * @param {object} body * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody * @param {object} options */ function SpringSolver(body, physicsBody, options) { _classCallCheck(this, SpringSolver); this.body = body; this.physicsBody = physicsBody; this.setOptions(options); } /** * * @param {object} options */ _createClass(SpringSolver, [{ key: "setOptions", value: function setOptions(options) { this.options = options; } /** * This function calculates the springforces on the nodes, accounting for the support nodes. * * @private */ }, { key: "solve", value: function solve() { var edgeLength, edge; var edgeIndices = this.physicsBody.physicsEdgeIndices; var edges = this.body.edges; var node1, node2, node3; // forces caused by the edges, modelled as springs for (var i = 0; i < edgeIndices.length; i++) { edge = edges[edgeIndices[i]]; if (edge.connected === true && edge.toId !== edge.fromId) { // only calculate forces if nodes are in the same sector if (this.body.nodes[edge.toId] !== undefined && this.body.nodes[edge.fromId] !== undefined) { if (edge.edgeType.via !== undefined) { edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length; node1 = edge.to; node2 = edge.edgeType.via; node3 = edge.from; this._calculateSpringForce(node1, node2, 0.5 * edgeLength); this._calculateSpringForce(node2, node3, 0.5 * edgeLength); } else { // the * 1.5 is here so the edge looks as large as a smooth edge. It does not initially because the smooth edges use // the support nodes which exert a repulsive force on the to and from nodes, making the edge appear larger. edgeLength = edge.options.length === undefined ? this.options.springLength * 1.5 : edge.options.length; this._calculateSpringForce(edge.from, edge.to, edgeLength); } } } } } /** * This is the code actually performing the calculation for the function above. * * @param {Node} node1 * @param {Node} node2 * @param {number} edgeLength * @private */ }, { key: "_calculateSpringForce", value: function _calculateSpringForce(node1, node2, edgeLength) { var dx = node1.x - node2.x; var dy = node1.y - node2.y; var distance = Math.max(Math.sqrt(dx * dx + dy * dy), 0.01); // the 1/distance is so the fx and fy can be calculated without sine or cosine. var springForce = this.options.springConstant * (edgeLength - distance) / distance; var fx = dx * springForce; var fy = dy * springForce; // handle the case where one node is not part of the physcis if (this.physicsBody.forces[node1.id] !== undefined) { this.physicsBody.forces[node1.id].x += fx; this.physicsBody.forces[node1.id].y += fy; } if (this.physicsBody.forces[node2.id] !== undefined) { this.physicsBody.forces[node2.id].x -= fx; this.physicsBody.forces[node2.id].y -= fy; } } }]); return SpringSolver; }(); /** * Hierarchical Spring Solver */ var HierarchicalSpringSolver = /*#__PURE__*/function () { /** * @param {object} body * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody * @param {object} options */ function HierarchicalSpringSolver(body, physicsBody, options) { _classCallCheck(this, HierarchicalSpringSolver); this.body = body; this.physicsBody = physicsBody; this.setOptions(options); } /** * * @param {object} options */ _createClass(HierarchicalSpringSolver, [{ key: "setOptions", value: function setOptions(options) { this.options = options; } /** * This function calculates the springforces on the nodes, accounting for the support nodes. * * @private */ }, { key: "solve", value: function solve() { var edgeLength, edge; var dx, dy, fx, fy, springForce, distance; var edges = this.body.edges; var factor = 0.5; var edgeIndices = this.physicsBody.physicsEdgeIndices; var nodeIndices = this.physicsBody.physicsNodeIndices; var forces = this.physicsBody.forces; // initialize the spring force counters for (var i = 0; i < nodeIndices.length; i++) { var nodeId = nodeIndices[i]; forces[nodeId].springFx = 0; forces[nodeId].springFy = 0; } // forces caused by the edges, modelled as springs for (var _i = 0; _i < edgeIndices.length; _i++) { edge = edges[edgeIndices[_i]]; if (edge.connected === true) { edgeLength = edge.options.length === undefined ? this.options.springLength : edge.options.length; dx = edge.from.x - edge.to.x; dy = edge.from.y - edge.to.y; distance = Math.sqrt(dx * dx + dy * dy); distance = distance === 0 ? 0.01 : distance; // the 1/distance is so the fx and fy can be calculated without sine or cosine. springForce = this.options.springConstant * (edgeLength - distance) / distance; fx = dx * springForce; fy = dy * springForce; if (edge.to.level != edge.from.level) { if (forces[edge.toId] !== undefined) { forces[edge.toId].springFx -= fx; forces[edge.toId].springFy -= fy; } if (forces[edge.fromId] !== undefined) { forces[edge.fromId].springFx += fx; forces[edge.fromId].springFy += fy; } } else { if (forces[edge.toId] !== undefined) { forces[edge.toId].x -= factor * fx; forces[edge.toId].y -= factor * fy; } if (forces[edge.fromId] !== undefined) { forces[edge.fromId].x += factor * fx; forces[edge.fromId].y += factor * fy; } } } } // normalize spring forces springForce = 1; var springFx, springFy; for (var _i2 = 0; _i2 < nodeIndices.length; _i2++) { var _nodeId = nodeIndices[_i2]; springFx = Math.min(springForce, Math.max(-springForce, forces[_nodeId].springFx)); springFy = Math.min(springForce, Math.max(-springForce, forces[_nodeId].springFy)); forces[_nodeId].x += springFx; forces[_nodeId].y += springFy; } // retain energy balance var totalFx = 0; var totalFy = 0; for (var _i3 = 0; _i3 < nodeIndices.length; _i3++) { var _nodeId2 = nodeIndices[_i3]; totalFx += forces[_nodeId2].x; totalFy += forces[_nodeId2].y; } var correctionFx = totalFx / nodeIndices.length; var correctionFy = totalFy / nodeIndices.length; for (var _i4 = 0; _i4 < nodeIndices.length; _i4++) { var _nodeId3 = nodeIndices[_i4]; forces[_nodeId3].x -= correctionFx; forces[_nodeId3].y -= correctionFy; } } }]); return HierarchicalSpringSolver; }(); /** * Central Gravity Solver */ var CentralGravitySolver = /*#__PURE__*/function () { /** * @param {object} body * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody * @param {object} options */ function CentralGravitySolver(body, physicsBody, options) { _classCallCheck(this, CentralGravitySolver); this.body = body; this.physicsBody = physicsBody; this.setOptions(options); } /** * * @param {object} options */ _createClass(CentralGravitySolver, [{ key: "setOptions", value: function setOptions(options) { this.options = options; } /** * Calculates forces for each node */ }, { key: "solve", value: function solve() { var dx, dy, distance, node; var nodes = this.body.nodes; var nodeIndices = this.physicsBody.physicsNodeIndices; var forces = this.physicsBody.forces; for (var i = 0; i < nodeIndices.length; i++) { var nodeId = nodeIndices[i]; node = nodes[nodeId]; dx = -node.x; dy = -node.y; distance = Math.sqrt(dx * dx + dy * dy); this._calculateForces(distance, dx, dy, forces, node); } } /** * Calculate the forces based on the distance. * * @param {number} distance * @param {number} dx * @param {number} dy * @param {Object<Node.id, vis.Node>} forces * @param {Node} node * @private */ }, { key: "_calculateForces", value: function _calculateForces(distance, dx, dy, forces, node) { var gravityForce = distance === 0 ? 0 : this.options.centralGravity / distance; forces[node.id].x = dx * gravityForce; forces[node.id].y = dy * gravityForce; } }]); return CentralGravitySolver; }(); function _createSuper$3(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$3(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$3() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * @augments BarnesHutSolver */ var ForceAtlas2BasedRepulsionSolver = /*#__PURE__*/function (_BarnesHutSolver) { _inherits(ForceAtlas2BasedRepulsionSolver, _BarnesHutSolver); var _super = _createSuper$3(ForceAtlas2BasedRepulsionSolver); /** * @param {object} body * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody * @param {object} options */ function ForceAtlas2BasedRepulsionSolver(body, physicsBody, options) { var _this; _classCallCheck(this, ForceAtlas2BasedRepulsionSolver); _this = _super.call(this, body, physicsBody, options); _this._rng = Alea("FORCE ATLAS 2 BASED REPULSION SOLVER"); return _this; } /** * Calculate the forces based on the distance. * * @param {number} distance * @param {number} dx * @param {number} dy * @param {Node} node * @param {object} parentBranch * @private */ _createClass(ForceAtlas2BasedRepulsionSolver, [{ key: "_calculateForces", value: function _calculateForces(distance, dx, dy, node, parentBranch) { if (distance === 0) { distance = 0.1 * this._rng(); dx = distance; } if (this.overlapAvoidanceFactor < 1 && node.shape.radius) { distance = Math.max(0.1 + this.overlapAvoidanceFactor * node.shape.radius, distance - node.shape.radius); } var degree = node.edges.length + 1; // the dividing by the distance cubed instead of squared allows us to get the fx and fy components without sines and cosines // it is shorthand for gravityforce with distance squared and fx = dx/distance * gravityForce var gravityForce = this.options.gravitationalConstant * parentBranch.mass * node.options.mass * degree / Math.pow(distance, 2); var fx = dx * gravityForce; var fy = dy * gravityForce; this.physicsBody.forces[node.id].x += fx; this.physicsBody.forces[node.id].y += fy; } }]); return ForceAtlas2BasedRepulsionSolver; }(BarnesHutSolver); function _createSuper$2(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$2(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$2() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * @augments CentralGravitySolver */ var ForceAtlas2BasedCentralGravitySolver = /*#__PURE__*/function (_CentralGravitySolver) { _inherits(ForceAtlas2BasedCentralGravitySolver, _CentralGravitySolver); var _super = _createSuper$2(ForceAtlas2BasedCentralGravitySolver); /** * @param {object} body * @param {{physicsNodeIndices: Array, physicsEdgeIndices: Array, forces: {}, velocities: {}}} physicsBody * @param {object} options */ function ForceAtlas2BasedCentralGravitySolver(body, physicsBody, options) { _classCallCheck(this, ForceAtlas2BasedCentralGravitySolver); return _super.call(this, body, physicsBody, options); } /** * Calculate the forces based on the distance. * * @param {number} distance * @param {number} dx * @param {number} dy * @param {Object<Node.id, Node>} forces * @param {Node} node * @private */ _createClass(ForceAtlas2BasedCentralGravitySolver, [{ key: "_calculateForces", value: function _calculateForces(distance, dx, dy, forces, node) { if (distance > 0) { var degree = node.edges.length + 1; var gravityForce = this.options.centralGravity * degree * node.options.mass; forces[node.id].x = dx * gravityForce; forces[node.id].y = dy * gravityForce; } } }]); return ForceAtlas2BasedCentralGravitySolver; }(CentralGravitySolver); /** * The physics engine */ var PhysicsEngine = /*#__PURE__*/function () { /** * @param {object} body */ function PhysicsEngine(body) { _classCallCheck(this, PhysicsEngine); this.body = body; this.physicsBody = { physicsNodeIndices: [], physicsEdgeIndices: [], forces: {}, velocities: {} }; this.physicsEnabled = true; this.simulationInterval = 1000 / 60; this.requiresTimeout = true; this.previousStates = {}; this.referenceState = {}; this.freezeCache = {}; this.renderTimer = undefined; // parameters for the adaptive timestep this.adaptiveTimestep = false; this.adaptiveTimestepEnabled = false; this.adaptiveCounter = 0; this.adaptiveInterval = 3; this.stabilized = false; this.startedStabilization = false; this.stabilizationIterations = 0; this.ready = false; // will be set to true if the stabilize // default options this.options = {}; this.defaultOptions = { enabled: true, barnesHut: { theta: 0.5, gravitationalConstant: -2000, centralGravity: 0.3, springLength: 95, springConstant: 0.04, damping: 0.09, avoidOverlap: 0 }, forceAtlas2Based: { theta: 0.5, gravitationalConstant: -50, centralGravity: 0.01, springConstant: 0.08, springLength: 100, damping: 0.4, avoidOverlap: 0 }, repulsion: { centralGravity: 0.2, springLength: 200, springConstant: 0.05, nodeDistance: 100, damping: 0.09, avoidOverlap: 0 }, hierarchicalRepulsion: { centralGravity: 0.0, springLength: 100, springConstant: 0.01, nodeDistance: 120, damping: 0.09 }, maxVelocity: 50, minVelocity: 0.75, // px/s solver: "barnesHut", stabilization: { enabled: true, iterations: 1000, // maximum number of iteration to stabilize updateInterval: 50, onlyDynamicEdges: false, fit: true }, timestep: 0.5, adaptiveTimestep: true, wind: { x: 0, y: 0 } }; _Object$assign(this.options, this.defaultOptions); this.timestep = 0.5; this.layoutFailed = false; this.bindEventListeners(); } /** * Binds event listeners */ _createClass(PhysicsEngine, [{ key: "bindEventListeners", value: function bindEventListeners() { var _this = this; this.body.emitter.on("initPhysics", function () { _this.initPhysics(); }); this.body.emitter.on("_layoutFailed", function () { _this.layoutFailed = true; }); this.body.emitter.on("resetPhysics", function () { _this.stopSimulation(); _this.ready = false; }); this.body.emitter.on("disablePhysics", function () { _this.physicsEnabled = false; _this.stopSimulation(); }); this.body.emitter.on("restorePhysics", function () { _this.setOptions(_this.options); if (_this.ready === true) { _this.startSimulation(); } }); this.body.emitter.on("startSimulation", function () { if (_this.ready === true) { _this.startSimulation(); } }); this.body.emitter.on("stopSimulation", function () { _this.stopSimulation(); }); this.body.emitter.on("destroy", function () { _this.stopSimulation(false); _this.body.emitter.off(); }); this.body.emitter.on("_dataChanged", function () { // Nodes and/or edges have been added or removed, update shortcut lists. _this.updatePhysicsData(); }); // debug: show forces // this.body.emitter.on("afterDrawing", (ctx) => {this._drawForces(ctx);}); } /** * set the physics options * * @param {object} options */ }, { key: "setOptions", value: function setOptions(options) { if (options !== undefined) { if (options === false) { this.options.enabled = false; this.physicsEnabled = false; this.stopSimulation(); } else if (options === true) { this.options.enabled = true; this.physicsEnabled = true; this.startSimulation(); } else { this.physicsEnabled = true; selectiveNotDeepExtend(["stabilization"], this.options, options); mergeOptions(this.options, options, "stabilization"); if (options.enabled === undefined) { this.options.enabled = true; } if (this.options.enabled === false) { this.physicsEnabled = false; this.stopSimulation(); } var wind = this.options.wind; if (wind) { if (typeof wind.x !== "number" || _Number$isNaN(wind.x)) { wind.x = 0; } if (typeof wind.y !== "number" || _Number$isNaN(wind.y)) { wind.y = 0; } } // set the timestep this.timestep = this.options.timestep; } } this.init(); } /** * configure the engine. */ }, { key: "init", value: function init() { var options; if (this.options.solver === "forceAtlas2Based") { options = this.options.forceAtlas2Based; this.nodesSolver = new ForceAtlas2BasedRepulsionSolver(this.body, this.physicsBody, options); this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); this.gravitySolver = new ForceAtlas2BasedCentralGravitySolver(this.body, this.physicsBody, options); } else if (this.options.solver === "repulsion") { options = this.options.repulsion; this.nodesSolver = new RepulsionSolver(this.body, this.physicsBody, options); this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); } else if (this.options.solver === "hierarchicalRepulsion") { options = this.options.hierarchicalRepulsion; this.nodesSolver = new HierarchicalRepulsionSolver(this.body, this.physicsBody, options); this.edgesSolver = new HierarchicalSpringSolver(this.body, this.physicsBody, options); this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); } else { // barnesHut options = this.options.barnesHut; this.nodesSolver = new BarnesHutSolver(this.body, this.physicsBody, options); this.edgesSolver = new SpringSolver(this.body, this.physicsBody, options); this.gravitySolver = new CentralGravitySolver(this.body, this.physicsBody, options); } this.modelOptions = options; } /** * initialize the engine */ }, { key: "initPhysics", value: function initPhysics() { if (this.physicsEnabled === true && this.options.enabled === true) { if (this.options.stabilization.enabled === true) { this.stabilize(); } else { this.stabilized = false; this.ready = true; this.body.emitter.emit("fit", {}, this.layoutFailed); // if the layout failed, we use the approximation for the zoom this.startSimulation(); } } else { this.ready = true; this.body.emitter.emit("fit"); } } /** * Start the simulation */ }, { key: "startSimulation", value: function startSimulation() { if (this.physicsEnabled === true && this.options.enabled === true) { this.stabilized = false; // when visible, adaptivity is disabled. this.adaptiveTimestep = false; // this sets the width of all nodes initially which could be required for the avoidOverlap this.body.emitter.emit("_resizeNodes"); if (this.viewFunction === undefined) { var _context; this.viewFunction = _bindInstanceProperty$1(_context = this.simulationStep).call(_context, this); this.body.emitter.on("initRedraw", this.viewFunction); this.body.emitter.emit("_startRendering"); } } else { this.body.emitter.emit("_redraw"); } } /** * Stop the simulation, force stabilization. * * @param {boolean} [emit=true] */ }, { key: "stopSimulation", value: function stopSimulation() { var emit = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; this.stabilized = true; if (emit === true) { this._emitStabilized(); } if (this.viewFunction !== undefined) { this.body.emitter.off("initRedraw", this.viewFunction); this.viewFunction = undefined; if (emit === true) { this.body.emitter.emit("_stopRendering"); } } } /** * The viewFunction inserts this step into each render loop. It calls the physics tick and handles the cleanup at stabilized. * */ }, { key: "simulationStep", value: function simulationStep() { // check if the physics have settled var startTime = _Date$now(); this.physicsTick(); var physicsTime = _Date$now() - startTime; // run double speed if it is a little graph if ((physicsTime < 0.4 * this.simulationInterval || this.runDoubleSpeed === true) && this.stabilized === false) { this.physicsTick(); // this makes sure there is no jitter. The decision is taken once to run it at double speed. this.runDoubleSpeed = true; } if (this.stabilized === true) { this.stopSimulation(); } } /** * trigger the stabilized event. * * @param {number} [amountOfIterations=this.stabilizationIterations] * @private */ }, { key: "_emitStabilized", value: function _emitStabilized() { var _this2 = this; var amountOfIterations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.stabilizationIterations; if (this.stabilizationIterations > 1 || this.startedStabilization === true) { _setTimeout(function () { _this2.body.emitter.emit("stabilized", { iterations: amountOfIterations }); _this2.startedStabilization = false; _this2.stabilizationIterations = 0; }, 0); } } /** * Calculate the forces for one physics iteration and move the nodes. * * @private */ }, { key: "physicsStep", value: function physicsStep() { this.gravitySolver.solve(); this.nodesSolver.solve(); this.edgesSolver.solve(); this.moveNodes(); } /** * Make dynamic adjustments to the timestep, based on current state. * * Helper function for physicsTick(). * * @private */ }, { key: "adjustTimeStep", value: function adjustTimeStep() { var factor = 1.2; // Factor for increasing the timestep on success. // we compare the two steps. if it is acceptable we double the step. if (this._evaluateStepQuality() === true) { this.timestep = factor * this.timestep; } else { // if not, we decrease the step to a minimum of the options timestep. // if the decreased timestep is smaller than the options step, we do not reset the counter // we assume that the options timestep is stable enough. if (this.timestep / factor < this.options.timestep) { this.timestep = this.options.timestep; } else { // if the timestep was larger than 2 times the option one we check the adaptivity again to ensure // that large instabilities do not form. this.adaptiveCounter = -1; // check again next iteration this.timestep = Math.max(this.options.timestep, this.timestep / factor); } } } /** * A single simulation step (or 'tick') in the physics simulation * * @private */ }, { key: "physicsTick", value: function physicsTick() { this._startStabilizing(); // this ensures that there is no start event when the network is already stable. if (this.stabilized === true) return; // adaptivity means the timestep adapts to the situation, only applicable for stabilization if (this.adaptiveTimestep === true && this.adaptiveTimestepEnabled === true) { // timestep remains stable for "interval" iterations. var doAdaptive = this.adaptiveCounter % this.adaptiveInterval === 0; if (doAdaptive) { // first the big step and revert. this.timestep = 2 * this.timestep; this.physicsStep(); this.revert(); // saves the reference state // now the normal step. Since this is the last step, it is the more stable one and we will take this. this.timestep = 0.5 * this.timestep; // since it's half the step, we do it twice. this.physicsStep(); this.physicsStep(); this.adjustTimeStep(); } else { this.physicsStep(); // normal step, keeping timestep constant } this.adaptiveCounter += 1; } else { // case for the static timestep, we reset it to the one in options and take a normal step. this.timestep = this.options.timestep; this.physicsStep(); } if (this.stabilized === true) this.revert(); this.stabilizationIterations++; } /** * Nodes and edges can have the physics toggles on or off. A collection of indices is created here so we can skip the check all the time. * * @private */ }, { key: "updatePhysicsData", value: function updatePhysicsData() { this.physicsBody.forces = {}; this.physicsBody.physicsNodeIndices = []; this.physicsBody.physicsEdgeIndices = []; var nodes = this.body.nodes; var edges = this.body.edges; // get node indices for physics for (var nodeId in nodes) { if (Object.prototype.hasOwnProperty.call(nodes, nodeId)) { if (nodes[nodeId].options.physics === true) { this.physicsBody.physicsNodeIndices.push(nodes[nodeId].id); } } } // get edge indices for physics for (var edgeId in edges) { if (Object.prototype.hasOwnProperty.call(edges, edgeId)) { if (edges[edgeId].options.physics === true) { this.physicsBody.physicsEdgeIndices.push(edges[edgeId].id); } } } // get the velocity and the forces vector for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { var _nodeId = this.physicsBody.physicsNodeIndices[i]; this.physicsBody.forces[_nodeId] = { x: 0, y: 0 }; // forces can be reset because they are recalculated. Velocities have to persist. if (this.physicsBody.velocities[_nodeId] === undefined) { this.physicsBody.velocities[_nodeId] = { x: 0, y: 0 }; } } // clean deleted nodes from the velocity vector for (var _nodeId2 in this.physicsBody.velocities) { if (nodes[_nodeId2] === undefined) { delete this.physicsBody.velocities[_nodeId2]; } } } /** * Revert the simulation one step. This is done so after stabilization, every new start of the simulation will also say stabilized. */ }, { key: "revert", value: function revert() { var nodeIds = _Object$keys(this.previousStates); var nodes = this.body.nodes; var velocities = this.physicsBody.velocities; this.referenceState = {}; for (var i = 0; i < nodeIds.length; i++) { var nodeId = nodeIds[i]; if (nodes[nodeId] !== undefined) { if (nodes[nodeId].options.physics === true) { this.referenceState[nodeId] = { positions: { x: nodes[nodeId].x, y: nodes[nodeId].y } }; velocities[nodeId].x = this.previousStates[nodeId].vx; velocities[nodeId].y = this.previousStates[nodeId].vy; nodes[nodeId].x = this.previousStates[nodeId].x; nodes[nodeId].y = this.previousStates[nodeId].y; } } else { delete this.previousStates[nodeId]; } } } /** * This compares the reference state to the current state * * @returns {boolean} * @private */ }, { key: "_evaluateStepQuality", value: function _evaluateStepQuality() { var dx, dy, dpos; var nodes = this.body.nodes; var reference = this.referenceState; var posThreshold = 0.3; for (var nodeId in this.referenceState) { if (Object.prototype.hasOwnProperty.call(this.referenceState, nodeId) && nodes[nodeId] !== undefined) { dx = nodes[nodeId].x - reference[nodeId].positions.x; dy = nodes[nodeId].y - reference[nodeId].positions.y; dpos = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); if (dpos > posThreshold) { return false; } } } return true; } /** * move the nodes one timestep and check if they are stabilized */ }, { key: "moveNodes", value: function moveNodes() { var nodeIndices = this.physicsBody.physicsNodeIndices; var maxNodeVelocity = 0; var averageNodeVelocity = 0; // the velocity threshold (energy in the system) for the adaptivity toggle var velocityAdaptiveThreshold = 5; for (var i = 0; i < nodeIndices.length; i++) { var nodeId = nodeIndices[i]; var nodeVelocity = this._performStep(nodeId); // stabilized is true if stabilized is true and velocity is smaller than vmin --> all nodes must be stabilized maxNodeVelocity = Math.max(maxNodeVelocity, nodeVelocity); averageNodeVelocity += nodeVelocity; } // evaluating the stabilized and adaptiveTimestepEnabled conditions this.adaptiveTimestepEnabled = averageNodeVelocity / nodeIndices.length < velocityAdaptiveThreshold; this.stabilized = maxNodeVelocity < this.options.minVelocity; } /** * Calculate new velocity for a coordinate direction * * @param {number} v velocity for current coordinate * @param {number} f regular force for current coordinate * @param {number} m mass of current node * @returns {number} new velocity for current coordinate * @private */ }, { key: "calculateComponentVelocity", value: function calculateComponentVelocity(v, f, m) { var df = this.modelOptions.damping * v; // damping force var a = (f - df) / m; // acceleration v += a * this.timestep; // Put a limit on the velocities if it is really high var maxV = this.options.maxVelocity || 1e9; if (Math.abs(v) > maxV) { v = v > 0 ? maxV : -maxV; } return v; } /** * Perform the actual step * * @param {Node.id} nodeId * @returns {number} the new velocity of given node * @private */ }, { key: "_performStep", value: function _performStep(nodeId) { var node = this.body.nodes[nodeId]; var force = this.physicsBody.forces[nodeId]; if (this.options.wind) { force.x += this.options.wind.x; force.y += this.options.wind.y; } var velocity = this.physicsBody.velocities[nodeId]; // store the state so we can revert this.previousStates[nodeId] = { x: node.x, y: node.y, vx: velocity.x, vy: velocity.y }; if (node.options.fixed.x === false) { velocity.x = this.calculateComponentVelocity(velocity.x, force.x, node.options.mass); node.x += velocity.x * this.timestep; } else { force.x = 0; velocity.x = 0; } if (node.options.fixed.y === false) { velocity.y = this.calculateComponentVelocity(velocity.y, force.y, node.options.mass); node.y += velocity.y * this.timestep; } else { force.y = 0; velocity.y = 0; } var totalVelocity = Math.sqrt(Math.pow(velocity.x, 2) + Math.pow(velocity.y, 2)); return totalVelocity; } /** * When initializing and stabilizing, we can freeze nodes with a predefined position. * This greatly speeds up stabilization because only the supportnodes for the smoothCurves have to settle. * * @private */ }, { key: "_freezeNodes", value: function _freezeNodes() { var nodes = this.body.nodes; for (var id in nodes) { if (Object.prototype.hasOwnProperty.call(nodes, id)) { if (nodes[id].x && nodes[id].y) { var fixed = nodes[id].options.fixed; this.freezeCache[id] = { x: fixed.x, y: fixed.y }; fixed.x = true; fixed.y = true; } } } } /** * Unfreezes the nodes that have been frozen by _freezeDefinedNodes. * * @private */ }, { key: "_restoreFrozenNodes", value: function _restoreFrozenNodes() { var nodes = this.body.nodes; for (var id in nodes) { if (Object.prototype.hasOwnProperty.call(nodes, id)) { if (this.freezeCache[id] !== undefined) { nodes[id].options.fixed.x = this.freezeCache[id].x; nodes[id].options.fixed.y = this.freezeCache[id].y; } } } this.freezeCache = {}; } /** * Find a stable position for all nodes * * @param {number} [iterations=this.options.stabilization.iterations] */ }, { key: "stabilize", value: function stabilize() { var _this3 = this; var iterations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.stabilization.iterations; if (typeof iterations !== "number") { iterations = this.options.stabilization.iterations; console.error("The stabilize method needs a numeric amount of iterations. Switching to default: ", iterations); } if (this.physicsBody.physicsNodeIndices.length === 0) { this.ready = true; return; } // enable adaptive timesteps this.adaptiveTimestep = this.options.adaptiveTimestep; // this sets the width of all nodes initially which could be required for the avoidOverlap this.body.emitter.emit("_resizeNodes"); this.stopSimulation(); // stop the render loop this.stabilized = false; // block redraw requests this.body.emitter.emit("_blockRedraw"); this.targetIterations = iterations; // start the stabilization if (this.options.stabilization.onlyDynamicEdges === true) { this._freezeNodes(); } this.stabilizationIterations = 0; _setTimeout(function () { return _this3._stabilizationBatch(); }, 0); } /** * If not already stabilizing, start it and emit a start event. * * @returns {boolean} true if stabilization started with this call * @private */ }, { key: "_startStabilizing", value: function _startStabilizing() { if (this.startedStabilization === true) return false; this.body.emitter.emit("startStabilizing"); this.startedStabilization = true; return true; } /** * One batch of stabilization * * @private */ }, { key: "_stabilizationBatch", value: function _stabilizationBatch() { var _this4 = this; var running = function running() { return _this4.stabilized === false && _this4.stabilizationIterations < _this4.targetIterations; }; var sendProgress = function sendProgress() { _this4.body.emitter.emit("stabilizationProgress", { iterations: _this4.stabilizationIterations, total: _this4.targetIterations }); }; if (this._startStabilizing()) { sendProgress(); // Ensure that there is at least one start event. } var count = 0; while (running() && count < this.options.stabilization.updateInterval) { this.physicsTick(); count++; } sendProgress(); if (running()) { var _context2; _setTimeout(_bindInstanceProperty$1(_context2 = this._stabilizationBatch).call(_context2, this), 0); } else { this._finalizeStabilization(); } } /** * Wrap up the stabilization, fit and emit the events. * * @private */ }, { key: "_finalizeStabilization", value: function _finalizeStabilization() { this.body.emitter.emit("_allowRedraw"); if (this.options.stabilization.fit === true) { this.body.emitter.emit("fit"); } if (this.options.stabilization.onlyDynamicEdges === true) { this._restoreFrozenNodes(); } this.body.emitter.emit("stabilizationIterationsDone"); this.body.emitter.emit("_requestRedraw"); if (this.stabilized === true) { this._emitStabilized(); } else { this.startSimulation(); } this.ready = true; } //--------------------------- DEBUGGING BELOW ---------------------------// /** * Debug function that display arrows for the forces currently active in the network. * * Use this when debugging only. * * @param {CanvasRenderingContext2D} ctx * @private */ }, { key: "_drawForces", value: function _drawForces(ctx) { for (var i = 0; i < this.physicsBody.physicsNodeIndices.length; i++) { var index = this.physicsBody.physicsNodeIndices[i]; var node = this.body.nodes[index]; var force = this.physicsBody.forces[index]; var factor = 20; var colorFactor = 0.03; var forceSize = Math.sqrt(Math.pow(force.x, 2) + Math.pow(force.x, 2)); var size = Math.min(Math.max(5, forceSize), 15); var arrowSize = 3 * size; var color = HSVToHex((180 - Math.min(1, Math.max(0, colorFactor * forceSize)) * 180) / 360, 1, 1); var point = { x: node.x + factor * force.x, y: node.y + factor * force.y }; ctx.lineWidth = size; ctx.strokeStyle = color; ctx.beginPath(); ctx.moveTo(node.x, node.y); ctx.lineTo(point.x, point.y); ctx.stroke(); var angle = Math.atan2(force.y, force.x); ctx.fillStyle = color; EndPoints.draw(ctx, { type: "arrow", point: point, angle: angle, length: arrowSize }); _fillInstanceProperty(ctx).call(ctx); } } }]); return PhysicsEngine; }(); // Unique ID creation requires a high quality random # generator. In the browser we therefore // require the crypto API and do not support built-in fallback to lower quality random number // generators (like Math.random()). let getRandomValues; const rnds8 = new Uint8Array(16); function rng() { // lazy load so that environments that need to polyfill have a chance to do so if (!getRandomValues) { // getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto); if (!getRandomValues) { throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported'); } } return getRandomValues(rnds8); } /** * Convert array of 16 byte values to UUID string format of the form: * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX */ const byteToHex = []; for (let i = 0; i < 256; ++i) { byteToHex.push((i + 0x100).toString(16).slice(1)); } function unsafeStringify(arr, offset = 0) { // Note: Be careful editing this code! It's been tuned for performance // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); } const randomUUID = typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto); var native = { randomUUID }; function v4(options, buf, offset) { if (native.randomUUID && !buf && !options) { return native.randomUUID(); } options = options || {}; const rnds = options.random || (options.rng || rng)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` rnds[6] = rnds[6] & 0x0f | 0x40; rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided if (buf) { offset = offset || 0; for (let i = 0; i < 16; ++i) { buf[offset + i] = rnds[i]; } return buf; } return unsafeStringify(rnds); } /** * Utility Class */ var NetworkUtil = /*#__PURE__*/function () { /** * @ignore */ function NetworkUtil() { _classCallCheck(this, NetworkUtil); } /** * Find the center position of the network considering the bounding boxes * * @param {Array.<Node>} allNodes * @param {Array.<Node>} [specificNodes=[]] * @returns {{minX: number, maxX: number, minY: number, maxY: number}} * @static */ _createClass(NetworkUtil, null, [{ key: "getRange", value: function getRange(allNodes) { var specificNodes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; if (specificNodes.length > 0) { for (var i = 0; i < specificNodes.length; i++) { node = allNodes[specificNodes[i]]; if (minX > node.shape.boundingBox.left) { minX = node.shape.boundingBox.left; } if (maxX < node.shape.boundingBox.right) { maxX = node.shape.boundingBox.right; } if (minY > node.shape.boundingBox.top) { minY = node.shape.boundingBox.top; } // top is negative, bottom is positive if (maxY < node.shape.boundingBox.bottom) { maxY = node.shape.boundingBox.bottom; } // top is negative, bottom is positive } } if (minX === 1e9 && maxX === -1e9 && minY === 1e9 && maxY === -1e9) { minY = 0, maxY = 0, minX = 0, maxX = 0; } return { minX: minX, maxX: maxX, minY: minY, maxY: maxY }; } /** * Find the center position of the network * * @param {Array.<Node>} allNodes * @param {Array.<Node>} [specificNodes=[]] * @returns {{minX: number, maxX: number, minY: number, maxY: number}} * @static */ }, { key: "getRangeCore", value: function getRangeCore(allNodes) { var specificNodes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; var minY = 1e9, maxY = -1e9, minX = 1e9, maxX = -1e9, node; if (specificNodes.length > 0) { for (var i = 0; i < specificNodes.length; i++) { node = allNodes[specificNodes[i]]; if (minX > node.x) { minX = node.x; } if (maxX < node.x) { maxX = node.x; } if (minY > node.y) { minY = node.y; } // top is negative, bottom is positive if (maxY < node.y) { maxY = node.y; } // top is negative, bottom is positive } } if (minX === 1e9 && maxX === -1e9 && minY === 1e9 && maxY === -1e9) { minY = 0, maxY = 0, minX = 0, maxX = 0; } return { minX: minX, maxX: maxX, minY: minY, maxY: maxY }; } /** * @param {object} range = {minX: minX, maxX: maxX, minY: minY, maxY: maxY}; * @returns {{x: number, y: number}} * @static */ }, { key: "findCenter", value: function findCenter(range) { return { x: 0.5 * (range.maxX + range.minX), y: 0.5 * (range.maxY + range.minY) }; } /** * This returns a clone of the options or options of the edge or node to be used for construction of new edges or check functions for new nodes. * * @param {vis.Item} item * @param {'node'|undefined} type * @returns {{}} * @static */ }, { key: "cloneOptions", value: function cloneOptions(item, type) { var clonedOptions = {}; if (type === undefined || type === "node") { deepExtend(clonedOptions, item.options, true); clonedOptions.x = item.x; clonedOptions.y = item.y; clonedOptions.amountOfConnections = item.edges.length; } else { deepExtend(clonedOptions, item.options, true); } return clonedOptions; } }]); return NetworkUtil; }(); function _createSuper$1(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$1(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct$1() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * A Cluster is a special Node that allows a group of Nodes positioned closely together * to be represented by a single Cluster Node. * * @augments Node */ var Cluster = /*#__PURE__*/function (_Node) { _inherits(Cluster, _Node); var _super = _createSuper$1(Cluster); /** * @param {object} options * @param {object} body * @param {Array.<HTMLImageElement>}imagelist * @param {Array} grouplist * @param {object} globalOptions * @param {object} defaultOptions Global default options for nodes */ function Cluster(options, body, imagelist, grouplist, globalOptions, defaultOptions) { var _this; _classCallCheck(this, Cluster); _this = _super.call(this, options, body, imagelist, grouplist, globalOptions, defaultOptions); _this.isCluster = true; _this.containedNodes = {}; _this.containedEdges = {}; return _this; } /** * Transfer child cluster data to current and disconnect the child cluster. * * Please consult the header comment in 'Clustering.js' for the fields set here. * * @param {string|number} childClusterId id of child cluster to open */ _createClass(Cluster, [{ key: "_openChildCluster", value: function _openChildCluster(childClusterId) { var _this2 = this; var childCluster = this.body.nodes[childClusterId]; if (this.containedNodes[childClusterId] === undefined) { throw new Error("node with id: " + childClusterId + " not in current cluster"); } if (!childCluster.isCluster) { throw new Error("node with id: " + childClusterId + " is not a cluster"); } // Disconnect child cluster from current cluster delete this.containedNodes[childClusterId]; forEach$1(childCluster.edges, function (edge) { delete _this2.containedEdges[edge.id]; }); // Transfer nodes and edges forEach$1(childCluster.containedNodes, function (node, nodeId) { _this2.containedNodes[nodeId] = node; }); childCluster.containedNodes = {}; forEach$1(childCluster.containedEdges, function (edge, edgeId) { _this2.containedEdges[edgeId] = edge; }); childCluster.containedEdges = {}; // Transfer edges within cluster edges which are clustered forEach$1(childCluster.edges, function (clusterEdge) { forEach$1(_this2.edges, function (parentClusterEdge) { var _context, _context2; // Assumption: a clustered edge can only be present in a single clustering edge // Not tested here var index = _indexOfInstanceProperty(_context = parentClusterEdge.clusteringEdgeReplacingIds).call(_context, clusterEdge.id); if (index === -1) return; forEach$1(clusterEdge.clusteringEdgeReplacingIds, function (srcId) { parentClusterEdge.clusteringEdgeReplacingIds.push(srcId); // Maintain correct bookkeeping for transferred edge _this2.body.edges[srcId].edgeReplacedById = parentClusterEdge.id; }); // Remove cluster edge from parent cluster edge _spliceInstanceProperty(_context2 = parentClusterEdge.clusteringEdgeReplacingIds).call(_context2, index, 1); }); }); childCluster.edges = []; } }]); return Cluster; }(Node); /** * The clustering engine */ var ClusterEngine = /*#__PURE__*/function () { /** * @param {object} body */ function ClusterEngine(body) { var _this = this; _classCallCheck(this, ClusterEngine); this.body = body; this.clusteredNodes = {}; // key: node id, value: { clusterId: <id of cluster>, node: <node instance>} this.clusteredEdges = {}; // key: edge id, value: restore information for given edge this.options = {}; this.defaultOptions = {}; _Object$assign(this.options, this.defaultOptions); this.body.emitter.on("_resetData", function () { _this.clusteredNodes = {}; _this.clusteredEdges = {}; }); } /** * * @param {number} hubsize * @param {object} options */ _createClass(ClusterEngine, [{ key: "clusterByHubsize", value: function clusterByHubsize(hubsize, options) { if (hubsize === undefined) { hubsize = this._getHubSize(); } else if (_typeof(hubsize) === "object") { options = this._checkOptions(hubsize); hubsize = this._getHubSize(); } var nodesToCluster = []; for (var i = 0; i < this.body.nodeIndices.length; i++) { var node = this.body.nodes[this.body.nodeIndices[i]]; if (node.edges.length >= hubsize) { nodesToCluster.push(node.id); } } for (var _i = 0; _i < nodesToCluster.length; _i++) { this.clusterByConnection(nodesToCluster[_i], options, true); } this.body.emitter.emit("_dataChanged"); } /** * loop over all nodes, check if they adhere to the condition and cluster if needed. * * @param {object} options * @param {boolean} [refreshData=true] */ }, { key: "cluster", value: function cluster() { var _this2 = this; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var refreshData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; if (options.joinCondition === undefined) { throw new Error("Cannot call clusterByNodeData without a joinCondition function in the options."); } // check if the options object is fine, append if needed options = this._checkOptions(options); var childNodesObj = {}; var childEdgesObj = {}; // collect the nodes that will be in the cluster forEach$1(this.body.nodes, function (node, nodeId) { if (node.options && options.joinCondition(node.options) === true) { childNodesObj[nodeId] = node; // collect the edges that will be in the cluster forEach$1(node.edges, function (edge) { if (_this2.clusteredEdges[edge.id] === undefined) { childEdgesObj[edge.id] = edge; } }); } }); this._cluster(childNodesObj, childEdgesObj, options, refreshData); } /** * Cluster all nodes in the network that have only X edges * * @param {number} edgeCount * @param {object} options * @param {boolean} [refreshData=true] */ }, { key: "clusterByEdgeCount", value: function clusterByEdgeCount(edgeCount, options) { var _this3 = this; var refreshData = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; options = this._checkOptions(options); var clusters = []; var usedNodes = {}; var edge, edges, relevantEdgeCount; // collect the nodes that will be in the cluster var _loop = function _loop() { var childNodesObj = {}; var childEdgesObj = {}; var nodeId = _this3.body.nodeIndices[i]; var node = _this3.body.nodes[nodeId]; // if this node is already used in another cluster this session, we do not have to re-evaluate it. if (usedNodes[nodeId] === undefined) { relevantEdgeCount = 0; edges = []; for (var j = 0; j < node.edges.length; j++) { edge = node.edges[j]; if (_this3.clusteredEdges[edge.id] === undefined) { if (edge.toId !== edge.fromId) { relevantEdgeCount++; } edges.push(edge); } } // this node qualifies, we collect its neighbours to start the clustering process. if (relevantEdgeCount === edgeCount) { var checkJoinCondition = function checkJoinCondition(node) { if (options.joinCondition === undefined || options.joinCondition === null) { return true; } var clonedOptions = NetworkUtil.cloneOptions(node); return options.joinCondition(clonedOptions); }; var gatheringSuccessful = true; for (var _j = 0; _j < edges.length; _j++) { edge = edges[_j]; var childNodeId = _this3._getConnectedId(edge, nodeId); // add the nodes to the list by the join condition. if (checkJoinCondition(node)) { childEdgesObj[edge.id] = edge; childNodesObj[nodeId] = node; childNodesObj[childNodeId] = _this3.body.nodes[childNodeId]; usedNodes[nodeId] = true; } else { // this node does not qualify after all. gatheringSuccessful = false; break; } } // add to the cluster queue if (_Object$keys(childNodesObj).length > 0 && _Object$keys(childEdgesObj).length > 0 && gatheringSuccessful === true) { /** * Search for cluster data that contains any of the node id's * * @returns {boolean} true if no joinCondition, otherwise return value of joinCondition */ var findClusterData = function findClusterData() { for (var n = 0; n < clusters.length; ++n) { // Search for a cluster containing any of the node id's for (var m in childNodesObj) { if (clusters[n].nodes[m] !== undefined) { return clusters[n]; } } } return undefined; }; // If any of the found nodes is part of a cluster found in this method, // add the current values to that cluster var foundCluster = findClusterData(); if (foundCluster !== undefined) { // Add nodes to found cluster if not present for (var m in childNodesObj) { if (foundCluster.nodes[m] === undefined) { foundCluster.nodes[m] = childNodesObj[m]; } } // Add edges to found cluster, if not present for (var _m in childEdgesObj) { if (foundCluster.edges[_m] === undefined) { foundCluster.edges[_m] = childEdgesObj[_m]; } } } else { // Create a new cluster group clusters.push({ nodes: childNodesObj, edges: childEdgesObj }); } } } } }; for (var i = 0; i < this.body.nodeIndices.length; i++) { _loop(); } for (var _i2 = 0; _i2 < clusters.length; _i2++) { this._cluster(clusters[_i2].nodes, clusters[_i2].edges, options, false); } if (refreshData === true) { this.body.emitter.emit("_dataChanged"); } } /** * Cluster all nodes in the network that have only 1 edge * * @param {object} options * @param {boolean} [refreshData=true] */ }, { key: "clusterOutliers", value: function clusterOutliers(options) { var refreshData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; this.clusterByEdgeCount(1, options, refreshData); } /** * Cluster all nodes in the network that have only 2 edge * * @param {object} options * @param {boolean} [refreshData=true] */ }, { key: "clusterBridges", value: function clusterBridges(options) { var refreshData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; this.clusterByEdgeCount(2, options, refreshData); } /** * suck all connected nodes of a node into the node. * * @param {Node.id} nodeId * @param {object} options * @param {boolean} [refreshData=true] */ }, { key: "clusterByConnection", value: function clusterByConnection(nodeId, options) { var _context; var refreshData = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; // kill conditions if (nodeId === undefined) { throw new Error("No nodeId supplied to clusterByConnection!"); } if (this.body.nodes[nodeId] === undefined) { throw new Error("The nodeId given to clusterByConnection does not exist!"); } var node = this.body.nodes[nodeId]; options = this._checkOptions(options, node); if (options.clusterNodeProperties.x === undefined) { options.clusterNodeProperties.x = node.x; } if (options.clusterNodeProperties.y === undefined) { options.clusterNodeProperties.y = node.y; } if (options.clusterNodeProperties.fixed === undefined) { options.clusterNodeProperties.fixed = {}; options.clusterNodeProperties.fixed.x = node.options.fixed.x; options.clusterNodeProperties.fixed.y = node.options.fixed.y; } var childNodesObj = {}; var childEdgesObj = {}; var parentNodeId = node.id; var parentClonedOptions = NetworkUtil.cloneOptions(node); childNodesObj[parentNodeId] = node; // collect the nodes that will be in the cluster for (var i = 0; i < node.edges.length; i++) { var edge = node.edges[i]; if (this.clusteredEdges[edge.id] === undefined) { var childNodeId = this._getConnectedId(edge, parentNodeId); // if the child node is not in a cluster if (this.clusteredNodes[childNodeId] === undefined) { if (childNodeId !== parentNodeId) { if (options.joinCondition === undefined) { childEdgesObj[edge.id] = edge; childNodesObj[childNodeId] = this.body.nodes[childNodeId]; } else { // clone the options and insert some additional parameters that could be interesting. var childClonedOptions = NetworkUtil.cloneOptions(this.body.nodes[childNodeId]); if (options.joinCondition(parentClonedOptions, childClonedOptions) === true) { childEdgesObj[edge.id] = edge; childNodesObj[childNodeId] = this.body.nodes[childNodeId]; } } } else { // swallow the edge if it is self-referencing. childEdgesObj[edge.id] = edge; } } } } var childNodeIDs = _mapInstanceProperty(_context = _Object$keys(childNodesObj)).call(_context, function (childNode) { return childNodesObj[childNode].id; }); for (var childNodeKey in childNodesObj) { if (!Object.prototype.hasOwnProperty.call(childNodesObj, childNodeKey)) continue; var childNode = childNodesObj[childNodeKey]; for (var y = 0; y < childNode.edges.length; y++) { var childEdge = childNode.edges[y]; if (_indexOfInstanceProperty(childNodeIDs).call(childNodeIDs, this._getConnectedId(childEdge, childNode.id)) > -1) { childEdgesObj[childEdge.id] = childEdge; } } } this._cluster(childNodesObj, childEdgesObj, options, refreshData); } /** * This function creates the edges that will be attached to the cluster * It looks for edges that are connected to the nodes from the "outside' of the cluster. * * @param {{Node.id: vis.Node}} childNodesObj * @param {{vis.Edge.id: vis.Edge}} childEdgesObj * @param {object} clusterNodeProperties * @param {object} clusterEdgeProperties * @private */ }, { key: "_createClusterEdges", value: function _createClusterEdges(childNodesObj, childEdgesObj, clusterNodeProperties, clusterEdgeProperties) { var edge, childNodeId, childNode, toId, fromId, otherNodeId; // loop over all child nodes and their edges to find edges going out of the cluster // these edges will be replaced by clusterEdges. var childKeys = _Object$keys(childNodesObj); var createEdges = []; for (var i = 0; i < childKeys.length; i++) { childNodeId = childKeys[i]; childNode = childNodesObj[childNodeId]; // construct new edges from the cluster to others for (var j = 0; j < childNode.edges.length; j++) { edge = childNode.edges[j]; // we only handle edges that are visible to the system, not the disabled ones from the clustering process. if (this.clusteredEdges[edge.id] === undefined) { // self-referencing edges will be added to the "hidden" list if (edge.toId == edge.fromId) { childEdgesObj[edge.id] = edge; } else { // set up the from and to. if (edge.toId == childNodeId) { // this is a double equals because ints and strings can be interchanged here. toId = clusterNodeProperties.id; fromId = edge.fromId; otherNodeId = fromId; } else { toId = edge.toId; fromId = clusterNodeProperties.id; otherNodeId = toId; } } // Only edges from the cluster outwards are being replaced. if (childNodesObj[otherNodeId] === undefined) { createEdges.push({ edge: edge, fromId: fromId, toId: toId }); } } } } // // Here we actually create the replacement edges. // // We could not do this in the loop above as the creation process // would add an edge to the edges array we are iterating over. // // NOTE: a clustered edge can have multiple base edges! // var newEdges = []; /** * Find a cluster edge which matches the given created edge. * * @param {vis.Edge} createdEdge * @returns {vis.Edge} */ var getNewEdge = function getNewEdge(createdEdge) { for (var _j2 = 0; _j2 < newEdges.length; _j2++) { var newEdge = newEdges[_j2]; // We replace both to and from edges with a single cluster edge var matchToDirection = createdEdge.fromId === newEdge.fromId && createdEdge.toId === newEdge.toId; var matchFromDirection = createdEdge.fromId === newEdge.toId && createdEdge.toId === newEdge.fromId; if (matchToDirection || matchFromDirection) { return newEdge; } } return null; }; for (var _j3 = 0; _j3 < createEdges.length; _j3++) { var createdEdge = createEdges[_j3]; var _edge = createdEdge.edge; var newEdge = getNewEdge(createdEdge); if (newEdge === null) { // Create a clustered edge for this connection newEdge = this._createClusteredEdge(createdEdge.fromId, createdEdge.toId, _edge, clusterEdgeProperties); newEdges.push(newEdge); } else { newEdge.clusteringEdgeReplacingIds.push(_edge.id); } // also reference the new edge in the old edge this.body.edges[_edge.id].edgeReplacedById = newEdge.id; // hide the replaced edge this._backupEdgeOptions(_edge); _edge.setOptions({ physics: false }); } } /** * This function checks the options that can be supplied to the different cluster functions * for certain fields and inserts defaults if needed * * @param {object} options * @returns {*} * @private */ }, { key: "_checkOptions", value: function _checkOptions() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; if (options.clusterEdgeProperties === undefined) { options.clusterEdgeProperties = {}; } if (options.clusterNodeProperties === undefined) { options.clusterNodeProperties = {}; } return options; } /** * * @param {object} childNodesObj | object with node objects, id as keys, same as childNodes except it also contains a source node * @param {object} childEdgesObj | object with edge objects, id as keys * @param {Array} options | object with {clusterNodeProperties, clusterEdgeProperties, processProperties} * @param {boolean} refreshData | when true, do not wrap up * @private */ }, { key: "_cluster", value: function _cluster(childNodesObj, childEdgesObj, options) { var refreshData = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; // Remove nodes which are already clustered var tmpNodesToRemove = []; for (var nodeId in childNodesObj) { if (Object.prototype.hasOwnProperty.call(childNodesObj, nodeId)) { if (this.clusteredNodes[nodeId] !== undefined) { tmpNodesToRemove.push(nodeId); } } } for (var n = 0; n < tmpNodesToRemove.length; ++n) { delete childNodesObj[tmpNodesToRemove[n]]; } // kill condition: no nodes don't bother if (_Object$keys(childNodesObj).length == 0) { return; } // allow clusters of 1 if options allow if (_Object$keys(childNodesObj).length == 1 && options.clusterNodeProperties.allowSingleNodeCluster != true) { return; } var clusterNodeProperties = deepExtend({}, options.clusterNodeProperties); // construct the clusterNodeProperties if (options.processProperties !== undefined) { // get the childNode options var childNodesOptions = []; for (var _nodeId in childNodesObj) { if (Object.prototype.hasOwnProperty.call(childNodesObj, _nodeId)) { var clonedOptions = NetworkUtil.cloneOptions(childNodesObj[_nodeId]); childNodesOptions.push(clonedOptions); } } // get cluster properties based on childNodes var childEdgesOptions = []; for (var edgeId in childEdgesObj) { if (Object.prototype.hasOwnProperty.call(childEdgesObj, edgeId)) { // these cluster edges will be removed on creation of the cluster. if (edgeId.substr(0, 12) !== "clusterEdge:") { var _clonedOptions = NetworkUtil.cloneOptions(childEdgesObj[edgeId], "edge"); childEdgesOptions.push(_clonedOptions); } } } clusterNodeProperties = options.processProperties(clusterNodeProperties, childNodesOptions, childEdgesOptions); if (!clusterNodeProperties) { throw new Error("The processProperties function does not return properties!"); } } // check if we have an unique id; if (clusterNodeProperties.id === undefined) { clusterNodeProperties.id = "cluster:" + v4(); } var clusterId = clusterNodeProperties.id; if (clusterNodeProperties.label === undefined) { clusterNodeProperties.label = "cluster"; } // give the clusterNode a position if it does not have one. var pos = undefined; if (clusterNodeProperties.x === undefined) { pos = this._getClusterPosition(childNodesObj); clusterNodeProperties.x = pos.x; } if (clusterNodeProperties.y === undefined) { if (pos === undefined) { pos = this._getClusterPosition(childNodesObj); } clusterNodeProperties.y = pos.y; } // force the ID to remain the same clusterNodeProperties.id = clusterId; // create the cluster Node // Note that allowSingleNodeCluster, if present, is stored in the options as well var clusterNode = this.body.functions.createNode(clusterNodeProperties, Cluster); clusterNode.containedNodes = childNodesObj; clusterNode.containedEdges = childEdgesObj; // cache a copy from the cluster edge properties if we have to reconnect others later on clusterNode.clusterEdgeProperties = options.clusterEdgeProperties; // finally put the cluster node into global this.body.nodes[clusterNodeProperties.id] = clusterNode; this._clusterEdges(childNodesObj, childEdgesObj, clusterNodeProperties, options.clusterEdgeProperties); // set ID to undefined so no duplicates arise clusterNodeProperties.id = undefined; // wrap up if (refreshData === true) { this.body.emitter.emit("_dataChanged"); } } /** * * @param {Edge} edge * @private */ }, { key: "_backupEdgeOptions", value: function _backupEdgeOptions(edge) { if (this.clusteredEdges[edge.id] === undefined) { this.clusteredEdges[edge.id] = { physics: edge.options.physics }; } } /** * * @param {Edge} edge * @private */ }, { key: "_restoreEdge", value: function _restoreEdge(edge) { var originalOptions = this.clusteredEdges[edge.id]; if (originalOptions !== undefined) { edge.setOptions({ physics: originalOptions.physics }); delete this.clusteredEdges[edge.id]; } } /** * Check if a node is a cluster. * * @param {Node.id} nodeId * @returns {*} */ }, { key: "isCluster", value: function isCluster(nodeId) { if (this.body.nodes[nodeId] !== undefined) { return this.body.nodes[nodeId].isCluster === true; } else { console.error("Node does not exist."); return false; } } /** * get the position of the cluster node based on what's inside * * @param {object} childNodesObj | object with node objects, id as keys * @returns {{x: number, y: number}} * @private */ }, { key: "_getClusterPosition", value: function _getClusterPosition(childNodesObj) { var childKeys = _Object$keys(childNodesObj); var minX = childNodesObj[childKeys[0]].x; var maxX = childNodesObj[childKeys[0]].x; var minY = childNodesObj[childKeys[0]].y; var maxY = childNodesObj[childKeys[0]].y; var node; for (var i = 1; i < childKeys.length; i++) { node = childNodesObj[childKeys[i]]; minX = node.x < minX ? node.x : minX; maxX = node.x > maxX ? node.x : maxX; minY = node.y < minY ? node.y : minY; maxY = node.y > maxY ? node.y : maxY; } return { x: 0.5 * (minX + maxX), y: 0.5 * (minY + maxY) }; } /** * Open a cluster by calling this function. * * @param {vis.Edge.id} clusterNodeId | the ID of the cluster node * @param {object} options * @param {boolean} refreshData | wrap up afterwards if not true */ }, { key: "openCluster", value: function openCluster(clusterNodeId, options) { var refreshData = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; // kill conditions if (clusterNodeId === undefined) { throw new Error("No clusterNodeId supplied to openCluster."); } var clusterNode = this.body.nodes[clusterNodeId]; if (clusterNode === undefined) { throw new Error("The clusterNodeId supplied to openCluster does not exist."); } if (clusterNode.isCluster !== true || clusterNode.containedNodes === undefined || clusterNode.containedEdges === undefined) { throw new Error("The node:" + clusterNodeId + " is not a valid cluster."); } // Check if current cluster is clustered itself var stack = this.findNode(clusterNodeId); var parentIndex = _indexOfInstanceProperty(stack).call(stack, clusterNodeId) - 1; if (parentIndex >= 0) { // Current cluster is clustered; transfer contained nodes and edges to parent var parentClusterNodeId = stack[parentIndex]; var parentClusterNode = this.body.nodes[parentClusterNodeId]; // clustering.clusteredNodes and clustering.clusteredEdges remain unchanged parentClusterNode._openChildCluster(clusterNodeId); // All components of child cluster node have been transferred. It can die now. delete this.body.nodes[clusterNodeId]; if (refreshData === true) { this.body.emitter.emit("_dataChanged"); } return; } // main body var containedNodes = clusterNode.containedNodes; var containedEdges = clusterNode.containedEdges; // allow the user to position the nodes after release. if (options !== undefined && options.releaseFunction !== undefined && typeof options.releaseFunction === "function") { var positions = {}; var clusterPosition = { x: clusterNode.x, y: clusterNode.y }; for (var nodeId in containedNodes) { if (Object.prototype.hasOwnProperty.call(containedNodes, nodeId)) { var containedNode = this.body.nodes[nodeId]; positions[nodeId] = { x: containedNode.x, y: containedNode.y }; } } var newPositions = options.releaseFunction(clusterPosition, positions); for (var _nodeId2 in containedNodes) { if (Object.prototype.hasOwnProperty.call(containedNodes, _nodeId2)) { var _containedNode = this.body.nodes[_nodeId2]; if (newPositions[_nodeId2] !== undefined) { _containedNode.x = newPositions[_nodeId2].x === undefined ? clusterNode.x : newPositions[_nodeId2].x; _containedNode.y = newPositions[_nodeId2].y === undefined ? clusterNode.y : newPositions[_nodeId2].y; } } } } else { // copy the position from the cluster forEach$1(containedNodes, function (containedNode) { // inherit position if (containedNode.options.fixed.x === false) { containedNode.x = clusterNode.x; } if (containedNode.options.fixed.y === false) { containedNode.y = clusterNode.y; } }); } // release nodes for (var _nodeId3 in containedNodes) { if (Object.prototype.hasOwnProperty.call(containedNodes, _nodeId3)) { var _containedNode2 = this.body.nodes[_nodeId3]; // inherit speed _containedNode2.vx = clusterNode.vx; _containedNode2.vy = clusterNode.vy; _containedNode2.setOptions({ physics: true }); delete this.clusteredNodes[_nodeId3]; } } // copy the clusterNode edges because we cannot iterate over an object that we add or remove from. var edgesToBeDeleted = []; for (var i = 0; i < clusterNode.edges.length; i++) { edgesToBeDeleted.push(clusterNode.edges[i]); } // actually handling the deleting. for (var _i3 = 0; _i3 < edgesToBeDeleted.length; _i3++) { var edge = edgesToBeDeleted[_i3]; var otherNodeId = this._getConnectedId(edge, clusterNodeId); var otherNode = this.clusteredNodes[otherNodeId]; for (var j = 0; j < edge.clusteringEdgeReplacingIds.length; j++) { var transferId = edge.clusteringEdgeReplacingIds[j]; var transferEdge = this.body.edges[transferId]; if (transferEdge === undefined) continue; // if the other node is in another cluster, we transfer ownership of this edge to the other cluster if (otherNode !== undefined) { // transfer ownership: var otherCluster = this.body.nodes[otherNode.clusterId]; otherCluster.containedEdges[transferEdge.id] = transferEdge; // delete local reference delete containedEdges[transferEdge.id]; // get to and from var fromId = transferEdge.fromId; var toId = transferEdge.toId; if (transferEdge.toId == otherNodeId) { toId = otherNode.clusterId; } else { fromId = otherNode.clusterId; } // create new cluster edge from the otherCluster this._createClusteredEdge(fromId, toId, transferEdge, otherCluster.clusterEdgeProperties, { hidden: false, physics: true }); } else { this._restoreEdge(transferEdge); } } edge.remove(); } // handle the releasing of the edges for (var edgeId in containedEdges) { if (Object.prototype.hasOwnProperty.call(containedEdges, edgeId)) { this._restoreEdge(containedEdges[edgeId]); } } // remove clusterNode delete this.body.nodes[clusterNodeId]; if (refreshData === true) { this.body.emitter.emit("_dataChanged"); } } /** * * @param {Cluster.id} clusterId * @returns {Array.<Node.id>} */ }, { key: "getNodesInCluster", value: function getNodesInCluster(clusterId) { var nodesArray = []; if (this.isCluster(clusterId) === true) { var containedNodes = this.body.nodes[clusterId].containedNodes; for (var nodeId in containedNodes) { if (Object.prototype.hasOwnProperty.call(containedNodes, nodeId)) { nodesArray.push(this.body.nodes[nodeId].id); } } } return nodesArray; } /** * Get the stack clusterId's that a certain node resides in. cluster A -> cluster B -> cluster C -> node * * If a node can't be found in the chain, return an empty array. * * @param {string|number} nodeId * @returns {Array} */ }, { key: "findNode", value: function findNode(nodeId) { var stack = []; var max = 100; var counter = 0; var node; while (this.clusteredNodes[nodeId] !== undefined && counter < max) { node = this.body.nodes[nodeId]; if (node === undefined) return []; stack.push(node.id); nodeId = this.clusteredNodes[nodeId].clusterId; counter++; } node = this.body.nodes[nodeId]; if (node === undefined) return []; stack.push(node.id); _reverseInstanceProperty(stack).call(stack); return stack; } /** * Using a clustered nodeId, update with the new options * * @param {Node.id} clusteredNodeId * @param {object} newOptions */ }, { key: "updateClusteredNode", value: function updateClusteredNode(clusteredNodeId, newOptions) { if (clusteredNodeId === undefined) { throw new Error("No clusteredNodeId supplied to updateClusteredNode."); } if (newOptions === undefined) { throw new Error("No newOptions supplied to updateClusteredNode."); } if (this.body.nodes[clusteredNodeId] === undefined) { throw new Error("The clusteredNodeId supplied to updateClusteredNode does not exist."); } this.body.nodes[clusteredNodeId].setOptions(newOptions); this.body.emitter.emit("_dataChanged"); } /** * Using a base edgeId, update all related clustered edges with the new options * * @param {vis.Edge.id} startEdgeId * @param {object} newOptions */ }, { key: "updateEdge", value: function updateEdge(startEdgeId, newOptions) { if (startEdgeId === undefined) { throw new Error("No startEdgeId supplied to updateEdge."); } if (newOptions === undefined) { throw new Error("No newOptions supplied to updateEdge."); } if (this.body.edges[startEdgeId] === undefined) { throw new Error("The startEdgeId supplied to updateEdge does not exist."); } var allEdgeIds = this.getClusteredEdges(startEdgeId); for (var i = 0; i < allEdgeIds.length; i++) { var edge = this.body.edges[allEdgeIds[i]]; edge.setOptions(newOptions); } this.body.emitter.emit("_dataChanged"); } /** * Get a stack of clusterEdgeId's (+base edgeid) that a base edge is the same as. cluster edge C -> cluster edge B -> cluster edge A -> base edge(edgeId) * * @param {vis.Edge.id} edgeId * @returns {Array.<vis.Edge.id>} */ }, { key: "getClusteredEdges", value: function getClusteredEdges(edgeId) { var stack = []; var max = 100; var counter = 0; while (edgeId !== undefined && this.body.edges[edgeId] !== undefined && counter < max) { stack.push(this.body.edges[edgeId].id); edgeId = this.body.edges[edgeId].edgeReplacedById; counter++; } _reverseInstanceProperty(stack).call(stack); return stack; } /** * Get the base edge id of clusterEdgeId. cluster edge (clusteredEdgeId) -> cluster edge B -> cluster edge C -> base edge * * @param {vis.Edge.id} clusteredEdgeId * @returns {vis.Edge.id} baseEdgeId * * TODO: deprecate in 5.0.0. Method getBaseEdges() is the correct one to use. */ }, { key: "getBaseEdge", value: function getBaseEdge(clusteredEdgeId) { // Just kludge this by returning the first base edge id found return this.getBaseEdges(clusteredEdgeId)[0]; } /** * Get all regular edges for this clustered edge id. * * @param {vis.Edge.id} clusteredEdgeId * @returns {Array.<vis.Edge.id>} all baseEdgeId's under this clustered edge */ }, { key: "getBaseEdges", value: function getBaseEdges(clusteredEdgeId) { var IdsToHandle = [clusteredEdgeId]; var doneIds = []; var foundIds = []; var max = 100; var counter = 0; while (IdsToHandle.length > 0 && counter < max) { var nextId = IdsToHandle.pop(); if (nextId === undefined) continue; // Paranoia here and onwards var nextEdge = this.body.edges[nextId]; if (nextEdge === undefined) continue; counter++; var replacingIds = nextEdge.clusteringEdgeReplacingIds; if (replacingIds === undefined) { // nextId is a base id foundIds.push(nextId); } else { // Another cluster edge, unravel this one as well for (var i = 0; i < replacingIds.length; ++i) { var replacingId = replacingIds[i]; // Don't add if already handled // TODO: never triggers; find a test-case which does if (_indexOfInstanceProperty(IdsToHandle).call(IdsToHandle, replacingIds) !== -1 || _indexOfInstanceProperty(doneIds).call(doneIds, replacingIds) !== -1) { continue; } IdsToHandle.push(replacingId); } } doneIds.push(nextId); } return foundIds; } /** * Get the Id the node is connected to * * @param {vis.Edge} edge * @param {Node.id} nodeId * @returns {*} * @private */ }, { key: "_getConnectedId", value: function _getConnectedId(edge, nodeId) { if (edge.toId != nodeId) { return edge.toId; } else if (edge.fromId != nodeId) { return edge.fromId; } else { return edge.fromId; } } /** * We determine how many connections denote an important hub. * We take the mean + 2*std as the important hub size. (Assuming a normal distribution of data, ~2.2%) * * @returns {number} * @private */ }, { key: "_getHubSize", value: function _getHubSize() { var average = 0; var averageSquared = 0; var hubCounter = 0; var largestHub = 0; for (var i = 0; i < this.body.nodeIndices.length; i++) { var node = this.body.nodes[this.body.nodeIndices[i]]; if (node.edges.length > largestHub) { largestHub = node.edges.length; } average += node.edges.length; averageSquared += Math.pow(node.edges.length, 2); hubCounter += 1; } average = average / hubCounter; averageSquared = averageSquared / hubCounter; var variance = averageSquared - Math.pow(average, 2); var standardDeviation = Math.sqrt(variance); var hubThreshold = Math.floor(average + 2 * standardDeviation); // always have at least one to cluster if (hubThreshold > largestHub) { hubThreshold = largestHub; } return hubThreshold; } /** * Create an edge for the cluster representation. * * @param {Node.id} fromId * @param {Node.id} toId * @param {vis.Edge} baseEdge * @param {object} clusterEdgeProperties * @param {object} extraOptions * @returns {Edge} newly created clustered edge * @private */ }, { key: "_createClusteredEdge", value: function _createClusteredEdge(fromId, toId, baseEdge, clusterEdgeProperties, extraOptions) { // copy the options of the edge we will replace var clonedOptions = NetworkUtil.cloneOptions(baseEdge, "edge"); // make sure the properties of clusterEdges are superimposed on it deepExtend(clonedOptions, clusterEdgeProperties); // set up the edge clonedOptions.from = fromId; clonedOptions.to = toId; clonedOptions.id = "clusterEdge:" + v4(); // apply the edge specific options to it if specified if (extraOptions !== undefined) { deepExtend(clonedOptions, extraOptions); } var newEdge = this.body.functions.createEdge(clonedOptions); newEdge.clusteringEdgeReplacingIds = [baseEdge.id]; newEdge.connect(); // Register the new edge this.body.edges[newEdge.id] = newEdge; return newEdge; } /** * Add the passed child nodes and edges to the given cluster node. * * @param {object | Node} childNodes hash of nodes or single node to add in cluster * @param {object | Edge} childEdges hash of edges or single edge to take into account when clustering * @param {Node} clusterNode cluster node to add nodes and edges to * @param {object} [clusterEdgeProperties] * @private */ }, { key: "_clusterEdges", value: function _clusterEdges(childNodes, childEdges, clusterNode, clusterEdgeProperties) { if (childEdges instanceof Edge) { var edge = childEdges; var obj = {}; obj[edge.id] = edge; childEdges = obj; } if (childNodes instanceof Node) { var node = childNodes; var _obj = {}; _obj[node.id] = node; childNodes = _obj; } if (clusterNode === undefined || clusterNode === null) { throw new Error("_clusterEdges: parameter clusterNode required"); } if (clusterEdgeProperties === undefined) { // Take the required properties from the cluster node clusterEdgeProperties = clusterNode.clusterEdgeProperties; } // create the new edges that will connect to the cluster. // All self-referencing edges will be added to childEdges here. this._createClusterEdges(childNodes, childEdges, clusterNode, clusterEdgeProperties); // disable the childEdges for (var edgeId in childEdges) { if (Object.prototype.hasOwnProperty.call(childEdges, edgeId)) { if (this.body.edges[edgeId] !== undefined) { var _edge2 = this.body.edges[edgeId]; // cache the options before changing this._backupEdgeOptions(_edge2); // disable physics and hide the edge _edge2.setOptions({ physics: false }); } } } // disable the childNodes for (var nodeId in childNodes) { if (Object.prototype.hasOwnProperty.call(childNodes, nodeId)) { this.clusteredNodes[nodeId] = { clusterId: clusterNode.id, node: this.body.nodes[nodeId] }; this.body.nodes[nodeId].setOptions({ physics: false }); } } } /** * Determine in which cluster given nodeId resides. * * If not in cluster, return undefined. * * NOTE: If you know a cleaner way to do this, please enlighten me (wimrijnders). * * @param {Node.id} nodeId * @returns {Node|undefined} Node instance for cluster, if present * @private */ }, { key: "_getClusterNodeForNode", value: function _getClusterNodeForNode(nodeId) { if (nodeId === undefined) return undefined; var clusteredNode = this.clusteredNodes[nodeId]; // NOTE: If no cluster info found, it should actually be an error if (clusteredNode === undefined) return undefined; var clusterId = clusteredNode.clusterId; if (clusterId === undefined) return undefined; return this.body.nodes[clusterId]; } /** * Internal helper function for conditionally removing items in array * * Done like this because Array.filter() is not fully supported by all IE's. * * @param {Array} arr * @param {Function} callback * @returns {Array} * @private */ }, { key: "_filter", value: function _filter(arr, callback) { var ret = []; forEach$1(arr, function (item) { if (callback(item)) { ret.push(item); } }); return ret; } /** * Scan all edges for changes in clustering and adjust this if necessary. * * Call this (internally) after there has been a change in node or edge data. * * Pre: States of this.body.nodes and this.body.edges consistent * Pre: this.clusteredNodes and this.clusteredEdge consistent with containedNodes and containedEdges * of cluster nodes. */ }, { key: "_updateState", value: function _updateState() { var _this4 = this; var nodeId; var deletedNodeIds = []; var deletedEdgeIds = {}; /** * Utility function to iterate over clustering nodes only * * @param {Function} callback function to call for each cluster node */ var eachClusterNode = function eachClusterNode(callback) { forEach$1(_this4.body.nodes, function (node) { if (node.isCluster === true) { callback(node); } }); }; // // Remove deleted regular nodes from clustering // // Determine the deleted nodes for (nodeId in this.clusteredNodes) { if (!Object.prototype.hasOwnProperty.call(this.clusteredNodes, nodeId)) continue; var node = this.body.nodes[nodeId]; if (node === undefined) { deletedNodeIds.push(nodeId); } } // Remove nodes from cluster nodes eachClusterNode(function (clusterNode) { for (var n = 0; n < deletedNodeIds.length; n++) { delete clusterNode.containedNodes[deletedNodeIds[n]]; } }); // Remove nodes from cluster list for (var n = 0; n < deletedNodeIds.length; n++) { delete this.clusteredNodes[deletedNodeIds[n]]; } // // Remove deleted edges from clustering // // Add the deleted clustered edges to the list forEach$1(this.clusteredEdges, function (edgeId) { var edge = _this4.body.edges[edgeId]; if (edge === undefined || !edge.endPointsValid()) { deletedEdgeIds[edgeId] = edgeId; } }); // Cluster nodes can also contain edges which are not clustered, // i.e. nodes 1-2 within cluster with an edge in between. // So the cluster nodes also need to be scanned for invalid edges eachClusterNode(function (clusterNode) { forEach$1(clusterNode.containedEdges, function (edge, edgeId) { if (!edge.endPointsValid() && !deletedEdgeIds[edgeId]) { deletedEdgeIds[edgeId] = edgeId; } }); }); // Also scan for cluster edges which need to be removed in the active list. // Regular edges have been removed beforehand, so this only picks up the cluster edges. forEach$1(this.body.edges, function (edge, edgeId) { // Explicitly scan the contained edges for validity var isValid = true; var replacedIds = edge.clusteringEdgeReplacingIds; if (replacedIds !== undefined) { var numValid = 0; forEach$1(replacedIds, function (containedEdgeId) { var containedEdge = _this4.body.edges[containedEdgeId]; if (containedEdge !== undefined && containedEdge.endPointsValid()) { numValid += 1; } }); isValid = numValid > 0; } if (!edge.endPointsValid() || !isValid) { deletedEdgeIds[edgeId] = edgeId; } }); // Remove edges from cluster nodes eachClusterNode(function (clusterNode) { forEach$1(deletedEdgeIds, function (deletedEdgeId) { delete clusterNode.containedEdges[deletedEdgeId]; forEach$1(clusterNode.edges, function (edge, m) { if (edge.id === deletedEdgeId) { clusterNode.edges[m] = null; // Don't want to directly delete here, because in the loop return; } edge.clusteringEdgeReplacingIds = _this4._filter(edge.clusteringEdgeReplacingIds, function (id) { return !deletedEdgeIds[id]; }); }); // Clean up the nulls clusterNode.edges = _this4._filter(clusterNode.edges, function (item) { return item !== null; }); }); }); // Remove from cluster list forEach$1(deletedEdgeIds, function (edgeId) { delete _this4.clusteredEdges[edgeId]; }); // Remove cluster edges from active list (this.body.edges). // deletedEdgeIds still contains id of regular edges, but these should all // be gone when you reach here. forEach$1(deletedEdgeIds, function (edgeId) { delete _this4.body.edges[edgeId]; }); // // Check changed cluster state of edges // // Iterating over keys here, because edges may be removed in the loop var ids = _Object$keys(this.body.edges); forEach$1(ids, function (edgeId) { var edge = _this4.body.edges[edgeId]; var shouldBeClustered = _this4._isClusteredNode(edge.fromId) || _this4._isClusteredNode(edge.toId); if (shouldBeClustered === _this4._isClusteredEdge(edge.id)) { return; // all is well } if (shouldBeClustered) { // add edge to clustering var clusterFrom = _this4._getClusterNodeForNode(edge.fromId); if (clusterFrom !== undefined) { _this4._clusterEdges(_this4.body.nodes[edge.fromId], edge, clusterFrom); } var clusterTo = _this4._getClusterNodeForNode(edge.toId); if (clusterTo !== undefined) { _this4._clusterEdges(_this4.body.nodes[edge.toId], edge, clusterTo); } // TODO: check that it works for both edges clustered // (This might be paranoia) } else { delete _this4._clusterEdges[edgeId]; _this4._restoreEdge(edge); // This should not be happening, the state should // be properly updated at this point. // // If it *is* reached during normal operation, then we have to implement // undo clustering for this edge here. // throw new Error('remove edge from clustering not implemented!') } }); // Clusters may be nested to any level. Keep on opening until nothing to open var changed = false; var continueLoop = true; var _loop2 = function _loop2() { var clustersToOpen = []; // Determine the id's of clusters that need opening eachClusterNode(function (clusterNode) { var numNodes = _Object$keys(clusterNode.containedNodes).length; var allowSingle = clusterNode.options.allowSingleNodeCluster === true; if (allowSingle && numNodes < 1 || !allowSingle && numNodes < 2) { clustersToOpen.push(clusterNode.id); } }); // Open them for (var _n = 0; _n < clustersToOpen.length; ++_n) { _this4.openCluster(clustersToOpen[_n], {}, false /* Don't refresh, we're in an refresh/update already */); } continueLoop = clustersToOpen.length > 0; changed = changed || continueLoop; }; while (continueLoop) { _loop2(); } if (changed) { this._updateState(); // Redo this method (recursion possible! should be safe) } } /** * Determine if node with given id is part of a cluster. * * @param {Node.id} nodeId * @returns {boolean} true if part of a cluster. */ }, { key: "_isClusteredNode", value: function _isClusteredNode(nodeId) { return this.clusteredNodes[nodeId] !== undefined; } /** * Determine if edge with given id is not visible due to clustering. * * An edge is considered clustered if: * - it is directly replaced by a clustering edge * - any of its connecting nodes is in a cluster * * @param {vis.Edge.id} edgeId * @returns {boolean} true if part of a cluster. */ }, { key: "_isClusteredEdge", value: function _isClusteredEdge(edgeId) { return this.clusteredEdges[edgeId] !== undefined; } }]); return ClusterEngine; }(); /** * Initializes window.requestAnimationFrame() to a usable form. * * Specifically, set up this method for the case of running on node.js with jsdom enabled. * * NOTES: * * On node.js, when calling this directly outside of this class, `window` is not defined. * This happens even if jsdom is used. * For node.js + jsdom, `window` is available at the moment the constructor is called. * For this reason, the called is placed within the constructor. * Even then, `window.requestAnimationFrame()` is not defined, so it still needs to be added. * During unit testing, it happens that the window object is reset during execution, causing * a runtime error due to missing `requestAnimationFrame()`. This needs to be compensated for, * see `_requestNextFrame()`. * Since this is a global object, it may affect other modules besides `Network`. With normal * usage, this does not cause any problems. During unit testing, errors may occur. These have * been compensated for, see comment block in _requestNextFrame(). * * @private */ function _initRequestAnimationFrame() { var func; if (window !== undefined) { func = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; } if (func === undefined) { // window or method not present, setting mock requestAnimationFrame window.requestAnimationFrame = function (callback) { //console.log("Called mock requestAnimationFrame"); callback(); }; } else { window.requestAnimationFrame = func; } } /** * The canvas renderer */ var CanvasRenderer = /*#__PURE__*/function () { /** * @param {object} body * @param {Canvas} canvas */ function CanvasRenderer(body, canvas) { _classCallCheck(this, CanvasRenderer); _initRequestAnimationFrame(); this.body = body; this.canvas = canvas; this.redrawRequested = false; this.renderTimer = undefined; this.requiresTimeout = true; this.renderingActive = false; this.renderRequests = 0; this.allowRedraw = true; this.dragging = false; this.zooming = false; this.options = {}; this.defaultOptions = { hideEdgesOnDrag: false, hideEdgesOnZoom: false, hideNodesOnDrag: false }; _Object$assign(this.options, this.defaultOptions); this._determineBrowserMethod(); this.bindEventListeners(); } /** * Binds event listeners */ _createClass(CanvasRenderer, [{ key: "bindEventListeners", value: function bindEventListeners() { var _this = this, _context2; this.body.emitter.on("dragStart", function () { _this.dragging = true; }); this.body.emitter.on("dragEnd", function () { _this.dragging = false; }); this.body.emitter.on("zoom", function () { _this.zooming = true; window.clearTimeout(_this.zoomTimeoutId); _this.zoomTimeoutId = _setTimeout(function () { var _context; _this.zooming = false; _bindInstanceProperty$1(_context = _this._requestRedraw).call(_context, _this)(); }, 250); }); this.body.emitter.on("_resizeNodes", function () { _this._resizeNodes(); }); this.body.emitter.on("_redraw", function () { if (_this.renderingActive === false) { _this._redraw(); } }); this.body.emitter.on("_blockRedraw", function () { _this.allowRedraw = false; }); this.body.emitter.on("_allowRedraw", function () { _this.allowRedraw = true; _this.redrawRequested = false; }); this.body.emitter.on("_requestRedraw", _bindInstanceProperty$1(_context2 = this._requestRedraw).call(_context2, this)); this.body.emitter.on("_startRendering", function () { _this.renderRequests += 1; _this.renderingActive = true; _this._startRendering(); }); this.body.emitter.on("_stopRendering", function () { _this.renderRequests -= 1; _this.renderingActive = _this.renderRequests > 0; _this.renderTimer = undefined; }); this.body.emitter.on("destroy", function () { _this.renderRequests = 0; _this.allowRedraw = false; _this.renderingActive = false; if (_this.requiresTimeout === true) { clearTimeout(_this.renderTimer); } else { window.cancelAnimationFrame(_this.renderTimer); } _this.body.emitter.off(); }); } /** * * @param {object} options */ }, { key: "setOptions", value: function setOptions(options) { if (options !== undefined) { var fields = ["hideEdgesOnDrag", "hideEdgesOnZoom", "hideNodesOnDrag"]; selectiveDeepExtend(fields, this.options, options); } } /** * Prepare the drawing of the next frame. * * Calls the callback when the next frame can or will be drawn. * * @param {Function} callback * @param {number} delay - timeout case only, wait this number of milliseconds * @returns {Function | undefined} * @private */ }, { key: "_requestNextFrame", value: function _requestNextFrame(callback, delay) { // During unit testing, it happens that the mock window object is reset while // the next frame is still pending. Then, either 'window' is not present, or // 'requestAnimationFrame()' is not present because it is not defined on the // mock window object. // // As a consequence, unrelated unit tests may appear to fail, even if the problem // described happens in the current unit test. // // This is not something that will happen in normal operation, but we still need // to take it into account. // if (typeof window === "undefined") return; // Doing `if (window === undefined)` does not work here! var timer; var myWindow = window; // Grab a reference to reduce the possibility that 'window' is reset // while running this method. if (this.requiresTimeout === true) { // wait given number of milliseconds and perform the animation step function timer = _setTimeout(callback, delay); } else { if (myWindow.requestAnimationFrame) { timer = myWindow.requestAnimationFrame(callback); } } return timer; } /** * * @private */ }, { key: "_startRendering", value: function _startRendering() { if (this.renderingActive === true) { if (this.renderTimer === undefined) { var _context3; this.renderTimer = this._requestNextFrame(_bindInstanceProperty$1(_context3 = this._renderStep).call(_context3, this), this.simulationInterval); } } } /** * * @private */ }, { key: "_renderStep", value: function _renderStep() { if (this.renderingActive === true) { // reset the renderTimer so a new scheduled animation step can be set this.renderTimer = undefined; if (this.requiresTimeout === true) { // this schedules a new simulation step this._startRendering(); } this._redraw(); if (this.requiresTimeout === false) { // this schedules a new simulation step this._startRendering(); } } } /** * Redraw the network with the current data * chart will be resized too. */ }, { key: "redraw", value: function redraw() { this.body.emitter.emit("setSize"); this._redraw(); } /** * Redraw the network with the current data * * @private */ }, { key: "_requestRedraw", value: function _requestRedraw() { var _this2 = this; if (this.redrawRequested !== true && this.renderingActive === false && this.allowRedraw === true) { this.redrawRequested = true; this._requestNextFrame(function () { _this2._redraw(false); }, 0); } } /** * Redraw the network with the current data * * @param {boolean} [hidden=false] | Used to get the first estimate of the node sizes. * Only the nodes are drawn after which they are quickly drawn over. * @private */ }, { key: "_redraw", value: function _redraw() { var hidden = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; if (this.allowRedraw === true) { this.body.emitter.emit("initRedraw"); this.redrawRequested = false; var drawLater = { drawExternalLabels: null }; // when the container div was hidden, this fixes it back up! if (this.canvas.frame.canvas.width === 0 || this.canvas.frame.canvas.height === 0) { this.canvas.setSize(); } this.canvas.setTransform(); var ctx = this.canvas.getContext(); // clear the canvas var w = this.canvas.frame.canvas.clientWidth; var h = this.canvas.frame.canvas.clientHeight; ctx.clearRect(0, 0, w, h); // if the div is hidden, we stop the redraw here for performance. if (this.canvas.frame.clientWidth === 0) { return; } // set scaling and translation ctx.save(); ctx.translate(this.body.view.translation.x, this.body.view.translation.y); ctx.scale(this.body.view.scale, this.body.view.scale); ctx.beginPath(); this.body.emitter.emit("beforeDrawing", ctx); ctx.closePath(); if (hidden === false) { if ((this.dragging === false || this.dragging === true && this.options.hideEdgesOnDrag === false) && (this.zooming === false || this.zooming === true && this.options.hideEdgesOnZoom === false)) { this._drawEdges(ctx); } } if (this.dragging === false || this.dragging === true && this.options.hideNodesOnDrag === false) { var _this$_drawNodes = this._drawNodes(ctx, hidden), drawExternalLabels = _this$_drawNodes.drawExternalLabels; drawLater.drawExternalLabels = drawExternalLabels; } // draw the arrows last so they will be at the top if (hidden === false) { if ((this.dragging === false || this.dragging === true && this.options.hideEdgesOnDrag === false) && (this.zooming === false || this.zooming === true && this.options.hideEdgesOnZoom === false)) { this._drawArrows(ctx); } } if (drawLater.drawExternalLabels != null) { drawLater.drawExternalLabels(); } if (hidden === false) { this._drawSelectionBox(ctx); } ctx.beginPath(); this.body.emitter.emit("afterDrawing", ctx); ctx.closePath(); // restore original scaling and translation ctx.restore(); if (hidden === true) { ctx.clearRect(0, 0, w, h); } } } /** * Redraw all nodes * * @param {CanvasRenderingContext2D} ctx * @param {boolean} [alwaysShow] * @private */ }, { key: "_resizeNodes", value: function _resizeNodes() { this.canvas.setTransform(); var ctx = this.canvas.getContext(); ctx.save(); ctx.translate(this.body.view.translation.x, this.body.view.translation.y); ctx.scale(this.body.view.scale, this.body.view.scale); var nodes = this.body.nodes; var node; // resize all nodes for (var nodeId in nodes) { if (Object.prototype.hasOwnProperty.call(nodes, nodeId)) { node = nodes[nodeId]; node.resize(ctx); node.updateBoundingBox(ctx, node.selected); } } // restore original scaling and translation ctx.restore(); } /** * Redraw all nodes * * @param {CanvasRenderingContext2D} ctx 2D context of a HTML canvas * @param {boolean} [alwaysShow] * @private * @returns {object} Callbacks to draw later on higher layers. */ }, { key: "_drawNodes", value: function _drawNodes(ctx) { var alwaysShow = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var nodes = this.body.nodes; var nodeIndices = this.body.nodeIndices; var node; var selected = []; var hovered = []; var margin = 20; var topLeft = this.canvas.DOMtoCanvas({ x: -margin, y: -margin }); var bottomRight = this.canvas.DOMtoCanvas({ x: this.canvas.frame.canvas.clientWidth + margin, y: this.canvas.frame.canvas.clientHeight + margin }); var viewableArea = { top: topLeft.y, left: topLeft.x, bottom: bottomRight.y, right: bottomRight.x }; var _drawExternalLabels = []; // draw unselected nodes; for (var _i = 0; _i < nodeIndices.length; _i++) { node = nodes[nodeIndices[_i]]; // set selected and hovered nodes aside if (node.hover) { hovered.push(nodeIndices[_i]); } else if (node.isSelected()) { selected.push(nodeIndices[_i]); } else { if (alwaysShow === true) { var drawLater = node.draw(ctx); if (drawLater.drawExternalLabel != null) { _drawExternalLabels.push(drawLater.drawExternalLabel); } } else if (node.isBoundingBoxOverlappingWith(viewableArea) === true) { var _drawLater = node.draw(ctx); if (_drawLater.drawExternalLabel != null) { _drawExternalLabels.push(_drawLater.drawExternalLabel); } } else { node.updateBoundingBox(ctx, node.selected); } } } var i; var selectedLength = selected.length; var hoveredLength = hovered.length; // draw the selected nodes on top for (i = 0; i < selectedLength; i++) { node = nodes[selected[i]]; var _drawLater2 = node.draw(ctx); if (_drawLater2.drawExternalLabel != null) { _drawExternalLabels.push(_drawLater2.drawExternalLabel); } } // draw hovered nodes above everything else: fixes https://github.com/visjs/vis-network/issues/226 for (i = 0; i < hoveredLength; i++) { node = nodes[hovered[i]]; var _drawLater3 = node.draw(ctx); if (_drawLater3.drawExternalLabel != null) { _drawExternalLabels.push(_drawLater3.drawExternalLabel); } } return { drawExternalLabels: function drawExternalLabels() { for (var _i2 = 0, _drawExternalLabels2 = _drawExternalLabels; _i2 < _drawExternalLabels2.length; _i2++) { var draw = _drawExternalLabels2[_i2]; draw(); } } }; } /** * Redraw all edges * * @param {CanvasRenderingContext2D} ctx 2D context of a HTML canvas * @private */ }, { key: "_drawEdges", value: function _drawEdges(ctx) { var edges = this.body.edges; var edgeIndices = this.body.edgeIndices; for (var i = 0; i < edgeIndices.length; i++) { var edge = edges[edgeIndices[i]]; if (edge.connected === true) { edge.draw(ctx); } } } /** * Redraw all arrows * * @param {CanvasRenderingContext2D} ctx 2D context of a HTML canvas * @private */ }, { key: "_drawArrows", value: function _drawArrows(ctx) { var edges = this.body.edges; var edgeIndices = this.body.edgeIndices; for (var i = 0; i < edgeIndices.length; i++) { var edge = edges[edgeIndices[i]]; if (edge.connected === true) { edge.drawArrows(ctx); } } } /** * Determine if the browser requires a setTimeout or a requestAnimationFrame. This was required because * some implementations (safari and IE9) did not support requestAnimationFrame * * @private */ }, { key: "_determineBrowserMethod", value: function _determineBrowserMethod() { if (typeof window !== "undefined") { var browserType = navigator.userAgent.toLowerCase(); this.requiresTimeout = false; if (_indexOfInstanceProperty(browserType).call(browserType, "msie 9.0") != -1) { // IE 9 this.requiresTimeout = true; } else if (_indexOfInstanceProperty(browserType).call(browserType, "safari") != -1) { // safari if (_indexOfInstanceProperty(browserType).call(browserType, "chrome") <= -1) { this.requiresTimeout = true; } } } else { this.requiresTimeout = true; } } /** * Redraw selection box * * @param {CanvasRenderingContext2D} ctx 2D context of a HTML canvas * @private */ }, { key: "_drawSelectionBox", value: function _drawSelectionBox(ctx) { if (this.body.selectionBox.show) { ctx.beginPath(); var width = this.body.selectionBox.position.end.x - this.body.selectionBox.position.start.x; var height = this.body.selectionBox.position.end.y - this.body.selectionBox.position.start.y; ctx.rect(this.body.selectionBox.position.start.x, this.body.selectionBox.position.start.y, width, height); ctx.fillStyle = "rgba(151, 194, 252, 0.2)"; ctx.fillRect(this.body.selectionBox.position.start.x, this.body.selectionBox.position.start.y, width, height); ctx.strokeStyle = "rgba(151, 194, 252, 1)"; ctx.stroke(); } else { ctx.closePath(); } } }]); return CanvasRenderer; }(); var setIntervalExports = {}; var setInterval$1 = { get exports() { return setIntervalExports; }, set exports(v) { setIntervalExports = v; } }; var path$2 = path$y; var setInterval = path$2.setInterval; (function (module) { module.exports = setInterval; })(setInterval$1); var _setInterval = /*@__PURE__*/getDefaultExportFromCjs(setIntervalExports); /** * Register a touch event, taking place before a gesture * * @param {Hammer} hammer A hammer instance * @param {Function} callback Callback, called as callback(event) */ function onTouch(hammer, callback) { callback.inputHandler = function (event) { if (event.isFirst) { callback(event); } }; hammer.on("hammer.input", callback.inputHandler); } /** * Register a release event, taking place after a gesture * * @param {Hammer} hammer A hammer instance * @param {Function} callback Callback, called as callback(event) * @returns {*} */ function onRelease(hammer, callback) { callback.inputHandler = function (event) { if (event.isFinal) { callback(event); } }; return hammer.on("hammer.input", callback.inputHandler); } /** * Create the main frame for the Network. * This function is executed once when a Network object is created. The frame * contains a canvas, and this canvas contains all objects like the axis and * nodes. */ var Canvas = /*#__PURE__*/function () { /** * @param {object} body */ function Canvas(body) { _classCallCheck(this, Canvas); this.body = body; this.pixelRatio = 1; this.cameraState = {}; this.initialized = false; this.canvasViewCenter = {}; this._cleanupCallbacks = []; this.options = {}; this.defaultOptions = { autoResize: true, height: "100%", width: "100%" }; _Object$assign(this.options, this.defaultOptions); this.bindEventListeners(); } /** * Binds event listeners */ _createClass(Canvas, [{ key: "bindEventListeners", value: function bindEventListeners() { var _this = this, _context; // bind the events this.body.emitter.once("resize", function (obj) { if (obj.width !== 0) { _this.body.view.translation.x = obj.width * 0.5; } if (obj.height !== 0) { _this.body.view.translation.y = obj.height * 0.5; } }); this.body.emitter.on("setSize", _bindInstanceProperty$1(_context = this.setSize).call(_context, this)); this.body.emitter.on("destroy", function () { _this.hammerFrame.destroy(); _this.hammer.destroy(); _this._cleanUp(); }); } /** * @param {object} options */ }, { key: "setOptions", value: function setOptions(options) { var _this2 = this; if (options !== undefined) { var fields = ["width", "height", "autoResize"]; selectiveDeepExtend(fields, this.options, options); } // Automatically adapt to changing size of the container element. this._cleanUp(); if (this.options.autoResize === true) { var _context2; if (window.ResizeObserver) { // decent browsers, immediate reactions var observer = new ResizeObserver(function () { var changed = _this2.setSize(); if (changed === true) { _this2.body.emitter.emit("_requestRedraw"); } }); var frame = this.frame; observer.observe(frame); this._cleanupCallbacks.push(function () { observer.unobserve(frame); }); } else { // IE11, continous polling var resizeTimer = _setInterval(function () { var changed = _this2.setSize(); if (changed === true) { _this2.body.emitter.emit("_requestRedraw"); } }, 1000); this._cleanupCallbacks.push(function () { clearInterval(resizeTimer); }); } // Automatically adapt to changing size of the browser. var resizeFunction = _bindInstanceProperty$1(_context2 = this._onResize).call(_context2, this); addEventListener(window, "resize", resizeFunction); this._cleanupCallbacks.push(function () { removeEventListener(window, "resize", resizeFunction); }); } } /** * @private */ }, { key: "_cleanUp", value: function _cleanUp() { var _context3, _context4, _context5; _forEachInstanceProperty(_context3 = _reverseInstanceProperty(_context4 = _spliceInstanceProperty(_context5 = this._cleanupCallbacks).call(_context5, 0)).call(_context4)).call(_context3, function (callback) { try { callback(); } catch (error) { console.error(error); } }); } /** * @private */ }, { key: "_onResize", value: function _onResize() { this.setSize(); this.body.emitter.emit("_redraw"); } /** * Get and store the cameraState * * @param {number} [pixelRatio=this.pixelRatio] * @private */ }, { key: "_getCameraState", value: function _getCameraState() { var pixelRatio = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.pixelRatio; if (this.initialized === true) { this.cameraState.previousWidth = this.frame.canvas.width / pixelRatio; this.cameraState.previousHeight = this.frame.canvas.height / pixelRatio; this.cameraState.scale = this.body.view.scale; this.cameraState.position = this.DOMtoCanvas({ x: 0.5 * this.frame.canvas.width / pixelRatio, y: 0.5 * this.frame.canvas.height / pixelRatio }); } } /** * Set the cameraState * * @private */ }, { key: "_setCameraState", value: function _setCameraState() { if (this.cameraState.scale !== undefined && this.frame.canvas.clientWidth !== 0 && this.frame.canvas.clientHeight !== 0 && this.pixelRatio !== 0 && this.cameraState.previousWidth > 0 && this.cameraState.previousHeight > 0) { var widthRatio = this.frame.canvas.width / this.pixelRatio / this.cameraState.previousWidth; var heightRatio = this.frame.canvas.height / this.pixelRatio / this.cameraState.previousHeight; var newScale = this.cameraState.scale; if (widthRatio != 1 && heightRatio != 1) { newScale = this.cameraState.scale * 0.5 * (widthRatio + heightRatio); } else if (widthRatio != 1) { newScale = this.cameraState.scale * widthRatio; } else if (heightRatio != 1) { newScale = this.cameraState.scale * heightRatio; } this.body.view.scale = newScale; // this comes from the view module. var currentViewCenter = this.DOMtoCanvas({ x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight }); var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node x: currentViewCenter.x - this.cameraState.position.x, y: currentViewCenter.y - this.cameraState.position.y }; this.body.view.translation.x += distanceFromCenter.x * this.body.view.scale; this.body.view.translation.y += distanceFromCenter.y * this.body.view.scale; } } /** * * @param {number|string} value * @returns {string} * @private */ }, { key: "_prepareValue", value: function _prepareValue(value) { if (typeof value === "number") { return value + "px"; } else if (typeof value === "string") { if (_indexOfInstanceProperty(value).call(value, "%") !== -1 || _indexOfInstanceProperty(value).call(value, "px") !== -1) { return value; } else if (_indexOfInstanceProperty(value).call(value, "%") === -1) { return value + "px"; } } throw new Error("Could not use the value supplied for width or height:" + value); } /** * Create the HTML */ }, { key: "_create", value: function _create() { // remove all elements from the container element. while (this.body.container.hasChildNodes()) { this.body.container.removeChild(this.body.container.firstChild); } this.frame = document.createElement("div"); this.frame.className = "vis-network"; this.frame.style.position = "relative"; this.frame.style.overflow = "hidden"; this.frame.tabIndex = 0; // tab index is required for keycharm to bind keystrokes to the div instead of the window ////////////////////////////////////////////////////////////////// this.frame.canvas = document.createElement("canvas"); this.frame.canvas.style.position = "relative"; this.frame.appendChild(this.frame.canvas); if (!this.frame.canvas.getContext) { var noCanvas = document.createElement("DIV"); noCanvas.style.color = "red"; noCanvas.style.fontWeight = "bold"; noCanvas.style.padding = "10px"; noCanvas.innerText = "Error: your browser does not support HTML canvas"; this.frame.canvas.appendChild(noCanvas); } else { this._setPixelRatio(); this.setTransform(); } // add the frame to the container element this.body.container.appendChild(this.frame); this.body.view.scale = 1; this.body.view.translation = { x: 0.5 * this.frame.canvas.clientWidth, y: 0.5 * this.frame.canvas.clientHeight }; this._bindHammer(); } /** * This function binds hammer, it can be repeated over and over due to the uniqueness check. * * @private */ }, { key: "_bindHammer", value: function _bindHammer() { var _this3 = this; if (this.hammer !== undefined) { this.hammer.destroy(); } this.drag = {}; this.pinch = {}; // init hammer this.hammer = new Hammer(this.frame.canvas); this.hammer.get("pinch").set({ enable: true }); // enable to get better response, todo: test on mobile. this.hammer.get("pan").set({ threshold: 5, direction: Hammer.DIRECTION_ALL }); onTouch(this.hammer, function (event) { _this3.body.eventListeners.onTouch(event); }); this.hammer.on("tap", function (event) { _this3.body.eventListeners.onTap(event); }); this.hammer.on("doubletap", function (event) { _this3.body.eventListeners.onDoubleTap(event); }); this.hammer.on("press", function (event) { _this3.body.eventListeners.onHold(event); }); this.hammer.on("panstart", function (event) { _this3.body.eventListeners.onDragStart(event); }); this.hammer.on("panmove", function (event) { _this3.body.eventListeners.onDrag(event); }); this.hammer.on("panend", function (event) { _this3.body.eventListeners.onDragEnd(event); }); this.hammer.on("pinch", function (event) { _this3.body.eventListeners.onPinch(event); }); // TODO: neatly cleanup these handlers when re-creating the Canvas, IF these are done with hammer, event.stopPropagation will not work? this.frame.canvas.addEventListener("wheel", function (event) { _this3.body.eventListeners.onMouseWheel(event); }); this.frame.canvas.addEventListener("mousemove", function (event) { _this3.body.eventListeners.onMouseMove(event); }); this.frame.canvas.addEventListener("contextmenu", function (event) { _this3.body.eventListeners.onContext(event); }); this.hammerFrame = new Hammer(this.frame); onRelease(this.hammerFrame, function (event) { _this3.body.eventListeners.onRelease(event); }); } /** * Set a new size for the network * * @param {string} width Width in pixels or percentage (for example '800px' * or '50%') * @param {string} height Height in pixels or percentage (for example '400px' * or '30%') * @returns {boolean} */ }, { key: "setSize", value: function setSize() { var width = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.width; var height = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.options.height; width = this._prepareValue(width); height = this._prepareValue(height); var emitEvent = false; var oldWidth = this.frame.canvas.width; var oldHeight = this.frame.canvas.height; // update the pixel ratio // // NOTE: Comment in following is rather inconsistent; this is the ONLY place in the code // where it is assumed that the pixel ratio could change at runtime. // The only way I can think of this happening is a rotating screen or tablet; but then // there should be a mechanism for reloading the data (TODO: check if this is present). // // If the assumption is true (i.e. pixel ratio can change at runtime), then *all* usage // of pixel ratio must be overhauled for this. // // For the time being, I will humor the assumption here, and in the rest of the code assume it is // constant. var previousRatio = this.pixelRatio; // we cache this because the camera state storage needs the old value this._setPixelRatio(); if (width != this.options.width || height != this.options.height || this.frame.style.width != width || this.frame.style.height != height) { this._getCameraState(previousRatio); this.frame.style.width = width; this.frame.style.height = height; this.frame.canvas.style.width = "100%"; this.frame.canvas.style.height = "100%"; this.frame.canvas.width = Math.round(this.frame.canvas.clientWidth * this.pixelRatio); this.frame.canvas.height = Math.round(this.frame.canvas.clientHeight * this.pixelRatio); this.options.width = width; this.options.height = height; this.canvasViewCenter = { x: 0.5 * this.frame.clientWidth, y: 0.5 * this.frame.clientHeight }; emitEvent = true; } else { // this would adapt the width of the canvas to the width from 100% if and only if // there is a change. var newWidth = Math.round(this.frame.canvas.clientWidth * this.pixelRatio); var newHeight = Math.round(this.frame.canvas.clientHeight * this.pixelRatio); // store the camera if there is a change in size. if (this.frame.canvas.width !== newWidth || this.frame.canvas.height !== newHeight) { this._getCameraState(previousRatio); } if (this.frame.canvas.width !== newWidth) { this.frame.canvas.width = newWidth; emitEvent = true; } if (this.frame.canvas.height !== newHeight) { this.frame.canvas.height = newHeight; emitEvent = true; } } if (emitEvent === true) { this.body.emitter.emit("resize", { width: Math.round(this.frame.canvas.width / this.pixelRatio), height: Math.round(this.frame.canvas.height / this.pixelRatio), oldWidth: Math.round(oldWidth / this.pixelRatio), oldHeight: Math.round(oldHeight / this.pixelRatio) }); // restore the camera on change. this._setCameraState(); } // set initialized so the get and set camera will work from now on. this.initialized = true; return emitEvent; } /** * * @returns {CanvasRenderingContext2D} */ }, { key: "getContext", value: function getContext() { return this.frame.canvas.getContext("2d"); } /** * Determine the pixel ratio for various browsers. * * @returns {number} * @private */ }, { key: "_determinePixelRatio", value: function _determinePixelRatio() { var ctx = this.getContext(); if (ctx === undefined) { throw new Error("Could not get canvax context"); } var numerator = 1; if (typeof window !== "undefined") { // (window !== undefined) doesn't work here! // Protection during unit tests, where 'window' can be missing numerator = window.devicePixelRatio || 1; } var denominator = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1; return numerator / denominator; } /** * Lazy determination of pixel ratio. * * @private */ }, { key: "_setPixelRatio", value: function _setPixelRatio() { this.pixelRatio = this._determinePixelRatio(); } /** * Set the transform in the contained context, based on its pixelRatio */ }, { key: "setTransform", value: function setTransform() { var ctx = this.getContext(); if (ctx === undefined) { throw new Error("Could not get canvax context"); } ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0); } /** * Convert the X coordinate in DOM-space (coordinate point in browser relative to the container div) to * the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) * * @param {number} x * @returns {number} * @private */ }, { key: "_XconvertDOMtoCanvas", value: function _XconvertDOMtoCanvas(x) { return (x - this.body.view.translation.x) / this.body.view.scale; } /** * Convert the X coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to * the X coordinate in DOM-space (coordinate point in browser relative to the container div) * * @param {number} x * @returns {number} * @private */ }, { key: "_XconvertCanvasToDOM", value: function _XconvertCanvasToDOM(x) { return x * this.body.view.scale + this.body.view.translation.x; } /** * Convert the Y coordinate in DOM-space (coordinate point in browser relative to the container div) to * the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) * * @param {number} y * @returns {number} * @private */ }, { key: "_YconvertDOMtoCanvas", value: function _YconvertDOMtoCanvas(y) { return (y - this.body.view.translation.y) / this.body.view.scale; } /** * Convert the Y coordinate in canvas-space (the simulation sandbox, which the camera looks upon) to * the Y coordinate in DOM-space (coordinate point in browser relative to the container div) * * @param {number} y * @returns {number} * @private */ }, { key: "_YconvertCanvasToDOM", value: function _YconvertCanvasToDOM(y) { return y * this.body.view.scale + this.body.view.translation.y; } /** * @param {point} pos * @returns {point} */ }, { key: "canvasToDOM", value: function canvasToDOM(pos) { return { x: this._XconvertCanvasToDOM(pos.x), y: this._YconvertCanvasToDOM(pos.y) }; } /** * * @param {point} pos * @returns {point} */ }, { key: "DOMtoCanvas", value: function DOMtoCanvas(pos) { return { x: this._XconvertDOMtoCanvas(pos.x), y: this._YconvertDOMtoCanvas(pos.y) }; } }]); return Canvas; }(); /** * Validate the fit options, replace missing optional values by defaults etc. * * @param rawOptions - The raw options. * @param allNodeIds - All node ids that will be used if nodes are omitted in * the raw options. * @returns Options with everything filled in and validated. */ function normalizeFitOptions(rawOptions, allNodeIds) { var options = _Object$assign({ nodes: allNodeIds, minZoomLevel: Number.MIN_VALUE, maxZoomLevel: 1 }, rawOptions !== null && rawOptions !== void 0 ? rawOptions : {}); if (!_Array$isArray(options.nodes)) { throw new TypeError("Nodes has to be an array of ids."); } if (options.nodes.length === 0) { options.nodes = allNodeIds; } if (!(typeof options.minZoomLevel === "number" && options.minZoomLevel > 0)) { throw new TypeError("Min zoom level has to be a number higher than zero."); } if (!(typeof options.maxZoomLevel === "number" && options.minZoomLevel <= options.maxZoomLevel)) { throw new TypeError("Max zoom level has to be a number higher than min zoom level."); } return options; } /** * The view */ var View = /*#__PURE__*/function () { /** * @param {object} body * @param {Canvas} canvas */ function View(body, canvas) { var _context, _this = this, _context2; _classCallCheck(this, View); this.body = body; this.canvas = canvas; this.animationSpeed = 1 / this.renderRefreshRate; this.animationEasingFunction = "easeInOutQuint"; this.easingTime = 0; this.sourceScale = 0; this.targetScale = 0; this.sourceTranslation = 0; this.targetTranslation = 0; this.lockedOnNodeId = undefined; this.lockedOnNodeOffset = undefined; this.touchTime = 0; this.viewFunction = undefined; this.body.emitter.on("fit", _bindInstanceProperty$1(_context = this.fit).call(_context, this)); this.body.emitter.on("animationFinished", function () { _this.body.emitter.emit("_stopRendering"); }); this.body.emitter.on("unlockNode", _bindInstanceProperty$1(_context2 = this.releaseNode).call(_context2, this)); } /** * * @param {object} [options={}] */ _createClass(View, [{ key: "setOptions", value: function setOptions() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; this.options = options; } /** * This function zooms out to fit all data on screen based on amount of nodes * * @param {object} [options={{nodes=Array}}] * @param options * @param {boolean} [initialZoom=false] | zoom based on fitted formula or range, true = fitted, default = false; */ }, { key: "fit", value: function fit(options) { var initialZoom = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; options = normalizeFitOptions(options, this.body.nodeIndices); var canvasWidth = this.canvas.frame.canvas.clientWidth; var canvasHeight = this.canvas.frame.canvas.clientHeight; var range; var zoomLevel; if (canvasWidth === 0 || canvasHeight === 0) { // There's no point in trying to fit into zero sized canvas. This could // potentially even result in invalid values being computed. For example // for network without nodes and zero sized canvas the zoom level would // end up being computed as 0/0 which results in NaN. In any other case // this would be 0/something which is again pointless to compute. zoomLevel = 1; range = NetworkUtil.getRange(this.body.nodes, options.nodes); } else if (initialZoom === true) { // check if more than half of the nodes have a predefined position. If so, we use the range, not the approximation. var positionDefined = 0; for (var nodeId in this.body.nodes) { if (Object.prototype.hasOwnProperty.call(this.body.nodes, nodeId)) { var node = this.body.nodes[nodeId]; if (node.predefinedPosition === true) { positionDefined += 1; } } } if (positionDefined > 0.5 * this.body.nodeIndices.length) { this.fit(options, false); return; } range = NetworkUtil.getRange(this.body.nodes, options.nodes); var numberOfNodes = this.body.nodeIndices.length; zoomLevel = 12.662 / (numberOfNodes + 7.4147) + 0.0964822; // this is obtained from fitting a dataset from 5 points with scale levels that looked good. // correct for larger canvasses. var factor = Math.min(canvasWidth / 600, canvasHeight / 600); zoomLevel *= factor; } else { this.body.emitter.emit("_resizeNodes"); range = NetworkUtil.getRange(this.body.nodes, options.nodes); var xDistance = Math.abs(range.maxX - range.minX) * 1.1; var yDistance = Math.abs(range.maxY - range.minY) * 1.1; var xZoomLevel = canvasWidth / xDistance; var yZoomLevel = canvasHeight / yDistance; zoomLevel = xZoomLevel <= yZoomLevel ? xZoomLevel : yZoomLevel; } if (zoomLevel > options.maxZoomLevel) { zoomLevel = options.maxZoomLevel; } else if (zoomLevel < options.minZoomLevel) { zoomLevel = options.minZoomLevel; } var center = NetworkUtil.findCenter(range); var animationOptions = { position: center, scale: zoomLevel, animation: options.animation }; this.moveTo(animationOptions); } // animation /** * Center a node in view. * * @param {number} nodeId * @param {number} [options] */ }, { key: "focus", value: function focus(nodeId) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (this.body.nodes[nodeId] !== undefined) { var nodePosition = { x: this.body.nodes[nodeId].x, y: this.body.nodes[nodeId].y }; options.position = nodePosition; options.lockedOnNode = nodeId; this.moveTo(options); } else { console.error("Node: " + nodeId + " cannot be found."); } } /** * * @param {object} options | options.offset = {x:number, y:number} // offset from the center in DOM pixels * | options.scale = number // scale to move to * | options.position = {x:number, y:number} // position to move to * | options.animation = {duration:number, easingFunction:String} || Boolean // position to move to */ }, { key: "moveTo", value: function moveTo(options) { if (options === undefined) { options = {}; return; } if (options.offset != null) { if (options.offset.x != null) { // Coerce and verify that x is valid. options.offset.x = +options.offset.x; if (!_Number$isFinite(options.offset.x)) { throw new TypeError('The option "offset.x" has to be a finite number.'); } } else { options.offset.x = 0; } if (options.offset.y != null) { // Coerce and verify that y is valid. options.offset.y = +options.offset.y; if (!_Number$isFinite(options.offset.y)) { throw new TypeError('The option "offset.y" has to be a finite number.'); } } else { options.offset.x = 0; } } else { options.offset = { x: 0, y: 0 }; } if (options.position != null) { if (options.position.x != null) { // Coerce and verify that x is valid. options.position.x = +options.position.x; if (!_Number$isFinite(options.position.x)) { throw new TypeError('The option "position.x" has to be a finite number.'); } } else { options.position.x = 0; } if (options.position.y != null) { // Coerce and verify that y is valid. options.position.y = +options.position.y; if (!_Number$isFinite(options.position.y)) { throw new TypeError('The option "position.y" has to be a finite number.'); } } else { options.position.x = 0; } } else { options.position = this.getViewPosition(); } if (options.scale != null) { // Coerce and verify that the scale is valid. options.scale = +options.scale; if (!(options.scale > 0)) { throw new TypeError('The option "scale" has to be a number greater than zero.'); } } else { options.scale = this.body.view.scale; } if (options.animation === undefined) { options.animation = { duration: 0 }; } if (options.animation === false) { options.animation = { duration: 0 }; } if (options.animation === true) { options.animation = {}; } if (options.animation.duration === undefined) { options.animation.duration = 1000; } // default duration if (options.animation.easingFunction === undefined) { options.animation.easingFunction = "easeInOutQuad"; } // default easing function this.animateView(options); } /** * * @param {object} options | options.offset = {x:number, y:number} // offset from the center in DOM pixels * | options.time = number // animation time in milliseconds * | options.scale = number // scale to animate to * | options.position = {x:number, y:number} // position to animate to * | options.easingFunction = String // linear, easeInQuad, easeOutQuad, easeInOutQuad, * // easeInCubic, easeOutCubic, easeInOutCubic, * // easeInQuart, easeOutQuart, easeInOutQuart, * // easeInQuint, easeOutQuint, easeInOutQuint */ }, { key: "animateView", value: function animateView(options) { if (options === undefined) { return; } this.animationEasingFunction = options.animation.easingFunction; // release if something focussed on the node this.releaseNode(); if (options.locked === true) { this.lockedOnNodeId = options.lockedOnNode; this.lockedOnNodeOffset = options.offset; } // forcefully complete the old animation if it was still running if (this.easingTime != 0) { this._transitionRedraw(true); // by setting easingtime to 1, we finish the animation. } this.sourceScale = this.body.view.scale; this.sourceTranslation = this.body.view.translation; this.targetScale = options.scale; // set the scale so the viewCenter is based on the correct zoom level. This is overridden in the transitionRedraw // but at least then we'll have the target transition this.body.view.scale = this.targetScale; var viewCenter = this.canvas.DOMtoCanvas({ x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight }); var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node x: viewCenter.x - options.position.x, y: viewCenter.y - options.position.y }; this.targetTranslation = { x: this.sourceTranslation.x + distanceFromCenter.x * this.targetScale + options.offset.x, y: this.sourceTranslation.y + distanceFromCenter.y * this.targetScale + options.offset.y }; // if the time is set to 0, don't do an animation if (options.animation.duration === 0) { if (this.lockedOnNodeId != undefined) { var _context3; this.viewFunction = _bindInstanceProperty$1(_context3 = this._lockedRedraw).call(_context3, this); this.body.emitter.on("initRedraw", this.viewFunction); } else { this.body.view.scale = this.targetScale; this.body.view.translation = this.targetTranslation; this.body.emitter.emit("_requestRedraw"); } } else { var _context4; this.animationSpeed = 1 / (60 * options.animation.duration * 0.001) || 1 / 60; // 60 for 60 seconds, 0.001 for milli's this.animationEasingFunction = options.animation.easingFunction; this.viewFunction = _bindInstanceProperty$1(_context4 = this._transitionRedraw).call(_context4, this); this.body.emitter.on("initRedraw", this.viewFunction); this.body.emitter.emit("_startRendering"); } } /** * used to animate smoothly by hijacking the redraw function. * * @private */ }, { key: "_lockedRedraw", value: function _lockedRedraw() { var nodePosition = { x: this.body.nodes[this.lockedOnNodeId].x, y: this.body.nodes[this.lockedOnNodeId].y }; var viewCenter = this.canvas.DOMtoCanvas({ x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight }); var distanceFromCenter = { // offset from view, distance view has to change by these x and y to center the node x: viewCenter.x - nodePosition.x, y: viewCenter.y - nodePosition.y }; var sourceTranslation = this.body.view.translation; var targetTranslation = { x: sourceTranslation.x + distanceFromCenter.x * this.body.view.scale + this.lockedOnNodeOffset.x, y: sourceTranslation.y + distanceFromCenter.y * this.body.view.scale + this.lockedOnNodeOffset.y }; this.body.view.translation = targetTranslation; } /** * Resets state of a locked on Node */ }, { key: "releaseNode", value: function releaseNode() { if (this.lockedOnNodeId !== undefined && this.viewFunction !== undefined) { this.body.emitter.off("initRedraw", this.viewFunction); this.lockedOnNodeId = undefined; this.lockedOnNodeOffset = undefined; } } /** * @param {boolean} [finished=false] * @private */ }, { key: "_transitionRedraw", value: function _transitionRedraw() { var finished = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; this.easingTime += this.animationSpeed; this.easingTime = finished === true ? 1.0 : this.easingTime; var progress = easingFunctions[this.animationEasingFunction](this.easingTime); this.body.view.scale = this.sourceScale + (this.targetScale - this.sourceScale) * progress; this.body.view.translation = { x: this.sourceTranslation.x + (this.targetTranslation.x - this.sourceTranslation.x) * progress, y: this.sourceTranslation.y + (this.targetTranslation.y - this.sourceTranslation.y) * progress }; // cleanup if (this.easingTime >= 1.0) { this.body.emitter.off("initRedraw", this.viewFunction); this.easingTime = 0; if (this.lockedOnNodeId != undefined) { var _context5; this.viewFunction = _bindInstanceProperty$1(_context5 = this._lockedRedraw).call(_context5, this); this.body.emitter.on("initRedraw", this.viewFunction); } this.body.emitter.emit("animationFinished"); } } /** * * @returns {number} */ }, { key: "getScale", value: function getScale() { return this.body.view.scale; } /** * * @returns {{x: number, y: number}} */ }, { key: "getViewPosition", value: function getViewPosition() { return this.canvas.DOMtoCanvas({ x: 0.5 * this.canvas.frame.canvas.clientWidth, y: 0.5 * this.canvas.frame.canvas.clientHeight }); } }]); return View; }(); /** * Created by Alex on 11/6/2014. */ function keycharm(options) { var preventDefault = options && options.preventDefault || false; var container = options && options.container || window; var _exportFunctions = {}; var _bound = { keydown: {}, keyup: {} }; var _keys = {}; var i; // a - z for (i = 97; i <= 122; i++) { _keys[String.fromCharCode(i)] = { code: 65 + (i - 97), shift: false }; } // A - Z for (i = 65; i <= 90; i++) { _keys[String.fromCharCode(i)] = { code: i, shift: true }; } // 0 - 9 for (i = 0; i <= 9; i++) { _keys['' + i] = { code: 48 + i, shift: false }; } // F1 - F12 for (i = 1; i <= 12; i++) { _keys['F' + i] = { code: 111 + i, shift: false }; } // num0 - num9 for (i = 0; i <= 9; i++) { _keys['num' + i] = { code: 96 + i, shift: false }; } // numpad misc _keys['num*'] = { code: 106, shift: false }; _keys['num+'] = { code: 107, shift: false }; _keys['num-'] = { code: 109, shift: false }; _keys['num/'] = { code: 111, shift: false }; _keys['num.'] = { code: 110, shift: false }; // arrows _keys['left'] = { code: 37, shift: false }; _keys['up'] = { code: 38, shift: false }; _keys['right'] = { code: 39, shift: false }; _keys['down'] = { code: 40, shift: false }; // extra keys _keys['space'] = { code: 32, shift: false }; _keys['enter'] = { code: 13, shift: false }; _keys['shift'] = { code: 16, shift: undefined }; _keys['esc'] = { code: 27, shift: false }; _keys['backspace'] = { code: 8, shift: false }; _keys['tab'] = { code: 9, shift: false }; _keys['ctrl'] = { code: 17, shift: false }; _keys['alt'] = { code: 18, shift: false }; _keys['delete'] = { code: 46, shift: false }; _keys['pageup'] = { code: 33, shift: false }; _keys['pagedown'] = { code: 34, shift: false }; // symbols _keys['='] = { code: 187, shift: false }; _keys['-'] = { code: 189, shift: false }; _keys[']'] = { code: 221, shift: false }; _keys['['] = { code: 219, shift: false }; var down = function (event) { handleEvent(event, 'keydown'); }; var up = function (event) { handleEvent(event, 'keyup'); }; // handle the actualy bound key with the event var handleEvent = function (event, type) { if (_bound[type][event.keyCode] !== undefined) { var bound = _bound[type][event.keyCode]; for (var i = 0; i < bound.length; i++) { if (bound[i].shift === undefined) { bound[i].fn(event); } else if (bound[i].shift == true && event.shiftKey == true) { bound[i].fn(event); } else if (bound[i].shift == false && event.shiftKey == false) { bound[i].fn(event); } } if (preventDefault == true) { event.preventDefault(); } } }; // bind a key to a callback _exportFunctions.bind = function (key, callback, type) { if (type === undefined) { type = 'keydown'; } if (_keys[key] === undefined) { throw new Error("unsupported key: " + key); } if (_bound[type][_keys[key].code] === undefined) { _bound[type][_keys[key].code] = []; } _bound[type][_keys[key].code].push({ fn: callback, shift: _keys[key].shift }); }; // bind all keys to a call back (demo purposes) _exportFunctions.bindAll = function (callback, type) { if (type === undefined) { type = 'keydown'; } for (var key in _keys) { if (_keys.hasOwnProperty(key)) { _exportFunctions.bind(key, callback, type); } } }; // get the key label from an event _exportFunctions.getKey = function (event) { for (var key in _keys) { if (_keys.hasOwnProperty(key)) { if (event.shiftKey == true && _keys[key].shift == true && event.keyCode == _keys[key].code) { return key; } else if (event.shiftKey == false && _keys[key].shift == false && event.keyCode == _keys[key].code) { return key; } else if (event.keyCode == _keys[key].code && key == 'shift') { return key; } } } return "unknown key, currently not supported"; }; // unbind either a specific callback from a key or all of them (by leaving callback undefined) _exportFunctions.unbind = function (key, callback, type) { if (type === undefined) { type = 'keydown'; } if (_keys[key] === undefined) { throw new Error("unsupported key: " + key); } if (callback !== undefined) { var newBindings = []; var bound = _bound[type][_keys[key].code]; if (bound !== undefined) { for (var i = 0; i < bound.length; i++) { if (!(bound[i].fn == callback && bound[i].shift == _keys[key].shift)) { newBindings.push(_bound[type][_keys[key].code][i]); } } } _bound[type][_keys[key].code] = newBindings; } else { _bound[type][_keys[key].code] = []; } }; // reset all bound variables. _exportFunctions.reset = function () { _bound = { keydown: {}, keyup: {} }; }; // unbind all listeners and reset all variables. _exportFunctions.destroy = function () { _bound = { keydown: {}, keyup: {} }; container.removeEventListener('keydown', down, true); container.removeEventListener('keyup', up, true); }; // create listeners. container.addEventListener('keydown', down, true); container.addEventListener('keyup', up, true); // return the public functions. return _exportFunctions; } /** * Navigation Handler */ var NavigationHandler = /*#__PURE__*/function () { /** * @param {object} body * @param {Canvas} canvas */ function NavigationHandler(body, canvas) { var _this = this; _classCallCheck(this, NavigationHandler); this.body = body; this.canvas = canvas; this.iconsCreated = false; this.navigationHammers = []; this.boundFunctions = {}; this.touchTime = 0; this.activated = false; this.body.emitter.on("activate", function () { _this.activated = true; _this.configureKeyboardBindings(); }); this.body.emitter.on("deactivate", function () { _this.activated = false; _this.configureKeyboardBindings(); }); this.body.emitter.on("destroy", function () { if (_this.keycharm !== undefined) { _this.keycharm.destroy(); } }); this.options = {}; } /** * * @param {object} options */ _createClass(NavigationHandler, [{ key: "setOptions", value: function setOptions(options) { if (options !== undefined) { this.options = options; this.create(); } } /** * Creates or refreshes navigation and sets key bindings */ }, { key: "create", value: function create() { if (this.options.navigationButtons === true) { if (this.iconsCreated === false) { this.loadNavigationElements(); } } else if (this.iconsCreated === true) { this.cleanNavigation(); } this.configureKeyboardBindings(); } /** * Cleans up previous navigation items */ }, { key: "cleanNavigation", value: function cleanNavigation() { // clean hammer bindings if (this.navigationHammers.length != 0) { for (var i = 0; i < this.navigationHammers.length; i++) { this.navigationHammers[i].destroy(); } this.navigationHammers = []; } // clean up previous navigation items if (this.navigationDOM && this.navigationDOM["wrapper"] && this.navigationDOM["wrapper"].parentNode) { this.navigationDOM["wrapper"].parentNode.removeChild(this.navigationDOM["wrapper"]); } this.iconsCreated = false; } /** * Creation of the navigation controls nodes. They are drawn over the rest of the nodes and are not affected by scale and translation * they have a triggerFunction which is called on click. If the position of the navigation controls is dependent * on this.frame.canvas.clientWidth or this.frame.canvas.clientHeight, we flag horizontalAlignLeft and verticalAlignTop false. * This means that the location will be corrected by the _relocateNavigation function on a size change of the canvas. * * @private */ }, { key: "loadNavigationElements", value: function loadNavigationElements() { var _this2 = this; this.cleanNavigation(); this.navigationDOM = {}; var navigationDivs = ["up", "down", "left", "right", "zoomIn", "zoomOut", "zoomExtends"]; var navigationDivActions = ["_moveUp", "_moveDown", "_moveLeft", "_moveRight", "_zoomIn", "_zoomOut", "_fit"]; this.navigationDOM["wrapper"] = document.createElement("div"); this.navigationDOM["wrapper"].className = "vis-navigation"; this.canvas.frame.appendChild(this.navigationDOM["wrapper"]); for (var i = 0; i < navigationDivs.length; i++) { this.navigationDOM[navigationDivs[i]] = document.createElement("div"); this.navigationDOM[navigationDivs[i]].className = "vis-button vis-" + navigationDivs[i]; this.navigationDOM["wrapper"].appendChild(this.navigationDOM[navigationDivs[i]]); var hammer = new Hammer(this.navigationDOM[navigationDivs[i]]); if (navigationDivActions[i] === "_fit") { var _context; onTouch(hammer, _bindInstanceProperty$1(_context = this._fit).call(_context, this)); } else { var _context2; onTouch(hammer, _bindInstanceProperty$1(_context2 = this.bindToRedraw).call(_context2, this, navigationDivActions[i])); } this.navigationHammers.push(hammer); } // use a hammer for the release so we do not require the one used in the rest of the network // the one the rest uses can be overloaded by the manipulation system. var hammerFrame = new Hammer(this.canvas.frame); onRelease(hammerFrame, function () { _this2._stopMovement(); }); this.navigationHammers.push(hammerFrame); this.iconsCreated = true; } /** * * @param {string} action */ }, { key: "bindToRedraw", value: function bindToRedraw(action) { if (this.boundFunctions[action] === undefined) { var _context3; this.boundFunctions[action] = _bindInstanceProperty$1(_context3 = this[action]).call(_context3, this); this.body.emitter.on("initRedraw", this.boundFunctions[action]); this.body.emitter.emit("_startRendering"); } } /** * * @param {string} action */ }, { key: "unbindFromRedraw", value: function unbindFromRedraw(action) { if (this.boundFunctions[action] !== undefined) { this.body.emitter.off("initRedraw", this.boundFunctions[action]); this.body.emitter.emit("_stopRendering"); delete this.boundFunctions[action]; } } /** * this stops all movement induced by the navigation buttons * * @private */ }, { key: "_fit", value: function _fit() { if (new Date().valueOf() - this.touchTime > 700) { // TODO: fix ugly hack to avoid hammer's double fireing of event (because we use release?) this.body.emitter.emit("fit", { duration: 700 }); this.touchTime = new Date().valueOf(); } } /** * this stops all movement induced by the navigation buttons * * @private */ }, { key: "_stopMovement", value: function _stopMovement() { for (var boundAction in this.boundFunctions) { if (Object.prototype.hasOwnProperty.call(this.boundFunctions, boundAction)) { this.body.emitter.off("initRedraw", this.boundFunctions[boundAction]); this.body.emitter.emit("_stopRendering"); } } this.boundFunctions = {}; } /** * * @private */ }, { key: "_moveUp", value: function _moveUp() { this.body.view.translation.y += this.options.keyboard.speed.y; } /** * * @private */ }, { key: "_moveDown", value: function _moveDown() { this.body.view.translation.y -= this.options.keyboard.speed.y; } /** * * @private */ }, { key: "_moveLeft", value: function _moveLeft() { this.body.view.translation.x += this.options.keyboard.speed.x; } /** * * @private */ }, { key: "_moveRight", value: function _moveRight() { this.body.view.translation.x -= this.options.keyboard.speed.x; } /** * * @private */ }, { key: "_zoomIn", value: function _zoomIn() { var scaleOld = this.body.view.scale; var scale = this.body.view.scale * (1 + this.options.keyboard.speed.zoom); var translation = this.body.view.translation; var scaleFrac = scale / scaleOld; var tx = (1 - scaleFrac) * this.canvas.canvasViewCenter.x + translation.x * scaleFrac; var ty = (1 - scaleFrac) * this.canvas.canvasViewCenter.y + translation.y * scaleFrac; this.body.view.scale = scale; this.body.view.translation = { x: tx, y: ty }; this.body.emitter.emit("zoom", { direction: "+", scale: this.body.view.scale, pointer: null }); } /** * * @private */ }, { key: "_zoomOut", value: function _zoomOut() { var scaleOld = this.body.view.scale; var scale = this.body.view.scale / (1 + this.options.keyboard.speed.zoom); var translation = this.body.view.translation; var scaleFrac = scale / scaleOld; var tx = (1 - scaleFrac) * this.canvas.canvasViewCenter.x + translation.x * scaleFrac; var ty = (1 - scaleFrac) * this.canvas.canvasViewCenter.y + translation.y * scaleFrac; this.body.view.scale = scale; this.body.view.translation = { x: tx, y: ty }; this.body.emitter.emit("zoom", { direction: "-", scale: this.body.view.scale, pointer: null }); } /** * bind all keys using keycharm. */ }, { key: "configureKeyboardBindings", value: function configureKeyboardBindings() { var _this3 = this; if (this.keycharm !== undefined) { this.keycharm.destroy(); } if (this.options.keyboard.enabled === true) { if (this.options.keyboard.bindToWindow === true) { this.keycharm = keycharm({ container: window, preventDefault: true }); } else { this.keycharm = keycharm({ container: this.canvas.frame, preventDefault: true }); } this.keycharm.reset(); if (this.activated === true) { var _context4, _context5, _context6, _context7, _context8, _context9, _context10, _context11, _context12, _context13, _context14, _context15, _context16, _context17, _context18, _context19, _context20, _context21, _context22, _context23, _context24, _context25, _context26, _context27; _bindInstanceProperty$1(_context4 = this.keycharm).call(_context4, "up", function () { _this3.bindToRedraw("_moveUp"); }, "keydown"); _bindInstanceProperty$1(_context5 = this.keycharm).call(_context5, "down", function () { _this3.bindToRedraw("_moveDown"); }, "keydown"); _bindInstanceProperty$1(_context6 = this.keycharm).call(_context6, "left", function () { _this3.bindToRedraw("_moveLeft"); }, "keydown"); _bindInstanceProperty$1(_context7 = this.keycharm).call(_context7, "right", function () { _this3.bindToRedraw("_moveRight"); }, "keydown"); _bindInstanceProperty$1(_context8 = this.keycharm).call(_context8, "=", function () { _this3.bindToRedraw("_zoomIn"); }, "keydown"); _bindInstanceProperty$1(_context9 = this.keycharm).call(_context9, "num+", function () { _this3.bindToRedraw("_zoomIn"); }, "keydown"); _bindInstanceProperty$1(_context10 = this.keycharm).call(_context10, "num-", function () { _this3.bindToRedraw("_zoomOut"); }, "keydown"); _bindInstanceProperty$1(_context11 = this.keycharm).call(_context11, "-", function () { _this3.bindToRedraw("_zoomOut"); }, "keydown"); _bindInstanceProperty$1(_context12 = this.keycharm).call(_context12, "[", function () { _this3.bindToRedraw("_zoomOut"); }, "keydown"); _bindInstanceProperty$1(_context13 = this.keycharm).call(_context13, "]", function () { _this3.bindToRedraw("_zoomIn"); }, "keydown"); _bindInstanceProperty$1(_context14 = this.keycharm).call(_context14, "pageup", function () { _this3.bindToRedraw("_zoomIn"); }, "keydown"); _bindInstanceProperty$1(_context15 = this.keycharm).call(_context15, "pagedown", function () { _this3.bindToRedraw("_zoomOut"); }, "keydown"); _bindInstanceProperty$1(_context16 = this.keycharm).call(_context16, "up", function () { _this3.unbindFromRedraw("_moveUp"); }, "keyup"); _bindInstanceProperty$1(_context17 = this.keycharm).call(_context17, "down", function () { _this3.unbindFromRedraw("_moveDown"); }, "keyup"); _bindInstanceProperty$1(_context18 = this.keycharm).call(_context18, "left", function () { _this3.unbindFromRedraw("_moveLeft"); }, "keyup"); _bindInstanceProperty$1(_context19 = this.keycharm).call(_context19, "right", function () { _this3.unbindFromRedraw("_moveRight"); }, "keyup"); _bindInstanceProperty$1(_context20 = this.keycharm).call(_context20, "=", function () { _this3.unbindFromRedraw("_zoomIn"); }, "keyup"); _bindInstanceProperty$1(_context21 = this.keycharm).call(_context21, "num+", function () { _this3.unbindFromRedraw("_zoomIn"); }, "keyup"); _bindInstanceProperty$1(_context22 = this.keycharm).call(_context22, "num-", function () { _this3.unbindFromRedraw("_zoomOut"); }, "keyup"); _bindInstanceProperty$1(_context23 = this.keycharm).call(_context23, "-", function () { _this3.unbindFromRedraw("_zoomOut"); }, "keyup"); _bindInstanceProperty$1(_context24 = this.keycharm).call(_context24, "[", function () { _this3.unbindFromRedraw("_zoomOut"); }, "keyup"); _bindInstanceProperty$1(_context25 = this.keycharm).call(_context25, "]", function () { _this3.unbindFromRedraw("_zoomIn"); }, "keyup"); _bindInstanceProperty$1(_context26 = this.keycharm).call(_context26, "pageup", function () { _this3.unbindFromRedraw("_zoomIn"); }, "keyup"); _bindInstanceProperty$1(_context27 = this.keycharm).call(_context27, "pagedown", function () { _this3.unbindFromRedraw("_zoomOut"); }, "keyup"); } } } }]); return NavigationHandler; }(); function _createForOfIteratorHelper$4(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray$4(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray$4(o, minLen) { var _context15; if (!o) return; if (typeof o === "string") return _arrayLikeToArray$4(o, minLen); var n = _sliceInstanceProperty(_context15 = Object.prototype.toString.call(o)).call(_context15, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from$1(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$4(o, minLen); } function _arrayLikeToArray$4(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } /** * Handler for interactions */ var InteractionHandler = /*#__PURE__*/function () { /** * @param {object} body * @param {Canvas} canvas * @param {SelectionHandler} selectionHandler */ function InteractionHandler(body, canvas, selectionHandler) { var _context, _context2, _context3, _context4, _context5, _context6, _context7, _context8, _context9, _context10, _context11, _context12, _context13; _classCallCheck(this, InteractionHandler); this.body = body; this.canvas = canvas; this.selectionHandler = selectionHandler; this.navigationHandler = new NavigationHandler(body, canvas); // bind the events from hammer to functions in this object this.body.eventListeners.onTap = _bindInstanceProperty$1(_context = this.onTap).call(_context, this); this.body.eventListeners.onTouch = _bindInstanceProperty$1(_context2 = this.onTouch).call(_context2, this); this.body.eventListeners.onDoubleTap = _bindInstanceProperty$1(_context3 = this.onDoubleTap).call(_context3, this); this.body.eventListeners.onHold = _bindInstanceProperty$1(_context4 = this.onHold).call(_context4, this); this.body.eventListeners.onDragStart = _bindInstanceProperty$1(_context5 = this.onDragStart).call(_context5, this); this.body.eventListeners.onDrag = _bindInstanceProperty$1(_context6 = this.onDrag).call(_context6, this); this.body.eventListeners.onDragEnd = _bindInstanceProperty$1(_context7 = this.onDragEnd).call(_context7, this); this.body.eventListeners.onMouseWheel = _bindInstanceProperty$1(_context8 = this.onMouseWheel).call(_context8, this); this.body.eventListeners.onPinch = _bindInstanceProperty$1(_context9 = this.onPinch).call(_context9, this); this.body.eventListeners.onMouseMove = _bindInstanceProperty$1(_context10 = this.onMouseMove).call(_context10, this); this.body.eventListeners.onRelease = _bindInstanceProperty$1(_context11 = this.onRelease).call(_context11, this); this.body.eventListeners.onContext = _bindInstanceProperty$1(_context12 = this.onContext).call(_context12, this); this.touchTime = 0; this.drag = {}; this.pinch = {}; this.popup = undefined; this.popupObj = undefined; this.popupTimer = undefined; this.body.functions.getPointer = _bindInstanceProperty$1(_context13 = this.getPointer).call(_context13, this); this.options = {}; this.defaultOptions = { dragNodes: true, dragView: true, hover: false, keyboard: { enabled: false, speed: { x: 10, y: 10, zoom: 0.02 }, bindToWindow: true, autoFocus: true }, navigationButtons: false, tooltipDelay: 300, zoomView: true, zoomSpeed: 1 }; _Object$assign(this.options, this.defaultOptions); this.bindEventListeners(); } /** * Binds event listeners */ _createClass(InteractionHandler, [{ key: "bindEventListeners", value: function bindEventListeners() { var _this = this; this.body.emitter.on("destroy", function () { clearTimeout(_this.popupTimer); delete _this.body.functions.getPointer; }); } /** * * @param {object} options */ }, { key: "setOptions", value: function setOptions(options) { if (options !== undefined) { // extend all but the values in fields var fields = ["hideEdgesOnDrag", "hideEdgesOnZoom", "hideNodesOnDrag", "keyboard", "multiselect", "selectable", "selectConnectedEdges"]; selectiveNotDeepExtend(fields, this.options, options); // merge the keyboard options in. mergeOptions(this.options, options, "keyboard"); if (options.tooltip) { _Object$assign(this.options.tooltip, options.tooltip); if (options.tooltip.color) { this.options.tooltip.color = parseColor(options.tooltip.color); } } } this.navigationHandler.setOptions(this.options); } /** * Get the pointer location from a touch location * * @param {{x: number, y: number}} touch * @returns {{x: number, y: number}} pointer * @private */ }, { key: "getPointer", value: function getPointer(touch) { return { x: touch.x - getAbsoluteLeft(this.canvas.frame.canvas), y: touch.y - getAbsoluteTop(this.canvas.frame.canvas) }; } /** * On start of a touch gesture, store the pointer * * @param {Event} event The event * @private */ }, { key: "onTouch", value: function onTouch(event) { if (new Date().valueOf() - this.touchTime > 50) { this.drag.pointer = this.getPointer(event.center); this.drag.pinched = false; this.pinch.scale = this.body.view.scale; // to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame) this.touchTime = new Date().valueOf(); } } /** * handle tap/click event: select/unselect a node * * @param {Event} event * @private */ }, { key: "onTap", value: function onTap(event) { var pointer = this.getPointer(event.center); var multiselect = this.selectionHandler.options.multiselect && (event.changedPointers[0].ctrlKey || event.changedPointers[0].metaKey); this.checkSelectionChanges(pointer, multiselect); this.selectionHandler.commitAndEmit(pointer, event); this.selectionHandler.generateClickEvent("click", event, pointer); } /** * handle doubletap event * * @param {Event} event * @private */ }, { key: "onDoubleTap", value: function onDoubleTap(event) { var pointer = this.getPointer(event.center); this.selectionHandler.generateClickEvent("doubleClick", event, pointer); } /** * handle long tap event: multi select nodes * * @param {Event} event * @private */ }, { key: "onHold", value: function onHold(event) { var pointer = this.getPointer(event.center); var multiselect = this.selectionHandler.options.multiselect; this.checkSelectionChanges(pointer, multiselect); this.selectionHandler.commitAndEmit(pointer, event); this.selectionHandler.generateClickEvent("click", event, pointer); this.selectionHandler.generateClickEvent("hold", event, pointer); } /** * handle the release of the screen * * @param {Event} event * @private */ }, { key: "onRelease", value: function onRelease(event) { if (new Date().valueOf() - this.touchTime > 10) { var pointer = this.getPointer(event.center); this.selectionHandler.generateClickEvent("release", event, pointer); // to avoid double fireing of this event because we have two hammer instances. (on canvas and on frame) this.touchTime = new Date().valueOf(); } } /** * * @param {Event} event */ }, { key: "onContext", value: function onContext(event) { var pointer = this.getPointer({ x: event.clientX, y: event.clientY }); this.selectionHandler.generateClickEvent("oncontext", event, pointer); } /** * Select and deselect nodes depending current selection change. * * @param {{x: number, y: number}} pointer * @param {boolean} [add=false] */ }, { key: "checkSelectionChanges", value: function checkSelectionChanges(pointer) { var add = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (add === true) { this.selectionHandler.selectAdditionalOnPoint(pointer); } else { this.selectionHandler.selectOnPoint(pointer); } } /** * Remove all node and edge id's from the first set that are present in the second one. * * @param {{nodes: Array.<Node>, edges: Array.<vis.Edge>}} firstSet * @param {{nodes: Array.<Node>, edges: Array.<vis.Edge>}} secondSet * @returns {{nodes: Array.<Node>, edges: Array.<vis.Edge>}} * @private */ }, { key: "_determineDifference", value: function _determineDifference(firstSet, secondSet) { var arrayDiff = function arrayDiff(firstArr, secondArr) { var result = []; for (var i = 0; i < firstArr.length; i++) { var value = firstArr[i]; if (_indexOfInstanceProperty(secondArr).call(secondArr, value) === -1) { result.push(value); } } return result; }; return { nodes: arrayDiff(firstSet.nodes, secondSet.nodes), edges: arrayDiff(firstSet.edges, secondSet.edges) }; } /** * This function is called by onDragStart. * It is separated out because we can then overload it for the datamanipulation system. * * @param {Event} event * @private */ }, { key: "onDragStart", value: function onDragStart(event) { // if already dragging, do not start // this can happen on touch screens with multiple fingers if (this.drag.dragging) { return; } //in case the touch event was triggered on an external div, do the initial touch now. if (this.drag.pointer === undefined) { this.onTouch(event); } // note: drag.pointer is set in onTouch to get the initial touch location var node = this.selectionHandler.getNodeAt(this.drag.pointer); this.drag.dragging = true; this.drag.selection = []; this.drag.translation = _Object$assign({}, this.body.view.translation); // copy the object this.drag.nodeId = undefined; if (event.srcEvent.shiftKey) { this.body.selectionBox.show = true; var pointer = this.getPointer(event.center); this.body.selectionBox.position.start = { x: this.canvas._XconvertDOMtoCanvas(pointer.x), y: this.canvas._YconvertDOMtoCanvas(pointer.y) }; this.body.selectionBox.position.end = { x: this.canvas._XconvertDOMtoCanvas(pointer.x), y: this.canvas._YconvertDOMtoCanvas(pointer.y) }; } else if (node !== undefined && this.options.dragNodes === true) { this.drag.nodeId = node.id; // select the clicked node if not yet selected if (node.isSelected() === false) { this.selectionHandler.setSelection({ nodes: [node.id] }); } // after select to contain the node this.selectionHandler.generateClickEvent("dragStart", event, this.drag.pointer); // create an array with the selected nodes and their original location and status var _iterator = _createForOfIteratorHelper$4(this.selectionHandler.getSelectedNodes()), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var _node = _step.value; var s = { id: _node.id, node: _node, // store original x, y, xFixed and yFixed, make the node temporarily Fixed x: _node.x, y: _node.y, xFixed: _node.options.fixed.x, yFixed: _node.options.fixed.y }; _node.options.fixed.x = true; _node.options.fixed.y = true; this.drag.selection.push(s); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } else { // fallback if no node is selected and thus the view is dragged. this.selectionHandler.generateClickEvent("dragStart", event, this.drag.pointer, undefined, true); } } /** * handle drag event * * @param {Event} event * @private */ }, { key: "onDrag", value: function onDrag(event) { var _this2 = this; if (this.drag.pinched === true) { return; } // remove the focus on node if it is focussed on by the focusOnNode this.body.emitter.emit("unlockNode"); var pointer = this.getPointer(event.center); var selection = this.drag.selection; if (selection && selection.length && this.options.dragNodes === true) { this.selectionHandler.generateClickEvent("dragging", event, pointer); // calculate delta's and new location var deltaX = pointer.x - this.drag.pointer.x; var deltaY = pointer.y - this.drag.pointer.y; // update position of all selected nodes _forEachInstanceProperty(selection).call(selection, function (selection) { var node = selection.node; // only move the node if it was not fixed initially if (selection.xFixed === false) { node.x = _this2.canvas._XconvertDOMtoCanvas(_this2.canvas._XconvertCanvasToDOM(selection.x) + deltaX); } // only move the node if it was not fixed initially if (selection.yFixed === false) { node.y = _this2.canvas._YconvertDOMtoCanvas(_this2.canvas._YconvertCanvasToDOM(selection.y) + deltaY); } }); // start the simulation of the physics this.body.emitter.emit("startSimulation"); } else { // create selection box if (event.srcEvent.shiftKey) { this.selectionHandler.generateClickEvent("dragging", event, pointer, undefined, true); // if the drag was not started properly because the click started outside the network div, start it now. if (this.drag.pointer === undefined) { this.onDragStart(event); return; } this.body.selectionBox.position.end = { x: this.canvas._XconvertDOMtoCanvas(pointer.x), y: this.canvas._YconvertDOMtoCanvas(pointer.y) }; this.body.emitter.emit("_requestRedraw"); } // move the network if (this.options.dragView === true && !event.srcEvent.shiftKey) { this.selectionHandler.generateClickEvent("dragging", event, pointer, undefined, true); // if the drag was not started properly because the click started outside the network div, start it now. if (this.drag.pointer === undefined) { this.onDragStart(event); return; } var diffX = pointer.x - this.drag.pointer.x; var diffY = pointer.y - this.drag.pointer.y; this.body.view.translation = { x: this.drag.translation.x + diffX, y: this.drag.translation.y + diffY }; this.body.emitter.emit("_requestRedraw"); } } } /** * handle drag start event * * @param {Event} event * @private */ }, { key: "onDragEnd", value: function onDragEnd(event) { var _this3 = this; this.drag.dragging = false; if (this.body.selectionBox.show) { var _context14; this.body.selectionBox.show = false; var selectionBoxPosition = this.body.selectionBox.position; var selectionBoxPositionMinMax = { minX: Math.min(selectionBoxPosition.start.x, selectionBoxPosition.end.x), minY: Math.min(selectionBoxPosition.start.y, selectionBoxPosition.end.y), maxX: Math.max(selectionBoxPosition.start.x, selectionBoxPosition.end.x), maxY: Math.max(selectionBoxPosition.start.y, selectionBoxPosition.end.y) }; var toBeSelectedNodes = _filterInstanceProperty(_context14 = this.body.nodeIndices).call(_context14, function (nodeId) { var node = _this3.body.nodes[nodeId]; return node.x >= selectionBoxPositionMinMax.minX && node.x <= selectionBoxPositionMinMax.maxX && node.y >= selectionBoxPositionMinMax.minY && node.y <= selectionBoxPositionMinMax.maxY; }); _forEachInstanceProperty(toBeSelectedNodes).call(toBeSelectedNodes, function (nodeId) { return _this3.selectionHandler.selectObject(_this3.body.nodes[nodeId]); }); var pointer = this.getPointer(event.center); this.selectionHandler.commitAndEmit(pointer, event); this.selectionHandler.generateClickEvent("dragEnd", event, this.getPointer(event.center), undefined, true); this.body.emitter.emit("_requestRedraw"); } else { var selection = this.drag.selection; if (selection && selection.length) { _forEachInstanceProperty(selection).call(selection, function (s) { // restore original xFixed and yFixed s.node.options.fixed.x = s.xFixed; s.node.options.fixed.y = s.yFixed; }); this.selectionHandler.generateClickEvent("dragEnd", event, this.getPointer(event.center)); this.body.emitter.emit("startSimulation"); } else { this.selectionHandler.generateClickEvent("dragEnd", event, this.getPointer(event.center), undefined, true); this.body.emitter.emit("_requestRedraw"); } } } /** * Handle pinch event * * @param {Event} event The event * @private */ }, { key: "onPinch", value: function onPinch(event) { var pointer = this.getPointer(event.center); this.drag.pinched = true; if (this.pinch["scale"] === undefined) { this.pinch.scale = 1; } // TODO: enabled moving while pinching? var scale = this.pinch.scale * event.scale; this.zoom(scale, pointer); } /** * Zoom the network in or out * * @param {number} scale a number around 1, and between 0.01 and 10 * @param {{x: number, y: number}} pointer Position on screen * @private */ }, { key: "zoom", value: function zoom(scale, pointer) { if (this.options.zoomView === true) { var scaleOld = this.body.view.scale; if (scale < 0.00001) { scale = 0.00001; } if (scale > 10) { scale = 10; } var preScaleDragPointer = undefined; if (this.drag !== undefined) { if (this.drag.dragging === true) { preScaleDragPointer = this.canvas.DOMtoCanvas(this.drag.pointer); } } // + this.canvas.frame.canvas.clientHeight / 2 var translation = this.body.view.translation; var scaleFrac = scale / scaleOld; var tx = (1 - scaleFrac) * pointer.x + translation.x * scaleFrac; var ty = (1 - scaleFrac) * pointer.y + translation.y * scaleFrac; this.body.view.scale = scale; this.body.view.translation = { x: tx, y: ty }; if (preScaleDragPointer != undefined) { var postScaleDragPointer = this.canvas.canvasToDOM(preScaleDragPointer); this.drag.pointer.x = postScaleDragPointer.x; this.drag.pointer.y = postScaleDragPointer.y; } this.body.emitter.emit("_requestRedraw"); if (scaleOld < scale) { this.body.emitter.emit("zoom", { direction: "+", scale: this.body.view.scale, pointer: pointer }); } else { this.body.emitter.emit("zoom", { direction: "-", scale: this.body.view.scale, pointer: pointer }); } } } /** * Event handler for mouse wheel event, used to zoom the timeline * See http://adomas.org/javascript-mouse-wheel/ * https://github.com/EightMedia/hammer.js/issues/256 * * @param {MouseEvent} event * @private */ }, { key: "onMouseWheel", value: function onMouseWheel(event) { if (this.options.zoomView === true) { // If delta is nonzero, handle it. // Basically, delta is now positive if wheel was scrolled up, // and negative, if wheel was scrolled down. if (event.deltaY !== 0) { // calculate the new scale var scale = this.body.view.scale; scale *= 1 + (event.deltaY < 0 ? 1 : -1) * (this.options.zoomSpeed * 0.1); // calculate the pointer location var pointer = this.getPointer({ x: event.clientX, y: event.clientY }); // apply the new scale this.zoom(scale, pointer); } // Prevent default actions caused by mouse wheel. event.preventDefault(); } } /** * Mouse move handler for checking whether the title moves over a node with a title. * * @param {Event} event * @private */ }, { key: "onMouseMove", value: function onMouseMove(event) { var _this4 = this; var pointer = this.getPointer({ x: event.clientX, y: event.clientY }); var popupVisible = false; // check if the previously selected node is still selected if (this.popup !== undefined) { if (this.popup.hidden === false) { this._checkHidePopup(pointer); } // if the popup was not hidden above if (this.popup.hidden === false) { popupVisible = true; this.popup.setPosition(pointer.x + 3, pointer.y - 5); this.popup.show(); } } // if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over. if (this.options.keyboard.autoFocus && this.options.keyboard.bindToWindow === false && this.options.keyboard.enabled === true) { this.canvas.frame.focus(); } // start a timeout that will check if the mouse is positioned above an element if (popupVisible === false) { if (this.popupTimer !== undefined) { clearInterval(this.popupTimer); // stop any running calculationTimer this.popupTimer = undefined; } if (!this.drag.dragging) { this.popupTimer = _setTimeout(function () { return _this4._checkShowPopup(pointer); }, this.options.tooltipDelay); } } // adding hover highlights if (this.options.hover === true) { this.selectionHandler.hoverObject(event, pointer); } } /** * Check if there is an element on the given position in the network * (a node or edge). If so, and if this element has a title, * show a popup window with its title. * * @param {{x:number, y:number}} pointer * @private */ }, { key: "_checkShowPopup", value: function _checkShowPopup(pointer) { var x = this.canvas._XconvertDOMtoCanvas(pointer.x); var y = this.canvas._YconvertDOMtoCanvas(pointer.y); var pointerObj = { left: x, top: y, right: x, bottom: y }; var previousPopupObjId = this.popupObj === undefined ? undefined : this.popupObj.id; var nodeUnderCursor = false; var popupType = "node"; // check if a node is under the cursor. if (this.popupObj === undefined) { // search the nodes for overlap, select the top one in case of multiple nodes var nodeIndices = this.body.nodeIndices; var nodes = this.body.nodes; var node; var overlappingNodes = []; for (var i = 0; i < nodeIndices.length; i++) { node = nodes[nodeIndices[i]]; if (node.isOverlappingWith(pointerObj) === true) { nodeUnderCursor = true; if (node.getTitle() !== undefined) { overlappingNodes.push(nodeIndices[i]); } } } if (overlappingNodes.length > 0) { // if there are overlapping nodes, select the last one, this is the one which is drawn on top of the others this.popupObj = nodes[overlappingNodes[overlappingNodes.length - 1]]; // if you hover over a node, the title of the edge is not supposed to be shown. nodeUnderCursor = true; } } if (this.popupObj === undefined && nodeUnderCursor === false) { // search the edges for overlap var edgeIndices = this.body.edgeIndices; var edges = this.body.edges; var edge; var overlappingEdges = []; for (var _i = 0; _i < edgeIndices.length; _i++) { edge = edges[edgeIndices[_i]]; if (edge.isOverlappingWith(pointerObj) === true) { if (edge.connected === true && edge.getTitle() !== undefined) { overlappingEdges.push(edgeIndices[_i]); } } } if (overlappingEdges.length > 0) { this.popupObj = edges[overlappingEdges[overlappingEdges.length - 1]]; popupType = "edge"; } } if (this.popupObj !== undefined) { // show popup message window if (this.popupObj.id !== previousPopupObjId) { if (this.popup === undefined) { this.popup = new Popup(this.canvas.frame); } this.popup.popupTargetType = popupType; this.popup.popupTargetId = this.popupObj.id; // adjust a small offset such that the mouse cursor is located in the // bottom left location of the popup, and you can easily move over the // popup area this.popup.setPosition(pointer.x + 3, pointer.y - 5); this.popup.setText(this.popupObj.getTitle()); this.popup.show(); this.body.emitter.emit("showPopup", this.popupObj.id); } } else { if (this.popup !== undefined) { this.popup.hide(); this.body.emitter.emit("hidePopup"); } } } /** * Check if the popup must be hidden, which is the case when the mouse is no * longer hovering on the object * * @param {{x:number, y:number}} pointer * @private */ }, { key: "_checkHidePopup", value: function _checkHidePopup(pointer) { var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); var stillOnObj = false; if (this.popup.popupTargetType === "node") { if (this.body.nodes[this.popup.popupTargetId] !== undefined) { stillOnObj = this.body.nodes[this.popup.popupTargetId].isOverlappingWith(pointerObj); // if the mouse is still one the node, we have to check if it is not also on one that is drawn on top of it. // we initially only check stillOnObj because this is much faster. if (stillOnObj === true) { var overNode = this.selectionHandler.getNodeAt(pointer); stillOnObj = overNode === undefined ? false : overNode.id === this.popup.popupTargetId; } } } else { if (this.selectionHandler.getNodeAt(pointer) === undefined) { if (this.body.edges[this.popup.popupTargetId] !== undefined) { stillOnObj = this.body.edges[this.popup.popupTargetId].isOverlappingWith(pointerObj); } } } if (stillOnObj === false) { this.popupObj = undefined; this.popup.hide(); this.body.emitter.emit("hidePopup"); } } }]); return InteractionHandler; }(); var setExports = {}; var set$2 = { get exports() { return setExports; }, set exports(v) { setExports = v; } }; var collection$1 = collection$3; var collectionStrong = collectionStrong$2; // `Set` constructor // https://tc39.es/ecma262/#sec-set-objects collection$1('Set', function (init) { return function Set() { return init(this, arguments.length ? arguments[0] : undefined); }; }, collectionStrong); var path$1 = path$y; var set$1 = path$1.Set; var parent$4 = set$1; var set = parent$4; (function (module) { module.exports = set; })(set$2); var _Set = /*@__PURE__*/getDefaultExportFromCjs(setExports); var weakMapExports = {}; var weakMap$2 = { get exports() { return weakMapExports; }, set exports(v) { weakMapExports = v; } }; var uncurryThis$2 = functionUncurryThis; var defineBuiltIns$1 = defineBuiltIns$3; var getWeakData = internalMetadataExports.getWeakData; var anInstance = anInstance$3; var anObject = anObject$d; var isNullOrUndefined = isNullOrUndefined$5; var isObject$1 = isObject$j; var iterate = iterate$3; var ArrayIterationModule = arrayIteration; var hasOwn = hasOwnProperty_1; var InternalStateModule = internalState; var setInternalState = InternalStateModule.set; var internalStateGetterFor = InternalStateModule.getterFor; var find = ArrayIterationModule.find; var findIndex = ArrayIterationModule.findIndex; var splice = uncurryThis$2([].splice); var id = 0; // fallback for uncaught frozen keys var uncaughtFrozenStore = function (state) { return state.frozen || (state.frozen = new UncaughtFrozenStore()); }; var UncaughtFrozenStore = function () { this.entries = []; }; var findUncaughtFrozen = function (store, key) { return find(store.entries, function (it) { return it[0] === key; }); }; UncaughtFrozenStore.prototype = { get: function (key) { var entry = findUncaughtFrozen(this, key); if (entry) return entry[1]; }, has: function (key) { return !!findUncaughtFrozen(this, key); }, set: function (key, value) { var entry = findUncaughtFrozen(this, key); if (entry) entry[1] = value;else this.entries.push([key, value]); }, 'delete': function (key) { var index = findIndex(this.entries, function (it) { return it[0] === key; }); if (~index) splice(this.entries, index, 1); return !!~index; } }; var collectionWeak$1 = { getConstructor: function (wrapper, CONSTRUCTOR_NAME, IS_MAP, ADDER) { var Constructor = wrapper(function (that, iterable) { anInstance(that, Prototype); setInternalState(that, { type: CONSTRUCTOR_NAME, id: id++, frozen: undefined }); if (!isNullOrUndefined(iterable)) iterate(iterable, that[ADDER], { that: that, AS_ENTRIES: IS_MAP }); }); var Prototype = Constructor.prototype; var getInternalState = internalStateGetterFor(CONSTRUCTOR_NAME); var define = function (that, key, value) { var state = getInternalState(that); var data = getWeakData(anObject(key), true); if (data === true) uncaughtFrozenStore(state).set(key, value);else data[state.id] = value; return that; }; defineBuiltIns$1(Prototype, { // `{ WeakMap, WeakSet }.prototype.delete(key)` methods // https://tc39.es/ecma262/#sec-weakmap.prototype.delete // https://tc39.es/ecma262/#sec-weakset.prototype.delete 'delete': function (key) { var state = getInternalState(this); if (!isObject$1(key)) return false; var data = getWeakData(key); if (data === true) return uncaughtFrozenStore(state)['delete'](key); return data && hasOwn(data, state.id) && delete data[state.id]; }, // `{ WeakMap, WeakSet }.prototype.has(key)` methods // https://tc39.es/ecma262/#sec-weakmap.prototype.has // https://tc39.es/ecma262/#sec-weakset.prototype.has has: function has(key) { var state = getInternalState(this); if (!isObject$1(key)) return false; var data = getWeakData(key); if (data === true) return uncaughtFrozenStore(state).has(key); return data && hasOwn(data, state.id); } }); defineBuiltIns$1(Prototype, IS_MAP ? { // `WeakMap.prototype.get(key)` method // https://tc39.es/ecma262/#sec-weakmap.prototype.get get: function get(key) { var state = getInternalState(this); if (isObject$1(key)) { var data = getWeakData(key); if (data === true) return uncaughtFrozenStore(state).get(key); return data ? data[state.id] : undefined; } }, // `WeakMap.prototype.set(key, value)` method // https://tc39.es/ecma262/#sec-weakmap.prototype.set set: function set(key, value) { return define(this, key, value); } } : { // `WeakSet.prototype.add(value)` method // https://tc39.es/ecma262/#sec-weakset.prototype.add add: function add(value) { return define(this, value, true); } }); return Constructor; } }; var FREEZING = freezing; var global$1 = global$l; var uncurryThis$1 = functionUncurryThis; var defineBuiltIns = defineBuiltIns$3; var InternalMetadataModule = internalMetadataExports; var collection = collection$3; var collectionWeak = collectionWeak$1; var isObject = isObject$j; var enforceInternalState = internalState.enforce; var fails$1 = fails$w; var NATIVE_WEAK_MAP = weakMapBasicDetection; var $Object = Object; // eslint-disable-next-line es/no-array-isarray -- safe var isArray = Array.isArray; // eslint-disable-next-line es/no-object-isextensible -- safe var isExtensible = $Object.isExtensible; // eslint-disable-next-line es/no-object-isfrozen -- safe var isFrozen = $Object.isFrozen; // eslint-disable-next-line es/no-object-issealed -- safe var isSealed = $Object.isSealed; // eslint-disable-next-line es/no-object-freeze -- safe var freeze = $Object.freeze; // eslint-disable-next-line es/no-object-seal -- safe var seal = $Object.seal; var FROZEN = {}; var SEALED = {}; var IS_IE11 = !global$1.ActiveXObject && 'ActiveXObject' in global$1; var InternalWeakMap; var wrapper = function (init) { return function WeakMap() { return init(this, arguments.length ? arguments[0] : undefined); }; }; // `WeakMap` constructor // https://tc39.es/ecma262/#sec-weakmap-constructor var $WeakMap = collection('WeakMap', wrapper, collectionWeak); var WeakMapPrototype = $WeakMap.prototype; var nativeSet = uncurryThis$1(WeakMapPrototype.set); // Chakra Edge bug: adding frozen arrays to WeakMap unfreeze them var hasMSEdgeFreezingBug = function () { return FREEZING && fails$1(function () { var frozenArray = freeze([]); nativeSet(new $WeakMap(), frozenArray, 1); return !isFrozen(frozenArray); }); }; // IE11 WeakMap frozen keys fix // We can't use feature detection because it crash some old IE builds // https://github.com/zloirock/core-js/issues/485 if (NATIVE_WEAK_MAP) if (IS_IE11) { InternalWeakMap = collectionWeak.getConstructor(wrapper, 'WeakMap', true); InternalMetadataModule.enable(); var nativeDelete = uncurryThis$1(WeakMapPrototype['delete']); var nativeHas = uncurryThis$1(WeakMapPrototype.has); var nativeGet = uncurryThis$1(WeakMapPrototype.get); defineBuiltIns(WeakMapPrototype, { 'delete': function (key) { if (isObject(key) && !isExtensible(key)) { var state = enforceInternalState(this); if (!state.frozen) state.frozen = new InternalWeakMap(); return nativeDelete(this, key) || state.frozen['delete'](key); } return nativeDelete(this, key); }, has: function has(key) { if (isObject(key) && !isExtensible(key)) { var state = enforceInternalState(this); if (!state.frozen) state.frozen = new InternalWeakMap(); return nativeHas(this, key) || state.frozen.has(key); } return nativeHas(this, key); }, get: function get(key) { if (isObject(key) && !isExtensible(key)) { var state = enforceInternalState(this); if (!state.frozen) state.frozen = new InternalWeakMap(); return nativeHas(this, key) ? nativeGet(this, key) : state.frozen.get(key); } return nativeGet(this, key); }, set: function set(key, value) { if (isObject(key) && !isExtensible(key)) { var state = enforceInternalState(this); if (!state.frozen) state.frozen = new InternalWeakMap(); nativeHas(this, key) ? nativeSet(this, key, value) : state.frozen.set(key, value); } else nativeSet(this, key, value); return this; } }); // Chakra Edge frozen keys fix } else if (hasMSEdgeFreezingBug()) { defineBuiltIns(WeakMapPrototype, { set: function set(key, value) { var arrayIntegrityLevel; if (isArray(key)) { if (isFrozen(key)) arrayIntegrityLevel = FROZEN;else if (isSealed(key)) arrayIntegrityLevel = SEALED; } nativeSet(this, key, value); if (arrayIntegrityLevel == FROZEN) freeze(key); if (arrayIntegrityLevel == SEALED) seal(key); return this; } }); } var path = path$y; var weakMap$1 = path.WeakMap; var parent$3 = weakMap$1; var weakMap = parent$3; (function (module) { module.exports = weakMap; })(weakMap$2); var _WeakMap = /*@__PURE__*/getDefaultExportFromCjs(weakMapExports); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ function __classPrivateFieldGet(receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); } function __classPrivateFieldSet(receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value), value; } function _createForOfIteratorHelper$3(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray$3(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray$3(o, minLen) { var _context2; if (!o) return; if (typeof o === "string") return _arrayLikeToArray$3(o, minLen); var n = _sliceInstanceProperty(_context2 = Object.prototype.toString.call(o)).call(_context2, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from$1(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$3(o, minLen); } function _arrayLikeToArray$3(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } var _SingleTypeSelectionAccumulator_previousSelection, _SingleTypeSelectionAccumulator_selection, _SelectionAccumulator_nodes, _SelectionAccumulator_edges, _SelectionAccumulator_commitHandler; /** * @param prev * @param next */ function diffSets(prev, next) { var diff = new _Set(); var _iterator = _createForOfIteratorHelper$3(next), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var item = _step.value; if (!prev.has(item)) { diff.add(item); } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return diff; } var SingleTypeSelectionAccumulator = /*#__PURE__*/function () { function SingleTypeSelectionAccumulator() { _classCallCheck(this, SingleTypeSelectionAccumulator); _SingleTypeSelectionAccumulator_previousSelection.set(this, new _Set()); _SingleTypeSelectionAccumulator_selection.set(this, new _Set()); } _createClass(SingleTypeSelectionAccumulator, [{ key: "size", get: function get() { return __classPrivateFieldGet(this, _SingleTypeSelectionAccumulator_selection, "f").size; } }, { key: "add", value: function add() { for (var _len = arguments.length, items = new Array(_len), _key = 0; _key < _len; _key++) { items[_key] = arguments[_key]; } for (var _i = 0, _items = items; _i < _items.length; _i++) { var item = _items[_i]; __classPrivateFieldGet(this, _SingleTypeSelectionAccumulator_selection, "f").add(item); } } }, { key: "delete", value: function _delete() { for (var _len2 = arguments.length, items = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { items[_key2] = arguments[_key2]; } for (var _i2 = 0, _items2 = items; _i2 < _items2.length; _i2++) { var item = _items2[_i2]; __classPrivateFieldGet(this, _SingleTypeSelectionAccumulator_selection, "f").delete(item); } } }, { key: "clear", value: function clear() { __classPrivateFieldGet(this, _SingleTypeSelectionAccumulator_selection, "f").clear(); } }, { key: "getSelection", value: function getSelection() { return _toConsumableArray(__classPrivateFieldGet(this, _SingleTypeSelectionAccumulator_selection, "f")); } }, { key: "getChanges", value: function getChanges() { return { added: _toConsumableArray(diffSets(__classPrivateFieldGet(this, _SingleTypeSelectionAccumulator_previousSelection, "f"), __classPrivateFieldGet(this, _SingleTypeSelectionAccumulator_selection, "f"))), deleted: _toConsumableArray(diffSets(__classPrivateFieldGet(this, _SingleTypeSelectionAccumulator_selection, "f"), __classPrivateFieldGet(this, _SingleTypeSelectionAccumulator_previousSelection, "f"))), previous: _toConsumableArray(new _Set(__classPrivateFieldGet(this, _SingleTypeSelectionAccumulator_previousSelection, "f"))), current: _toConsumableArray(new _Set(__classPrivateFieldGet(this, _SingleTypeSelectionAccumulator_selection, "f"))) }; } }, { key: "commit", value: function commit() { var changes = this.getChanges(); __classPrivateFieldSet(this, _SingleTypeSelectionAccumulator_previousSelection, __classPrivateFieldGet(this, _SingleTypeSelectionAccumulator_selection, "f"), "f"); __classPrivateFieldSet(this, _SingleTypeSelectionAccumulator_selection, new _Set(__classPrivateFieldGet(this, _SingleTypeSelectionAccumulator_previousSelection, "f")), "f"); var _iterator2 = _createForOfIteratorHelper$3(changes.added), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var item = _step2.value; item.select(); } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } var _iterator3 = _createForOfIteratorHelper$3(changes.deleted), _step3; try { for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { var _item = _step3.value; _item.unselect(); } } catch (err) { _iterator3.e(err); } finally { _iterator3.f(); } return changes; } }]); return SingleTypeSelectionAccumulator; }(); _SingleTypeSelectionAccumulator_previousSelection = new _WeakMap(), _SingleTypeSelectionAccumulator_selection = new _WeakMap(); var SelectionAccumulator = /*#__PURE__*/function () { function SelectionAccumulator() { var commitHandler = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {}; _classCallCheck(this, SelectionAccumulator); _SelectionAccumulator_nodes.set(this, new SingleTypeSelectionAccumulator()); _SelectionAccumulator_edges.set(this, new SingleTypeSelectionAccumulator()); _SelectionAccumulator_commitHandler.set(this, void 0); __classPrivateFieldSet(this, _SelectionAccumulator_commitHandler, commitHandler, "f"); } _createClass(SelectionAccumulator, [{ key: "sizeNodes", get: function get() { return __classPrivateFieldGet(this, _SelectionAccumulator_nodes, "f").size; } }, { key: "sizeEdges", get: function get() { return __classPrivateFieldGet(this, _SelectionAccumulator_edges, "f").size; } }, { key: "getNodes", value: function getNodes() { return __classPrivateFieldGet(this, _SelectionAccumulator_nodes, "f").getSelection(); } }, { key: "getEdges", value: function getEdges() { return __classPrivateFieldGet(this, _SelectionAccumulator_edges, "f").getSelection(); } }, { key: "addNodes", value: function addNodes() { var _classPrivateFieldGe; (_classPrivateFieldGe = __classPrivateFieldGet(this, _SelectionAccumulator_nodes, "f")).add.apply(_classPrivateFieldGe, arguments); } }, { key: "addEdges", value: function addEdges() { var _classPrivateFieldGe2; (_classPrivateFieldGe2 = __classPrivateFieldGet(this, _SelectionAccumulator_edges, "f")).add.apply(_classPrivateFieldGe2, arguments); } }, { key: "deleteNodes", value: function deleteNodes(node) { __classPrivateFieldGet(this, _SelectionAccumulator_nodes, "f").delete(node); } }, { key: "deleteEdges", value: function deleteEdges(edge) { __classPrivateFieldGet(this, _SelectionAccumulator_edges, "f").delete(edge); } }, { key: "clear", value: function clear() { __classPrivateFieldGet(this, _SelectionAccumulator_nodes, "f").clear(); __classPrivateFieldGet(this, _SelectionAccumulator_edges, "f").clear(); } }, { key: "commit", value: function commit() { var _classPrivateFieldGe3, _context; var summary = { nodes: __classPrivateFieldGet(this, _SelectionAccumulator_nodes, "f").commit(), edges: __classPrivateFieldGet(this, _SelectionAccumulator_edges, "f").commit() }; for (var _len3 = arguments.length, rest = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { rest[_key3] = arguments[_key3]; } (_classPrivateFieldGe3 = __classPrivateFieldGet(this, _SelectionAccumulator_commitHandler, "f")).call.apply(_classPrivateFieldGe3, _concatInstanceProperty(_context = [this, summary]).call(_context, rest)); return summary; } }]); return SelectionAccumulator; }(); _SelectionAccumulator_nodes = new _WeakMap(), _SelectionAccumulator_edges = new _WeakMap(), _SelectionAccumulator_commitHandler = new _WeakMap(); function _createForOfIteratorHelper$2(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray$2(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray$2(o, minLen) { var _context3; if (!o) return; if (typeof o === "string") return _arrayLikeToArray$2(o, minLen); var n = _sliceInstanceProperty(_context3 = Object.prototype.toString.call(o)).call(_context3, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from$1(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$2(o, minLen); } function _arrayLikeToArray$2(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } /** * The handler for selections */ var SelectionHandler = /*#__PURE__*/function () { /** * @param {object} body * @param {Canvas} canvas */ function SelectionHandler(body, canvas) { var _this = this; _classCallCheck(this, SelectionHandler); this.body = body; this.canvas = canvas; // TODO: Consider firing an event on any change to the selection, not // only those caused by clicks and taps. It would be easy to implement // now and (at least to me) it seems like something that could be // quite useful. this._selectionAccumulator = new SelectionAccumulator(); this.hoverObj = { nodes: {}, edges: {} }; this.options = {}; this.defaultOptions = { multiselect: false, selectable: true, selectConnectedEdges: true, hoverConnectedEdges: true }; _Object$assign(this.options, this.defaultOptions); this.body.emitter.on("_dataChanged", function () { _this.updateSelection(); }); } /** * * @param {object} [options] */ _createClass(SelectionHandler, [{ key: "setOptions", value: function setOptions(options) { if (options !== undefined) { var fields = ["multiselect", "hoverConnectedEdges", "selectable", "selectConnectedEdges"]; selectiveDeepExtend(fields, this.options, options); } } /** * handles the selection part of the tap; * * @param {{x: number, y: number}} pointer * @returns {boolean} */ }, { key: "selectOnPoint", value: function selectOnPoint(pointer) { var selected = false; if (this.options.selectable === true) { var obj = this.getNodeAt(pointer) || this.getEdgeAt(pointer); // unselect after getting the objects in order to restore width and height. this.unselectAll(); if (obj !== undefined) { selected = this.selectObject(obj); } this.body.emitter.emit("_requestRedraw"); } return selected; } /** * * @param {{x: number, y: number}} pointer * @returns {boolean} */ }, { key: "selectAdditionalOnPoint", value: function selectAdditionalOnPoint(pointer) { var selectionChanged = false; if (this.options.selectable === true) { var obj = this.getNodeAt(pointer) || this.getEdgeAt(pointer); if (obj !== undefined) { selectionChanged = true; if (obj.isSelected() === true) { this.deselectObject(obj); } else { this.selectObject(obj); } this.body.emitter.emit("_requestRedraw"); } } return selectionChanged; } /** * Create an object containing the standard fields for an event. * * @param {Event} event * @param {{x: number, y: number}} pointer Object with the x and y screen coordinates of the mouse * @returns {{}} * @private */ }, { key: "_initBaseEvent", value: function _initBaseEvent(event, pointer) { var properties = {}; properties["pointer"] = { DOM: { x: pointer.x, y: pointer.y }, canvas: this.canvas.DOMtoCanvas(pointer) }; properties["event"] = event; return properties; } /** * Generate an event which the user can catch. * * This adds some extra data to the event with respect to cursor position and * selected nodes and edges. * * @param {string} eventType Name of event to send * @param {Event} event * @param {{x: number, y: number}} pointer Object with the x and y screen coordinates of the mouse * @param {object | undefined} oldSelection If present, selection state before event occured * @param {boolean|undefined} [emptySelection=false] Indicate if selection data should be passed */ }, { key: "generateClickEvent", value: function generateClickEvent(eventType, event, pointer, oldSelection) { var emptySelection = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; var properties = this._initBaseEvent(event, pointer); if (emptySelection === true) { properties.nodes = []; properties.edges = []; } else { var tmp = this.getSelection(); properties.nodes = tmp.nodes; properties.edges = tmp.edges; } if (oldSelection !== undefined) { properties["previousSelection"] = oldSelection; } if (eventType == "click") { // For the time being, restrict this functionality to // just the click event. properties.items = this.getClickedItems(pointer); } if (event.controlEdge !== undefined) { properties.controlEdge = event.controlEdge; } this.body.emitter.emit(eventType, properties); } /** * * @param {object} obj * @param {boolean} [highlightEdges=this.options.selectConnectedEdges] * @returns {boolean} */ }, { key: "selectObject", value: function selectObject(obj) { var highlightEdges = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.options.selectConnectedEdges; if (obj !== undefined) { if (obj instanceof Node) { if (highlightEdges === true) { var _this$_selectionAccum; (_this$_selectionAccum = this._selectionAccumulator).addEdges.apply(_this$_selectionAccum, _toConsumableArray(obj.edges)); } this._selectionAccumulator.addNodes(obj); } else { this._selectionAccumulator.addEdges(obj); } return true; } return false; } /** * * @param {object} obj */ }, { key: "deselectObject", value: function deselectObject(obj) { if (obj.isSelected() === true) { obj.selected = false; this._removeFromSelection(obj); } } /** * retrieve all nodes overlapping with given object * * @param {object} object An object with parameters left, top, right, bottom * @returns {number[]} An array with id's of the overlapping nodes * @private */ }, { key: "_getAllNodesOverlappingWith", value: function _getAllNodesOverlappingWith(object) { var overlappingNodes = []; var nodes = this.body.nodes; for (var i = 0; i < this.body.nodeIndices.length; i++) { var nodeId = this.body.nodeIndices[i]; if (nodes[nodeId].isOverlappingWith(object)) { overlappingNodes.push(nodeId); } } return overlappingNodes; } /** * Return a position object in canvasspace from a single point in screenspace * * @param {{x: number, y: number}} pointer * @returns {{left: number, top: number, right: number, bottom: number}} * @private */ }, { key: "_pointerToPositionObject", value: function _pointerToPositionObject(pointer) { var canvasPos = this.canvas.DOMtoCanvas(pointer); return { left: canvasPos.x - 1, top: canvasPos.y + 1, right: canvasPos.x + 1, bottom: canvasPos.y - 1 }; } /** * Get the top node at the passed point (like a click) * * @param {{x: number, y: number}} pointer * @param {boolean} [returnNode=true] * @returns {Node | undefined} node */ }, { key: "getNodeAt", value: function getNodeAt(pointer) { var returnNode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; // we first check if this is an navigation controls element var positionObject = this._pointerToPositionObject(pointer); var overlappingNodes = this._getAllNodesOverlappingWith(positionObject); // if there are overlapping nodes, select the last one, this is the // one which is drawn on top of the others if (overlappingNodes.length > 0) { if (returnNode === true) { return this.body.nodes[overlappingNodes[overlappingNodes.length - 1]]; } else { return overlappingNodes[overlappingNodes.length - 1]; } } else { return undefined; } } /** * retrieve all edges overlapping with given object, selector is around center * * @param {object} object An object with parameters left, top, right, bottom * @param {number[]} overlappingEdges An array with id's of the overlapping nodes * @private */ }, { key: "_getEdgesOverlappingWith", value: function _getEdgesOverlappingWith(object, overlappingEdges) { var edges = this.body.edges; for (var i = 0; i < this.body.edgeIndices.length; i++) { var edgeId = this.body.edgeIndices[i]; if (edges[edgeId].isOverlappingWith(object)) { overlappingEdges.push(edgeId); } } } /** * retrieve all nodes overlapping with given object * * @param {object} object An object with parameters left, top, right, bottom * @returns {number[]} An array with id's of the overlapping nodes * @private */ }, { key: "_getAllEdgesOverlappingWith", value: function _getAllEdgesOverlappingWith(object) { var overlappingEdges = []; this._getEdgesOverlappingWith(object, overlappingEdges); return overlappingEdges; } /** * Get the edges nearest to the passed point (like a click) * * @param {{x: number, y: number}} pointer * @param {boolean} [returnEdge=true] * @returns {Edge | undefined} node */ }, { key: "getEdgeAt", value: function getEdgeAt(pointer) { var returnEdge = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; // Iterate over edges, pick closest within 10 var canvasPos = this.canvas.DOMtoCanvas(pointer); var mindist = 10; var overlappingEdge = null; var edges = this.body.edges; for (var i = 0; i < this.body.edgeIndices.length; i++) { var edgeId = this.body.edgeIndices[i]; var edge = edges[edgeId]; if (edge.connected) { var xFrom = edge.from.x; var yFrom = edge.from.y; var xTo = edge.to.x; var yTo = edge.to.y; var dist = edge.edgeType.getDistanceToEdge(xFrom, yFrom, xTo, yTo, canvasPos.x, canvasPos.y); if (dist < mindist) { overlappingEdge = edgeId; mindist = dist; } } } if (overlappingEdge !== null) { if (returnEdge === true) { return this.body.edges[overlappingEdge]; } else { return overlappingEdge; } } else { return undefined; } } /** * Add object to the selection array. * * @param {object} obj * @private */ }, { key: "_addToHover", value: function _addToHover(obj) { if (obj instanceof Node) { this.hoverObj.nodes[obj.id] = obj; } else { this.hoverObj.edges[obj.id] = obj; } } /** * Remove a single option from selection. * * @param {object} obj * @private */ }, { key: "_removeFromSelection", value: function _removeFromSelection(obj) { if (obj instanceof Node) { var _this$_selectionAccum2; this._selectionAccumulator.deleteNodes(obj); (_this$_selectionAccum2 = this._selectionAccumulator).deleteEdges.apply(_this$_selectionAccum2, _toConsumableArray(obj.edges)); } else { this._selectionAccumulator.deleteEdges(obj); } } /** * Unselect all nodes and edges. */ }, { key: "unselectAll", value: function unselectAll() { this._selectionAccumulator.clear(); } /** * return the number of selected nodes * * @returns {number} */ }, { key: "getSelectedNodeCount", value: function getSelectedNodeCount() { return this._selectionAccumulator.sizeNodes; } /** * return the number of selected edges * * @returns {number} */ }, { key: "getSelectedEdgeCount", value: function getSelectedEdgeCount() { return this._selectionAccumulator.sizeEdges; } /** * select the edges connected to the node that is being selected * * @param {Node} node * @private */ }, { key: "_hoverConnectedEdges", value: function _hoverConnectedEdges(node) { for (var i = 0; i < node.edges.length; i++) { var edge = node.edges[i]; edge.hover = true; this._addToHover(edge); } } /** * Remove the highlight from a node or edge, in response to mouse movement * * @param {Event} event * @param {{x: number, y: number}} pointer object with the x and y screen coordinates of the mouse * @param {Node|vis.Edge} object * @private */ }, { key: "emitBlurEvent", value: function emitBlurEvent(event, pointer, object) { var properties = this._initBaseEvent(event, pointer); if (object.hover === true) { object.hover = false; if (object instanceof Node) { properties.node = object.id; this.body.emitter.emit("blurNode", properties); } else { properties.edge = object.id; this.body.emitter.emit("blurEdge", properties); } } } /** * Create the highlight for a node or edge, in response to mouse movement * * @param {Event} event * @param {{x: number, y: number}} pointer object with the x and y screen coordinates of the mouse * @param {Node|vis.Edge} object * @returns {boolean} hoverChanged * @private */ }, { key: "emitHoverEvent", value: function emitHoverEvent(event, pointer, object) { var properties = this._initBaseEvent(event, pointer); var hoverChanged = false; if (object.hover === false) { object.hover = true; this._addToHover(object); hoverChanged = true; if (object instanceof Node) { properties.node = object.id; this.body.emitter.emit("hoverNode", properties); } else { properties.edge = object.id; this.body.emitter.emit("hoverEdge", properties); } } return hoverChanged; } /** * Perform actions in response to a mouse movement. * * @param {Event} event * @param {{x: number, y: number}} pointer | object with the x and y screen coordinates of the mouse */ }, { key: "hoverObject", value: function hoverObject(event, pointer) { var object = this.getNodeAt(pointer); if (object === undefined) { object = this.getEdgeAt(pointer); } var hoverChanged = false; // remove all node hover highlights for (var nodeId in this.hoverObj.nodes) { if (Object.prototype.hasOwnProperty.call(this.hoverObj.nodes, nodeId)) { if (object === undefined || object instanceof Node && object.id != nodeId || object instanceof Edge) { this.emitBlurEvent(event, pointer, this.hoverObj.nodes[nodeId]); delete this.hoverObj.nodes[nodeId]; hoverChanged = true; } } } // removing all edge hover highlights for (var edgeId in this.hoverObj.edges) { if (Object.prototype.hasOwnProperty.call(this.hoverObj.edges, edgeId)) { // if the hover has been changed here it means that the node has been hovered over or off // we then do not use the emitBlurEvent method here. if (hoverChanged === true) { this.hoverObj.edges[edgeId].hover = false; delete this.hoverObj.edges[edgeId]; } // if the blur remains the same and the object is undefined (mouse off) or another // edge has been hovered, or another node has been hovered we blur the edge. else if (object === undefined || object instanceof Edge && object.id != edgeId || object instanceof Node && !object.hover) { this.emitBlurEvent(event, pointer, this.hoverObj.edges[edgeId]); delete this.hoverObj.edges[edgeId]; hoverChanged = true; } } } if (object !== undefined) { var hoveredEdgesCount = _Object$keys(this.hoverObj.edges).length; var hoveredNodesCount = _Object$keys(this.hoverObj.nodes).length; var newOnlyHoveredEdge = object instanceof Edge && hoveredEdgesCount === 0 && hoveredNodesCount === 0; var newOnlyHoveredNode = object instanceof Node && hoveredEdgesCount === 0 && hoveredNodesCount === 0; if (hoverChanged || newOnlyHoveredEdge || newOnlyHoveredNode) { hoverChanged = this.emitHoverEvent(event, pointer, object); } if (object instanceof Node && this.options.hoverConnectedEdges === true) { this._hoverConnectedEdges(object); } } if (hoverChanged === true) { this.body.emitter.emit("_requestRedraw"); } } /** * Commit the selection changes but don't emit any events. */ }, { key: "commitWithoutEmitting", value: function commitWithoutEmitting() { this._selectionAccumulator.commit(); } /** * Select and deselect nodes depending current selection change. * * For changing nodes, select/deselect events are fired. * * NOTE: For a given edge, if one connecting node is deselected and with the * same click the other node is selected, no events for the edge will fire. It * was selected and it will remain selected. * * @param {{x: number, y: number}} pointer - The x and y coordinates of the * click, tap, dragend… that triggered this. * @param {UIEvent} event - The event that triggered this. */ }, { key: "commitAndEmit", value: function commitAndEmit(pointer, event) { var selected = false; var selectionChanges = this._selectionAccumulator.commit(); var previousSelection = { nodes: selectionChanges.nodes.previous, edges: selectionChanges.edges.previous }; if (selectionChanges.edges.deleted.length > 0) { this.generateClickEvent("deselectEdge", event, pointer, previousSelection); selected = true; } if (selectionChanges.nodes.deleted.length > 0) { this.generateClickEvent("deselectNode", event, pointer, previousSelection); selected = true; } if (selectionChanges.nodes.added.length > 0) { this.generateClickEvent("selectNode", event, pointer); selected = true; } if (selectionChanges.edges.added.length > 0) { this.generateClickEvent("selectEdge", event, pointer); selected = true; } // fire the select event if anything has been selected or deselected if (selected === true) { // select or unselect this.generateClickEvent("select", event, pointer); } } /** * Retrieve the currently selected node and edge ids. * * @returns {{nodes: Array.<string>, edges: Array.<string>}} Arrays with the * ids of the selected nodes and edges. */ }, { key: "getSelection", value: function getSelection() { return { nodes: this.getSelectedNodeIds(), edges: this.getSelectedEdgeIds() }; } /** * Retrieve the currently selected nodes. * * @returns {Array} An array with selected nodes. */ }, { key: "getSelectedNodes", value: function getSelectedNodes() { return this._selectionAccumulator.getNodes(); } /** * Retrieve the currently selected edges. * * @returns {Array} An array with selected edges. */ }, { key: "getSelectedEdges", value: function getSelectedEdges() { return this._selectionAccumulator.getEdges(); } /** * Retrieve the currently selected node ids. * * @returns {Array} An array with the ids of the selected nodes. */ }, { key: "getSelectedNodeIds", value: function getSelectedNodeIds() { var _context; return _mapInstanceProperty(_context = this._selectionAccumulator.getNodes()).call(_context, function (node) { return node.id; }); } /** * Retrieve the currently selected edge ids. * * @returns {Array} An array with the ids of the selected edges. */ }, { key: "getSelectedEdgeIds", value: function getSelectedEdgeIds() { var _context2; return _mapInstanceProperty(_context2 = this._selectionAccumulator.getEdges()).call(_context2, function (edge) { return edge.id; }); } /** * Updates the current selection * * @param {{nodes: Array.<string>, edges: Array.<string>}} selection * @param {object} options Options */ }, { key: "setSelection", value: function setSelection(selection) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (!selection || !selection.nodes && !selection.edges) { throw new TypeError("Selection must be an object with nodes and/or edges properties"); } // first unselect any selected node, if option is true or undefined if (options.unselectAll || options.unselectAll === undefined) { this.unselectAll(); } if (selection.nodes) { var _iterator = _createForOfIteratorHelper$2(selection.nodes), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var id = _step.value; var node = this.body.nodes[id]; if (!node) { throw new RangeError('Node with id "' + id + '" not found'); } // don't select edges with it this.selectObject(node, options.highlightEdges); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } if (selection.edges) { var _iterator2 = _createForOfIteratorHelper$2(selection.edges), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var _id = _step2.value; var edge = this.body.edges[_id]; if (!edge) { throw new RangeError('Edge with id "' + _id + '" not found'); } this.selectObject(edge); } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } } this.body.emitter.emit("_requestRedraw"); this._selectionAccumulator.commit(); } /** * select zero or more nodes with the option to highlight edges * * @param {number[] | string[]} selection An array with the ids of the * selected nodes. * @param {boolean} [highlightEdges] */ }, { key: "selectNodes", value: function selectNodes(selection) { var highlightEdges = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; if (!selection || selection.length === undefined) throw "Selection must be an array with ids"; this.setSelection({ nodes: selection }, { highlightEdges: highlightEdges }); } /** * select zero or more edges * * @param {number[] | string[]} selection An array with the ids of the * selected nodes. */ }, { key: "selectEdges", value: function selectEdges(selection) { if (!selection || selection.length === undefined) throw "Selection must be an array with ids"; this.setSelection({ edges: selection }); } /** * Validate the selection: remove ids of nodes which no longer exist * * @private */ }, { key: "updateSelection", value: function updateSelection() { for (var node in this._selectionAccumulator.getNodes()) { if (!Object.prototype.hasOwnProperty.call(this.body.nodes, node.id)) { this._selectionAccumulator.deleteNodes(node); } } for (var edge in this._selectionAccumulator.getEdges()) { if (!Object.prototype.hasOwnProperty.call(this.body.edges, edge.id)) { this._selectionAccumulator.deleteEdges(edge); } } } /** * Determine all the visual elements clicked which are on the given point. * * All elements are returned; this includes nodes, edges and their labels. * The order returned is from highest to lowest, i.e. element 0 of the return * value is the topmost item clicked on. * * The return value consists of an array of the following possible elements: * * - `{nodeId:number}` - node with given id clicked on * - `{nodeId:number, labelId:0}` - label of node with given id clicked on * - `{edgeId:number}` - edge with given id clicked on * - `{edge:number, labelId:0}` - label of edge with given id clicked on * * ## NOTES * * - Currently, there is only one label associated with a node or an edge, * but this is expected to change somewhere in the future. * - Since there is no z-indexing yet, it is not really possible to set the nodes and * edges in the correct order. For the time being, nodes come first. * * @param {point} pointer mouse position in screen coordinates * @returns {Array.<nodeClickItem|nodeLabelClickItem|edgeClickItem|edgeLabelClickItem>} * @private */ }, { key: "getClickedItems", value: function getClickedItems(pointer) { var point = this.canvas.DOMtoCanvas(pointer); var items = []; // Note reverse order; we want the topmost clicked items to be first in the array // Also note that selected nodes are disregarded here; these normally display on top var nodeIndices = this.body.nodeIndices; var nodes = this.body.nodes; for (var i = nodeIndices.length - 1; i >= 0; i--) { var node = nodes[nodeIndices[i]]; var ret = node.getItemsOnPoint(point); items.push.apply(items, ret); // Append the return value to the running list. } var edgeIndices = this.body.edgeIndices; var edges = this.body.edges; for (var _i = edgeIndices.length - 1; _i >= 0; _i--) { var edge = edges[edgeIndices[_i]]; var _ret = edge.getItemsOnPoint(point); items.push.apply(items, _ret); // Append the return value to the running list. } return items; } }]); return SelectionHandler; }(); var sortExports = {}; var sort$3 = { get exports() { return sortExports; }, set exports(v) { sortExports = v; } }; var arraySlice = arraySliceSimple; var floor = Math.floor; var mergeSort = function (array, comparefn) { var length = array.length; var middle = floor(length / 2); return length < 8 ? insertionSort(array, comparefn) : merge(array, mergeSort(arraySlice(array, 0, middle), comparefn), mergeSort(arraySlice(array, middle), comparefn), comparefn); }; var insertionSort = function (array, comparefn) { var length = array.length; var i = 1; var element, j; while (i < length) { j = i; element = array[i]; while (j && comparefn(array[j - 1], element) > 0) { array[j] = array[--j]; } if (j !== i++) array[j] = element; } return array; }; var merge = function (array, left, right, comparefn) { var llength = left.length; var rlength = right.length; var lindex = 0; var rindex = 0; while (lindex < llength || rindex < rlength) { array[lindex + rindex] = lindex < llength && rindex < rlength ? comparefn(left[lindex], right[rindex]) <= 0 ? left[lindex++] : right[rindex++] : lindex < llength ? left[lindex++] : right[rindex++]; } return array; }; var arraySort = mergeSort; var userAgent$1 = engineUserAgent; var firefox = userAgent$1.match(/firefox\/(\d+)/i); var engineFfVersion = !!firefox && +firefox[1]; var UA = engineUserAgent; var engineIsIeOrEdge = /MSIE|Trident/.test(UA); var userAgent = engineUserAgent; var webkit = userAgent.match(/AppleWebKit\/(\d+)\./); var engineWebkitVersion = !!webkit && +webkit[1]; var $$2 = _export; var uncurryThis = functionUncurryThis; var aCallable$1 = aCallable$7; var toObject$1 = toObject$d; var lengthOfArrayLike$1 = lengthOfArrayLike$b; var deletePropertyOrThrow = deletePropertyOrThrow$2; var toString = toString$a; var fails = fails$w; var internalSort = arraySort; var arrayMethodIsStrict$2 = arrayMethodIsStrict$6; var FF = engineFfVersion; var IE_OR_EDGE = engineIsIeOrEdge; var V8 = engineV8Version; var WEBKIT = engineWebkitVersion; var test = []; var nativeSort = uncurryThis(test.sort); var push = uncurryThis(test.push); // IE8- var FAILS_ON_UNDEFINED = fails(function () { test.sort(undefined); }); // V8 bug var FAILS_ON_NULL = fails(function () { test.sort(null); }); // Old WebKit var STRICT_METHOD$1 = arrayMethodIsStrict$2('sort'); var STABLE_SORT = !fails(function () { // feature detection can be too slow, so check engines versions if (V8) return V8 < 70; if (FF && FF > 3) return; if (IE_OR_EDGE) return true; if (WEBKIT) return WEBKIT < 603; var result = ''; var code, chr, value, index; // generate an array with more 512 elements (Chakra and old V8 fails only in this case) for (code = 65; code < 76; code++) { chr = String.fromCharCode(code); switch (code) { case 66: case 69: case 70: case 72: value = 3; break; case 68: case 71: value = 4; break; default: value = 2; } for (index = 0; index < 47; index++) { test.push({ k: chr + index, v: value }); } } test.sort(function (a, b) { return b.v - a.v; }); for (index = 0; index < test.length; index++) { chr = test[index].k.charAt(0); if (result.charAt(result.length - 1) !== chr) result += chr; } return result !== 'DGBEFHACIJK'; }); var FORCED$1 = FAILS_ON_UNDEFINED || !FAILS_ON_NULL || !STRICT_METHOD$1 || !STABLE_SORT; var getSortCompare = function (comparefn) { return function (x, y) { if (y === undefined) return -1; if (x === undefined) return 1; if (comparefn !== undefined) return +comparefn(x, y) || 0; return toString(x) > toString(y) ? 1 : -1; }; }; // `Array.prototype.sort` method // https://tc39.es/ecma262/#sec-array.prototype.sort $$2({ target: 'Array', proto: true, forced: FORCED$1 }, { sort: function sort(comparefn) { if (comparefn !== undefined) aCallable$1(comparefn); var array = toObject$1(this); if (STABLE_SORT) return comparefn === undefined ? nativeSort(array) : nativeSort(array, comparefn); var items = []; var arrayLength = lengthOfArrayLike$1(array); var itemsLength, index; for (index = 0; index < arrayLength; index++) { if (index in array) push(items, array[index]); } internalSort(items, getSortCompare(comparefn)); itemsLength = lengthOfArrayLike$1(items); index = 0; while (index < itemsLength) array[index] = items[index++]; while (index < arrayLength) deletePropertyOrThrow(array, index++); return array; } }); var entryVirtual$2 = entryVirtual$i; var sort$2 = entryVirtual$2('Array').sort; var isPrototypeOf$2 = objectIsPrototypeOf; var method$2 = sort$2; var ArrayPrototype$2 = Array.prototype; var sort$1 = function (it) { var own = it.sort; return it === ArrayPrototype$2 || isPrototypeOf$2(ArrayPrototype$2, it) && own === ArrayPrototype$2.sort ? method$2 : own; }; var parent$2 = sort$1; var sort = parent$2; (function (module) { module.exports = sort; })(sort$3); var _sortInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(sortExports); var reduceExports = {}; var reduce$3 = { get exports() { return reduceExports; }, set exports(v) { reduceExports = v; } }; var aCallable = aCallable$7; var toObject = toObject$d; var IndexedObject = indexedObject; var lengthOfArrayLike = lengthOfArrayLike$b; var $TypeError = TypeError; // `Array.prototype.{ reduce, reduceRight }` methods implementation var createMethod = function (IS_RIGHT) { return function (that, callbackfn, argumentsLength, memo) { aCallable(callbackfn); var O = toObject(that); var self = IndexedObject(O); var length = lengthOfArrayLike(O); var index = IS_RIGHT ? length - 1 : 0; var i = IS_RIGHT ? -1 : 1; if (argumentsLength < 2) while (true) { if (index in self) { memo = self[index]; index += i; break; } index += i; if (IS_RIGHT ? index < 0 : length <= index) { throw $TypeError('Reduce of empty array with no initial value'); } } for (; IS_RIGHT ? index >= 0 : length > index; index += i) if (index in self) { memo = callbackfn(memo, self[index], index, O); } return memo; }; }; var arrayReduce = { // `Array.prototype.reduce` method // https://tc39.es/ecma262/#sec-array.prototype.reduce left: createMethod(false), // `Array.prototype.reduceRight` method // https://tc39.es/ecma262/#sec-array.prototype.reduceright right: createMethod(true) }; var classof = classofRaw$2; var engineIsNode = typeof process != 'undefined' && classof(process) == 'process'; var $$1 = _export; var $reduce = arrayReduce.left; var arrayMethodIsStrict$1 = arrayMethodIsStrict$6; var CHROME_VERSION = engineV8Version; var IS_NODE = engineIsNode; // Chrome 80-82 has a critical bug // https://bugs.chromium.org/p/chromium/issues/detail?id=1049982 var CHROME_BUG = !IS_NODE && CHROME_VERSION > 79 && CHROME_VERSION < 83; var FORCED = CHROME_BUG || !arrayMethodIsStrict$1('reduce'); // `Array.prototype.reduce` method // https://tc39.es/ecma262/#sec-array.prototype.reduce $$1({ target: 'Array', proto: true, forced: FORCED }, { reduce: function reduce(callbackfn /* , initialValue */) { var length = arguments.length; return $reduce(this, callbackfn, length, length > 1 ? arguments[1] : undefined); } }); var entryVirtual$1 = entryVirtual$i; var reduce$2 = entryVirtual$1('Array').reduce; var isPrototypeOf$1 = objectIsPrototypeOf; var method$1 = reduce$2; var ArrayPrototype$1 = Array.prototype; var reduce$1 = function (it) { var own = it.reduce; return it === ArrayPrototype$1 || isPrototypeOf$1(ArrayPrototype$1, it) && own === ArrayPrototype$1.reduce ? method$1 : own; }; var parent$1 = reduce$1; var reduce = parent$1; (function (module) { module.exports = reduce; })(reduce$3); var _reduceInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(reduceExports); var timsortExports = {}; var timsort$1 = { get exports() { return timsortExports; }, set exports(v) { timsortExports = v; } }; var timsort = {}; /**** * The MIT License * * Copyright (c) 2015 Marco Ziccardi * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * ****/ (function (exports) { (function (global, factory) { { factory(exports); } })(commonjsGlobal, function (exports) { exports.__esModule = true; exports.sort = sort; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } var DEFAULT_MIN_MERGE = 32; var DEFAULT_MIN_GALLOPING = 7; var DEFAULT_TMP_STORAGE_LENGTH = 256; var POWERS_OF_TEN = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9]; function log10(x) { if (x < 1e5) { if (x < 1e2) { return x < 1e1 ? 0 : 1; } if (x < 1e4) { return x < 1e3 ? 2 : 3; } return 4; } if (x < 1e7) { return x < 1e6 ? 5 : 6; } if (x < 1e9) { return x < 1e8 ? 7 : 8; } return 9; } function alphabeticalCompare(a, b) { if (a === b) { return 0; } if (~~a === a && ~~b === b) { if (a === 0 || b === 0) { return a < b ? -1 : 1; } if (a < 0 || b < 0) { if (b >= 0) { return -1; } if (a >= 0) { return 1; } a = -a; b = -b; } var al = log10(a); var bl = log10(b); var t = 0; if (al < bl) { a *= POWERS_OF_TEN[bl - al - 1]; b /= 10; t = -1; } else if (al > bl) { b *= POWERS_OF_TEN[al - bl - 1]; a /= 10; t = 1; } if (a === b) { return t; } return a < b ? -1 : 1; } var aStr = String(a); var bStr = String(b); if (aStr === bStr) { return 0; } return aStr < bStr ? -1 : 1; } function minRunLength(n) { var r = 0; while (n >= DEFAULT_MIN_MERGE) { r |= n & 1; n >>= 1; } return n + r; } function makeAscendingRun(array, lo, hi, compare) { var runHi = lo + 1; if (runHi === hi) { return 1; } if (compare(array[runHi++], array[lo]) < 0) { while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) { runHi++; } reverseRun(array, lo, runHi); } else { while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) { runHi++; } } return runHi - lo; } function reverseRun(array, lo, hi) { hi--; while (lo < hi) { var t = array[lo]; array[lo++] = array[hi]; array[hi--] = t; } } function binaryInsertionSort(array, lo, hi, start, compare) { if (start === lo) { start++; } for (; start < hi; start++) { var pivot = array[start]; var left = lo; var right = start; while (left < right) { var mid = left + right >>> 1; if (compare(pivot, array[mid]) < 0) { right = mid; } else { left = mid + 1; } } var n = start - left; switch (n) { case 3: array[left + 3] = array[left + 2]; case 2: array[left + 2] = array[left + 1]; case 1: array[left + 1] = array[left]; break; default: while (n > 0) { array[left + n] = array[left + n - 1]; n--; } } array[left] = pivot; } } function gallopLeft(value, array, start, length, hint, compare) { var lastOffset = 0; var maxOffset = 0; var offset = 1; if (compare(value, array[start + hint]) > 0) { maxOffset = length - hint; while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) { lastOffset = offset; offset = (offset << 1) + 1; if (offset <= 0) { offset = maxOffset; } } if (offset > maxOffset) { offset = maxOffset; } lastOffset += hint; offset += hint; } else { maxOffset = hint + 1; while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) { lastOffset = offset; offset = (offset << 1) + 1; if (offset <= 0) { offset = maxOffset; } } if (offset > maxOffset) { offset = maxOffset; } var tmp = lastOffset; lastOffset = hint - offset; offset = hint - tmp; } lastOffset++; while (lastOffset < offset) { var m = lastOffset + (offset - lastOffset >>> 1); if (compare(value, array[start + m]) > 0) { lastOffset = m + 1; } else { offset = m; } } return offset; } function gallopRight(value, array, start, length, hint, compare) { var lastOffset = 0; var maxOffset = 0; var offset = 1; if (compare(value, array[start + hint]) < 0) { maxOffset = hint + 1; while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) { lastOffset = offset; offset = (offset << 1) + 1; if (offset <= 0) { offset = maxOffset; } } if (offset > maxOffset) { offset = maxOffset; } var tmp = lastOffset; lastOffset = hint - offset; offset = hint - tmp; } else { maxOffset = length - hint; while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) { lastOffset = offset; offset = (offset << 1) + 1; if (offset <= 0) { offset = maxOffset; } } if (offset > maxOffset) { offset = maxOffset; } lastOffset += hint; offset += hint; } lastOffset++; while (lastOffset < offset) { var m = lastOffset + (offset - lastOffset >>> 1); if (compare(value, array[start + m]) < 0) { offset = m; } else { lastOffset = m + 1; } } return offset; } var TimSort = function () { function TimSort(array, compare) { _classCallCheck(this, TimSort); this.array = null; this.compare = null; this.minGallop = DEFAULT_MIN_GALLOPING; this.length = 0; this.tmpStorageLength = DEFAULT_TMP_STORAGE_LENGTH; this.stackLength = 0; this.runStart = null; this.runLength = null; this.stackSize = 0; this.array = array; this.compare = compare; this.length = array.length; if (this.length < 2 * DEFAULT_TMP_STORAGE_LENGTH) { this.tmpStorageLength = this.length >>> 1; } this.tmp = new Array(this.tmpStorageLength); this.stackLength = this.length < 120 ? 5 : this.length < 1542 ? 10 : this.length < 119151 ? 19 : 40; this.runStart = new Array(this.stackLength); this.runLength = new Array(this.stackLength); } TimSort.prototype.pushRun = function pushRun(runStart, runLength) { this.runStart[this.stackSize] = runStart; this.runLength[this.stackSize] = runLength; this.stackSize += 1; }; TimSort.prototype.mergeRuns = function mergeRuns() { while (this.stackSize > 1) { var n = this.stackSize - 2; if (n >= 1 && this.runLength[n - 1] <= this.runLength[n] + this.runLength[n + 1] || n >= 2 && this.runLength[n - 2] <= this.runLength[n] + this.runLength[n - 1]) { if (this.runLength[n - 1] < this.runLength[n + 1]) { n--; } } else if (this.runLength[n] > this.runLength[n + 1]) { break; } this.mergeAt(n); } }; TimSort.prototype.forceMergeRuns = function forceMergeRuns() { while (this.stackSize > 1) { var n = this.stackSize - 2; if (n > 0 && this.runLength[n - 1] < this.runLength[n + 1]) { n--; } this.mergeAt(n); } }; TimSort.prototype.mergeAt = function mergeAt(i) { var compare = this.compare; var array = this.array; var start1 = this.runStart[i]; var length1 = this.runLength[i]; var start2 = this.runStart[i + 1]; var length2 = this.runLength[i + 1]; this.runLength[i] = length1 + length2; if (i === this.stackSize - 3) { this.runStart[i + 1] = this.runStart[i + 2]; this.runLength[i + 1] = this.runLength[i + 2]; } this.stackSize--; var k = gallopRight(array[start2], array, start1, length1, 0, compare); start1 += k; length1 -= k; if (length1 === 0) { return; } length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare); if (length2 === 0) { return; } if (length1 <= length2) { this.mergeLow(start1, length1, start2, length2); } else { this.mergeHigh(start1, length1, start2, length2); } }; TimSort.prototype.mergeLow = function mergeLow(start1, length1, start2, length2) { var compare = this.compare; var array = this.array; var tmp = this.tmp; var i = 0; for (i = 0; i < length1; i++) { tmp[i] = array[start1 + i]; } var cursor1 = 0; var cursor2 = start2; var dest = start1; array[dest++] = array[cursor2++]; if (--length2 === 0) { for (i = 0; i < length1; i++) { array[dest + i] = tmp[cursor1 + i]; } return; } if (length1 === 1) { for (i = 0; i < length2; i++) { array[dest + i] = array[cursor2 + i]; } array[dest + length2] = tmp[cursor1]; return; } var minGallop = this.minGallop; while (true) { var count1 = 0; var count2 = 0; var exit = false; do { if (compare(array[cursor2], tmp[cursor1]) < 0) { array[dest++] = array[cursor2++]; count2++; count1 = 0; if (--length2 === 0) { exit = true; break; } } else { array[dest++] = tmp[cursor1++]; count1++; count2 = 0; if (--length1 === 1) { exit = true; break; } } } while ((count1 | count2) < minGallop); if (exit) { break; } do { count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare); if (count1 !== 0) { for (i = 0; i < count1; i++) { array[dest + i] = tmp[cursor1 + i]; } dest += count1; cursor1 += count1; length1 -= count1; if (length1 <= 1) { exit = true; break; } } array[dest++] = array[cursor2++]; if (--length2 === 0) { exit = true; break; } count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare); if (count2 !== 0) { for (i = 0; i < count2; i++) { array[dest + i] = array[cursor2 + i]; } dest += count2; cursor2 += count2; length2 -= count2; if (length2 === 0) { exit = true; break; } } array[dest++] = tmp[cursor1++]; if (--length1 === 1) { exit = true; break; } minGallop--; } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING); if (exit) { break; } if (minGallop < 0) { minGallop = 0; } minGallop += 2; } this.minGallop = minGallop; if (minGallop < 1) { this.minGallop = 1; } if (length1 === 1) { for (i = 0; i < length2; i++) { array[dest + i] = array[cursor2 + i]; } array[dest + length2] = tmp[cursor1]; } else if (length1 === 0) { throw new Error('mergeLow preconditions were not respected'); } else { for (i = 0; i < length1; i++) { array[dest + i] = tmp[cursor1 + i]; } } }; TimSort.prototype.mergeHigh = function mergeHigh(start1, length1, start2, length2) { var compare = this.compare; var array = this.array; var tmp = this.tmp; var i = 0; for (i = 0; i < length2; i++) { tmp[i] = array[start2 + i]; } var cursor1 = start1 + length1 - 1; var cursor2 = length2 - 1; var dest = start2 + length2 - 1; var customCursor = 0; var customDest = 0; array[dest--] = array[cursor1--]; if (--length1 === 0) { customCursor = dest - (length2 - 1); for (i = 0; i < length2; i++) { array[customCursor + i] = tmp[i]; } return; } if (length2 === 1) { dest -= length1; cursor1 -= length1; customDest = dest + 1; customCursor = cursor1 + 1; for (i = length1 - 1; i >= 0; i--) { array[customDest + i] = array[customCursor + i]; } array[dest] = tmp[cursor2]; return; } var minGallop = this.minGallop; while (true) { var count1 = 0; var count2 = 0; var exit = false; do { if (compare(tmp[cursor2], array[cursor1]) < 0) { array[dest--] = array[cursor1--]; count1++; count2 = 0; if (--length1 === 0) { exit = true; break; } } else { array[dest--] = tmp[cursor2--]; count2++; count1 = 0; if (--length2 === 1) { exit = true; break; } } } while ((count1 | count2) < minGallop); if (exit) { break; } do { count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare); if (count1 !== 0) { dest -= count1; cursor1 -= count1; length1 -= count1; customDest = dest + 1; customCursor = cursor1 + 1; for (i = count1 - 1; i >= 0; i--) { array[customDest + i] = array[customCursor + i]; } if (length1 === 0) { exit = true; break; } } array[dest--] = tmp[cursor2--]; if (--length2 === 1) { exit = true; break; } count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare); if (count2 !== 0) { dest -= count2; cursor2 -= count2; length2 -= count2; customDest = dest + 1; customCursor = cursor2 + 1; for (i = 0; i < count2; i++) { array[customDest + i] = tmp[customCursor + i]; } if (length2 <= 1) { exit = true; break; } } array[dest--] = array[cursor1--]; if (--length1 === 0) { exit = true; break; } minGallop--; } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING); if (exit) { break; } if (minGallop < 0) { minGallop = 0; } minGallop += 2; } this.minGallop = minGallop; if (minGallop < 1) { this.minGallop = 1; } if (length2 === 1) { dest -= length1; cursor1 -= length1; customDest = dest + 1; customCursor = cursor1 + 1; for (i = length1 - 1; i >= 0; i--) { array[customDest + i] = array[customCursor + i]; } array[dest] = tmp[cursor2]; } else if (length2 === 0) { throw new Error('mergeHigh preconditions were not respected'); } else { customCursor = dest - (length2 - 1); for (i = 0; i < length2; i++) { array[customCursor + i] = tmp[i]; } } }; return TimSort; }(); function sort(array, compare, lo, hi) { if (!Array.isArray(array)) { throw new TypeError('Can only sort arrays'); } if (!compare) { compare = alphabeticalCompare; } else if (typeof compare !== 'function') { hi = lo; lo = compare; compare = alphabeticalCompare; } if (!lo) { lo = 0; } if (!hi) { hi = array.length; } var remaining = hi - lo; if (remaining < 2) { return; } var runLength = 0; if (remaining < DEFAULT_MIN_MERGE) { runLength = makeAscendingRun(array, lo, hi, compare); binaryInsertionSort(array, lo, hi, lo + runLength, compare); return; } var ts = new TimSort(array, compare); var minRun = minRunLength(remaining); do { runLength = makeAscendingRun(array, lo, hi, compare); if (runLength < minRun) { var force = remaining; if (force > minRun) { force = minRun; } binaryInsertionSort(array, lo, lo + force, lo + runLength, compare); runLength = force; } ts.pushRun(lo, runLength); ts.mergeRuns(); remaining -= runLength; lo += runLength; } while (remaining !== 0); ts.forceMergeRuns(); } }); })(timsort); (function (module) { module.exports = timsort; })(timsort$1); var TimSort = /*@__PURE__*/getDefaultExportFromCjs(timsortExports); function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = _Reflect$construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !_Reflect$construct) return false; if (_Reflect$construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } /** * Interface definition for direction strategy classes. * * This class describes the interface for the Strategy * pattern classes used to differentiate horizontal and vertical * direction of hierarchical results. * * For a given direction, one coordinate will be 'fixed', meaning that it is * determined by level. * The other coordinate is 'unfixed', meaning that the nodes on a given level * can still move along that coordinate. So: * * - `vertical` layout: `x` unfixed, `y` fixed per level * - `horizontal` layout: `x` fixed per level, `y` unfixed * * The local methods are stubs and should be regarded as abstract. * Derived classes **must** implement all the methods themselves. * * @private */ var DirectionInterface = /*#__PURE__*/function () { function DirectionInterface() { _classCallCheck(this, DirectionInterface); } _createClass(DirectionInterface, [{ key: "abstract", value: /** * @ignore */ function abstract() { throw new Error("Can't instantiate abstract class!"); } /** * This is a dummy call which is used to suppress the jsdoc errors of type: * * "'param' is assigned a value but never used" * * @ignore */ }, { key: "fake_use", value: function fake_use() { // Do nothing special } /** * Type to use to translate dynamic curves to, in the case of hierarchical layout. * Dynamic curves do not work for these. * * The value should be perpendicular to the actual direction of the layout. * * @returns {string} Direction, either 'vertical' or 'horizontal' */ }, { key: "curveType", value: function curveType() { return this.abstract(); } /** * Return the value of the coordinate that is not fixed for this direction. * * @param {Node} node The node to read * @returns {number} Value of the unfixed coordinate */ }, { key: "getPosition", value: function getPosition(node) { this.fake_use(node); return this.abstract(); } /** * Set the value of the coordinate that is not fixed for this direction. * * @param {Node} node The node to adjust * @param {number} position * @param {number} [level] if specified, the hierarchy level that this node should be fixed to */ }, { key: "setPosition", value: function setPosition(node, position) { var level = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined; this.fake_use(node, position, level); this.abstract(); } /** * Get the width of a tree. * * A `tree` here is a subset of nodes within the network which are not connected to other nodes, * only among themselves. In essence, it is a sub-network. * * @param {number} index The index number of a tree * @returns {number} the width of a tree in the view coordinates */ }, { key: "getTreeSize", value: function getTreeSize(index) { this.fake_use(index); return this.abstract(); } /** * Sort array of nodes on the unfixed coordinates. * * Note:** chrome has non-stable sorting implementation, which * has a tendency to change the order of the array items, * even if the custom sort function returns 0. * * For this reason, an external sort implementation is used, * which has the added benefit of being faster than the standard * platforms implementation. This has been verified on `node.js`, * `firefox` and `chrome` (all linux). * * @param {Array.<Node>} nodeArray array of nodes to sort */ }, { key: "sort", value: function sort(nodeArray) { this.fake_use(nodeArray); this.abstract(); } /** * Assign the fixed coordinate of the node to the given level * * @param {Node} node The node to adjust * @param {number} level The level to fix to */ }, { key: "fix", value: function fix(node, level) { this.fake_use(node, level); this.abstract(); } /** * Add an offset to the unfixed coordinate of the given node. * * @param {NodeId} nodeId Id of the node to adjust * @param {number} diff Offset to add to the unfixed coordinate */ }, { key: "shift", value: function shift(nodeId, diff) { this.fake_use(nodeId, diff); this.abstract(); } }]); return DirectionInterface; }(); /** * Vertical Strategy * * Coordinate `y` is fixed on levels, coordinate `x` is unfixed. * * @augments DirectionInterface * @private */ var VerticalStrategy = /*#__PURE__*/function (_DirectionInterface) { _inherits(VerticalStrategy, _DirectionInterface); var _super = _createSuper(VerticalStrategy); /** * Constructor * * @param {object} layout reference to the parent LayoutEngine instance. */ function VerticalStrategy(layout) { var _this; _classCallCheck(this, VerticalStrategy); _this = _super.call(this); _this.layout = layout; return _this; } /** @inheritDoc */ _createClass(VerticalStrategy, [{ key: "curveType", value: function curveType() { return "horizontal"; } /** @inheritDoc */ }, { key: "getPosition", value: function getPosition(node) { return node.x; } /** @inheritDoc */ }, { key: "setPosition", value: function setPosition(node, position) { var level = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined; if (level !== undefined) { this.layout.hierarchical.addToOrdering(node, level); } node.x = position; } /** @inheritDoc */ }, { key: "getTreeSize", value: function getTreeSize(index) { var res = this.layout.hierarchical.getTreeSize(this.layout.body.nodes, index); return { min: res.min_x, max: res.max_x }; } /** @inheritDoc */ }, { key: "sort", value: function sort(nodeArray) { timsortExports.sort(nodeArray, function (a, b) { return a.x - b.x; }); } /** @inheritDoc */ }, { key: "fix", value: function fix(node, level) { node.y = this.layout.options.hierarchical.levelSeparation * level; node.options.fixed.y = true; } /** @inheritDoc */ }, { key: "shift", value: function shift(nodeId, diff) { this.layout.body.nodes[nodeId].x += diff; } }]); return VerticalStrategy; }(DirectionInterface); /** * Horizontal Strategy * * Coordinate `x` is fixed on levels, coordinate `y` is unfixed. * * @augments DirectionInterface * @private */ var HorizontalStrategy = /*#__PURE__*/function (_DirectionInterface2) { _inherits(HorizontalStrategy, _DirectionInterface2); var _super2 = _createSuper(HorizontalStrategy); /** * Constructor * * @param {object} layout reference to the parent LayoutEngine instance. */ function HorizontalStrategy(layout) { var _this2; _classCallCheck(this, HorizontalStrategy); _this2 = _super2.call(this); _this2.layout = layout; return _this2; } /** @inheritDoc */ _createClass(HorizontalStrategy, [{ key: "curveType", value: function curveType() { return "vertical"; } /** @inheritDoc */ }, { key: "getPosition", value: function getPosition(node) { return node.y; } /** @inheritDoc */ }, { key: "setPosition", value: function setPosition(node, position) { var level = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined; if (level !== undefined) { this.layout.hierarchical.addToOrdering(node, level); } node.y = position; } /** @inheritDoc */ }, { key: "getTreeSize", value: function getTreeSize(index) { var res = this.layout.hierarchical.getTreeSize(this.layout.body.nodes, index); return { min: res.min_y, max: res.max_y }; } /** @inheritDoc */ }, { key: "sort", value: function sort(nodeArray) { timsortExports.sort(nodeArray, function (a, b) { return a.y - b.y; }); } /** @inheritDoc */ }, { key: "fix", value: function fix(node, level) { node.x = this.layout.options.hierarchical.levelSeparation * level; node.options.fixed.x = true; } /** @inheritDoc */ }, { key: "shift", value: function shift(nodeId, diff) { this.layout.body.nodes[nodeId].y += diff; } }]); return HorizontalStrategy; }(DirectionInterface); var everyExports = {}; var every$3 = { get exports() { return everyExports; }, set exports(v) { everyExports = v; } }; var $ = _export; var $every = arrayIteration.every; var arrayMethodIsStrict = arrayMethodIsStrict$6; var STRICT_METHOD = arrayMethodIsStrict('every'); // `Array.prototype.every` method // https://tc39.es/ecma262/#sec-array.prototype.every $({ target: 'Array', proto: true, forced: !STRICT_METHOD }, { every: function every(callbackfn /* , thisArg */) { return $every(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined); } }); var entryVirtual = entryVirtual$i; var every$2 = entryVirtual('Array').every; var isPrototypeOf = objectIsPrototypeOf; var method = every$2; var ArrayPrototype = Array.prototype; var every$1 = function (it) { var own = it.every; return it === ArrayPrototype || isPrototypeOf(ArrayPrototype, it) && own === ArrayPrototype.every ? method : own; }; var parent = every$1; var every = parent; (function (module) { module.exports = every; })(every$3); var _everyInstanceProperty = /*@__PURE__*/getDefaultExportFromCjs(everyExports); function _createForOfIteratorHelper$1(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray$1(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray$1(o, minLen) { var _context9; if (!o) return; if (typeof o === "string") return _arrayLikeToArray$1(o, minLen); var n = _sliceInstanceProperty(_context9 = Object.prototype.toString.call(o)).call(_context9, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from$1(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$1(o, minLen); } function _arrayLikeToArray$1(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } /** * Try to assign levels to nodes according to their positions in the cyclic “hierarchy”. * * @param nodes - Visible nodes of the graph. * @param levels - If present levels will be added to it, if not a new object will be created. * @returns Populated node levels. */ function fillLevelsByDirectionCyclic(nodes, levels) { var edges = new _Set(); _forEachInstanceProperty(nodes).call(nodes, function (node) { var _context; _forEachInstanceProperty(_context = node.edges).call(_context, function (edge) { if (edge.connected) { edges.add(edge); } }); }); _forEachInstanceProperty(edges).call(edges, function (edge) { var fromId = edge.from.id; var toId = edge.to.id; if (levels[fromId] == null) { levels[fromId] = 0; } if (levels[toId] == null || levels[fromId] >= levels[toId]) { levels[toId] = levels[fromId] + 1; } }); return levels; } /** * Assign levels to nodes according to their positions in the hierarchy. Leaves will be lined up at the bottom and all other nodes as close to their children as possible. * * @param nodes - Visible nodes of the graph. * @returns Populated node levels. */ function fillLevelsByDirectionLeaves(nodes) { return fillLevelsByDirection( // Pick only leaves (nodes without children). function (node) { var _context2, _context3; return _everyInstanceProperty(_context2 = _filterInstanceProperty(_context3 = node.edges // Take only visible nodes into account. ).call(_context3, function (edge) { return nodes.has(edge.toId); }) // Check that all edges lead to this node (leaf). ).call(_context2, function (edge) { return edge.to === node; }); }, // Use the lowest level. function (newLevel, oldLevel) { return oldLevel > newLevel; }, // Go against the direction of the edges. "from", nodes); } /** * Assign levels to nodes according to their positions in the hierarchy. Roots will be lined up at the top and all nodes as close to their parents as possible. * * @param nodes - Visible nodes of the graph. * @returns Populated node levels. */ function fillLevelsByDirectionRoots(nodes) { return fillLevelsByDirection( // Pick only roots (nodes without parents). function (node) { var _context4, _context5; return _everyInstanceProperty(_context4 = _filterInstanceProperty(_context5 = node.edges // Take only visible nodes into account. ).call(_context5, function (edge) { return nodes.has(edge.toId); }) // Check that all edges lead from this node (root). ).call(_context4, function (edge) { return edge.from === node; }); }, // Use the highest level. function (newLevel, oldLevel) { return oldLevel < newLevel; }, // Go in the direction of the edges. "to", nodes); } /** * Assign levels to nodes according to their positions in the hierarchy. * * @param isEntryNode - Checks and return true if the graph should be traversed from this node. * @param shouldLevelBeReplaced - Checks and returns true if the level of given node should be updated to the new value. * @param direction - Wheter the graph should be traversed in the direction of the edges `"to"` or in the other way `"from"`. * @param nodes - Visible nodes of the graph. * @returns Populated node levels. */ function fillLevelsByDirection(isEntryNode, shouldLevelBeReplaced, direction, nodes) { var _context6; var levels = _Object$create$1(null); // If acyclic, the graph can be walked through with (most likely way) fewer // steps than the number bellow. The exact value isn't too important as long // as it's quick to compute (doesn't impact acyclic graphs too much), is // higher than the number of steps actually needed (doesn't cut off before // acyclic graph is walked through) and prevents infinite loops (cuts off for // cyclic graphs). var limit = _reduceInstanceProperty(_context6 = _toConsumableArray(_valuesInstanceProperty(nodes).call(nodes))).call(_context6, function (acc, node) { return acc + 1 + node.edges.length; }, 0); var edgeIdProp = direction + "Id"; var newLevelDiff = direction === "to" ? 1 : -1; var _iterator = _createForOfIteratorHelper$1(nodes), _step; try { var _loop = function _loop() { var _step$value = _slicedToArray(_step.value, 2), entryNodeId = _step$value[0], entryNode = _step$value[1]; if ( // Skip if the node is not visible. !nodes.has(entryNodeId) || // Skip if the node is not an entry node. !isEntryNode(entryNode)) { return "continue"; } // Line up all the entry nodes on level 0. levels[entryNodeId] = 0; var stack = [entryNode]; var done = 0; var node; var _loop2 = function _loop2() { var _context7, _context8; if (!nodes.has(entryNodeId)) { // Skip if the node is not visible. return "continue"; } var newLevel = levels[node.id] + newLevelDiff; _forEachInstanceProperty(_context7 = _filterInstanceProperty(_context8 = node.edges).call(_context8, function (edge) { return ( // Ignore disconnected edges. edge.connected && // Ignore circular edges. edge.to !== edge.from && // Ignore edges leading to the node that's currently being processed. edge[direction] !== node && // Ignore edges connecting to an invisible node. nodes.has(edge.toId) && // Ignore edges connecting from an invisible node. nodes.has(edge.fromId) ); })).call(_context7, function (edge) { var targetNodeId = edge[edgeIdProp]; var oldLevel = levels[targetNodeId]; if (oldLevel == null || shouldLevelBeReplaced(newLevel, oldLevel)) { levels[targetNodeId] = newLevel; stack.push(edge[direction]); } }); if (done > limit) { // This would run forever on a cyclic graph. return { v: { v: fillLevelsByDirectionCyclic(nodes, levels) } }; } else { ++done; } }; while (node = stack.pop()) { var _ret2 = _loop2(); if (_ret2 === "continue") continue; if (_typeof(_ret2) === "object") return _ret2.v; } }; for (_iterator.s(); !(_step = _iterator.n()).done;) { var _ret = _loop(); if (_ret === "continue") continue; if (_typeof(_ret) === "object") return _ret.v; } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return levels; } /** * There's a mix-up with terms in the code. Following are the formal definitions: * * tree - a strict hierarchical network, i.e. every node has at most one parent * forest - a collection of trees. These distinct trees are thus not connected. * * So: * - in a network that is not a tree, there exist nodes with multiple parents. * - a network consisting of unconnected sub-networks, of which at least one * is not a tree, is not a forest. * * In the code, the definitions are: * * tree - any disconnected sub-network, strict hierarchical or not. * forest - a bunch of these sub-networks * * The difference between tree and not-tree is important in the code, notably within * to the block-shifting algorithm. The algorithm assumes formal trees and fails * for not-trees, often in a spectacular manner (search for 'exploding network' in the issues). * * In order to distinguish the definitions in the following code, the adjective 'formal' is * used. If 'formal' is absent, you must assume the non-formal definition. * * ---------------------------------------------------------------------------------- * NOTES * ===== * * A hierarchical layout is a different thing from a hierarchical network. * The layout is a way to arrange the nodes in the view; this can be done * on non-hierarchical networks as well. The converse is also possible. */ /** * Container for derived data on current network, relating to hierarchy. * * @private */ var HierarchicalStatus = /*#__PURE__*/function () { /** * @ignore */ function HierarchicalStatus() { _classCallCheck(this, HierarchicalStatus); this.childrenReference = {}; // child id's per node id this.parentReference = {}; // parent id's per node id this.trees = {}; // tree id per node id; i.e. to which tree does given node id belong this.distributionOrdering = {}; // The nodes per level, in the display order this.levels = {}; // hierarchy level per node id this.distributionIndex = {}; // The position of the node in the level sorting order, per node id. this.isTree = false; // True if current network is a formal tree this.treeIndex = -1; // Highest tree id in current network. } /** * Add the relation between given nodes to the current state. * * @param {Node.id} parentNodeId * @param {Node.id} childNodeId */ _createClass(HierarchicalStatus, [{ key: "addRelation", value: function addRelation(parentNodeId, childNodeId) { if (this.childrenReference[parentNodeId] === undefined) { this.childrenReference[parentNodeId] = []; } this.childrenReference[parentNodeId].push(childNodeId); if (this.parentReference[childNodeId] === undefined) { this.parentReference[childNodeId] = []; } this.parentReference[childNodeId].push(parentNodeId); } /** * Check if the current state is for a formal tree or formal forest. * * This is the case if every node has at most one parent. * * Pre: parentReference init'ed properly for current network */ }, { key: "checkIfTree", value: function checkIfTree() { for (var i in this.parentReference) { if (this.parentReference[i].length > 1) { this.isTree = false; return; } } this.isTree = true; } /** * Return the number of separate trees in the current network. * * @returns {number} */ }, { key: "numTrees", value: function numTrees() { return this.treeIndex + 1; // This assumes the indexes are assigned consecitively } /** * Assign a tree id to a node * * @param {Node} node * @param {string|number} treeId */ }, { key: "setTreeIndex", value: function setTreeIndex(node, treeId) { if (treeId === undefined) return; // Don't bother if (this.trees[node.id] === undefined) { this.trees[node.id] = treeId; this.treeIndex = Math.max(treeId, this.treeIndex); } } /** * Ensure level for given id is defined. * * Sets level to zero for given node id if not already present * * @param {Node.id} nodeId */ }, { key: "ensureLevel", value: function ensureLevel(nodeId) { if (this.levels[nodeId] === undefined) { this.levels[nodeId] = 0; } } /** * get the maximum level of a branch. * * TODO: Never entered; find a test case to test this! * * @param {Node.id} nodeId * @returns {number} */ }, { key: "getMaxLevel", value: function getMaxLevel(nodeId) { var _this = this; var accumulator = {}; var _getMaxLevel = function _getMaxLevel(nodeId) { if (accumulator[nodeId] !== undefined) { return accumulator[nodeId]; } var level = _this.levels[nodeId]; if (_this.childrenReference[nodeId]) { var children = _this.childrenReference[nodeId]; if (children.length > 0) { for (var i = 0; i < children.length; i++) { level = Math.max(level, _getMaxLevel(children[i])); } } } accumulator[nodeId] = level; return level; }; return _getMaxLevel(nodeId); } /** * * @param {Node} nodeA * @param {Node} nodeB */ }, { key: "levelDownstream", value: function levelDownstream(nodeA, nodeB) { if (this.levels[nodeB.id] === undefined) { // set initial level if (this.levels[nodeA.id] === undefined) { this.levels[nodeA.id] = 0; } // set level this.levels[nodeB.id] = this.levels[nodeA.id] + 1; } } /** * Small util method to set the minimum levels of the nodes to zero. * * @param {Array.<Node>} nodes */ }, { key: "setMinLevelToZero", value: function setMinLevelToZero(nodes) { var minLevel = 1e9; // get the minimum level for (var nodeId in nodes) { if (Object.prototype.hasOwnProperty.call(nodes, nodeId)) { if (this.levels[nodeId] !== undefined) { minLevel = Math.min(this.levels[nodeId], minLevel); } } } // subtract the minimum from the set so we have a range starting from 0 for (var _nodeId in nodes) { if (Object.prototype.hasOwnProperty.call(nodes, _nodeId)) { if (this.levels[_nodeId] !== undefined) { this.levels[_nodeId] -= minLevel; } } } } /** * Get the min and max xy-coordinates of a given tree * * @param {Array.<Node>} nodes * @param {number} index * @returns {{min_x: number, max_x: number, min_y: number, max_y: number}} */ }, { key: "getTreeSize", value: function getTreeSize(nodes, index) { var min_x = 1e9; var max_x = -1e9; var min_y = 1e9; var max_y = -1e9; for (var nodeId in this.trees) { if (Object.prototype.hasOwnProperty.call(this.trees, nodeId)) { if (this.trees[nodeId] === index) { var node = nodes[nodeId]; min_x = Math.min(node.x, min_x); max_x = Math.max(node.x, max_x); min_y = Math.min(node.y, min_y); max_y = Math.max(node.y, max_y); } } } return { min_x: min_x, max_x: max_x, min_y: min_y, max_y: max_y }; } /** * Check if two nodes have the same parent(s) * * @param {Node} node1 * @param {Node} node2 * @returns {boolean} true if the two nodes have a same ancestor node, false otherwise */ }, { key: "hasSameParent", value: function hasSameParent(node1, node2) { var parents1 = this.parentReference[node1.id]; var parents2 = this.parentReference[node2.id]; if (parents1 === undefined || parents2 === undefined) { return false; } for (var i = 0; i < parents1.length; i++) { for (var j = 0; j < parents2.length; j++) { if (parents1[i] == parents2[j]) { return true; } } } return false; } /** * Check if two nodes are in the same tree. * * @param {Node} node1 * @param {Node} node2 * @returns {boolean} true if this is so, false otherwise */ }, { key: "inSameSubNetwork", value: function inSameSubNetwork(node1, node2) { return this.trees[node1.id] === this.trees[node2.id]; } /** * Get a list of the distinct levels in the current network * * @returns {Array} */ }, { key: "getLevels", value: function getLevels() { return _Object$keys(this.distributionOrdering); } /** * Add a node to the ordering per level * * @param {Node} node * @param {number} level */ }, { key: "addToOrdering", value: function addToOrdering(node, level) { if (this.distributionOrdering[level] === undefined) { this.distributionOrdering[level] = []; } var isPresent = false; var curLevel = this.distributionOrdering[level]; for (var n in curLevel) { //if (curLevel[n].id === node.id) { if (curLevel[n] === node) { isPresent = true; break; } } if (!isPresent) { this.distributionOrdering[level].push(node); this.distributionIndex[node.id] = this.distributionOrdering[level].length - 1; } } }]); return HierarchicalStatus; }(); /** * The Layout Engine */ var LayoutEngine = /*#__PURE__*/function () { /** * @param {object} body */ function LayoutEngine(body) { _classCallCheck(this, LayoutEngine); this.body = body; // Make sure there always is some RNG because the setOptions method won't // set it unless there's a seed for it. this._resetRNG(Math.random() + ":" + _Date$now()); this.setPhysics = false; this.options = {}; this.optionsBackup = { physics: {} }; this.defaultOptions = { randomSeed: undefined, improvedLayout: true, clusterThreshold: 150, hierarchical: { enabled: false, levelSeparation: 150, nodeSpacing: 100, treeSpacing: 200, blockShifting: true, edgeMinimization: true, parentCentralization: true, direction: "UD", // UD, DU, LR, RL sortMethod: "hubsize" // hubsize, directed } }; _Object$assign(this.options, this.defaultOptions); this.bindEventListeners(); } /** * Binds event listeners */ _createClass(LayoutEngine, [{ key: "bindEventListeners", value: function bindEventListeners() { var _this2 = this; this.body.emitter.on("_dataChanged", function () { _this2.setupHierarchicalLayout(); }); this.body.emitter.on("_dataLoaded", function () { _this2.layoutNetwork(); }); this.body.emitter.on("_resetHierarchicalLayout", function () { _this2.setupHierarchicalLayout(); }); this.body.emitter.on("_adjustEdgesForHierarchicalLayout", function () { if (_this2.options.hierarchical.enabled !== true) { return; } // get the type of static smooth curve in case it is required var type = _this2.direction.curveType(); // force all edges into static smooth curves. _this2.body.emitter.emit("_forceDisableDynamicCurves", type, false); }); } /** * * @param {object} options * @param {object} allOptions * @returns {object} */ }, { key: "setOptions", value: function setOptions(options, allOptions) { if (options !== undefined) { var hierarchical = this.options.hierarchical; var prevHierarchicalState = hierarchical.enabled; selectiveDeepExtend(["randomSeed", "improvedLayout", "clusterThreshold"], this.options, options); mergeOptions(this.options, options, "hierarchical"); if (options.randomSeed !== undefined) { this._resetRNG(options.randomSeed); } if (hierarchical.enabled === true) { if (prevHierarchicalState === true) { // refresh the overridden options for nodes and edges. this.body.emitter.emit("refresh", true); } // make sure the level separation is the right way up if (hierarchical.direction === "RL" || hierarchical.direction === "DU") { if (hierarchical.levelSeparation > 0) { hierarchical.levelSeparation *= -1; } } else { if (hierarchical.levelSeparation < 0) { hierarchical.levelSeparation *= -1; } } this.setDirectionStrategy(); this.body.emitter.emit("_resetHierarchicalLayout"); // because the hierarchical system needs it's own physics and smooth curve settings, // we adapt the other options if needed. return this.adaptAllOptionsForHierarchicalLayout(allOptions); } else { if (prevHierarchicalState === true) { // refresh the overridden options for nodes and edges. this.body.emitter.emit("refresh"); return deepExtend(allOptions, this.optionsBackup); } } } return allOptions; } /** * Reset the random number generator with given seed. * * @param {any} seed - The seed that will be forwarded the the RNG. */ }, { key: "_resetRNG", value: function _resetRNG(seed) { this.initialRandomSeed = seed; this._rng = Alea(this.initialRandomSeed); } /** * * @param {object} allOptions * @returns {object} */ }, { key: "adaptAllOptionsForHierarchicalLayout", value: function adaptAllOptionsForHierarchicalLayout(allOptions) { if (this.options.hierarchical.enabled === true) { var backupPhysics = this.optionsBackup.physics; // set the physics if (allOptions.physics === undefined || allOptions.physics === true) { allOptions.physics = { enabled: backupPhysics.enabled === undefined ? true : backupPhysics.enabled, solver: "hierarchicalRepulsion" }; backupPhysics.enabled = backupPhysics.enabled === undefined ? true : backupPhysics.enabled; backupPhysics.solver = backupPhysics.solver || "barnesHut"; } else if (_typeof(allOptions.physics) === "object") { backupPhysics.enabled = allOptions.physics.enabled === undefined ? true : allOptions.physics.enabled; backupPhysics.solver = allOptions.physics.solver || "barnesHut"; allOptions.physics.solver = "hierarchicalRepulsion"; } else if (allOptions.physics !== false) { backupPhysics.solver = "barnesHut"; allOptions.physics = { solver: "hierarchicalRepulsion" }; } // get the type of static smooth curve in case it is required var type = this.direction.curveType(); // disable smooth curves if nothing is defined. If smooth curves have been turned on, // turn them into static smooth curves. if (allOptions.edges === undefined) { this.optionsBackup.edges = { smooth: { enabled: true, type: "dynamic" } }; allOptions.edges = { smooth: false }; } else if (allOptions.edges.smooth === undefined) { this.optionsBackup.edges = { smooth: { enabled: true, type: "dynamic" } }; allOptions.edges.smooth = false; } else { if (typeof allOptions.edges.smooth === "boolean") { this.optionsBackup.edges = { smooth: allOptions.edges.smooth }; allOptions.edges.smooth = { enabled: allOptions.edges.smooth, type: type }; } else { var smooth = allOptions.edges.smooth; // allow custom types except for dynamic if (smooth.type !== undefined && smooth.type !== "dynamic") { type = smooth.type; } // TODO: this is options merging; see if the standard routines can be used here. this.optionsBackup.edges = { smooth: { enabled: smooth.enabled === undefined ? true : smooth.enabled, type: smooth.type === undefined ? "dynamic" : smooth.type, roundness: smooth.roundness === undefined ? 0.5 : smooth.roundness, forceDirection: smooth.forceDirection === undefined ? false : smooth.forceDirection } }; // NOTE: Copying an object to self; this is basically setting defaults for undefined variables allOptions.edges.smooth = { enabled: smooth.enabled === undefined ? true : smooth.enabled, type: type, roundness: smooth.roundness === undefined ? 0.5 : smooth.roundness, forceDirection: smooth.forceDirection === undefined ? false : smooth.forceDirection }; } } // Force all edges into static smooth curves. // Only applies to edges that do not use the global options for smooth. this.body.emitter.emit("_forceDisableDynamicCurves", type); } return allOptions; } /** * * @param {Array.<Node>} nodesArray */ }, { key: "positionInitially", value: function positionInitially(nodesArray) { if (this.options.hierarchical.enabled !== true) { this._resetRNG(this.initialRandomSeed); var radius = nodesArray.length + 50; for (var i = 0; i < nodesArray.length; i++) { var node = nodesArray[i]; var angle = 2 * Math.PI * this._rng(); if (node.x === undefined) { node.x = radius * Math.cos(angle); } if (node.y === undefined) { node.y = radius * Math.sin(angle); } } } } /** * Use Kamada Kawai to position nodes. This is quite a heavy algorithm so if there are a lot of nodes we * cluster them first to reduce the amount. */ }, { key: "layoutNetwork", value: function layoutNetwork() { if (this.options.hierarchical.enabled !== true && this.options.improvedLayout === true) { var indices = this.body.nodeIndices; // first check if we should Kamada Kawai to layout. The threshold is if less than half of the visible // nodes have predefined positions we use this. var positionDefined = 0; for (var i = 0; i < indices.length; i++) { var node = this.body.nodes[indices[i]]; if (node.predefinedPosition === true) { positionDefined += 1; } } // if less than half of the nodes have a predefined position we continue if (positionDefined < 0.5 * indices.length) { var MAX_LEVELS = 10; var level = 0; var clusterThreshold = this.options.clusterThreshold; // // Define the options for the hidden cluster nodes // These options don't propagate outside the clustering phase. // // Some options are explicitly disabled, because they may be set in group or default node options. // The clusters are never displayed, so most explicit settings here serve as performance optimizations. // // The explicit setting of 'shape' is to avoid `shape: 'image'`; images are not passed to the hidden // cluster nodes, leading to an exception on creation. // // All settings here are performance related, except when noted otherwise. // var clusterOptions = { clusterNodeProperties: { shape: "ellipse", // Bugfix: avoid type 'image', no images supplied label: "", // avoid label handling group: "", // avoid group handling font: { multi: false } // avoid font propagation }, clusterEdgeProperties: { label: "", // avoid label handling font: { multi: false }, // avoid font propagation smooth: { enabled: false // avoid drawing penalty for complex edges } } }; // if there are a lot of nodes, we cluster before we run the algorithm. // NOTE: this part fails to find clusters for large scale-free networks, which should // be easily clusterable. // TODO: examine why this is so if (indices.length > clusterThreshold) { var startLength = indices.length; while (indices.length > clusterThreshold && level <= MAX_LEVELS) { //console.time("clustering") level += 1; var before = indices.length; // if there are many nodes we do a hubsize cluster if (level % 3 === 0) { this.body.modules.clustering.clusterBridges(clusterOptions); } else { this.body.modules.clustering.clusterOutliers(clusterOptions); } var after = indices.length; if (before == after && level % 3 !== 0) { this._declusterAll(); this.body.emitter.emit("_layoutFailed"); console.info("This network could not be positioned by this version of the improved layout algorithm." + " Please disable improvedLayout for better performance."); return; } //console.timeEnd("clustering") //console.log(before,level,after); } // increase the size of the edges this.body.modules.kamadaKawai.setOptions({ springLength: Math.max(150, 2 * startLength) }); } if (level > MAX_LEVELS) { console.info("The clustering didn't succeed within the amount of interations allowed," + " progressing with partial result."); } // position the system for these nodes and edges this.body.modules.kamadaKawai.solve(indices, this.body.edgeIndices, true); // shift to center point this._shiftToCenter(); // perturb the nodes a little bit to force the physics to kick in var offset = 70; for (var _i = 0; _i < indices.length; _i++) { // Only perturb the nodes that aren't fixed var _node = this.body.nodes[indices[_i]]; if (_node.predefinedPosition === false) { _node.x += (0.5 - this._rng()) * offset; _node.y += (0.5 - this._rng()) * offset; } } // uncluster all clusters this._declusterAll(); // reposition all bezier nodes. this.body.emitter.emit("_repositionBezierNodes"); } } } /** * Move all the nodes towards to the center so gravitational pull wil not move the nodes away from view * * @private */ }, { key: "_shiftToCenter", value: function _shiftToCenter() { var range = NetworkUtil.getRangeCore(this.body.nodes, this.body.nodeIndices); var center = NetworkUtil.findCenter(range); for (var i = 0; i < this.body.nodeIndices.length; i++) { var node = this.body.nodes[this.body.nodeIndices[i]]; node.x -= center.x; node.y -= center.y; } } /** * Expands all clusters * * @private */ }, { key: "_declusterAll", value: function _declusterAll() { var clustersPresent = true; while (clustersPresent === true) { clustersPresent = false; for (var i = 0; i < this.body.nodeIndices.length; i++) { if (this.body.nodes[this.body.nodeIndices[i]].isCluster === true) { clustersPresent = true; this.body.modules.clustering.openCluster(this.body.nodeIndices[i], {}, false); } } if (clustersPresent === true) { this.body.emitter.emit("_dataChanged"); } } } /** * * @returns {number|*} */ }, { key: "getSeed", value: function getSeed() { return this.initialRandomSeed; } /** * This is the main function to layout the nodes in a hierarchical way. * It checks if the node details are supplied correctly * * @private */ }, { key: "setupHierarchicalLayout", value: function setupHierarchicalLayout() { if (this.options.hierarchical.enabled === true && this.body.nodeIndices.length > 0) { // get the size of the largest hubs and check if the user has defined a level for a node. var node, nodeId; var definedLevel = false; var undefinedLevel = false; this.lastNodeOnLevel = {}; this.hierarchical = new HierarchicalStatus(); for (nodeId in this.body.nodes) { if (Object.prototype.hasOwnProperty.call(this.body.nodes, nodeId)) { node = this.body.nodes[nodeId]; if (node.options.level !== undefined) { definedLevel = true; this.hierarchical.levels[nodeId] = node.options.level; } else { undefinedLevel = true; } } } // if the user defined some levels but not all, alert and run without hierarchical layout if (undefinedLevel === true && definedLevel === true) { throw new Error("To use the hierarchical layout, nodes require either no predefined levels" + " or levels have to be defined for all nodes."); } else { // define levels if undefined by the users. Based on hubsize. if (undefinedLevel === true) { var sortMethod = this.options.hierarchical.sortMethod; if (sortMethod === "hubsize") { this._determineLevelsByHubsize(); } else if (sortMethod === "directed") { this._determineLevelsDirected(); } else if (sortMethod === "custom") { this._determineLevelsCustomCallback(); } } // fallback for cases where there are nodes but no edges for (var _nodeId2 in this.body.nodes) { if (Object.prototype.hasOwnProperty.call(this.body.nodes, _nodeId2)) { this.hierarchical.ensureLevel(_nodeId2); } } // check the distribution of the nodes per level. var distribution = this._getDistribution(); // get the parent children relations. this._generateMap(); // place the nodes on the canvas. this._placeNodesByHierarchy(distribution); // condense the whitespace. this._condenseHierarchy(); // shift to center so gravity does not have to do much this._shiftToCenter(); } } } /** * @private */ }, { key: "_condenseHierarchy", value: function _condenseHierarchy() { var _this3 = this; // Global var in this scope to define when the movement has stopped. var stillShifting = false; var branches = {}; // first we have some methods to help shifting trees around. // the main method to shift the trees var shiftTrees = function shiftTrees() { var treeSizes = getTreeSizes(); var shiftBy = 0; for (var i = 0; i < treeSizes.length - 1; i++) { var diff = treeSizes[i].max - treeSizes[i + 1].min; shiftBy += diff + _this3.options.hierarchical.treeSpacing; shiftTree(i + 1, shiftBy); } }; // shift a single tree by an offset var shiftTree = function shiftTree(index, offset) { var trees = _this3.hierarchical.trees; for (var nodeId in trees) { if (Object.prototype.hasOwnProperty.call(trees, nodeId)) { if (trees[nodeId] === index) { _this3.direction.shift(nodeId, offset); } } } }; // get the width of all trees var getTreeSizes = function getTreeSizes() { var treeWidths = []; for (var i = 0; i < _this3.hierarchical.numTrees(); i++) { treeWidths.push(_this3.direction.getTreeSize(i)); } return treeWidths; }; // get a map of all nodes in this branch var getBranchNodes = function getBranchNodes(source, map) { if (map[source.id]) { return; } map[source.id] = true; if (_this3.hierarchical.childrenReference[source.id]) { var children = _this3.hierarchical.childrenReference[source.id]; if (children.length > 0) { for (var i = 0; i < children.length; i++) { getBranchNodes(_this3.body.nodes[children[i]], map); } } } }; // get a min max width as well as the maximum movement space it has on either sides // we use min max terminology because width and height can interchange depending on the direction of the layout var getBranchBoundary = function getBranchBoundary(branchMap) { var maxLevel = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1e9; var minSpace = 1e9; var maxSpace = 1e9; var min = 1e9; var max = -1e9; for (var branchNode in branchMap) { if (Object.prototype.hasOwnProperty.call(branchMap, branchNode)) { var node = _this3.body.nodes[branchNode]; var level = _this3.hierarchical.levels[node.id]; var position = _this3.direction.getPosition(node); // get the space around the node. var _this3$_getSpaceAroun = _this3._getSpaceAroundNode(node, branchMap), _this3$_getSpaceAroun2 = _slicedToArray(_this3$_getSpaceAroun, 2), minSpaceNode = _this3$_getSpaceAroun2[0], maxSpaceNode = _this3$_getSpaceAroun2[1]; minSpace = Math.min(minSpaceNode, minSpace); maxSpace = Math.min(maxSpaceNode, maxSpace); // the width is only relevant for the levels two nodes have in common. This is why we filter on this. if (level <= maxLevel) { min = Math.min(position, min); max = Math.max(position, max); } } } return [min, max, minSpace, maxSpace]; }; // check what the maximum level is these nodes have in common. var getCollisionLevel = function getCollisionLevel(node1, node2) { var maxLevel1 = _this3.hierarchical.getMaxLevel(node1.id); var maxLevel2 = _this3.hierarchical.getMaxLevel(node2.id); return Math.min(maxLevel1, maxLevel2); }; /** * Condense elements. These can be nodes or branches depending on the callback. * * @param {Function} callback * @param {Array.<number>} levels * @param {*} centerParents */ var shiftElementsCloser = function shiftElementsCloser(callback, levels, centerParents) { var hier = _this3.hierarchical; for (var i = 0; i < levels.length; i++) { var level = levels[i]; var levelNodes = hier.distributionOrdering[level]; if (levelNodes.length > 1) { for (var j = 0; j < levelNodes.length - 1; j++) { var node1 = levelNodes[j]; var node2 = levelNodes[j + 1]; // NOTE: logic maintained as it was; if nodes have same ancestor, // then of course they are in the same sub-network. if (hier.hasSameParent(node1, node2) && hier.inSameSubNetwork(node1, node2)) { callback(node1, node2, centerParents); } } } } }; // callback for shifting branches var branchShiftCallback = function branchShiftCallback(node1, node2) { var centerParent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; //window.CALLBACKS.push(() => { var pos1 = _this3.direction.getPosition(node1); var pos2 = _this3.direction.getPosition(node2); var diffAbs = Math.abs(pos2 - pos1); var nodeSpacing = _this3.options.hierarchical.nodeSpacing; //console.log("NOW CHECKING:", node1.id, node2.id, diffAbs); if (diffAbs > nodeSpacing) { var branchNodes1 = {}; var branchNodes2 = {}; getBranchNodes(node1, branchNodes1); getBranchNodes(node2, branchNodes2); // check the largest distance between the branches var maxLevel = getCollisionLevel(node1, node2); var branchNodeBoundary1 = getBranchBoundary(branchNodes1, maxLevel); var branchNodeBoundary2 = getBranchBoundary(branchNodes2, maxLevel); var max1 = branchNodeBoundary1[1]; var min2 = branchNodeBoundary2[0]; var minSpace2 = branchNodeBoundary2[2]; //console.log(node1.id, getBranchBoundary(branchNodes1, maxLevel), node2.id, // getBranchBoundary(branchNodes2, maxLevel), maxLevel); var diffBranch = Math.abs(max1 - min2); if (diffBranch > nodeSpacing) { var offset = max1 - min2 + nodeSpacing; if (offset < -minSpace2 + nodeSpacing) { offset = -minSpace2 + nodeSpacing; //console.log("RESETTING OFFSET", max1 - min2 + this.options.hierarchical.nodeSpacing, -minSpace2, offset); } if (offset < 0) { //console.log("SHIFTING", node2.id, offset); _this3._shiftBlock(node2.id, offset); stillShifting = true; if (centerParent === true) _this3._centerParent(node2); } } } //this.body.emitter.emit("_redraw");}) }; var minimizeEdgeLength = function minimizeEdgeLength(iterations, node) { //window.CALLBACKS.push(() => { // console.log("ts",node.id); var nodeId = node.id; var allEdges = node.edges; var nodeLevel = _this3.hierarchical.levels[node.id]; // gather constants var C2 = _this3.options.hierarchical.levelSeparation * _this3.options.hierarchical.levelSeparation; var referenceNodes = {}; var aboveEdges = []; for (var i = 0; i < allEdges.length; i++) { var edge = allEdges[i]; if (edge.toId != edge.fromId) { var otherNode = edge.toId == nodeId ? edge.from : edge.to; referenceNodes[allEdges[i].id] = otherNode; if (_this3.hierarchical.levels[otherNode.id] < nodeLevel) { aboveEdges.push(edge); } } } // differentiated sum of lengths based on only moving one node over one axis var getFx = function getFx(point, edges) { var sum = 0; for (var _i2 = 0; _i2 < edges.length; _i2++) { if (referenceNodes[edges[_i2].id] !== undefined) { var a = _this3.direction.getPosition(referenceNodes[edges[_i2].id]) - point; sum += a / Math.sqrt(a * a + C2); } } return sum; }; // doubly differentiated sum of lengths based on only moving one node over one axis var getDFx = function getDFx(point, edges) { var sum = 0; for (var _i3 = 0; _i3 < edges.length; _i3++) { if (referenceNodes[edges[_i3].id] !== undefined) { var a = _this3.direction.getPosition(referenceNodes[edges[_i3].id]) - point; sum -= C2 * Math.pow(a * a + C2, -1.5); } } return sum; }; var getGuess = function getGuess(iterations, edges) { var guess = _this3.direction.getPosition(node); // Newton's method for optimization var guessMap = {}; for (var _i4 = 0; _i4 < iterations; _i4++) { var fx = getFx(guess, edges); var dfx = getDFx(guess, edges); // we limit the movement to avoid instability. var limit = 40; var ratio = Math.max(-limit, Math.min(limit, Math.round(fx / dfx))); guess = guess - ratio; // reduce duplicates if (guessMap[guess] !== undefined) { break; } guessMap[guess] = _i4; } return guess; }; var moveBranch = function moveBranch(guess) { // position node if there is space var nodePosition = _this3.direction.getPosition(node); // check movable area of the branch if (branches[node.id] === undefined) { var branchNodes = {}; getBranchNodes(node, branchNodes); branches[node.id] = branchNodes; } var branchBoundary = getBranchBoundary(branches[node.id]); var minSpaceBranch = branchBoundary[2]; var maxSpaceBranch = branchBoundary[3]; var diff = guess - nodePosition; // check if we are allowed to move the node: var branchOffset = 0; if (diff > 0) { branchOffset = Math.min(diff, maxSpaceBranch - _this3.options.hierarchical.nodeSpacing); } else if (diff < 0) { branchOffset = -Math.min(-diff, minSpaceBranch - _this3.options.hierarchical.nodeSpacing); } if (branchOffset != 0) { //console.log("moving branch:",branchOffset, maxSpaceBranch, minSpaceBranch) _this3._shiftBlock(node.id, branchOffset); //this.body.emitter.emit("_redraw"); stillShifting = true; } }; var moveNode = function moveNode(guess) { var nodePosition = _this3.direction.getPosition(node); // position node if there is space var _this3$_getSpaceAroun3 = _this3._getSpaceAroundNode(node), _this3$_getSpaceAroun4 = _slicedToArray(_this3$_getSpaceAroun3, 2), minSpace = _this3$_getSpaceAroun4[0], maxSpace = _this3$_getSpaceAroun4[1]; var diff = guess - nodePosition; // check if we are allowed to move the node: var newPosition = nodePosition; if (diff > 0) { newPosition = Math.min(nodePosition + (maxSpace - _this3.options.hierarchical.nodeSpacing), guess); } else if (diff < 0) { newPosition = Math.max(nodePosition - (minSpace - _this3.options.hierarchical.nodeSpacing), guess); } if (newPosition !== nodePosition) { //console.log("moving Node:",diff, minSpace, maxSpace); _this3.direction.setPosition(node, newPosition); //this.body.emitter.emit("_redraw"); stillShifting = true; } }; var guess = getGuess(iterations, aboveEdges); moveBranch(guess); guess = getGuess(iterations, allEdges); moveNode(guess); //}) }; // method to remove whitespace between branches. Because we do bottom up, we can center the parents. var minimizeEdgeLengthBottomUp = function minimizeEdgeLengthBottomUp(iterations) { var levels = _this3.hierarchical.getLevels(); levels = _reverseInstanceProperty(levels).call(levels); for (var i = 0; i < iterations; i++) { stillShifting = false; for (var j = 0; j < levels.length; j++) { var level = levels[j]; var levelNodes = _this3.hierarchical.distributionOrdering[level]; for (var k = 0; k < levelNodes.length; k++) { minimizeEdgeLength(1000, levelNodes[k]); } } if (stillShifting !== true) { //console.log("FINISHED minimizeEdgeLengthBottomUp IN " + i); break; } } }; // method to remove whitespace between branches. Because we do bottom up, we can center the parents. var shiftBranchesCloserBottomUp = function shiftBranchesCloserBottomUp(iterations) { var levels = _this3.hierarchical.getLevels(); levels = _reverseInstanceProperty(levels).call(levels); for (var i = 0; i < iterations; i++) { stillShifting = false; shiftElementsCloser(branchShiftCallback, levels, true); if (stillShifting !== true) { //console.log("FINISHED shiftBranchesCloserBottomUp IN " + (i+1)); break; } } }; // center all parents var centerAllParents = function centerAllParents() { for (var nodeId in _this3.body.nodes) { if (Object.prototype.hasOwnProperty.call(_this3.body.nodes, nodeId)) _this3._centerParent(_this3.body.nodes[nodeId]); } }; // center all parents var centerAllParentsBottomUp = function centerAllParentsBottomUp() { var levels = _this3.hierarchical.getLevels(); levels = _reverseInstanceProperty(levels).call(levels); for (var i = 0; i < levels.length; i++) { var level = levels[i]; var levelNodes = _this3.hierarchical.distributionOrdering[level]; for (var j = 0; j < levelNodes.length; j++) { _this3._centerParent(levelNodes[j]); } } }; // the actual work is done here. if (this.options.hierarchical.blockShifting === true) { shiftBranchesCloserBottomUp(5); centerAllParents(); } // minimize edge length if (this.options.hierarchical.edgeMinimization === true) { minimizeEdgeLengthBottomUp(20); } if (this.options.hierarchical.parentCentralization === true) { centerAllParentsBottomUp(); } shiftTrees(); } /** * This gives the space around the node. IF a map is supplied, it will only check against nodes NOT in the map. * This is used to only get the distances to nodes outside of a branch. * * @param {Node} node * @param {{Node.id: vis.Node}} map * @returns {number[]} * @private */ }, { key: "_getSpaceAroundNode", value: function _getSpaceAroundNode(node, map) { var useMap = true; if (map === undefined) { useMap = false; } var level = this.hierarchical.levels[node.id]; if (level !== undefined) { var index = this.hierarchical.distributionIndex[node.id]; var position = this.direction.getPosition(node); var ordering = this.hierarchical.distributionOrdering[level]; var minSpace = 1e9; var maxSpace = 1e9; if (index !== 0) { var prevNode = ordering[index - 1]; if (useMap === true && map[prevNode.id] === undefined || useMap === false) { var prevPos = this.direction.getPosition(prevNode); minSpace = position - prevPos; } } if (index != ordering.length - 1) { var nextNode = ordering[index + 1]; if (useMap === true && map[nextNode.id] === undefined || useMap === false) { var nextPos = this.direction.getPosition(nextNode); maxSpace = Math.min(maxSpace, nextPos - position); } } return [minSpace, maxSpace]; } else { return [0, 0]; } } /** * We use this method to center a parent node and check if it does not cross other nodes when it does. * * @param {Node} node * @private */ }, { key: "_centerParent", value: function _centerParent(node) { if (this.hierarchical.parentReference[node.id]) { var parents = this.hierarchical.parentReference[node.id]; for (var i = 0; i < parents.length; i++) { var parentId = parents[i]; var parentNode = this.body.nodes[parentId]; var children = this.hierarchical.childrenReference[parentId]; if (children !== undefined) { // get the range of the children var newPosition = this._getCenterPosition(children); var position = this.direction.getPosition(parentNode); var _this$_getSpaceAround = this._getSpaceAroundNode(parentNode), _this$_getSpaceAround2 = _slicedToArray(_this$_getSpaceAround, 2), minSpace = _this$_getSpaceAround2[0], maxSpace = _this$_getSpaceAround2[1]; var diff = position - newPosition; if (diff < 0 && Math.abs(diff) < maxSpace - this.options.hierarchical.nodeSpacing || diff > 0 && Math.abs(diff) < minSpace - this.options.hierarchical.nodeSpacing) { this.direction.setPosition(parentNode, newPosition); } } } } } /** * This function places the nodes on the canvas based on the hierarchial distribution. * * @param {object} distribution | obtained by the function this._getDistribution() * @private */ }, { key: "_placeNodesByHierarchy", value: function _placeNodesByHierarchy(distribution) { this.positionedNodes = {}; // start placing all the level 0 nodes first. Then recursively position their branches. for (var level in distribution) { if (Object.prototype.hasOwnProperty.call(distribution, level)) { var _context; // sort nodes in level by position: var nodeArray = _Object$keys(distribution[level]); nodeArray = this._indexArrayToNodes(nodeArray); _sortInstanceProperty(_context = this.direction).call(_context, nodeArray); var handledNodeCount = 0; for (var i = 0; i < nodeArray.length; i++) { var node = nodeArray[i]; if (this.positionedNodes[node.id] === undefined) { var spacing = this.options.hierarchical.nodeSpacing; var pos = spacing * handledNodeCount; // We get the X or Y values we need and store them in pos and previousPos. // The get and set make sure we get X or Y if (handledNodeCount > 0) { pos = this.direction.getPosition(nodeArray[i - 1]) + spacing; } this.direction.setPosition(node, pos, level); this._validatePositionAndContinue(node, level, pos); handledNodeCount++; } } } } } /** * This is a recursively called function to enumerate the branches from the largest hubs and place the nodes * on a X position that ensures there will be no overlap. * * @param {Node.id} parentId * @param {number} parentLevel * @private */ }, { key: "_placeBranchNodes", value: function _placeBranchNodes(parentId, parentLevel) { var _context2; var childRef = this.hierarchical.childrenReference[parentId]; // if this is not a parent, cancel the placing. This can happen with multiple parents to one child. if (childRef === undefined) { return; } // get a list of childNodes var childNodes = []; for (var i = 0; i < childRef.length; i++) { childNodes.push(this.body.nodes[childRef[i]]); } // use the positions to order the nodes. _sortInstanceProperty(_context2 = this.direction).call(_context2, childNodes); // position the childNodes for (var _i5 = 0; _i5 < childNodes.length; _i5++) { var childNode = childNodes[_i5]; var childNodeLevel = this.hierarchical.levels[childNode.id]; // check if the child node is below the parent node and if it has already been positioned. if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) { // get the amount of space required for this node. If parent the width is based on the amount of children. var spacing = this.options.hierarchical.nodeSpacing; var pos = void 0; // we get the X or Y values we need and store them in pos and previousPos. // The get and set make sure we get X or Y if (_i5 === 0) { pos = this.direction.getPosition(this.body.nodes[parentId]); } else { pos = this.direction.getPosition(childNodes[_i5 - 1]) + spacing; } this.direction.setPosition(childNode, pos, childNodeLevel); this._validatePositionAndContinue(childNode, childNodeLevel, pos); } else { return; } } // center the parent nodes. var center = this._getCenterPosition(childNodes); this.direction.setPosition(this.body.nodes[parentId], center, parentLevel); } /** * This method checks for overlap and if required shifts the branch. It also keeps records of positioned nodes. * Finally it will call _placeBranchNodes to place the branch nodes. * * @param {Node} node * @param {number} level * @param {number} pos * @private */ }, { key: "_validatePositionAndContinue", value: function _validatePositionAndContinue(node, level, pos) { // This method only works for formal trees and formal forests // Early exit if this is not the case if (!this.hierarchical.isTree) return; // if overlap has been detected, we shift the branch if (this.lastNodeOnLevel[level] !== undefined) { var previousPos = this.direction.getPosition(this.body.nodes[this.lastNodeOnLevel[level]]); if (pos - previousPos < this.options.hierarchical.nodeSpacing) { var diff = previousPos + this.options.hierarchical.nodeSpacing - pos; var sharedParent = this._findCommonParent(this.lastNodeOnLevel[level], node.id); this._shiftBlock(sharedParent.withChild, diff); } } this.lastNodeOnLevel[level] = node.id; // store change in position. this.positionedNodes[node.id] = true; this._placeBranchNodes(node.id, level); } /** * Receives an array with node indices and returns an array with the actual node references. * Used for sorting based on node properties. * * @param {Array.<Node.id>} idArray * @returns {Array.<Node>} */ }, { key: "_indexArrayToNodes", value: function _indexArrayToNodes(idArray) { var array = []; for (var i = 0; i < idArray.length; i++) { array.push(this.body.nodes[idArray[i]]); } return array; } /** * This function get the distribution of levels based on hubsize * * @returns {object} * @private */ }, { key: "_getDistribution", value: function _getDistribution() { var distribution = {}; var nodeId, node; // we fix Y because the hierarchy is vertical, // we fix X so we do not give a node an x position for a second time. // the fix of X is removed after the x value has been set. for (nodeId in this.body.nodes) { if (Object.prototype.hasOwnProperty.call(this.body.nodes, nodeId)) { node = this.body.nodes[nodeId]; var level = this.hierarchical.levels[nodeId] === undefined ? 0 : this.hierarchical.levels[nodeId]; this.direction.fix(node, level); if (distribution[level] === undefined) { distribution[level] = {}; } distribution[level][nodeId] = node; } } return distribution; } /** * Return the active (i.e. visible) edges for this node * * @param {Node} node * @returns {Array.<vis.Edge>} Array of edge instances * @private */ }, { key: "_getActiveEdges", value: function _getActiveEdges(node) { var _this4 = this; var result = []; forEach$1(node.edges, function (edge) { var _context3; if (_indexOfInstanceProperty(_context3 = _this4.body.edgeIndices).call(_context3, edge.id) !== -1) { result.push(edge); } }); return result; } /** * Get the hubsizes for all active nodes. * * @returns {number} * @private */ }, { key: "_getHubSizes", value: function _getHubSizes() { var _this5 = this; var hubSizes = {}; var nodeIds = this.body.nodeIndices; forEach$1(nodeIds, function (nodeId) { var node = _this5.body.nodes[nodeId]; var hubSize = _this5._getActiveEdges(node).length; hubSizes[hubSize] = true; }); // Make an array of the size sorted descending var result = []; forEach$1(hubSizes, function (size) { result.push(Number(size)); }); _sortInstanceProperty(TimSort).call(TimSort, result, function (a, b) { return b - a; }); return result; } /** * this function allocates nodes in levels based on the recursive branching from the largest hubs. * * @private */ }, { key: "_determineLevelsByHubsize", value: function _determineLevelsByHubsize() { var _this6 = this; var levelDownstream = function levelDownstream(nodeA, nodeB) { _this6.hierarchical.levelDownstream(nodeA, nodeB); }; var hubSizes = this._getHubSizes(); var _loop = function _loop() { var hubSize = hubSizes[i]; if (hubSize === 0) return "break"; forEach$1(_this6.body.nodeIndices, function (nodeId) { var node = _this6.body.nodes[nodeId]; if (hubSize === _this6._getActiveEdges(node).length) { _this6._crawlNetwork(levelDownstream, nodeId); } }); }; for (var i = 0; i < hubSizes.length; ++i) { var _ret = _loop(); if (_ret === "break") break; } } /** * TODO: release feature * TODO: Determine if this feature is needed at all * * @private */ }, { key: "_determineLevelsCustomCallback", value: function _determineLevelsCustomCallback() { var _this7 = this; var minLevel = 100000; // TODO: this should come from options. // eslint-disable-next-line no-unused-vars -- This should eventually be implemented with these parameters used. var customCallback = function customCallback(nodeA, nodeB, edge) {}; // TODO: perhaps move to HierarchicalStatus. // But I currently don't see the point, this method is not used. var levelByDirection = function levelByDirection(nodeA, nodeB, edge) { var levelA = _this7.hierarchical.levels[nodeA.id]; // set initial level if (levelA === undefined) { levelA = _this7.hierarchical.levels[nodeA.id] = minLevel; } var diff = customCallback(NetworkUtil.cloneOptions(nodeA, "node"), NetworkUtil.cloneOptions(nodeB, "node"), NetworkUtil.cloneOptions(edge, "edge")); _this7.hierarchical.levels[nodeB.id] = levelA + diff; }; this._crawlNetwork(levelByDirection); this.hierarchical.setMinLevelToZero(this.body.nodes); } /** * Allocate nodes in levels based on the direction of the edges. * * @private */ }, { key: "_determineLevelsDirected", value: function _determineLevelsDirected() { var _context4, _this8 = this; var nodes = _reduceInstanceProperty(_context4 = this.body.nodeIndices).call(_context4, function (acc, id) { acc.set(id, _this8.body.nodes[id]); return acc; }, new _Map()); if (this.options.hierarchical.shakeTowards === "roots") { this.hierarchical.levels = fillLevelsByDirectionRoots(nodes); } else { this.hierarchical.levels = fillLevelsByDirectionLeaves(nodes); } this.hierarchical.setMinLevelToZero(this.body.nodes); } /** * Update the bookkeeping of parent and child. * * @private */ }, { key: "_generateMap", value: function _generateMap() { var _this9 = this; var fillInRelations = function fillInRelations(parentNode, childNode) { if (_this9.hierarchical.levels[childNode.id] > _this9.hierarchical.levels[parentNode.id]) { _this9.hierarchical.addRelation(parentNode.id, childNode.id); } }; this._crawlNetwork(fillInRelations); this.hierarchical.checkIfTree(); } /** * Crawl over the entire network and use a callback on each node couple that is connected to each other. * * @param {Function} [callback=function(){}] | will receive nodeA, nodeB and the connecting edge. A and B are distinct. * @param {Node.id} startingNodeId * @private */ }, { key: "_crawlNetwork", value: function _crawlNetwork() { var _this10 = this; var callback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {}; var startingNodeId = arguments.length > 1 ? arguments[1] : undefined; var progress = {}; var crawler = function crawler(node, tree) { if (progress[node.id] === undefined) { _this10.hierarchical.setTreeIndex(node, tree); progress[node.id] = true; var childNode; var edges = _this10._getActiveEdges(node); for (var i = 0; i < edges.length; i++) { var edge = edges[i]; if (edge.connected === true) { if (edge.toId == node.id) { // Not '===' because id's can be string and numeric childNode = edge.from; } else { childNode = edge.to; } if (node.id != childNode.id) { // Not '!==' because id's can be string and numeric callback(node, childNode, edge); crawler(childNode, tree); } } } } }; if (startingNodeId === undefined) { // Crawl over all nodes var treeIndex = 0; // Serves to pass a unique id for the current distinct tree for (var i = 0; i < this.body.nodeIndices.length; i++) { var nodeId = this.body.nodeIndices[i]; if (progress[nodeId] === undefined) { var node = this.body.nodes[nodeId]; crawler(node, treeIndex); treeIndex += 1; } } } else { // Crawl from the given starting node var _node2 = this.body.nodes[startingNodeId]; if (_node2 === undefined) { console.error("Node not found:", startingNodeId); return; } crawler(_node2); } } /** * Shift a branch a certain distance * * @param {Node.id} parentId * @param {number} diff * @private */ }, { key: "_shiftBlock", value: function _shiftBlock(parentId, diff) { var _this11 = this; var progress = {}; var shifter = function shifter(parentId) { if (progress[parentId]) { return; } progress[parentId] = true; _this11.direction.shift(parentId, diff); var childRef = _this11.hierarchical.childrenReference[parentId]; if (childRef !== undefined) { for (var i = 0; i < childRef.length; i++) { shifter(childRef[i]); } } }; shifter(parentId); } /** * Find a common parent between branches. * * @param {Node.id} childA * @param {Node.id} childB * @returns {{foundParent, withChild}} * @private */ }, { key: "_findCommonParent", value: function _findCommonParent(childA, childB) { var _this12 = this; var parents = {}; var iterateParents = function iterateParents(parents, child) { var parentRef = _this12.hierarchical.parentReference[child]; if (parentRef !== undefined) { for (var i = 0; i < parentRef.length; i++) { var parent = parentRef[i]; parents[parent] = true; iterateParents(parents, parent); } } }; var findParent = function findParent(parents, child) { var parentRef = _this12.hierarchical.parentReference[child]; if (parentRef !== undefined) { for (var i = 0; i < parentRef.length; i++) { var parent = parentRef[i]; if (parents[parent] !== undefined) { return { foundParent: parent, withChild: child }; } var branch = findParent(parents, parent); if (branch.foundParent !== null) { return branch; } } } return { foundParent: null, withChild: child }; }; iterateParents(parents, childA); return findParent(parents, childB); } /** * Set the strategy pattern for handling the coordinates given the current direction. * * The individual instances contain all the operations and data specific to a layout direction. * * @param {Node} node * @param {{x: number, y: number}} position * @param {number} level * @param {boolean} [doNotUpdate=false] * @private */ }, { key: "setDirectionStrategy", value: function setDirectionStrategy() { var isVertical = this.options.hierarchical.direction === "UD" || this.options.hierarchical.direction === "DU"; if (isVertical) { this.direction = new VerticalStrategy(this); } else { this.direction = new HorizontalStrategy(this); } } /** * Determine the center position of a branch from the passed list of child nodes * * This takes into account the positions of all the child nodes. * * @param {Array.<Node|vis.Node.id>} childNodes Array of either child nodes or node id's * @returns {number} * @private */ }, { key: "_getCenterPosition", value: function _getCenterPosition(childNodes) { var minPos = 1e9; var maxPos = -1e9; for (var i = 0; i < childNodes.length; i++) { var childNode = void 0; if (childNodes[i].id !== undefined) { childNode = childNodes[i]; } else { var childNodeId = childNodes[i]; childNode = this.body.nodes[childNodeId]; } var position = this.direction.getPosition(childNode); minPos = Math.min(minPos, position); maxPos = Math.max(maxPos, position); } return 0.5 * (minPos + maxPos); } }]); return LayoutEngine; }(); function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof _Symbol !== "undefined" && _getIteratorMethod(o) || o["@@iterator"]; if (!it) { if (_Array$isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray(o, minLen) { var _context32; if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = _sliceInstanceProperty(_context32 = Object.prototype.toString.call(o)).call(_context32, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return _Array$from$1(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } /** * Clears the toolbar div element of children * * @private */ var ManipulationSystem = /*#__PURE__*/function () { /** * @param {object} body * @param {Canvas} canvas * @param {SelectionHandler} selectionHandler * @param {InteractionHandler} interactionHandler */ function ManipulationSystem(body, canvas, selectionHandler, interactionHandler) { var _this = this, _context, _context2; _classCallCheck(this, ManipulationSystem); this.body = body; this.canvas = canvas; this.selectionHandler = selectionHandler; this.interactionHandler = interactionHandler; this.editMode = false; this.manipulationDiv = undefined; this.editModeDiv = undefined; this.closeDiv = undefined; this._domEventListenerCleanupQueue = []; this.temporaryUIFunctions = {}; this.temporaryEventFunctions = []; this.touchTime = 0; this.temporaryIds = { nodes: [], edges: [] }; this.guiEnabled = false; this.inMode = false; this.selectedControlNode = undefined; this.options = {}; this.defaultOptions = { enabled: false, initiallyActive: false, addNode: true, addEdge: true, editNode: undefined, editEdge: true, deleteNode: true, deleteEdge: true, controlNodeStyle: { shape: "dot", size: 6, color: { background: "#ff0000", border: "#3c3c3c", highlight: { background: "#07f968", border: "#3c3c3c" } }, borderWidth: 2, borderWidthSelected: 2 } }; _Object$assign(this.options, this.defaultOptions); this.body.emitter.on("destroy", function () { _this._clean(); }); this.body.emitter.on("_dataChanged", _bindInstanceProperty$1(_context = this._restore).call(_context, this)); this.body.emitter.on("_resetData", _bindInstanceProperty$1(_context2 = this._restore).call(_context2, this)); } /** * If something changes in the data during editing, switch back to the initial datamanipulation state and close all edit modes. * * @private */ _createClass(ManipulationSystem, [{ key: "_restore", value: function _restore() { if (this.inMode !== false) { if (this.options.initiallyActive === true) { this.enableEditMode(); } else { this.disableEditMode(); } } } /** * Set the Options * * @param {object} options * @param {object} allOptions * @param {object} globalOptions */ }, { key: "setOptions", value: function setOptions(options, allOptions, globalOptions) { if (allOptions !== undefined) { if (allOptions.locale !== undefined) { this.options.locale = allOptions.locale; } else { this.options.locale = globalOptions.locale; } if (allOptions.locales !== undefined) { this.options.locales = allOptions.locales; } else { this.options.locales = globalOptions.locales; } } if (options !== undefined) { if (typeof options === "boolean") { this.options.enabled = options; } else { this.options.enabled = true; deepExtend(this.options, options); } if (this.options.initiallyActive === true) { this.editMode = true; } this._setup(); } } /** * Enable or disable edit-mode. Draws the DOM required and cleans up after itself. * * @private */ }, { key: "toggleEditMode", value: function toggleEditMode() { if (this.editMode === true) { this.disableEditMode(); } else { this.enableEditMode(); } } /** * Enables Edit Mode */ }, { key: "enableEditMode", value: function enableEditMode() { this.editMode = true; this._clean(); if (this.guiEnabled === true) { this.manipulationDiv.style.display = "block"; this.closeDiv.style.display = "block"; this.editModeDiv.style.display = "none"; this.showManipulatorToolbar(); } } /** * Disables Edit Mode */ }, { key: "disableEditMode", value: function disableEditMode() { this.editMode = false; this._clean(); if (this.guiEnabled === true) { this.manipulationDiv.style.display = "none"; this.closeDiv.style.display = "none"; this.editModeDiv.style.display = "block"; this._createEditButton(); } } /** * Creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar. * * @private */ }, { key: "showManipulatorToolbar", value: function showManipulatorToolbar() { // restore the state of any bound functions or events, remove control nodes, restore physics this._clean(); // reset global variables this.manipulationDOM = {}; // if the gui is enabled, draw all elements. if (this.guiEnabled === true) { var _context3, _context4; // a _restore will hide these menus this.editMode = true; this.manipulationDiv.style.display = "block"; this.closeDiv.style.display = "block"; var selectedNodeCount = this.selectionHandler.getSelectedNodeCount(); var selectedEdgeCount = this.selectionHandler.getSelectedEdgeCount(); var selectedTotalCount = selectedNodeCount + selectedEdgeCount; var locale = this.options.locales[this.options.locale]; var needSeperator = false; if (this.options.addNode !== false) { this._createAddNodeButton(locale); needSeperator = true; } if (this.options.addEdge !== false) { if (needSeperator === true) { this._createSeperator(1); } else { needSeperator = true; } this._createAddEdgeButton(locale); } if (selectedNodeCount === 1 && typeof this.options.editNode === "function") { if (needSeperator === true) { this._createSeperator(2); } else { needSeperator = true; } this._createEditNodeButton(locale); } else if (selectedEdgeCount === 1 && selectedNodeCount === 0 && this.options.editEdge !== false) { if (needSeperator === true) { this._createSeperator(3); } else { needSeperator = true; } this._createEditEdgeButton(locale); } // remove buttons if (selectedTotalCount !== 0) { if (selectedNodeCount > 0 && this.options.deleteNode !== false) { if (needSeperator === true) { this._createSeperator(4); } this._createDeleteButton(locale); } else if (selectedNodeCount === 0 && this.options.deleteEdge !== false) { if (needSeperator === true) { this._createSeperator(4); } this._createDeleteButton(locale); } } // bind the close button this._bindElementEvents(this.closeDiv, _bindInstanceProperty$1(_context3 = this.toggleEditMode).call(_context3, this)); // refresh this bar based on what has been selected this._temporaryBindEvent("select", _bindInstanceProperty$1(_context4 = this.showManipulatorToolbar).call(_context4, this)); } // redraw to show any possible changes this.body.emitter.emit("_redraw"); } /** * Create the toolbar for adding Nodes */ }, { key: "addNodeMode", value: function addNodeMode() { var _context6; // when using the gui, enable edit mode if it wasnt already. if (this.editMode !== true) { this.enableEditMode(); } // restore the state of any bound functions or events, remove control nodes, restore physics this._clean(); this.inMode = "addNode"; if (this.guiEnabled === true) { var _context5; var locale = this.options.locales[this.options.locale]; this.manipulationDOM = {}; this._createBackButton(locale); this._createSeperator(); this._createDescription(locale["addDescription"] || this.options.locales["en"]["addDescription"]); // bind the close button this._bindElementEvents(this.closeDiv, _bindInstanceProperty$1(_context5 = this.toggleEditMode).call(_context5, this)); } this._temporaryBindEvent("click", _bindInstanceProperty$1(_context6 = this._performAddNode).call(_context6, this)); } /** * call the bound function to handle the editing of the node. The node has to be selected. */ }, { key: "editNode", value: function editNode() { var _this2 = this; // when using the gui, enable edit mode if it wasnt already. if (this.editMode !== true) { this.enableEditMode(); } // restore the state of any bound functions or events, remove control nodes, restore physics this._clean(); var node = this.selectionHandler.getSelectedNodes()[0]; if (node !== undefined) { this.inMode = "editNode"; if (typeof this.options.editNode === "function") { if (node.isCluster !== true) { var data = deepExtend({}, node.options, false); data.x = node.x; data.y = node.y; if (this.options.editNode.length === 2) { this.options.editNode(data, function (finalizedData) { if (finalizedData !== null && finalizedData !== undefined && _this2.inMode === "editNode") { // if for whatever reason the mode has changes (due to dataset change) disregard the callback) { _this2.body.data.nodes.getDataSet().update(finalizedData); } _this2.showManipulatorToolbar(); }); } else { throw new Error("The function for edit does not support two arguments (data, callback)"); } } else { alert(this.options.locales[this.options.locale]["editClusterError"] || this.options.locales["en"]["editClusterError"]); } } else { throw new Error("No function has been configured to handle the editing of nodes."); } } else { this.showManipulatorToolbar(); } } /** * create the toolbar to connect nodes */ }, { key: "addEdgeMode", value: function addEdgeMode() { var _context8, _context9, _context10, _context11, _context12; // when using the gui, enable edit mode if it wasnt already. if (this.editMode !== true) { this.enableEditMode(); } // restore the state of any bound functions or events, remove control nodes, restore physics this._clean(); this.inMode = "addEdge"; if (this.guiEnabled === true) { var _context7; var locale = this.options.locales[this.options.locale]; this.manipulationDOM = {}; this._createBackButton(locale); this._createSeperator(); this._createDescription(locale["edgeDescription"] || this.options.locales["en"]["edgeDescription"]); // bind the close button this._bindElementEvents(this.closeDiv, _bindInstanceProperty$1(_context7 = this.toggleEditMode).call(_context7, this)); } // temporarily overload functions this._temporaryBindUI("onTouch", _bindInstanceProperty$1(_context8 = this._handleConnect).call(_context8, this)); this._temporaryBindUI("onDragEnd", _bindInstanceProperty$1(_context9 = this._finishConnect).call(_context9, this)); this._temporaryBindUI("onDrag", _bindInstanceProperty$1(_context10 = this._dragControlNode).call(_context10, this)); this._temporaryBindUI("onRelease", _bindInstanceProperty$1(_context11 = this._finishConnect).call(_context11, this)); this._temporaryBindUI("onDragStart", _bindInstanceProperty$1(_context12 = this._dragStartEdge).call(_context12, this)); this._temporaryBindUI("onHold", function () {}); } /** * create the toolbar to edit edges */ }, { key: "editEdgeMode", value: function editEdgeMode() { // when using the gui, enable edit mode if it wasn't already. if (this.editMode !== true) { this.enableEditMode(); } // restore the state of any bound functions or events, remove control nodes, restore physics this._clean(); this.inMode = "editEdge"; if (_typeof(this.options.editEdge) === "object" && typeof this.options.editEdge.editWithoutDrag === "function") { this.edgeBeingEditedId = this.selectionHandler.getSelectedEdgeIds()[0]; if (this.edgeBeingEditedId !== undefined) { var edge = this.body.edges[this.edgeBeingEditedId]; this._performEditEdge(edge.from.id, edge.to.id); return; } } if (this.guiEnabled === true) { var _context13; var locale = this.options.locales[this.options.locale]; this.manipulationDOM = {}; this._createBackButton(locale); this._createSeperator(); this._createDescription(locale["editEdgeDescription"] || this.options.locales["en"]["editEdgeDescription"]); // bind the close button this._bindElementEvents(this.closeDiv, _bindInstanceProperty$1(_context13 = this.toggleEditMode).call(_context13, this)); } this.edgeBeingEditedId = this.selectionHandler.getSelectedEdgeIds()[0]; if (this.edgeBeingEditedId !== undefined) { var _context14, _context15, _context16, _context17; var _edge = this.body.edges[this.edgeBeingEditedId]; // create control nodes var controlNodeFrom = this._getNewTargetNode(_edge.from.x, _edge.from.y); var controlNodeTo = this._getNewTargetNode(_edge.to.x, _edge.to.y); this.temporaryIds.nodes.push(controlNodeFrom.id); this.temporaryIds.nodes.push(controlNodeTo.id); this.body.nodes[controlNodeFrom.id] = controlNodeFrom; this.body.nodeIndices.push(controlNodeFrom.id); this.body.nodes[controlNodeTo.id] = controlNodeTo; this.body.nodeIndices.push(controlNodeTo.id); // temporarily overload UI functions, cleaned up automatically because of _temporaryBindUI this._temporaryBindUI("onTouch", _bindInstanceProperty$1(_context14 = this._controlNodeTouch).call(_context14, this)); // used to get the position this._temporaryBindUI("onTap", function () {}); // disabled this._temporaryBindUI("onHold", function () {}); // disabled this._temporaryBindUI("onDragStart", _bindInstanceProperty$1(_context15 = this._controlNodeDragStart).call(_context15, this)); // used to select control node this._temporaryBindUI("onDrag", _bindInstanceProperty$1(_context16 = this._controlNodeDrag).call(_context16, this)); // used to drag control node this._temporaryBindUI("onDragEnd", _bindInstanceProperty$1(_context17 = this._controlNodeDragEnd).call(_context17, this)); // used to connect or revert control nodes this._temporaryBindUI("onMouseMove", function () {}); // disabled // create function to position control nodes correctly on movement // automatically cleaned up because we use the temporary bind this._temporaryBindEvent("beforeDrawing", function (ctx) { var positions = _edge.edgeType.findBorderPositions(ctx); if (controlNodeFrom.selected === false) { controlNodeFrom.x = positions.from.x; controlNodeFrom.y = positions.from.y; } if (controlNodeTo.selected === false) { controlNodeTo.x = positions.to.x; controlNodeTo.y = positions.to.y; } }); this.body.emitter.emit("_redraw"); } else { this.showManipulatorToolbar(); } } /** * delete everything in the selection */ }, { key: "deleteSelected", value: function deleteSelected() { var _this3 = this; // when using the gui, enable edit mode if it wasnt already. if (this.editMode !== true) { this.enableEditMode(); } // restore the state of any bound functions or events, remove control nodes, restore physics this._clean(); this.inMode = "delete"; var selectedNodes = this.selectionHandler.getSelectedNodeIds(); var selectedEdges = this.selectionHandler.getSelectedEdgeIds(); var deleteFunction = undefined; if (selectedNodes.length > 0) { for (var i = 0; i < selectedNodes.length; i++) { if (this.body.nodes[selectedNodes[i]].isCluster === true) { alert(this.options.locales[this.options.locale]["deleteClusterError"] || this.options.locales["en"]["deleteClusterError"]); return; } } if (typeof this.options.deleteNode === "function") { deleteFunction = this.options.deleteNode; } } else if (selectedEdges.length > 0) { if (typeof this.options.deleteEdge === "function") { deleteFunction = this.options.deleteEdge; } } if (typeof deleteFunction === "function") { var data = { nodes: selectedNodes, edges: selectedEdges }; if (deleteFunction.length === 2) { deleteFunction(data, function (finalizedData) { if (finalizedData !== null && finalizedData !== undefined && _this3.inMode === "delete") { // if for whatever reason the mode has changes (due to dataset change) disregard the callback) { _this3.body.data.edges.getDataSet().remove(finalizedData.edges); _this3.body.data.nodes.getDataSet().remove(finalizedData.nodes); _this3.body.emitter.emit("startSimulation"); _this3.showManipulatorToolbar(); } else { _this3.body.emitter.emit("startSimulation"); _this3.showManipulatorToolbar(); } }); } else { throw new Error("The function for delete does not support two arguments (data, callback)"); } } else { this.body.data.edges.getDataSet().remove(selectedEdges); this.body.data.nodes.getDataSet().remove(selectedNodes); this.body.emitter.emit("startSimulation"); this.showManipulatorToolbar(); } } //********************************************** PRIVATE ***************************************// /** * draw or remove the DOM * * @private */ }, { key: "_setup", value: function _setup() { if (this.options.enabled === true) { // Enable the GUI this.guiEnabled = true; this._createWrappers(); if (this.editMode === false) { this._createEditButton(); } else { this.showManipulatorToolbar(); } } else { this._removeManipulationDOM(); // disable the gui this.guiEnabled = false; } } /** * create the div overlays that contain the DOM * * @private */ }, { key: "_createWrappers", value: function _createWrappers() { // load the manipulator HTML elements. All styling done in css. if (this.manipulationDiv === undefined) { this.manipulationDiv = document.createElement("div"); this.manipulationDiv.className = "vis-manipulation"; if (this.editMode === true) { this.manipulationDiv.style.display = "block"; } else { this.manipulationDiv.style.display = "none"; } this.canvas.frame.appendChild(this.manipulationDiv); } // container for the edit button. if (this.editModeDiv === undefined) { this.editModeDiv = document.createElement("div"); this.editModeDiv.className = "vis-edit-mode"; if (this.editMode === true) { this.editModeDiv.style.display = "none"; } else { this.editModeDiv.style.display = "block"; } this.canvas.frame.appendChild(this.editModeDiv); } // container for the close div button if (this.closeDiv === undefined) { var _this$options$locales, _this$options$locales2; this.closeDiv = document.createElement("button"); this.closeDiv.className = "vis-close"; this.closeDiv.setAttribute("aria-label", (_this$options$locales = (_this$options$locales2 = this.options.locales[this.options.locale]) === null || _this$options$locales2 === void 0 ? void 0 : _this$options$locales2["close"]) !== null && _this$options$locales !== void 0 ? _this$options$locales : this.options.locales["en"]["close"]); this.closeDiv.style.display = this.manipulationDiv.style.display; this.canvas.frame.appendChild(this.closeDiv); } } /** * generate a new target node. Used for creating new edges and editing edges * * @param {number} x * @param {number} y * @returns {Node} * @private */ }, { key: "_getNewTargetNode", value: function _getNewTargetNode(x, y) { var controlNodeStyle = deepExtend({}, this.options.controlNodeStyle); controlNodeStyle.id = "targetNode" + v4(); controlNodeStyle.hidden = false; controlNodeStyle.physics = false; controlNodeStyle.x = x; controlNodeStyle.y = y; // we have to define the bounding box in order for the nodes to be drawn immediately var node = this.body.functions.createNode(controlNodeStyle); node.shape.boundingBox = { left: x, right: x, top: y, bottom: y }; return node; } /** * Create the edit button */ }, { key: "_createEditButton", value: function _createEditButton() { var _context18; // restore everything to it's original state (if applicable) this._clean(); // reset the manipulationDOM this.manipulationDOM = {}; // empty the editModeDiv recursiveDOMDelete(this.editModeDiv); // create the contents for the editMode button var locale = this.options.locales[this.options.locale]; var button = this._createButton("editMode", "vis-edit vis-edit-mode", locale["edit"] || this.options.locales["en"]["edit"]); this.editModeDiv.appendChild(button); // bind a hammer listener to the button, calling the function toggleEditMode. this._bindElementEvents(button, _bindInstanceProperty$1(_context18 = this.toggleEditMode).call(_context18, this)); } /** * this function cleans up after everything this module does. Temporary elements, functions and events are removed, physics restored, hammers removed. * * @private */ }, { key: "_clean", value: function _clean() { // not in mode this.inMode = false; // _clean the divs if (this.guiEnabled === true) { recursiveDOMDelete(this.editModeDiv); recursiveDOMDelete(this.manipulationDiv); // removes all the bindings and overloads this._cleanupDOMEventListeners(); } // remove temporary nodes and edges this._cleanupTemporaryNodesAndEdges(); // restore overloaded UI functions this._unbindTemporaryUIs(); // remove the temporaryEventFunctions this._unbindTemporaryEvents(); // restore the physics if required this.body.emitter.emit("restorePhysics"); } /** * Each dom element has it's own hammer. They are stored in this.manipulationHammers. This cleans them up. * * @private */ }, { key: "_cleanupDOMEventListeners", value: function _cleanupDOMEventListeners() { var _context19; // _clean DOM event listener bindings var _iterator = _createForOfIteratorHelper(_spliceInstanceProperty(_context19 = this._domEventListenerCleanupQueue).call(_context19, 0)), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var callback = _step.value; callback(); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } /** * Remove all DOM elements created by this module. * * @private */ }, { key: "_removeManipulationDOM", value: function _removeManipulationDOM() { // removes all the bindings and overloads this._clean(); // empty the manipulation divs recursiveDOMDelete(this.manipulationDiv); recursiveDOMDelete(this.editModeDiv); recursiveDOMDelete(this.closeDiv); // remove the manipulation divs if (this.manipulationDiv) { this.canvas.frame.removeChild(this.manipulationDiv); } if (this.editModeDiv) { this.canvas.frame.removeChild(this.editModeDiv); } if (this.closeDiv) { this.canvas.frame.removeChild(this.closeDiv); } // set the references to undefined this.manipulationDiv = undefined; this.editModeDiv = undefined; this.closeDiv = undefined; } /** * create a seperator line. the index is to differentiate in the manipulation dom * * @param {number} [index=1] * @private */ }, { key: "_createSeperator", value: function _createSeperator() { var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; this.manipulationDOM["seperatorLineDiv" + index] = document.createElement("div"); this.manipulationDOM["seperatorLineDiv" + index].className = "vis-separator-line"; this.manipulationDiv.appendChild(this.manipulationDOM["seperatorLineDiv" + index]); } // ---------------------- DOM functions for buttons --------------------------// /** * * @param {Locale} locale * @private */ }, { key: "_createAddNodeButton", value: function _createAddNodeButton(locale) { var _context20; var button = this._createButton("addNode", "vis-add", locale["addNode"] || this.options.locales["en"]["addNode"]); this.manipulationDiv.appendChild(button); this._bindElementEvents(button, _bindInstanceProperty$1(_context20 = this.addNodeMode).call(_context20, this)); } /** * * @param {Locale} locale * @private */ }, { key: "_createAddEdgeButton", value: function _createAddEdgeButton(locale) { var _context21; var button = this._createButton("addEdge", "vis-connect", locale["addEdge"] || this.options.locales["en"]["addEdge"]); this.manipulationDiv.appendChild(button); this._bindElementEvents(button, _bindInstanceProperty$1(_context21 = this.addEdgeMode).call(_context21, this)); } /** * * @param {Locale} locale * @private */ }, { key: "_createEditNodeButton", value: function _createEditNodeButton(locale) { var _context22; var button = this._createButton("editNode", "vis-edit", locale["editNode"] || this.options.locales["en"]["editNode"]); this.manipulationDiv.appendChild(button); this._bindElementEvents(button, _bindInstanceProperty$1(_context22 = this.editNode).call(_context22, this)); } /** * * @param {Locale} locale * @private */ }, { key: "_createEditEdgeButton", value: function _createEditEdgeButton(locale) { var _context23; var button = this._createButton("editEdge", "vis-edit", locale["editEdge"] || this.options.locales["en"]["editEdge"]); this.manipulationDiv.appendChild(button); this._bindElementEvents(button, _bindInstanceProperty$1(_context23 = this.editEdgeMode).call(_context23, this)); } /** * * @param {Locale} locale * @private */ }, { key: "_createDeleteButton", value: function _createDeleteButton(locale) { var _context24; var deleteBtnClass; if (this.options.rtl) { deleteBtnClass = "vis-delete-rtl"; } else { deleteBtnClass = "vis-delete"; } var button = this._createButton("delete", deleteBtnClass, locale["del"] || this.options.locales["en"]["del"]); this.manipulationDiv.appendChild(button); this._bindElementEvents(button, _bindInstanceProperty$1(_context24 = this.deleteSelected).call(_context24, this)); } /** * * @param {Locale} locale * @private */ }, { key: "_createBackButton", value: function _createBackButton(locale) { var _context25; var button = this._createButton("back", "vis-back", locale["back"] || this.options.locales["en"]["back"]); this.manipulationDiv.appendChild(button); this._bindElementEvents(button, _bindInstanceProperty$1(_context25 = this.showManipulatorToolbar).call(_context25, this)); } /** * * @param {number|string} id * @param {string} className * @param {label} label * @param {string} labelClassName * @returns {HTMLElement} * @private */ }, { key: "_createButton", value: function _createButton(id, className, label) { var labelClassName = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : "vis-label"; this.manipulationDOM[id + "Div"] = document.createElement("button"); this.manipulationDOM[id + "Div"].className = "vis-button " + className; this.manipulationDOM[id + "Label"] = document.createElement("div"); this.manipulationDOM[id + "Label"].className = labelClassName; this.manipulationDOM[id + "Label"].innerText = label; this.manipulationDOM[id + "Div"].appendChild(this.manipulationDOM[id + "Label"]); return this.manipulationDOM[id + "Div"]; } /** * * @param {Label} label * @private */ }, { key: "_createDescription", value: function _createDescription(label) { this.manipulationDOM["descriptionLabel"] = document.createElement("div"); this.manipulationDOM["descriptionLabel"].className = "vis-none"; this.manipulationDOM["descriptionLabel"].innerText = label; this.manipulationDiv.appendChild(this.manipulationDOM["descriptionLabel"]); } // -------------------------- End of DOM functions for buttons ------------------------------// /** * this binds an event until cleanup by the clean functions. * * @param {Event} event The event * @param {Function} newFunction * @private */ }, { key: "_temporaryBindEvent", value: function _temporaryBindEvent(event, newFunction) { this.temporaryEventFunctions.push({ event: event, boundFunction: newFunction }); this.body.emitter.on(event, newFunction); } /** * this overrides an UI function until cleanup by the clean function * * @param {string} UIfunctionName * @param {Function} newFunction * @private */ }, { key: "_temporaryBindUI", value: function _temporaryBindUI(UIfunctionName, newFunction) { if (this.body.eventListeners[UIfunctionName] !== undefined) { this.temporaryUIFunctions[UIfunctionName] = this.body.eventListeners[UIfunctionName]; this.body.eventListeners[UIfunctionName] = newFunction; } else { throw new Error("This UI function does not exist. Typo? You tried: " + UIfunctionName + " possible are: " + _JSON$stringify(_Object$keys(this.body.eventListeners))); } } /** * Restore the overridden UI functions to their original state. * * @private */ }, { key: "_unbindTemporaryUIs", value: function _unbindTemporaryUIs() { for (var functionName in this.temporaryUIFunctions) { if (Object.prototype.hasOwnProperty.call(this.temporaryUIFunctions, functionName)) { this.body.eventListeners[functionName] = this.temporaryUIFunctions[functionName]; delete this.temporaryUIFunctions[functionName]; } } this.temporaryUIFunctions = {}; } /** * Unbind the events created by _temporaryBindEvent * * @private */ }, { key: "_unbindTemporaryEvents", value: function _unbindTemporaryEvents() { for (var i = 0; i < this.temporaryEventFunctions.length; i++) { var eventName = this.temporaryEventFunctions[i].event; var boundFunction = this.temporaryEventFunctions[i].boundFunction; this.body.emitter.off(eventName, boundFunction); } this.temporaryEventFunctions = []; } /** * Bind an hammer instance to a DOM element. * * @param {Element} domElement * @param {Function} boundFunction */ }, { key: "_bindElementEvents", value: function _bindElementEvents(domElement, boundFunction) { // Bind touch events. var hammer = new Hammer(domElement, {}); onTouch(hammer, boundFunction); this._domEventListenerCleanupQueue.push(function () { hammer.destroy(); }); // Bind keyboard events. var keyupListener = function keyupListener(_ref) { var keyCode = _ref.keyCode, key = _ref.key; if (key === "Enter" || key === " " || keyCode === 13 || keyCode === 32) { boundFunction(); } }; domElement.addEventListener("keyup", keyupListener, false); this._domEventListenerCleanupQueue.push(function () { domElement.removeEventListener("keyup", keyupListener, false); }); } /** * Neatly clean up temporary edges and nodes * * @private */ }, { key: "_cleanupTemporaryNodesAndEdges", value: function _cleanupTemporaryNodesAndEdges() { // _clean temporary edges for (var i = 0; i < this.temporaryIds.edges.length; i++) { var _context26; this.body.edges[this.temporaryIds.edges[i]].disconnect(); delete this.body.edges[this.temporaryIds.edges[i]]; var indexTempEdge = _indexOfInstanceProperty(_context26 = this.body.edgeIndices).call(_context26, this.temporaryIds.edges[i]); if (indexTempEdge !== -1) { var _context27; _spliceInstanceProperty(_context27 = this.body.edgeIndices).call(_context27, indexTempEdge, 1); } } // _clean temporary nodes for (var _i = 0; _i < this.temporaryIds.nodes.length; _i++) { var _context28; delete this.body.nodes[this.temporaryIds.nodes[_i]]; var indexTempNode = _indexOfInstanceProperty(_context28 = this.body.nodeIndices).call(_context28, this.temporaryIds.nodes[_i]); if (indexTempNode !== -1) { var _context29; _spliceInstanceProperty(_context29 = this.body.nodeIndices).call(_context29, indexTempNode, 1); } } this.temporaryIds = { nodes: [], edges: [] }; } // ------------------------------------------ EDIT EDGE FUNCTIONS -----------------------------------------// /** * the touch is used to get the position of the initial click * * @param {Event} event The event * @private */ }, { key: "_controlNodeTouch", value: function _controlNodeTouch(event) { this.selectionHandler.unselectAll(); this.lastTouch = this.body.functions.getPointer(event.center); this.lastTouch.translation = _Object$assign({}, this.body.view.translation); // copy the object } /** * the drag start is used to mark one of the control nodes as selected. * * @private */ }, { key: "_controlNodeDragStart", value: function _controlNodeDragStart() { var pointer = this.lastTouch; var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); var from = this.body.nodes[this.temporaryIds.nodes[0]]; var to = this.body.nodes[this.temporaryIds.nodes[1]]; var edge = this.body.edges[this.edgeBeingEditedId]; this.selectedControlNode = undefined; var fromSelect = from.isOverlappingWith(pointerObj); var toSelect = to.isOverlappingWith(pointerObj); if (fromSelect === true) { this.selectedControlNode = from; edge.edgeType.from = from; } else if (toSelect === true) { this.selectedControlNode = to; edge.edgeType.to = to; } // we use the selection to find the node that is being dragged. We explicitly select it here. if (this.selectedControlNode !== undefined) { this.selectionHandler.selectObject(this.selectedControlNode); } this.body.emitter.emit("_redraw"); } /** * dragging the control nodes or the canvas * * @param {Event} event The event * @private */ }, { key: "_controlNodeDrag", value: function _controlNodeDrag(event) { this.body.emitter.emit("disablePhysics"); var pointer = this.body.functions.getPointer(event.center); var pos = this.canvas.DOMtoCanvas(pointer); if (this.selectedControlNode !== undefined) { this.selectedControlNode.x = pos.x; this.selectedControlNode.y = pos.y; } else { this.interactionHandler.onDrag(event); } this.body.emitter.emit("_redraw"); } /** * connecting or restoring the control nodes. * * @param {Event} event The event * @private */ }, { key: "_controlNodeDragEnd", value: function _controlNodeDragEnd(event) { var pointer = this.body.functions.getPointer(event.center); var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); var edge = this.body.edges[this.edgeBeingEditedId]; // if the node that was dragged is not a control node, return if (this.selectedControlNode === undefined) { return; } // we use the selection to find the node that is being dragged. We explicitly DEselect the control node here. this.selectionHandler.unselectAll(); var overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj); var node = undefined; for (var i = overlappingNodeIds.length - 1; i >= 0; i--) { if (overlappingNodeIds[i] !== this.selectedControlNode.id) { node = this.body.nodes[overlappingNodeIds[i]]; break; } } // perform the connection if (node !== undefined && this.selectedControlNode !== undefined) { if (node.isCluster === true) { alert(this.options.locales[this.options.locale]["createEdgeError"] || this.options.locales["en"]["createEdgeError"]); } else { var from = this.body.nodes[this.temporaryIds.nodes[0]]; if (this.selectedControlNode.id === from.id) { this._performEditEdge(node.id, edge.to.id); } else { this._performEditEdge(edge.from.id, node.id); } } } else { edge.updateEdgeType(); this.body.emitter.emit("restorePhysics"); } this.body.emitter.emit("_redraw"); } // ------------------------------------ END OF EDIT EDGE FUNCTIONS -----------------------------------------// // ------------------------------------------- ADD EDGE FUNCTIONS -----------------------------------------// /** * the function bound to the selection event. It checks if you want to connect a cluster and changes the description * to walk the user through the process. * * @param {Event} event * @private */ }, { key: "_handleConnect", value: function _handleConnect(event) { // check to avoid double fireing of this function. if (new Date().valueOf() - this.touchTime > 100) { this.lastTouch = this.body.functions.getPointer(event.center); this.lastTouch.translation = _Object$assign({}, this.body.view.translation); // copy the object this.interactionHandler.drag.pointer = this.lastTouch; // Drag pointer is not updated when adding edges this.interactionHandler.drag.translation = this.lastTouch.translation; var pointer = this.lastTouch; var node = this.selectionHandler.getNodeAt(pointer); if (node !== undefined) { if (node.isCluster === true) { alert(this.options.locales[this.options.locale]["createEdgeError"] || this.options.locales["en"]["createEdgeError"]); } else { // create a node the temporary line can look at var targetNode = this._getNewTargetNode(node.x, node.y); this.body.nodes[targetNode.id] = targetNode; this.body.nodeIndices.push(targetNode.id); // create a temporary edge var connectionEdge = this.body.functions.createEdge({ id: "connectionEdge" + v4(), from: node.id, to: targetNode.id, physics: false, smooth: { enabled: true, type: "continuous", roundness: 0.5 } }); this.body.edges[connectionEdge.id] = connectionEdge; this.body.edgeIndices.push(connectionEdge.id); this.temporaryIds.nodes.push(targetNode.id); this.temporaryIds.edges.push(connectionEdge.id); } } this.touchTime = new Date().valueOf(); } } /** * * @param {Event} event * @private */ }, { key: "_dragControlNode", value: function _dragControlNode(event) { var pointer = this.body.functions.getPointer(event.center); var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); // remember the edge id var connectFromId = undefined; if (this.temporaryIds.edges[0] !== undefined) { connectFromId = this.body.edges[this.temporaryIds.edges[0]].fromId; } // get the overlapping node but NOT the temporary node; var overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj); var node = undefined; for (var i = overlappingNodeIds.length - 1; i >= 0; i--) { var _context30; // if the node id is NOT a temporary node, accept the node. if (_indexOfInstanceProperty(_context30 = this.temporaryIds.nodes).call(_context30, overlappingNodeIds[i]) === -1) { node = this.body.nodes[overlappingNodeIds[i]]; break; } } event.controlEdge = { from: connectFromId, to: node ? node.id : undefined }; this.selectionHandler.generateClickEvent("controlNodeDragging", event, pointer); if (this.temporaryIds.nodes[0] !== undefined) { var targetNode = this.body.nodes[this.temporaryIds.nodes[0]]; // there is only one temp node in the add edge mode. targetNode.x = this.canvas._XconvertDOMtoCanvas(pointer.x); targetNode.y = this.canvas._YconvertDOMtoCanvas(pointer.y); this.body.emitter.emit("_redraw"); } else { this.interactionHandler.onDrag(event); } } /** * Connect the new edge to the target if one exists, otherwise remove temp line * * @param {Event} event The event * @private */ }, { key: "_finishConnect", value: function _finishConnect(event) { var pointer = this.body.functions.getPointer(event.center); var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); // remember the edge id var connectFromId = undefined; if (this.temporaryIds.edges[0] !== undefined) { connectFromId = this.body.edges[this.temporaryIds.edges[0]].fromId; } // get the overlapping node but NOT the temporary node; var overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj); var node = undefined; for (var i = overlappingNodeIds.length - 1; i >= 0; i--) { var _context31; // if the node id is NOT a temporary node, accept the node. if (_indexOfInstanceProperty(_context31 = this.temporaryIds.nodes).call(_context31, overlappingNodeIds[i]) === -1) { node = this.body.nodes[overlappingNodeIds[i]]; break; } } // clean temporary nodes and edges. this._cleanupTemporaryNodesAndEdges(); // perform the connection if (node !== undefined) { if (node.isCluster === true) { alert(this.options.locales[this.options.locale]["createEdgeError"] || this.options.locales["en"]["createEdgeError"]); } else { if (this.body.nodes[connectFromId] !== undefined && this.body.nodes[node.id] !== undefined) { this._performAddEdge(connectFromId, node.id); } } } event.controlEdge = { from: connectFromId, to: node ? node.id : undefined }; this.selectionHandler.generateClickEvent("controlNodeDragEnd", event, pointer); // No need to do _generateclickevent('dragEnd') here, the regular dragEnd event fires. this.body.emitter.emit("_redraw"); } /** * * @param {Event} event * @private */ }, { key: "_dragStartEdge", value: function _dragStartEdge(event) { var pointer = this.lastTouch; this.selectionHandler.generateClickEvent("dragStart", event, pointer, undefined, true); } // --------------------------------------- END OF ADD EDGE FUNCTIONS -------------------------------------// // ------------------------------ Performing all the actual data manipulation ------------------------// /** * Adds a node on the specified location * * @param {object} clickData * @private */ }, { key: "_performAddNode", value: function _performAddNode(clickData) { var _this4 = this; var defaultData = { id: v4(), x: clickData.pointer.canvas.x, y: clickData.pointer.canvas.y, label: "new" }; if (typeof this.options.addNode === "function") { if (this.options.addNode.length === 2) { this.options.addNode(defaultData, function (finalizedData) { if (finalizedData !== null && finalizedData !== undefined && _this4.inMode === "addNode") { // if for whatever reason the mode has changes (due to dataset change) disregard the callback _this4.body.data.nodes.getDataSet().add(finalizedData); } _this4.showManipulatorToolbar(); }); } else { this.showManipulatorToolbar(); throw new Error("The function for add does not support two arguments (data,callback)"); } } else { this.body.data.nodes.getDataSet().add(defaultData); this.showManipulatorToolbar(); } } /** * connect two nodes with a new edge. * * @param {Node.id} sourceNodeId * @param {Node.id} targetNodeId * @private */ }, { key: "_performAddEdge", value: function _performAddEdge(sourceNodeId, targetNodeId) { var _this5 = this; var defaultData = { from: sourceNodeId, to: targetNodeId }; if (typeof this.options.addEdge === "function") { if (this.options.addEdge.length === 2) { this.options.addEdge(defaultData, function (finalizedData) { if (finalizedData !== null && finalizedData !== undefined && _this5.inMode === "addEdge") { // if for whatever reason the mode has changes (due to dataset change) disregard the callback _this5.body.data.edges.getDataSet().add(finalizedData); _this5.selectionHandler.unselectAll(); _this5.showManipulatorToolbar(); } }); } else { throw new Error("The function for connect does not support two arguments (data,callback)"); } } else { this.body.data.edges.getDataSet().add(defaultData); this.selectionHandler.unselectAll(); this.showManipulatorToolbar(); } } /** * connect two nodes with a new edge. * * @param {Node.id} sourceNodeId * @param {Node.id} targetNodeId * @private */ }, { key: "_performEditEdge", value: function _performEditEdge(sourceNodeId, targetNodeId) { var _this6 = this; var defaultData = { id: this.edgeBeingEditedId, from: sourceNodeId, to: targetNodeId, label: this.body.data.edges.get(this.edgeBeingEditedId).label }; var eeFunct = this.options.editEdge; if (_typeof(eeFunct) === "object") { eeFunct = eeFunct.editWithoutDrag; } if (typeof eeFunct === "function") { if (eeFunct.length === 2) { eeFunct(defaultData, function (finalizedData) { if (finalizedData === null || finalizedData === undefined || _this6.inMode !== "editEdge") { // if for whatever reason the mode has changes (due to dataset change) disregard the callback) { _this6.body.edges[defaultData.id].updateEdgeType(); _this6.body.emitter.emit("_redraw"); _this6.showManipulatorToolbar(); } else { _this6.body.data.edges.getDataSet().update(finalizedData); _this6.selectionHandler.unselectAll(); _this6.showManipulatorToolbar(); } }); } else { throw new Error("The function for edit does not support two arguments (data, callback)"); } } else { this.body.data.edges.getDataSet().update(defaultData); this.selectionHandler.unselectAll(); this.showManipulatorToolbar(); } } }]); return ManipulationSystem; }(); /** * This object contains all possible options. It will check if the types are correct, if required if the option is one * of the allowed values. * * __any__ means that the name of the property does not matter. * __type__ is a required field for all objects and contains the allowed types of all objects */ var string = "string"; var bool = "boolean"; var number = "number"; var array = "array"; var object = "object"; // should only be in a __type__ property var dom = "dom"; var any = "any"; // List of endpoints var endPoints = ["arrow", "bar", "box", "circle", "crow", "curve", "diamond", "image", "inv_curve", "inv_triangle", "triangle", "vee"]; /* eslint-disable @typescript-eslint/naming-convention -- The __*__ format is used to prevent collisions with actual option names. */ var nodeOptions = { borderWidth: { number: number }, borderWidthSelected: { number: number, undefined: "undefined" }, brokenImage: { string: string, undefined: "undefined" }, chosen: { label: { boolean: bool, function: "function" }, node: { boolean: bool, function: "function" }, __type__: { object: object, boolean: bool } }, color: { border: { string: string }, background: { string: string }, highlight: { border: { string: string }, background: { string: string }, __type__: { object: object, string: string } }, hover: { border: { string: string }, background: { string: string }, __type__: { object: object, string: string } }, __type__: { object: object, string: string } }, opacity: { number: number, undefined: "undefined" }, fixed: { x: { boolean: bool }, y: { boolean: bool }, __type__: { object: object, boolean: bool } }, font: { align: { string: string }, color: { string: string }, size: { number: number }, face: { string: string }, background: { string: string }, strokeWidth: { number: number }, strokeColor: { string: string }, vadjust: { number: number }, multi: { boolean: bool, string: string }, bold: { color: { string: string }, size: { number: number }, face: { string: string }, mod: { string: string }, vadjust: { number: number }, __type__: { object: object, string: string } }, boldital: { color: { string: string }, size: { number: number }, face: { string: string }, mod: { string: string }, vadjust: { number: number }, __type__: { object: object, string: string } }, ital: { color: { string: string }, size: { number: number }, face: { string: string }, mod: { string: string }, vadjust: { number: number }, __type__: { object: object, string: string } }, mono: { color: { string: string }, size: { number: number }, face: { string: string }, mod: { string: string }, vadjust: { number: number }, __type__: { object: object, string: string } }, __type__: { object: object, string: string } }, group: { string: string, number: number, undefined: "undefined" }, heightConstraint: { minimum: { number: number }, valign: { string: string }, __type__: { object: object, boolean: bool, number: number } }, hidden: { boolean: bool }, icon: { face: { string: string }, code: { string: string }, size: { number: number }, color: { string: string }, weight: { string: string, number: number }, __type__: { object: object } }, id: { string: string, number: number }, image: { selected: { string: string, undefined: "undefined" }, unselected: { string: string, undefined: "undefined" }, __type__: { object: object, string: string } }, imagePadding: { top: { number: number }, right: { number: number }, bottom: { number: number }, left: { number: number }, __type__: { object: object, number: number } }, label: { string: string, undefined: "undefined" }, labelHighlightBold: { boolean: bool }, level: { number: number, undefined: "undefined" }, margin: { top: { number: number }, right: { number: number }, bottom: { number: number }, left: { number: number }, __type__: { object: object, number: number } }, mass: { number: number }, physics: { boolean: bool }, scaling: { min: { number: number }, max: { number: number }, label: { enabled: { boolean: bool }, min: { number: number }, max: { number: number }, maxVisible: { number: number }, drawThreshold: { number: number }, __type__: { object: object, boolean: bool } }, customScalingFunction: { function: "function" }, __type__: { object: object } }, shadow: { enabled: { boolean: bool }, color: { string: string }, size: { number: number }, x: { number: number }, y: { number: number }, __type__: { object: object, boolean: bool } }, shape: { string: ["custom", "ellipse", "circle", "database", "box", "text", "image", "circularImage", "diamond", "dot", "star", "triangle", "triangleDown", "square", "icon", "hexagon"] }, ctxRenderer: { function: "function" }, shapeProperties: { borderDashes: { boolean: bool, array: array }, borderRadius: { number: number }, interpolation: { boolean: bool }, useImageSize: { boolean: bool }, useBorderWithImage: { boolean: bool }, coordinateOrigin: { string: ["center", "top-left"] }, __type__: { object: object } }, size: { number: number }, title: { string: string, dom: dom, undefined: "undefined" }, value: { number: number, undefined: "undefined" }, widthConstraint: { minimum: { number: number }, maximum: { number: number }, __type__: { object: object, boolean: bool, number: number } }, x: { number: number }, y: { number: number }, __type__: { object: object } }; var allOptions = { configure: { enabled: { boolean: bool }, filter: { boolean: bool, string: string, array: array, function: "function" }, container: { dom: dom }, showButton: { boolean: bool }, __type__: { object: object, boolean: bool, string: string, array: array, function: "function" } }, edges: { arrows: { to: { enabled: { boolean: bool }, scaleFactor: { number: number }, type: { string: endPoints }, imageHeight: { number: number }, imageWidth: { number: number }, src: { string: string }, __type__: { object: object, boolean: bool } }, middle: { enabled: { boolean: bool }, scaleFactor: { number: number }, type: { string: endPoints }, imageWidth: { number: number }, imageHeight: { number: number }, src: { string: string }, __type__: { object: object, boolean: bool } }, from: { enabled: { boolean: bool }, scaleFactor: { number: number }, type: { string: endPoints }, imageWidth: { number: number }, imageHeight: { number: number }, src: { string: string }, __type__: { object: object, boolean: bool } }, __type__: { string: ["from", "to", "middle"], object: object } }, endPointOffset: { from: { number: number }, to: { number: number }, __type__: { object: object, number: number } }, arrowStrikethrough: { boolean: bool }, background: { enabled: { boolean: bool }, color: { string: string }, size: { number: number }, dashes: { boolean: bool, array: array }, __type__: { object: object, boolean: bool } }, chosen: { label: { boolean: bool, function: "function" }, edge: { boolean: bool, function: "function" }, __type__: { object: object, boolean: bool } }, color: { color: { string: string }, highlight: { string: string }, hover: { string: string }, inherit: { string: ["from", "to", "both"], boolean: bool }, opacity: { number: number }, __type__: { object: object, string: string } }, dashes: { boolean: bool, array: array }, font: { color: { string: string }, size: { number: number }, face: { string: string }, background: { string: string }, strokeWidth: { number: number }, strokeColor: { string: string }, align: { string: ["horizontal", "top", "middle", "bottom"] }, vadjust: { number: number }, multi: { boolean: bool, string: string }, bold: { color: { string: string }, size: { number: number }, face: { string: string }, mod: { string: string }, vadjust: { number: number }, __type__: { object: object, string: string } }, boldital: { color: { string: string }, size: { number: number }, face: { string: string }, mod: { string: string }, vadjust: { number: number }, __type__: { object: object, string: string } }, ital: { color: { string: string }, size: { number: number }, face: { string: string }, mod: { string: string }, vadjust: { number: number }, __type__: { object: object, string: string } }, mono: { color: { string: string }, size: { number: number }, face: { string: string }, mod: { string: string }, vadjust: { number: number }, __type__: { object: object, string: string } }, __type__: { object: object, string: string } }, hidden: { boolean: bool }, hoverWidth: { function: "function", number: number }, label: { string: string, undefined: "undefined" }, labelHighlightBold: { boolean: bool }, length: { number: number, undefined: "undefined" }, physics: { boolean: bool }, scaling: { min: { number: number }, max: { number: number }, label: { enabled: { boolean: bool }, min: { number: number }, max: { number: number }, maxVisible: { number: number }, drawThreshold: { number: number }, __type__: { object: object, boolean: bool } }, customScalingFunction: { function: "function" }, __type__: { object: object } }, selectionWidth: { function: "function", number: number }, selfReferenceSize: { number: number }, selfReference: { size: { number: number }, angle: { number: number }, renderBehindTheNode: { boolean: bool }, __type__: { object: object } }, shadow: { enabled: { boolean: bool }, color: { string: string }, size: { number: number }, x: { number: number }, y: { number: number }, __type__: { object: object, boolean: bool } }, smooth: { enabled: { boolean: bool }, type: { string: ["dynamic", "continuous", "discrete", "diagonalCross", "straightCross", "horizontal", "vertical", "curvedCW", "curvedCCW", "cubicBezier"] }, roundness: { number: number }, forceDirection: { string: ["horizontal", "vertical", "none"], boolean: bool }, __type__: { object: object, boolean: bool } }, title: { string: string, undefined: "undefined" }, width: { number: number }, widthConstraint: { maximum: { number: number }, __type__: { object: object, boolean: bool, number: number } }, value: { number: number, undefined: "undefined" }, __type__: { object: object } }, groups: { useDefaultGroups: { boolean: bool }, __any__: nodeOptions, __type__: { object: object } }, interaction: { dragNodes: { boolean: bool }, dragView: { boolean: bool }, hideEdgesOnDrag: { boolean: bool }, hideEdgesOnZoom: { boolean: bool }, hideNodesOnDrag: { boolean: bool }, hover: { boolean: bool }, keyboard: { enabled: { boolean: bool }, speed: { x: { number: number }, y: { number: number }, zoom: { number: number }, __type__: { object: object } }, bindToWindow: { boolean: bool }, autoFocus: { boolean: bool }, __type__: { object: object, boolean: bool } }, multiselect: { boolean: bool }, navigationButtons: { boolean: bool }, selectable: { boolean: bool }, selectConnectedEdges: { boolean: bool }, hoverConnectedEdges: { boolean: bool }, tooltipDelay: { number: number }, zoomView: { boolean: bool }, zoomSpeed: { number: number }, __type__: { object: object } }, layout: { randomSeed: { undefined: "undefined", number: number, string: string }, improvedLayout: { boolean: bool }, clusterThreshold: { number: number }, hierarchical: { enabled: { boolean: bool }, levelSeparation: { number: number }, nodeSpacing: { number: number }, treeSpacing: { number: number }, blockShifting: { boolean: bool }, edgeMinimization: { boolean: bool }, parentCentralization: { boolean: bool }, direction: { string: ["UD", "DU", "LR", "RL"] }, sortMethod: { string: ["hubsize", "directed"] }, shakeTowards: { string: ["leaves", "roots"] }, __type__: { object: object, boolean: bool } }, __type__: { object: object } }, manipulation: { enabled: { boolean: bool }, initiallyActive: { boolean: bool }, addNode: { boolean: bool, function: "function" }, addEdge: { boolean: bool, function: "function" }, editNode: { function: "function" }, editEdge: { editWithoutDrag: { function: "function" }, __type__: { object: object, boolean: bool, function: "function" } }, deleteNode: { boolean: bool, function: "function" }, deleteEdge: { boolean: bool, function: "function" }, controlNodeStyle: nodeOptions, __type__: { object: object, boolean: bool } }, nodes: nodeOptions, physics: { enabled: { boolean: bool }, barnesHut: { theta: { number: number }, gravitationalConstant: { number: number }, centralGravity: { number: number }, springLength: { number: number }, springConstant: { number: number }, damping: { number: number }, avoidOverlap: { number: number }, __type__: { object: object } }, forceAtlas2Based: { theta: { number: number }, gravitationalConstant: { number: number }, centralGravity: { number: number }, springLength: { number: number }, springConstant: { number: number }, damping: { number: number }, avoidOverlap: { number: number }, __type__: { object: object } }, repulsion: { centralGravity: { number: number }, springLength: { number: number }, springConstant: { number: number }, nodeDistance: { number: number }, damping: { number: number }, __type__: { object: object } }, hierarchicalRepulsion: { centralGravity: { number: number }, springLength: { number: number }, springConstant: { number: number }, nodeDistance: { number: number }, damping: { number: number }, avoidOverlap: { number: number }, __type__: { object: object } }, maxVelocity: { number: number }, minVelocity: { number: number }, solver: { string: ["barnesHut", "repulsion", "hierarchicalRepulsion", "forceAtlas2Based"] }, stabilization: { enabled: { boolean: bool }, iterations: { number: number }, updateInterval: { number: number }, onlyDynamicEdges: { boolean: bool }, fit: { boolean: bool }, __type__: { object: object, boolean: bool } }, timestep: { number: number }, adaptiveTimestep: { boolean: bool }, wind: { x: { number: number }, y: { number: number }, __type__: { object: object } }, __type__: { object: object, boolean: bool } }, //globals : autoResize: { boolean: bool }, clickToUse: { boolean: bool }, locale: { string: string }, locales: { __any__: { any: any }, __type__: { object: object } }, height: { string: string }, width: { string: string }, __type__: { object: object } }; /* eslint-enable @typescript-eslint/naming-convention */ /** * This provides ranges, initial values, steps and dropdown menu choices for the * configuration. * * @remarks * Checkbox: `boolean` * The value supllied will be used as the initial value. * * Text field: `string` * The passed text will be used as the initial value. Any text will be * accepted afterwards. * * Number range: `[number, number, number, number]` * The meanings are `[initial value, min, max, step]`. * * Dropdown: `[Exclude<string, "color">, ...(string | number | boolean)[]]` * Translations for people with poor understanding of TypeScript: the first * value always has to be a string but never `"color"`, the rest can be any * combination of strings, numbers and booleans. * * Color picker: `["color", string]` * The first value says this will be a color picker not a dropdown menu. The * next value is the initial color. */ var configureOptions = { nodes: { borderWidth: [1, 0, 10, 1], borderWidthSelected: [2, 0, 10, 1], color: { border: ["color", "#2B7CE9"], background: ["color", "#97C2FC"], highlight: { border: ["color", "#2B7CE9"], background: ["color", "#D2E5FF"] }, hover: { border: ["color", "#2B7CE9"], background: ["color", "#D2E5FF"] } }, opacity: [0, 0, 1, 0.1], fixed: { x: false, y: false }, font: { color: ["color", "#343434"], size: [14, 0, 100, 1], face: ["arial", "verdana", "tahoma"], background: ["color", "none"], strokeWidth: [0, 0, 50, 1], strokeColor: ["color", "#ffffff"] }, //group: 'string', hidden: false, labelHighlightBold: true, //icon: { // face: 'string', //'FontAwesome', // code: 'string', //'\uf007', // size: [50, 0, 200, 1], //50, // color: ['color','#2B7CE9'] //'#aa00ff' //}, //image: 'string', // --> URL physics: true, scaling: { min: [10, 0, 200, 1], max: [30, 0, 200, 1], label: { enabled: false, min: [14, 0, 200, 1], max: [30, 0, 200, 1], maxVisible: [30, 0, 200, 1], drawThreshold: [5, 0, 20, 1] } }, shadow: { enabled: false, color: "rgba(0,0,0,0.5)", size: [10, 0, 20, 1], x: [5, -30, 30, 1], y: [5, -30, 30, 1] }, shape: ["ellipse", "box", "circle", "database", "diamond", "dot", "square", "star", "text", "triangle", "triangleDown", "hexagon"], shapeProperties: { borderDashes: false, borderRadius: [6, 0, 20, 1], interpolation: true, useImageSize: false }, size: [25, 0, 200, 1] }, edges: { arrows: { to: { enabled: false, scaleFactor: [1, 0, 3, 0.05], type: "arrow" }, middle: { enabled: false, scaleFactor: [1, 0, 3, 0.05], type: "arrow" }, from: { enabled: false, scaleFactor: [1, 0, 3, 0.05], type: "arrow" } }, endPointOffset: { from: [0, -10, 10, 1], to: [0, -10, 10, 1] }, arrowStrikethrough: true, color: { color: ["color", "#848484"], highlight: ["color", "#848484"], hover: ["color", "#848484"], inherit: ["from", "to", "both", true, false], opacity: [1, 0, 1, 0.05] }, dashes: false, font: { color: ["color", "#343434"], size: [14, 0, 100, 1], face: ["arial", "verdana", "tahoma"], background: ["color", "none"], strokeWidth: [2, 0, 50, 1], strokeColor: ["color", "#ffffff"], align: ["horizontal", "top", "middle", "bottom"] }, hidden: false, hoverWidth: [1.5, 0, 5, 0.1], labelHighlightBold: true, physics: true, scaling: { min: [1, 0, 100, 1], max: [15, 0, 100, 1], label: { enabled: true, min: [14, 0, 200, 1], max: [30, 0, 200, 1], maxVisible: [30, 0, 200, 1], drawThreshold: [5, 0, 20, 1] } }, selectionWidth: [1.5, 0, 5, 0.1], selfReferenceSize: [20, 0, 200, 1], selfReference: { size: [20, 0, 200, 1], angle: [Math.PI / 2, -6 * Math.PI, 6 * Math.PI, Math.PI / 8], renderBehindTheNode: true }, shadow: { enabled: false, color: "rgba(0,0,0,0.5)", size: [10, 0, 20, 1], x: [5, -30, 30, 1], y: [5, -30, 30, 1] }, smooth: { enabled: true, type: ["dynamic", "continuous", "discrete", "diagonalCross", "straightCross", "horizontal", "vertical", "curvedCW", "curvedCCW", "cubicBezier"], forceDirection: ["horizontal", "vertical", "none"], roundness: [0.5, 0, 1, 0.05] }, width: [1, 0, 30, 1] }, layout: { //randomSeed: [0, 0, 500, 1], //improvedLayout: true, hierarchical: { enabled: false, levelSeparation: [150, 20, 500, 5], nodeSpacing: [100, 20, 500, 5], treeSpacing: [200, 20, 500, 5], blockShifting: true, edgeMinimization: true, parentCentralization: true, direction: ["UD", "DU", "LR", "RL"], sortMethod: ["hubsize", "directed"], shakeTowards: ["leaves", "roots"] // leaves, roots } }, interaction: { dragNodes: true, dragView: true, hideEdgesOnDrag: false, hideEdgesOnZoom: false, hideNodesOnDrag: false, hover: false, keyboard: { enabled: false, speed: { x: [10, 0, 40, 1], y: [10, 0, 40, 1], zoom: [0.02, 0, 0.1, 0.005] }, bindToWindow: true, autoFocus: true }, multiselect: false, navigationButtons: false, selectable: true, selectConnectedEdges: true, hoverConnectedEdges: true, tooltipDelay: [300, 0, 1000, 25], zoomView: true, zoomSpeed: [1, 0.1, 2, 0.1] }, manipulation: { enabled: false, initiallyActive: false }, physics: { enabled: true, barnesHut: { theta: [0.5, 0.1, 1, 0.05], gravitationalConstant: [-2000, -30000, 0, 50], centralGravity: [0.3, 0, 10, 0.05], springLength: [95, 0, 500, 5], springConstant: [0.04, 0, 1.2, 0.005], damping: [0.09, 0, 1, 0.01], avoidOverlap: [0, 0, 1, 0.01] }, forceAtlas2Based: { theta: [0.5, 0.1, 1, 0.05], gravitationalConstant: [-50, -500, 0, 1], centralGravity: [0.01, 0, 1, 0.005], springLength: [95, 0, 500, 5], springConstant: [0.08, 0, 1.2, 0.005], damping: [0.4, 0, 1, 0.01], avoidOverlap: [0, 0, 1, 0.01] }, repulsion: { centralGravity: [0.2, 0, 10, 0.05], springLength: [200, 0, 500, 5], springConstant: [0.05, 0, 1.2, 0.005], nodeDistance: [100, 0, 500, 5], damping: [0.09, 0, 1, 0.01] }, hierarchicalRepulsion: { centralGravity: [0.2, 0, 10, 0.05], springLength: [100, 0, 500, 5], springConstant: [0.01, 0, 1.2, 0.005], nodeDistance: [120, 0, 500, 5], damping: [0.09, 0, 1, 0.01], avoidOverlap: [0, 0, 1, 0.01] }, maxVelocity: [50, 0, 150, 1], minVelocity: [0.1, 0.01, 0.5, 0.01], solver: ["barnesHut", "forceAtlas2Based", "repulsion", "hierarchicalRepulsion"], timestep: [0.5, 0.01, 1, 0.01], wind: { x: [0, -10, 10, 0.1], y: [0, -10, 10, 0.1] } //adaptiveTimestep: true } }; var configuratorHideOption = function configuratorHideOption(parentPath, optionName, options) { var _context; if (_includesInstanceProperty(parentPath).call(parentPath, "physics") && _includesInstanceProperty(_context = configureOptions.physics.solver).call(_context, optionName) && options.physics.solver !== optionName && optionName !== "wind") { return true; } return false; }; var options = /*#__PURE__*/Object.freeze({ __proto__: null, allOptions: allOptions, configuratorHideOption: configuratorHideOption, configureOptions: configureOptions }); /** * The Floyd–Warshall algorithm is an algorithm for finding shortest paths in * a weighted graph with positive or negative edge weights (but with no negative * cycles). - https://en.wikipedia.org/wiki/Floyd–Warshall_algorithm */ var FloydWarshall = /*#__PURE__*/function () { /** * @ignore */ function FloydWarshall() { _classCallCheck(this, FloydWarshall); } /** * * @param {object} body * @param {Array.<Node>} nodesArray * @param {Array.<Edge>} edgesArray * @returns {{}} */ _createClass(FloydWarshall, [{ key: "getDistances", value: function getDistances(body, nodesArray, edgesArray) { var D_matrix = {}; var edges = body.edges; // prepare matrix with large numbers for (var i = 0; i < nodesArray.length; i++) { var node = nodesArray[i]; var cell = {}; D_matrix[node] = cell; for (var j = 0; j < nodesArray.length; j++) { cell[nodesArray[j]] = i == j ? 0 : 1e9; } } // put the weights for the edges in. This assumes unidirectionality. for (var _i = 0; _i < edgesArray.length; _i++) { var edge = edges[edgesArray[_i]]; // edge has to be connected if it counts to the distances. If it is connected to inner clusters it will crash so we also check if it is in the D_matrix if (edge.connected === true && D_matrix[edge.fromId] !== undefined && D_matrix[edge.toId] !== undefined) { D_matrix[edge.fromId][edge.toId] = 1; D_matrix[edge.toId][edge.fromId] = 1; } } var nodeCount = nodesArray.length; // Adapted FloydWarshall based on unidirectionality to greatly reduce complexity. for (var k = 0; k < nodeCount; k++) { var knode = nodesArray[k]; var kcolm = D_matrix[knode]; for (var _i2 = 0; _i2 < nodeCount - 1; _i2++) { var inode = nodesArray[_i2]; var icolm = D_matrix[inode]; for (var _j = _i2 + 1; _j < nodeCount; _j++) { var jnode = nodesArray[_j]; var jcolm = D_matrix[jnode]; var val = Math.min(icolm[jnode], icolm[knode] + kcolm[jnode]); icolm[jnode] = val; jcolm[inode] = val; } } } return D_matrix; } }]); return FloydWarshall; }(); /** * KamadaKawai positions the nodes initially based on * * "AN ALGORITHM FOR DRAWING GENERAL UNDIRECTED GRAPHS" * -- Tomihisa KAMADA and Satoru KAWAI in 1989 * * Possible optimizations in the distance calculation can be implemented. */ var KamadaKawai = /*#__PURE__*/function () { /** * @param {object} body * @param {number} edgeLength * @param {number} edgeStrength */ function KamadaKawai(body, edgeLength, edgeStrength) { _classCallCheck(this, KamadaKawai); this.body = body; this.springLength = edgeLength; this.springConstant = edgeStrength; this.distanceSolver = new FloydWarshall(); } /** * Not sure if needed but can be used to update the spring length and spring constant * * @param {object} options */ _createClass(KamadaKawai, [{ key: "setOptions", value: function setOptions(options) { if (options) { if (options.springLength) { this.springLength = options.springLength; } if (options.springConstant) { this.springConstant = options.springConstant; } } } /** * Position the system * * @param {Array.<Node>} nodesArray * @param {Array.<vis.Edge>} edgesArray * @param {boolean} [ignoreClusters=false] */ }, { key: "solve", value: function solve(nodesArray, edgesArray) { var ignoreClusters = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; // get distance matrix var D_matrix = this.distanceSolver.getDistances(this.body, nodesArray, edgesArray); // distance matrix // get the L Matrix this._createL_matrix(D_matrix); // get the K Matrix this._createK_matrix(D_matrix); // initial E Matrix this._createE_matrix(); // calculate positions var threshold = 0.01; var innerThreshold = 1; var iterations = 0; var maxIterations = Math.max(1000, Math.min(10 * this.body.nodeIndices.length, 6000)); var maxInnerIterations = 5; var maxEnergy = 1e9; var highE_nodeId = 0, dE_dx = 0, dE_dy = 0, delta_m = 0, subIterations = 0; while (maxEnergy > threshold && iterations < maxIterations) { iterations += 1; var _this$_getHighestEner = this._getHighestEnergyNode(ignoreClusters); var _this$_getHighestEner2 = _slicedToArray(_this$_getHighestEner, 4); highE_nodeId = _this$_getHighestEner2[0]; maxEnergy = _this$_getHighestEner2[1]; dE_dx = _this$_getHighestEner2[2]; dE_dy = _this$_getHighestEner2[3]; delta_m = maxEnergy; subIterations = 0; while (delta_m > innerThreshold && subIterations < maxInnerIterations) { subIterations += 1; this._moveNode(highE_nodeId, dE_dx, dE_dy); var _this$_getEnergy = this._getEnergy(highE_nodeId); var _this$_getEnergy2 = _slicedToArray(_this$_getEnergy, 3); delta_m = _this$_getEnergy2[0]; dE_dx = _this$_getEnergy2[1]; dE_dy = _this$_getEnergy2[2]; } } } /** * get the node with the highest energy * * @param {boolean} ignoreClusters * @returns {number[]} * @private */ }, { key: "_getHighestEnergyNode", value: function _getHighestEnergyNode(ignoreClusters) { var nodesArray = this.body.nodeIndices; var nodes = this.body.nodes; var maxEnergy = 0; var maxEnergyNodeId = nodesArray[0]; var dE_dx_max = 0, dE_dy_max = 0; for (var nodeIdx = 0; nodeIdx < nodesArray.length; nodeIdx++) { var m = nodesArray[nodeIdx]; // by not evaluating nodes with predefined positions we should only move nodes that have no positions. if (nodes[m].predefinedPosition !== true || nodes[m].isCluster === true && ignoreClusters === true || nodes[m].options.fixed.x !== true || nodes[m].options.fixed.y !== true) { var _this$_getEnergy3 = this._getEnergy(m), _this$_getEnergy4 = _slicedToArray(_this$_getEnergy3, 3), delta_m = _this$_getEnergy4[0], dE_dx = _this$_getEnergy4[1], dE_dy = _this$_getEnergy4[2]; if (maxEnergy < delta_m) { maxEnergy = delta_m; maxEnergyNodeId = m; dE_dx_max = dE_dx; dE_dy_max = dE_dy; } } } return [maxEnergyNodeId, maxEnergy, dE_dx_max, dE_dy_max]; } /** * calculate the energy of a single node * * @param {Node.id} m * @returns {number[]} * @private */ }, { key: "_getEnergy", value: function _getEnergy(m) { var _this$E_sums$m = _slicedToArray(this.E_sums[m], 2), dE_dx = _this$E_sums$m[0], dE_dy = _this$E_sums$m[1]; var delta_m = Math.sqrt(Math.pow(dE_dx, 2) + Math.pow(dE_dy, 2)); return [delta_m, dE_dx, dE_dy]; } /** * move the node based on it's energy * the dx and dy are calculated from the linear system proposed by Kamada and Kawai * * @param {number} m * @param {number} dE_dx * @param {number} dE_dy * @private */ }, { key: "_moveNode", value: function _moveNode(m, dE_dx, dE_dy) { var nodesArray = this.body.nodeIndices; var nodes = this.body.nodes; var d2E_dx2 = 0; var d2E_dxdy = 0; var d2E_dy2 = 0; var x_m = nodes[m].x; var y_m = nodes[m].y; var km = this.K_matrix[m]; var lm = this.L_matrix[m]; for (var iIdx = 0; iIdx < nodesArray.length; iIdx++) { var i = nodesArray[iIdx]; if (i !== m) { var x_i = nodes[i].x; var y_i = nodes[i].y; var kmat = km[i]; var lmat = lm[i]; var denominator = 1.0 / Math.pow(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2), 1.5); d2E_dx2 += kmat * (1 - lmat * Math.pow(y_m - y_i, 2) * denominator); d2E_dxdy += kmat * (lmat * (x_m - x_i) * (y_m - y_i) * denominator); d2E_dy2 += kmat * (1 - lmat * Math.pow(x_m - x_i, 2) * denominator); } } // make the variable names easier to make the solving of the linear system easier to read var A = d2E_dx2, B = d2E_dxdy, C = dE_dx, D = d2E_dy2, E = dE_dy; // solve the linear system for dx and dy var dy = (C / A + E / B) / (B / A - D / B); var dx = -(B * dy + C) / A; // move the node nodes[m].x += dx; nodes[m].y += dy; // Recalculate E_matrix (should be incremental) this._updateE_matrix(m); } /** * Create the L matrix: edge length times shortest path * * @param {object} D_matrix * @private */ }, { key: "_createL_matrix", value: function _createL_matrix(D_matrix) { var nodesArray = this.body.nodeIndices; var edgeLength = this.springLength; this.L_matrix = []; for (var i = 0; i < nodesArray.length; i++) { this.L_matrix[nodesArray[i]] = {}; for (var j = 0; j < nodesArray.length; j++) { this.L_matrix[nodesArray[i]][nodesArray[j]] = edgeLength * D_matrix[nodesArray[i]][nodesArray[j]]; } } } /** * Create the K matrix: spring constants times shortest path * * @param {object} D_matrix * @private */ }, { key: "_createK_matrix", value: function _createK_matrix(D_matrix) { var nodesArray = this.body.nodeIndices; var edgeStrength = this.springConstant; this.K_matrix = []; for (var i = 0; i < nodesArray.length; i++) { this.K_matrix[nodesArray[i]] = {}; for (var j = 0; j < nodesArray.length; j++) { this.K_matrix[nodesArray[i]][nodesArray[j]] = edgeStrength * Math.pow(D_matrix[nodesArray[i]][nodesArray[j]], -2); } } } /** * Create matrix with all energies between nodes * * @private */ }, { key: "_createE_matrix", value: function _createE_matrix() { var nodesArray = this.body.nodeIndices; var nodes = this.body.nodes; this.E_matrix = {}; this.E_sums = {}; for (var mIdx = 0; mIdx < nodesArray.length; mIdx++) { this.E_matrix[nodesArray[mIdx]] = []; } for (var _mIdx = 0; _mIdx < nodesArray.length; _mIdx++) { var m = nodesArray[_mIdx]; var x_m = nodes[m].x; var y_m = nodes[m].y; var dE_dx = 0; var dE_dy = 0; for (var iIdx = _mIdx; iIdx < nodesArray.length; iIdx++) { var i = nodesArray[iIdx]; if (i !== m) { var x_i = nodes[i].x; var y_i = nodes[i].y; var denominator = 1.0 / Math.sqrt(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2)); this.E_matrix[m][iIdx] = [this.K_matrix[m][i] * (x_m - x_i - this.L_matrix[m][i] * (x_m - x_i) * denominator), this.K_matrix[m][i] * (y_m - y_i - this.L_matrix[m][i] * (y_m - y_i) * denominator)]; this.E_matrix[i][_mIdx] = this.E_matrix[m][iIdx]; dE_dx += this.E_matrix[m][iIdx][0]; dE_dy += this.E_matrix[m][iIdx][1]; } } //Store sum this.E_sums[m] = [dE_dx, dE_dy]; } } /** * Update method, just doing single column (rows are auto-updated) (update all sums) * * @param {number} m * @private */ }, { key: "_updateE_matrix", value: function _updateE_matrix(m) { var nodesArray = this.body.nodeIndices; var nodes = this.body.nodes; var colm = this.E_matrix[m]; var kcolm = this.K_matrix[m]; var lcolm = this.L_matrix[m]; var x_m = nodes[m].x; var y_m = nodes[m].y; var dE_dx = 0; var dE_dy = 0; for (var iIdx = 0; iIdx < nodesArray.length; iIdx++) { var i = nodesArray[iIdx]; if (i !== m) { //Keep old energy value for sum modification below var cell = colm[iIdx]; var oldDx = cell[0]; var oldDy = cell[1]; //Calc new energy: var x_i = nodes[i].x; var y_i = nodes[i].y; var denominator = 1.0 / Math.sqrt(Math.pow(x_m - x_i, 2) + Math.pow(y_m - y_i, 2)); var dx = kcolm[i] * (x_m - x_i - lcolm[i] * (x_m - x_i) * denominator); var dy = kcolm[i] * (y_m - y_i - lcolm[i] * (y_m - y_i) * denominator); colm[iIdx] = [dx, dy]; dE_dx += dx; dE_dy += dy; //add new energy to sum of each column var sum = this.E_sums[i]; sum[0] += dx - oldDx; sum[1] += dy - oldDy; } } //Store sum at -1 index this.E_sums[m] = [dE_dx, dE_dy]; } }]); return KamadaKawai; }(); /** * Create a network visualization, displaying nodes and edges. * * @param {Element} container The DOM element in which the Network will * be created. Normally a div element. * @param {object} data An object containing parameters * {Array} nodes * {Array} edges * @param {object} options Options * @class Network */ function Network(container, data, options) { var _context, _context2, _context3, _context4, _this = this; if (!(this instanceof Network)) { throw new SyntaxError("Constructor must be called with the new operator"); } // set constant values this.options = {}; this.defaultOptions = { locale: "en", locales: locales, clickToUse: false }; _Object$assign(this.options, this.defaultOptions); /** * Containers for nodes and edges. * * 'edges' and 'nodes' contain the full definitions of all the network elements. * 'nodeIndices' and 'edgeIndices' contain the id's of the active elements. * * The distinction is important, because a defined node need not be active, i.e. * visible on the canvas. This happens in particular when clusters are defined, in * that case there will be nodes and edges not displayed. * The bottom line is that all code with actions related to visibility, *must* use * 'nodeIndices' and 'edgeIndices', not 'nodes' and 'edges' directly. */ this.body = { container: container, // See comment above for following fields nodes: {}, nodeIndices: [], edges: {}, edgeIndices: [], emitter: { on: _bindInstanceProperty$1(_context = this.on).call(_context, this), off: _bindInstanceProperty$1(_context2 = this.off).call(_context2, this), emit: _bindInstanceProperty$1(_context3 = this.emit).call(_context3, this), once: _bindInstanceProperty$1(_context4 = this.once).call(_context4, this) }, eventListeners: { onTap: function onTap() {}, onTouch: function onTouch() {}, onDoubleTap: function onDoubleTap() {}, onHold: function onHold() {}, onDragStart: function onDragStart() {}, onDrag: function onDrag() {}, onDragEnd: function onDragEnd() {}, onMouseWheel: function onMouseWheel() {}, onPinch: function onPinch() {}, onMouseMove: function onMouseMove() {}, onRelease: function onRelease() {}, onContext: function onContext() {} }, data: { nodes: null, // A DataSet or DataView edges: null // A DataSet or DataView }, functions: { createNode: function createNode() {}, createEdge: function createEdge() {}, getPointer: function getPointer() {} }, modules: {}, view: { scale: 1, translation: { x: 0, y: 0 } }, selectionBox: { show: false, position: { start: { x: 0, y: 0 }, end: { x: 0, y: 0 } } } }; // bind the event listeners this.bindEventListeners(); // setting up all modules this.images = new Images(function () { return _this.body.emitter.emit("_requestRedraw"); }); // object with images this.groups = new Groups(); // object with groups this.canvas = new Canvas(this.body); // DOM handler this.selectionHandler = new SelectionHandler(this.body, this.canvas); // Selection handler this.interactionHandler = new InteractionHandler(this.body, this.canvas, this.selectionHandler); // Interaction handler handles all the hammer bindings (that are bound by canvas), key this.view = new View(this.body, this.canvas); // camera handler, does animations and zooms this.renderer = new CanvasRenderer(this.body, this.canvas); // renderer, starts renderloop, has events that modules can hook into this.physics = new PhysicsEngine(this.body); // physics engine, does all the simulations this.layoutEngine = new LayoutEngine(this.body); // layout engine for inital layout and hierarchical layout this.clustering = new ClusterEngine(this.body); // clustering api this.manipulation = new ManipulationSystem(this.body, this.canvas, this.selectionHandler, this.interactionHandler); // data manipulation system this.nodesHandler = new NodesHandler(this.body, this.images, this.groups, this.layoutEngine); // Handle adding, deleting and updating of nodes as well as global options this.edgesHandler = new EdgesHandler(this.body, this.images, this.groups); // Handle adding, deleting and updating of edges as well as global options this.body.modules["kamadaKawai"] = new KamadaKawai(this.body, 150, 0.05); // Layouting algorithm. this.body.modules["clustering"] = this.clustering; // create the DOM elements this.canvas._create(); // apply options this.setOptions(options); // load data (the disable start variable will be the same as the enabled clustering) this.setData(data); } // Extend Network with an Emitter mixin Emitter(Network.prototype); /** * Set options * * @param {object} options */ Network.prototype.setOptions = function (options) { var _this2 = this; if (options === null) { options = undefined; // This ensures that options handling doesn't crash in the handling } if (options !== undefined) { var errorFound = Validator.validate(options, allOptions); if (errorFound === true) { console.error("%cErrors have been found in the supplied options object.", VALIDATOR_PRINT_STYLE); } // copy the global fields over var fields = ["locale", "locales", "clickToUse"]; selectiveDeepExtend(fields, this.options, options); // normalize the locale or use English if (options.locale !== undefined) { options.locale = normalizeLanguageCode(options.locales || this.options.locales, options.locale); } // the hierarchical system can adapt the edges and the physics to it's own options because not all combinations work with the hierarichical system. options = this.layoutEngine.setOptions(options.layout, options); this.canvas.setOptions(options); // options for canvas are in globals // pass the options to the modules this.groups.setOptions(options.groups); this.nodesHandler.setOptions(options.nodes); this.edgesHandler.setOptions(options.edges); this.physics.setOptions(options.physics); this.manipulation.setOptions(options.manipulation, options, this.options); // manipulation uses the locales in the globals this.interactionHandler.setOptions(options.interaction); this.renderer.setOptions(options.interaction); // options for rendering are in interaction this.selectionHandler.setOptions(options.interaction); // options for selection are in interaction // reload the settings of the nodes to apply changes in groups that are not referenced by pointer. if (options.groups !== undefined) { this.body.emitter.emit("refreshNodes"); } // these two do not have options at the moment, here for completeness //this.view.setOptions(options.view); //this.clustering.setOptions(options.clustering); if ("configure" in options) { if (!this.configurator) { this.configurator = new Configurator(this, this.body.container, configureOptions, this.canvas.pixelRatio, configuratorHideOption); } this.configurator.setOptions(options.configure); } // if the configuration system is enabled, copy all options and put them into the config system if (this.configurator && this.configurator.options.enabled === true) { var networkOptions = { nodes: {}, edges: {}, layout: {}, interaction: {}, manipulation: {}, physics: {}, global: {} }; deepExtend(networkOptions.nodes, this.nodesHandler.options); deepExtend(networkOptions.edges, this.edgesHandler.options); deepExtend(networkOptions.layout, this.layoutEngine.options); // load the selectionHandler and render default options in to the interaction group deepExtend(networkOptions.interaction, this.selectionHandler.options); deepExtend(networkOptions.interaction, this.renderer.options); deepExtend(networkOptions.interaction, this.interactionHandler.options); deepExtend(networkOptions.manipulation, this.manipulation.options); deepExtend(networkOptions.physics, this.physics.options); // load globals into the global object deepExtend(networkOptions.global, this.canvas.options); deepExtend(networkOptions.global, this.options); this.configurator.setModuleOptions(networkOptions); } // handle network global options if (options.clickToUse !== undefined) { if (options.clickToUse === true) { if (this.activator === undefined) { this.activator = new Activator(this.canvas.frame); this.activator.on("change", function () { _this2.body.emitter.emit("activate"); }); } } else { if (this.activator !== undefined) { this.activator.destroy(); delete this.activator; } this.body.emitter.emit("activate"); } } else { this.body.emitter.emit("activate"); } this.canvas.setSize(); // start the physics simulation. Can be safely called multiple times. this.body.emitter.emit("startSimulation"); } }; /** * Update the visible nodes and edges list with the most recent node state. * * Visible nodes are stored in this.body.nodeIndices. * Visible edges are stored in this.body.edgeIndices. * A node or edges is visible if it is not hidden or clustered. * * @private */ Network.prototype._updateVisibleIndices = function () { var nodes = this.body.nodes; var edges = this.body.edges; this.body.nodeIndices = []; this.body.edgeIndices = []; for (var nodeId in nodes) { if (Object.prototype.hasOwnProperty.call(nodes, nodeId)) { if (!this.clustering._isClusteredNode(nodeId) && nodes[nodeId].options.hidden === false) { this.body.nodeIndices.push(nodes[nodeId].id); } } } for (var edgeId in edges) { if (Object.prototype.hasOwnProperty.call(edges, edgeId)) { var edge = edges[edgeId]; // It can happen that this is executed *after* a node edge has been removed, // but *before* the edge itself has been removed. Taking this into account. var fromNode = nodes[edge.fromId]; var toNode = nodes[edge.toId]; var edgeNodesPresent = fromNode !== undefined && toNode !== undefined; var isVisible = !this.clustering._isClusteredEdge(edgeId) && edge.options.hidden === false && edgeNodesPresent && fromNode.options.hidden === false && // Also hidden if any of its connecting nodes are hidden toNode.options.hidden === false; // idem if (isVisible) { this.body.edgeIndices.push(edge.id); } } } }; /** * Bind all events */ Network.prototype.bindEventListeners = function () { var _this3 = this; // This event will trigger a rebuilding of the cache everything. // Used when nodes or edges have been added or removed. this.body.emitter.on("_dataChanged", function () { _this3.edgesHandler._updateState(); _this3.body.emitter.emit("_dataUpdated"); }); // this is called when options of EXISTING nodes or edges have changed. this.body.emitter.on("_dataUpdated", function () { // Order important in following block _this3.clustering._updateState(); _this3._updateVisibleIndices(); _this3._updateValueRange(_this3.body.nodes); _this3._updateValueRange(_this3.body.edges); // start simulation (can be called safely, even if already running) _this3.body.emitter.emit("startSimulation"); _this3.body.emitter.emit("_requestRedraw"); }); }; /** * Set nodes and edges, and optionally options as well. * * @param {object} data Object containing parameters: * {Array | DataSet | DataView} [nodes] Array with nodes * {Array | DataSet | DataView} [edges] Array with edges * {String} [dot] String containing data in DOT format * {String} [gephi] String containing data in gephi JSON format * {Options} [options] Object with options */ Network.prototype.setData = function (data) { // reset the physics engine. this.body.emitter.emit("resetPhysics"); this.body.emitter.emit("_resetData"); // unselect all to ensure no selections from old data are carried over. this.selectionHandler.unselectAll(); if (data && data.dot && (data.nodes || data.edges)) { throw new SyntaxError('Data must contain either parameter "dot" or ' + ' parameter pair "nodes" and "edges", but not both.'); } // set options this.setOptions(data && data.options); // set all data if (data && data.dot) { console.warn("The dot property has been deprecated. Please use the static convertDot method to convert DOT into vis.network format and use the normal data format with nodes and edges. This converter is used like this: var data = vis.network.convertDot(dotString);"); // parse DOT file var dotData = DOTToGraph(data.dot); this.setData(dotData); return; } else if (data && data.gephi) { // parse DOT file console.warn("The gephi property has been deprecated. Please use the static convertGephi method to convert gephi into vis.network format and use the normal data format with nodes and edges. This converter is used like this: var data = vis.network.convertGephi(gephiJson);"); var gephiData = parseGephi(data.gephi); this.setData(gephiData); return; } else { this.nodesHandler.setData(data && data.nodes, true); this.edgesHandler.setData(data && data.edges, true); } // emit change in data this.body.emitter.emit("_dataChanged"); // emit data loaded this.body.emitter.emit("_dataLoaded"); // find a stable position or start animating to a stable position this.body.emitter.emit("initPhysics"); }; /** * Cleans up all bindings of the network, removing it fully from the memory IF the variable is set to null after calling this function. * var network = new vis.Network(..); * network.destroy(); * network = null; */ Network.prototype.destroy = function () { this.body.emitter.emit("destroy"); // clear events this.body.emitter.off(); this.off(); // delete modules delete this.groups; delete this.canvas; delete this.selectionHandler; delete this.interactionHandler; delete this.view; delete this.renderer; delete this.physics; delete this.layoutEngine; delete this.clustering; delete this.manipulation; delete this.nodesHandler; delete this.edgesHandler; delete this.configurator; delete this.images; for (var nodeId in this.body.nodes) { if (!Object.prototype.hasOwnProperty.call(this.body.nodes, nodeId)) continue; delete this.body.nodes[nodeId]; } for (var edgeId in this.body.edges) { if (!Object.prototype.hasOwnProperty.call(this.body.edges, edgeId)) continue; delete this.body.edges[edgeId]; } // remove the container and everything inside it recursively recursiveDOMDelete(this.body.container); }; /** * Update the values of all object in the given array according to the current * value range of the objects in the array. * * @param {object} obj An object containing a set of Edges or Nodes * The objects must have a method getValue() and * setValueRange(min, max). * @private */ Network.prototype._updateValueRange = function (obj) { var id; // determine the range of the objects var valueMin = undefined; var valueMax = undefined; var valueTotal = 0; for (id in obj) { if (Object.prototype.hasOwnProperty.call(obj, id)) { var value = obj[id].getValue(); if (value !== undefined) { valueMin = valueMin === undefined ? value : Math.min(value, valueMin); valueMax = valueMax === undefined ? value : Math.max(value, valueMax); valueTotal += value; } } } // adjust the range of all objects if (valueMin !== undefined && valueMax !== undefined) { for (id in obj) { if (Object.prototype.hasOwnProperty.call(obj, id)) { obj[id].setValueRange(valueMin, valueMax, valueTotal); } } } }; /** * Returns true when the Network is active. * * @returns {boolean} */ Network.prototype.isActive = function () { return !this.activator || this.activator.active; }; Network.prototype.setSize = function () { return this.canvas.setSize.apply(this.canvas, arguments); }; Network.prototype.canvasToDOM = function () { return this.canvas.canvasToDOM.apply(this.canvas, arguments); }; Network.prototype.DOMtoCanvas = function () { return this.canvas.DOMtoCanvas.apply(this.canvas, arguments); }; /** * Nodes can be in clusters. Clusters can also be in clusters. This function returns and array of * nodeIds showing where the node is. * * If any nodeId in the chain, especially the first passed in as a parameter, is not present in * the current nodes list, an empty array is returned. * * Example: * cluster 'A' contains cluster 'B', * cluster 'B' contains cluster 'C', * cluster 'C' contains node 'fred'. * `jsnetwork.clustering.findNode('fred')` will return `['A','B','C','fred']`. * * @param {string|number} nodeId * @returns {Array} */ Network.prototype.findNode = function () { return this.clustering.findNode.apply(this.clustering, arguments); }; Network.prototype.isCluster = function () { return this.clustering.isCluster.apply(this.clustering, arguments); }; Network.prototype.openCluster = function () { return this.clustering.openCluster.apply(this.clustering, arguments); }; Network.prototype.cluster = function () { return this.clustering.cluster.apply(this.clustering, arguments); }; Network.prototype.getNodesInCluster = function () { return this.clustering.getNodesInCluster.apply(this.clustering, arguments); }; Network.prototype.clusterByConnection = function () { return this.clustering.clusterByConnection.apply(this.clustering, arguments); }; Network.prototype.clusterByHubsize = function () { return this.clustering.clusterByHubsize.apply(this.clustering, arguments); }; Network.prototype.updateClusteredNode = function () { return this.clustering.updateClusteredNode.apply(this.clustering, arguments); }; Network.prototype.getClusteredEdges = function () { return this.clustering.getClusteredEdges.apply(this.clustering, arguments); }; Network.prototype.getBaseEdge = function () { return this.clustering.getBaseEdge.apply(this.clustering, arguments); }; Network.prototype.getBaseEdges = function () { return this.clustering.getBaseEdges.apply(this.clustering, arguments); }; Network.prototype.updateEdge = function () { return this.clustering.updateEdge.apply(this.clustering, arguments); }; /** * This method will cluster all nodes with 1 edge with their respective connected node. * The options object is explained in full <a data-scroll="" data-options="{ "easing": "easeInCubic" }" href="#optionsObject">below</a>. * * @param {object} [options] * @returns {undefined} */ Network.prototype.clusterOutliers = function () { return this.clustering.clusterOutliers.apply(this.clustering, arguments); }; Network.prototype.getSeed = function () { return this.layoutEngine.getSeed.apply(this.layoutEngine, arguments); }; Network.prototype.enableEditMode = function () { return this.manipulation.enableEditMode.apply(this.manipulation, arguments); }; Network.prototype.disableEditMode = function () { return this.manipulation.disableEditMode.apply(this.manipulation, arguments); }; Network.prototype.addNodeMode = function () { return this.manipulation.addNodeMode.apply(this.manipulation, arguments); }; Network.prototype.editNode = function () { return this.manipulation.editNode.apply(this.manipulation, arguments); }; Network.prototype.editNodeMode = function () { console.warn("Deprecated: Please use editNode instead of editNodeMode."); return this.manipulation.editNode.apply(this.manipulation, arguments); }; Network.prototype.addEdgeMode = function () { return this.manipulation.addEdgeMode.apply(this.manipulation, arguments); }; Network.prototype.editEdgeMode = function () { return this.manipulation.editEdgeMode.apply(this.manipulation, arguments); }; Network.prototype.deleteSelected = function () { return this.manipulation.deleteSelected.apply(this.manipulation, arguments); }; Network.prototype.getPositions = function () { return this.nodesHandler.getPositions.apply(this.nodesHandler, arguments); }; Network.prototype.getPosition = function () { return this.nodesHandler.getPosition.apply(this.nodesHandler, arguments); }; Network.prototype.storePositions = function () { return this.nodesHandler.storePositions.apply(this.nodesHandler, arguments); }; Network.prototype.moveNode = function () { return this.nodesHandler.moveNode.apply(this.nodesHandler, arguments); }; Network.prototype.getBoundingBox = function () { return this.nodesHandler.getBoundingBox.apply(this.nodesHandler, arguments); }; Network.prototype.getConnectedNodes = function (objectId) { if (this.body.nodes[objectId] !== undefined) { return this.nodesHandler.getConnectedNodes.apply(this.nodesHandler, arguments); } else { return this.edgesHandler.getConnectedNodes.apply(this.edgesHandler, arguments); } }; Network.prototype.getConnectedEdges = function () { return this.nodesHandler.getConnectedEdges.apply(this.nodesHandler, arguments); }; Network.prototype.startSimulation = function () { return this.physics.startSimulation.apply(this.physics, arguments); }; Network.prototype.stopSimulation = function () { return this.physics.stopSimulation.apply(this.physics, arguments); }; Network.prototype.stabilize = function () { return this.physics.stabilize.apply(this.physics, arguments); }; Network.prototype.getSelection = function () { return this.selectionHandler.getSelection.apply(this.selectionHandler, arguments); }; Network.prototype.setSelection = function () { return this.selectionHandler.setSelection.apply(this.selectionHandler, arguments); }; Network.prototype.getSelectedNodes = function () { return this.selectionHandler.getSelectedNodeIds.apply(this.selectionHandler, arguments); }; Network.prototype.getSelectedEdges = function () { return this.selectionHandler.getSelectedEdgeIds.apply(this.selectionHandler, arguments); }; Network.prototype.getNodeAt = function () { var node = this.selectionHandler.getNodeAt.apply(this.selectionHandler, arguments); if (node !== undefined && node.id !== undefined) { return node.id; } return node; }; Network.prototype.getEdgeAt = function () { var edge = this.selectionHandler.getEdgeAt.apply(this.selectionHandler, arguments); if (edge !== undefined && edge.id !== undefined) { return edge.id; } return edge; }; Network.prototype.selectNodes = function () { return this.selectionHandler.selectNodes.apply(this.selectionHandler, arguments); }; Network.prototype.selectEdges = function () { return this.selectionHandler.selectEdges.apply(this.selectionHandler, arguments); }; Network.prototype.unselectAll = function () { this.selectionHandler.unselectAll.apply(this.selectionHandler, arguments); this.selectionHandler.commitWithoutEmitting.apply(this.selectionHandler); this.redraw(); }; Network.prototype.redraw = function () { return this.renderer.redraw.apply(this.renderer, arguments); }; Network.prototype.getScale = function () { return this.view.getScale.apply(this.view, arguments); }; Network.prototype.getViewPosition = function () { return this.view.getViewPosition.apply(this.view, arguments); }; Network.prototype.fit = function () { return this.view.fit.apply(this.view, arguments); }; Network.prototype.moveTo = function () { return this.view.moveTo.apply(this.view, arguments); }; Network.prototype.focus = function () { return this.view.focus.apply(this.view, arguments); }; Network.prototype.releaseNode = function () { return this.view.releaseNode.apply(this.view, arguments); }; Network.prototype.getOptionsFromConfigurator = function () { var options = {}; if (this.configurator) { options = this.configurator.getOptions.apply(this.configurator); } return options; }; var parseDOTNetwork = DOTToGraph; // DataSet, utils etc. can't be reexported here because that would cause stack // overflow in UMD builds. They all export vis namespace therefore reexporting // leads to loading vis to load vis to load vis… /***/ }), /***/ 9228: /*!************************************************!*\ !*** ./node_modules/vis-network/peer/index.js ***! \************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "Network": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.Network), /* harmony export */ "NetworkImages": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.NetworkImages), /* harmony export */ "networkDOTParser": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.networkDOTParser), /* harmony export */ "networkGephiParser": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.networkGephiParser), /* harmony export */ "networkOptions": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.networkOptions), /* harmony export */ "parseDOTNetwork": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.parseDOTNetwork), /* harmony export */ "parseGephiNetwork": () => (/* reexport safe */ _esm__WEBPACK_IMPORTED_MODULE_0__.parseGephiNetwork) /* harmony export */ }); /* harmony import */ var _esm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./esm */ 7818); /***/ }), /***/ 5218: /*!***************************************************************************!*\ !*** ./node_modules/webpack-dev-server/client/clients/WebSocketClient.js ***! \***************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (/* binding */ WebSocketClient) /* harmony export */ }); /* harmony import */ var _utils_log_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils/log.js */ 4664); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } var WebSocketClient = /*#__PURE__*/function () { /** * @param {string} url */ function WebSocketClient(url) { _classCallCheck(this, WebSocketClient); this.client = new WebSocket(url); this.client.onerror = function (error) { _utils_log_js__WEBPACK_IMPORTED_MODULE_0__.log.error(error); }; } /** * @param {(...args: any[]) => void} f */ _createClass(WebSocketClient, [{ key: "onOpen", value: function onOpen(f) { this.client.onopen = f; } /** * @param {(...args: any[]) => void} f */ }, { key: "onClose", value: function onClose(f) { this.client.onclose = f; } // call f with the message string as the first argument /** * @param {(...args: any[]) => void} f */ }, { key: "onMessage", value: function onMessage(f) { this.client.onmessage = function (e) { f(e.data); }; } }]); return WebSocketClient; }(); /***/ }), /***/ 1211: /*!************************************************************************************************************************************************************************************************************************************************************!*\ !*** ./node_modules/webpack-dev-server/client/index.js?protocol=auto%3A&username=&password=&hostname=0.0.0.0&port=0&pathname=%2Fng-cli-ws&logging=info&overlay=%7B%22errors%22%3Atrue%2C%22warnings%22%3Afalse%7D&reconnect=10&hot=false&live-reload=true ***! \************************************************************************************************************************************************************************************************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; var __resourceQuery = "?protocol=auto%3A&username=&password=&hostname=0.0.0.0&port=0&pathname=%2Fng-cli-ws&logging=info&overlay=%7B%22errors%22%3Atrue%2C%22warnings%22%3Afalse%7D&reconnect=10&hot=false&live-reload=true"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var webpack_hot_log_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! webpack/hot/log.js */ 1877); /* harmony import */ var webpack_hot_log_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(webpack_hot_log_js__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _utils_stripAnsi_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utils/stripAnsi.js */ 8931); /* harmony import */ var _utils_parseURL_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./utils/parseURL.js */ 8587); /* harmony import */ var _socket_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./socket.js */ 9178); /* harmony import */ var _overlay_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./overlay.js */ 4754); /* harmony import */ var _utils_log_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./utils/log.js */ 4664); /* harmony import */ var _utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./utils/sendMessage.js */ 430); /* harmony import */ var _utils_reloadApp_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./utils/reloadApp.js */ 4163); /* harmony import */ var _utils_createSocketURL_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./utils/createSocketURL.js */ 7308); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /* global __resourceQuery, __webpack_hash__ */ /// <reference types="webpack/module" /> /** * @typedef {Object} Options * @property {boolean} hot * @property {boolean} liveReload * @property {boolean} progress * @property {boolean | { warnings?: boolean, errors?: boolean, trustedTypesPolicyName?: string }} overlay * @property {string} [logging] * @property {number} [reconnect] */ /** * @typedef {Object} Status * @property {boolean} isUnloading * @property {string} currentHash * @property {string} [previousHash] */ /** * @type {Status} */ var status = { isUnloading: false, // TODO Workaround for webpack v4, `__webpack_hash__` is not replaced without HotModuleReplacement // eslint-disable-next-line camelcase currentHash: true ? __webpack_require__.h() : 0 }; /** @type {Options} */ var options = { hot: false, liveReload: false, progress: false, overlay: false }; var parsedResourceQuery = (0,_utils_parseURL_js__WEBPACK_IMPORTED_MODULE_2__["default"])(__resourceQuery); var enabledFeatures = { "Hot Module Replacement": false, "Live Reloading": false, Progress: false, Overlay: false }; if (parsedResourceQuery.hot === "true") { options.hot = true; enabledFeatures["Hot Module Replacement"] = true; } if (parsedResourceQuery["live-reload"] === "true") { options.liveReload = true; enabledFeatures["Live Reloading"] = true; } if (parsedResourceQuery.progress === "true") { options.progress = true; enabledFeatures.Progress = true; } if (parsedResourceQuery.overlay) { try { options.overlay = JSON.parse(parsedResourceQuery.overlay); } catch (e) { _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.error("Error parsing overlay options from resource query:", e); } // Fill in default "true" params for partially-specified objects. if (typeof options.overlay === "object") { options.overlay = _objectSpread({ errors: true, warnings: true }, options.overlay); } enabledFeatures.Overlay = true; } if (parsedResourceQuery.logging) { options.logging = parsedResourceQuery.logging; } if (typeof parsedResourceQuery.reconnect !== "undefined") { options.reconnect = Number(parsedResourceQuery.reconnect); } /** * @param {string} level */ function setAllLogLevel(level) { // This is needed because the HMR logger operate separately from dev server logger webpack_hot_log_js__WEBPACK_IMPORTED_MODULE_0___default().setLogLevel(level === "verbose" || level === "log" ? "info" : level); (0,_utils_log_js__WEBPACK_IMPORTED_MODULE_5__.setLogLevel)(level); } if (options.logging) { setAllLogLevel(options.logging); } (0,_utils_log_js__WEBPACK_IMPORTED_MODULE_5__.logEnabledFeatures)(enabledFeatures); self.addEventListener("beforeunload", function () { status.isUnloading = true; }); var onSocketMessage = { hot: function hot() { if (parsedResourceQuery.hot === "false") { return; } options.hot = true; }, liveReload: function liveReload() { if (parsedResourceQuery["live-reload"] === "false") { return; } options.liveReload = true; }, invalid: function invalid() { _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("App updated. Recompiling..."); // Fixes #1042. overlay doesn't clear if errors are fixed but warnings remain. if (options.overlay) { (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.hide)(); } (0,_utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__["default"])("Invalid"); }, /** * @param {string} hash */ hash: function hash(_hash) { status.previousHash = status.currentHash; status.currentHash = _hash; }, logging: setAllLogLevel, /** * @param {boolean} value */ overlay: function overlay(value) { if (typeof document === "undefined") { return; } options.overlay = value; }, /** * @param {number} value */ reconnect: function reconnect(value) { if (parsedResourceQuery.reconnect === "false") { return; } options.reconnect = value; }, /** * @param {boolean} value */ progress: function progress(value) { options.progress = value; }, /** * @param {{ pluginName?: string, percent: number, msg: string }} data */ "progress-update": function progressUpdate(data) { if (options.progress) { _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("".concat(data.pluginName ? "[".concat(data.pluginName, "] ") : "").concat(data.percent, "% - ").concat(data.msg, ".")); } (0,_utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__["default"])("Progress", data); }, "still-ok": function stillOk() { _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("Nothing changed."); if (options.overlay) { (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.hide)(); } (0,_utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__["default"])("StillOk"); }, ok: function ok() { (0,_utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__["default"])("Ok"); if (options.overlay) { (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.hide)(); } (0,_utils_reloadApp_js__WEBPACK_IMPORTED_MODULE_7__["default"])(options, status); }, // TODO: remove in v5 in favor of 'static-changed' /** * @param {string} file */ "content-changed": function contentChanged(file) { _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("".concat(file ? "\"".concat(file, "\"") : "Content", " from static directory was changed. Reloading...")); self.location.reload(); }, /** * @param {string} file */ "static-changed": function staticChanged(file) { _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("".concat(file ? "\"".concat(file, "\"") : "Content", " from static directory was changed. Reloading...")); self.location.reload(); }, /** * @param {Error[]} warnings * @param {any} params */ warnings: function warnings(_warnings, params) { _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.warn("Warnings while compiling."); var printableWarnings = _warnings.map(function (error) { var _formatProblem = (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.formatProblem)("warning", error), header = _formatProblem.header, body = _formatProblem.body; return "".concat(header, "\n").concat((0,_utils_stripAnsi_js__WEBPACK_IMPORTED_MODULE_1__["default"])(body)); }); (0,_utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__["default"])("Warnings", printableWarnings); for (var i = 0; i < printableWarnings.length; i++) { _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.warn(printableWarnings[i]); } var needShowOverlayForWarnings = typeof options.overlay === "boolean" ? options.overlay : options.overlay && options.overlay.warnings; if (needShowOverlayForWarnings) { var trustedTypesPolicyName = typeof options.overlay === "object" && options.overlay.trustedTypesPolicyName; (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.show)("warning", _warnings, trustedTypesPolicyName || null); } if (params && params.preventReloading) { return; } (0,_utils_reloadApp_js__WEBPACK_IMPORTED_MODULE_7__["default"])(options, status); }, /** * @param {Error[]} errors */ errors: function errors(_errors) { _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.error("Errors while compiling. Reload prevented."); var printableErrors = _errors.map(function (error) { var _formatProblem2 = (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.formatProblem)("error", error), header = _formatProblem2.header, body = _formatProblem2.body; return "".concat(header, "\n").concat((0,_utils_stripAnsi_js__WEBPACK_IMPORTED_MODULE_1__["default"])(body)); }); (0,_utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__["default"])("Errors", printableErrors); for (var i = 0; i < printableErrors.length; i++) { _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.error(printableErrors[i]); } var needShowOverlayForErrors = typeof options.overlay === "boolean" ? options.overlay : options.overlay && options.overlay.errors; if (needShowOverlayForErrors) { var trustedTypesPolicyName = typeof options.overlay === "object" && options.overlay.trustedTypesPolicyName; (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.show)("error", _errors, trustedTypesPolicyName || null); } }, /** * @param {Error} error */ error: function error(_error) { _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.error(_error); }, close: function close() { _utils_log_js__WEBPACK_IMPORTED_MODULE_5__.log.info("Disconnected!"); if (options.overlay) { (0,_overlay_js__WEBPACK_IMPORTED_MODULE_4__.hide)(); } (0,_utils_sendMessage_js__WEBPACK_IMPORTED_MODULE_6__["default"])("Close"); } }; var socketURL = (0,_utils_createSocketURL_js__WEBPACK_IMPORTED_MODULE_8__["default"])(parsedResourceQuery); (0,_socket_js__WEBPACK_IMPORTED_MODULE_3__["default"])(socketURL, onSocketMessage, options.reconnect); /***/ }), /***/ 3431: /*!************************************************************************!*\ !*** ./node_modules/webpack-dev-server/client/modules/logger/index.js ***! \************************************************************************/ /***/ ((__unused_webpack_module, exports) => { /******/(function () { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = { /***/"./client-src/modules/logger/SyncBailHookFake.js": /*!*******************************************************!*\ !*** ./client-src/modules/logger/SyncBailHookFake.js ***! \*******************************************************/ /***/ function (module) { /** * Client stub for tapable SyncBailHook */ module.exports = function clientTapableSyncBailHook() { return { call: function call() {} }; }; /***/ }, /***/"./node_modules/webpack/lib/logging/Logger.js": /*!****************************************************!*\ !*** ./node_modules/webpack/lib/logging/Logger.js ***! \****************************************************/ /***/ function (__unused_webpack_module, exports) { /* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _iterableToArray(iter) { if (typeof (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }) !== "undefined" && iter[(typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }).iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } var LogType = Object.freeze({ error: /** @type {"error"} */ "error", // message, c style arguments warn: /** @type {"warn"} */ "warn", // message, c style arguments info: /** @type {"info"} */ "info", // message, c style arguments log: /** @type {"log"} */ "log", // message, c style arguments debug: /** @type {"debug"} */ "debug", // message, c style arguments trace: /** @type {"trace"} */ "trace", // no arguments group: /** @type {"group"} */ "group", // [label] groupCollapsed: /** @type {"groupCollapsed"} */ "groupCollapsed", // [label] groupEnd: /** @type {"groupEnd"} */ "groupEnd", // [label] profile: /** @type {"profile"} */ "profile", // [profileName] profileEnd: /** @type {"profileEnd"} */ "profileEnd", // [profileName] time: /** @type {"time"} */ "time", // name, time as [seconds, nanoseconds] clear: /** @type {"clear"} */ "clear", // no arguments status: /** @type {"status"} */ "status" // message, arguments }); exports.LogType = LogType; /** @typedef {typeof LogType[keyof typeof LogType]} LogTypeEnum */ var LOG_SYMBOL = (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; })("webpack logger raw log method"); var TIMERS_SYMBOL = (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; })("webpack logger times"); var TIMERS_AGGREGATES_SYMBOL = (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; })("webpack logger aggregated times"); var WebpackLogger = /*#__PURE__*/function () { /** * @param {function(LogTypeEnum, any[]=): void} log log function * @param {function(string | function(): string): WebpackLogger} getChildLogger function to create child logger */ function WebpackLogger(log, getChildLogger) { _classCallCheck(this, WebpackLogger); this[LOG_SYMBOL] = log; this.getChildLogger = getChildLogger; } _createClass(WebpackLogger, [{ key: "error", value: function error() { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } this[LOG_SYMBOL](LogType.error, args); } }, { key: "warn", value: function warn() { for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } this[LOG_SYMBOL](LogType.warn, args); } }, { key: "info", value: function info() { for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } this[LOG_SYMBOL](LogType.info, args); } }, { key: "log", value: function log() { for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { args[_key4] = arguments[_key4]; } this[LOG_SYMBOL](LogType.log, args); } }, { key: "debug", value: function debug() { for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { args[_key5] = arguments[_key5]; } this[LOG_SYMBOL](LogType.debug, args); } }, { key: "assert", value: function assert(assertion) { if (!assertion) { for (var _len6 = arguments.length, args = new Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) { args[_key6 - 1] = arguments[_key6]; } this[LOG_SYMBOL](LogType.error, args); } } }, { key: "trace", value: function trace() { this[LOG_SYMBOL](LogType.trace, ["Trace"]); } }, { key: "clear", value: function clear() { this[LOG_SYMBOL](LogType.clear); } }, { key: "status", value: function status() { for (var _len7 = arguments.length, args = new Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { args[_key7] = arguments[_key7]; } this[LOG_SYMBOL](LogType.status, args); } }, { key: "group", value: function group() { for (var _len8 = arguments.length, args = new Array(_len8), _key8 = 0; _key8 < _len8; _key8++) { args[_key8] = arguments[_key8]; } this[LOG_SYMBOL](LogType.group, args); } }, { key: "groupCollapsed", value: function groupCollapsed() { for (var _len9 = arguments.length, args = new Array(_len9), _key9 = 0; _key9 < _len9; _key9++) { args[_key9] = arguments[_key9]; } this[LOG_SYMBOL](LogType.groupCollapsed, args); } }, { key: "groupEnd", value: function groupEnd() { for (var _len10 = arguments.length, args = new Array(_len10), _key10 = 0; _key10 < _len10; _key10++) { args[_key10] = arguments[_key10]; } this[LOG_SYMBOL](LogType.groupEnd, args); } }, { key: "profile", value: function profile(label) { this[LOG_SYMBOL](LogType.profile, [label]); } }, { key: "profileEnd", value: function profileEnd(label) { this[LOG_SYMBOL](LogType.profileEnd, [label]); } }, { key: "time", value: function time(label) { this[TIMERS_SYMBOL] = this[TIMERS_SYMBOL] || new Map(); this[TIMERS_SYMBOL].set(label, process.hrtime()); } }, { key: "timeLog", value: function timeLog(label) { var prev = this[TIMERS_SYMBOL] && this[TIMERS_SYMBOL].get(label); if (!prev) { throw new Error("No such label '".concat(label, "' for WebpackLogger.timeLog()")); } var time = process.hrtime(prev); this[LOG_SYMBOL](LogType.time, [label].concat(_toConsumableArray(time))); } }, { key: "timeEnd", value: function timeEnd(label) { var prev = this[TIMERS_SYMBOL] && this[TIMERS_SYMBOL].get(label); if (!prev) { throw new Error("No such label '".concat(label, "' for WebpackLogger.timeEnd()")); } var time = process.hrtime(prev); this[TIMERS_SYMBOL].delete(label); this[LOG_SYMBOL](LogType.time, [label].concat(_toConsumableArray(time))); } }, { key: "timeAggregate", value: function timeAggregate(label) { var prev = this[TIMERS_SYMBOL] && this[TIMERS_SYMBOL].get(label); if (!prev) { throw new Error("No such label '".concat(label, "' for WebpackLogger.timeAggregate()")); } var time = process.hrtime(prev); this[TIMERS_SYMBOL].delete(label); this[TIMERS_AGGREGATES_SYMBOL] = this[TIMERS_AGGREGATES_SYMBOL] || new Map(); var current = this[TIMERS_AGGREGATES_SYMBOL].get(label); if (current !== undefined) { if (time[1] + current[1] > 1e9) { time[0] += current[0] + 1; time[1] = time[1] - 1e9 + current[1]; } else { time[0] += current[0]; time[1] += current[1]; } } this[TIMERS_AGGREGATES_SYMBOL].set(label, time); } }, { key: "timeAggregateEnd", value: function timeAggregateEnd(label) { if (this[TIMERS_AGGREGATES_SYMBOL] === undefined) return; var time = this[TIMERS_AGGREGATES_SYMBOL].get(label); if (time === undefined) return; this[TIMERS_AGGREGATES_SYMBOL].delete(label); this[LOG_SYMBOL](LogType.time, [label].concat(_toConsumableArray(time))); } }]); return WebpackLogger; }(); exports.Logger = WebpackLogger; /***/ }, /***/"./node_modules/webpack/lib/logging/createConsoleLogger.js": /*!*****************************************************************!*\ !*** ./node_modules/webpack/lib/logging/createConsoleLogger.js ***! \*****************************************************************/ /***/ function (module, __unused_webpack_exports, __nested_webpack_require_12589__) { /* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _iterableToArray(iter) { if (typeof (typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }) !== "undefined" && iter[(typeof Symbol !== "undefined" ? Symbol : function (i) { return i; }).iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } var _require = __nested_webpack_require_12589__( /*! ./Logger */"./node_modules/webpack/lib/logging/Logger.js"), LogType = _require.LogType; /** @typedef {import("../../declarations/WebpackOptions").FilterItemTypes} FilterItemTypes */ /** @typedef {import("../../declarations/WebpackOptions").FilterTypes} FilterTypes */ /** @typedef {import("./Logger").LogTypeEnum} LogTypeEnum */ /** @typedef {function(string): boolean} FilterFunction */ /** * @typedef {Object} LoggerConsole * @property {function(): void} clear * @property {function(): void} trace * @property {(...args: any[]) => void} info * @property {(...args: any[]) => void} log * @property {(...args: any[]) => void} warn * @property {(...args: any[]) => void} error * @property {(...args: any[]) => void=} debug * @property {(...args: any[]) => void=} group * @property {(...args: any[]) => void=} groupCollapsed * @property {(...args: any[]) => void=} groupEnd * @property {(...args: any[]) => void=} status * @property {(...args: any[]) => void=} profile * @property {(...args: any[]) => void=} profileEnd * @property {(...args: any[]) => void=} logTime */ /** * @typedef {Object} LoggerOptions * @property {false|true|"none"|"error"|"warn"|"info"|"log"|"verbose"} level loglevel * @property {FilterTypes|boolean} debug filter for debug logging * @property {LoggerConsole} console the console to log to */ /** * @param {FilterItemTypes} item an input item * @returns {FilterFunction} filter function */ var filterToFunction = function filterToFunction(item) { if (typeof item === "string") { var regExp = new RegExp("[\\\\/]".concat(item.replace( // eslint-disable-next-line no-useless-escape /[-[\]{}()*+?.\\^$|]/g, "\\$&"), "([\\\\/]|$|!|\\?)")); return function (ident) { return regExp.test(ident); }; } if (item && typeof item === "object" && typeof item.test === "function") { return function (ident) { return item.test(ident); }; } if (typeof item === "function") { return item; } if (typeof item === "boolean") { return function () { return item; }; } }; /** * @enum {number} */ var LogLevel = { none: 6, false: 6, error: 5, warn: 4, info: 3, log: 2, true: 2, verbose: 1 }; /** * @param {LoggerOptions} options options object * @returns {function(string, LogTypeEnum, any[]): void} logging function */ module.exports = function (_ref) { var _ref$level = _ref.level, level = _ref$level === void 0 ? "info" : _ref$level, _ref$debug = _ref.debug, debug = _ref$debug === void 0 ? false : _ref$debug, console = _ref.console; var debugFilters = typeof debug === "boolean" ? [function () { return debug; }] : /** @type {FilterItemTypes[]} */ [].concat(debug).map(filterToFunction); /** @type {number} */ var loglevel = LogLevel["".concat(level)] || 0; /** * @param {string} name name of the logger * @param {LogTypeEnum} type type of the log entry * @param {any[]} args arguments of the log entry * @returns {void} */ var logger = function logger(name, type, args) { var labeledArgs = function labeledArgs() { if (Array.isArray(args)) { if (args.length > 0 && typeof args[0] === "string") { return ["[".concat(name, "] ").concat(args[0])].concat(_toConsumableArray(args.slice(1))); } else { return ["[".concat(name, "]")].concat(_toConsumableArray(args)); } } else { return []; } }; var debug = debugFilters.some(function (f) { return f(name); }); switch (type) { case LogType.debug: if (!debug) return; // eslint-disable-next-line node/no-unsupported-features/node-builtins if (typeof console.debug === "function") { // eslint-disable-next-line node/no-unsupported-features/node-builtins console.debug.apply(console, _toConsumableArray(labeledArgs())); } else { console.log.apply(console, _toConsumableArray(labeledArgs())); } break; case LogType.log: if (!debug && loglevel > LogLevel.log) return; console.log.apply(console, _toConsumableArray(labeledArgs())); break; case LogType.info: if (!debug && loglevel > LogLevel.info) return; console.info.apply(console, _toConsumableArray(labeledArgs())); break; case LogType.warn: if (!debug && loglevel > LogLevel.warn) return; console.warn.apply(console, _toConsumableArray(labeledArgs())); break; case LogType.error: if (!debug && loglevel > LogLevel.error) return; console.error.apply(console, _toConsumableArray(labeledArgs())); break; case LogType.trace: if (!debug) return; console.trace(); break; case LogType.groupCollapsed: if (!debug && loglevel > LogLevel.log) return; if (!debug && loglevel > LogLevel.verbose) { // eslint-disable-next-line node/no-unsupported-features/node-builtins if (typeof console.groupCollapsed === "function") { // eslint-disable-next-line node/no-unsupported-features/node-builtins console.groupCollapsed.apply(console, _toConsumableArray(labeledArgs())); } else { console.log.apply(console, _toConsumableArray(labeledArgs())); } break; } // falls through case LogType.group: if (!debug && loglevel > LogLevel.log) return; // eslint-disable-next-line node/no-unsupported-features/node-builtins if (typeof console.group === "function") { // eslint-disable-next-line node/no-unsupported-features/node-builtins console.group.apply(console, _toConsumableArray(labeledArgs())); } else { console.log.apply(console, _toConsumableArray(labeledArgs())); } break; case LogType.groupEnd: if (!debug && loglevel > LogLevel.log) return; // eslint-disable-next-line node/no-unsupported-features/node-builtins if (typeof console.groupEnd === "function") { // eslint-disable-next-line node/no-unsupported-features/node-builtins console.groupEnd(); } break; case LogType.time: { if (!debug && loglevel > LogLevel.log) return; var ms = args[1] * 1000 + args[2] / 1000000; var msg = "[".concat(name, "] ").concat(args[0], ": ").concat(ms, " ms"); if (typeof console.logTime === "function") { console.logTime(msg); } else { console.log(msg); } break; } case LogType.profile: // eslint-disable-next-line node/no-unsupported-features/node-builtins if (typeof console.profile === "function") { // eslint-disable-next-line node/no-unsupported-features/node-builtins console.profile.apply(console, _toConsumableArray(labeledArgs())); } break; case LogType.profileEnd: // eslint-disable-next-line node/no-unsupported-features/node-builtins if (typeof console.profileEnd === "function") { // eslint-disable-next-line node/no-unsupported-features/node-builtins console.profileEnd.apply(console, _toConsumableArray(labeledArgs())); } break; case LogType.clear: if (!debug && loglevel > LogLevel.log) return; // eslint-disable-next-line node/no-unsupported-features/node-builtins if (typeof console.clear === "function") { // eslint-disable-next-line node/no-unsupported-features/node-builtins console.clear(); } break; case LogType.status: if (!debug && loglevel > LogLevel.info) return; if (typeof console.status === "function") { if (args.length === 0) { console.status(); } else { console.status.apply(console, _toConsumableArray(labeledArgs())); } } else { if (args.length !== 0) { console.info.apply(console, _toConsumableArray(labeledArgs())); } } break; default: throw new Error("Unexpected LogType ".concat(type)); } }; return logger; }; /***/ }, /***/"./node_modules/webpack/lib/logging/runtime.js": /*!*****************************************************!*\ !*** ./node_modules/webpack/lib/logging/runtime.js ***! \*****************************************************/ /***/ function (__unused_webpack_module, exports, __nested_webpack_require_24187__) { /* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } var SyncBailHook = __nested_webpack_require_24187__( /*! tapable/lib/SyncBailHook */"./client-src/modules/logger/SyncBailHookFake.js"); var _require = __nested_webpack_require_24187__( /*! ./Logger */"./node_modules/webpack/lib/logging/Logger.js"), Logger = _require.Logger; var createConsoleLogger = __nested_webpack_require_24187__( /*! ./createConsoleLogger */"./node_modules/webpack/lib/logging/createConsoleLogger.js"); /** @type {createConsoleLogger.LoggerOptions} */ var currentDefaultLoggerOptions = { level: "info", debug: false, console: console }; var currentDefaultLogger = createConsoleLogger(currentDefaultLoggerOptions); /** * @param {string} name name of the logger * @returns {Logger} a logger */ exports.getLogger = function (name) { return new Logger(function (type, args) { if (exports.hooks.log.call(name, type, args) === undefined) { currentDefaultLogger(name, type, args); } }, function (childName) { return exports.getLogger("".concat(name, "/").concat(childName)); }); }; /** * @param {createConsoleLogger.LoggerOptions} options new options, merge with old options * @returns {void} */ exports.configureDefaultLogger = function (options) { _extends(currentDefaultLoggerOptions, options); currentDefaultLogger = createConsoleLogger(currentDefaultLoggerOptions); }; exports.hooks = { log: new SyncBailHook(["origin", "type", "args"]) }; /***/ } /******/ }; /************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ /******/ // The require function /******/ function __nested_webpack_require_26652__(moduleId) { /******/ // Check if module is in cache /******/var cachedModule = __webpack_module_cache__[moduleId]; /******/ if (cachedModule !== undefined) { /******/return cachedModule.exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { /******/ // no module.id needed /******/ // no module.loaded needed /******/exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ __webpack_modules__[moduleId](module, module.exports, __nested_webpack_require_26652__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ /******/ /* webpack/runtime/define property getters */ /******/ !function () { /******/ // define getter functions for harmony exports /******/__nested_webpack_require_26652__.d = function (exports, definition) { /******/for (var key in definition) { /******/if (__nested_webpack_require_26652__.o(definition, key) && !__nested_webpack_require_26652__.o(exports, key)) { /******/Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); /******/ } /******/ } /******/ }; /******/ }(); /******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ /******/ !function () { /******/__nested_webpack_require_26652__.o = function (obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }; /******/ }(); /******/ /******/ /* webpack/runtime/make namespace object */ /******/ !function () { /******/ // define __esModule on exports /******/__nested_webpack_require_26652__.r = function (exports) { /******/if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ }(); /******/ /************************************************************************/ var __webpack_exports__ = {}; // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. !function () { /*!********************************************!*\ !*** ./client-src/modules/logger/index.js ***! \********************************************/ __nested_webpack_require_26652__.r(__webpack_exports__); /* harmony export */ __nested_webpack_require_26652__.d(__webpack_exports__, { /* harmony export */"default": function () { return (/* reexport default export from named module */webpack_lib_logging_runtime_js__WEBPACK_IMPORTED_MODULE_0__ ); } /* harmony export */ }); /* harmony import */ var webpack_lib_logging_runtime_js__WEBPACK_IMPORTED_MODULE_0__ = __nested_webpack_require_26652__( /*! webpack/lib/logging/runtime.js */"./node_modules/webpack/lib/logging/runtime.js"); }(); var __webpack_export_target__ = exports; for (var i in __webpack_exports__) __webpack_export_target__[i] = __webpack_exports__[i]; if (__webpack_exports__.__esModule) Object.defineProperty(__webpack_export_target__, "__esModule", { value: true }); /******/ })(); /***/ }), /***/ 4754: /*!***********************************************************!*\ !*** ./node_modules/webpack-dev-server/client/overlay.js ***! \***********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "formatProblem": () => (/* binding */ formatProblem), /* harmony export */ "hide": () => (/* binding */ hide), /* harmony export */ "show": () => (/* binding */ show) /* harmony export */ }); /* harmony import */ var ansi_html_community__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ansi-html-community */ 7716); /* harmony import */ var ansi_html_community__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(ansi_html_community__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var html_entities__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! html-entities */ 4948); /* harmony import */ var html_entities__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(html_entities__WEBPACK_IMPORTED_MODULE_1__); // The error overlay is inspired (and mostly copied) from Create React App (https://github.com/facebookincubator/create-react-app) // They, in turn, got inspired by webpack-hot-middleware (https://github.com/glenjamin/webpack-hot-middleware). var colors = { reset: ["transparent", "transparent"], black: "181818", red: "E36049", green: "B3CB74", yellow: "FFD080", blue: "7CAFC2", magenta: "7FACCA", cyan: "C3C2EF", lightgrey: "EBE7E3", darkgrey: "6D7891" }; /** @type {HTMLIFrameElement | null | undefined} */ var iframeContainerElement; /** @type {HTMLDivElement | null | undefined} */ var containerElement; /** @type {Array<(element: HTMLDivElement) => void>} */ var onLoadQueue = []; /** @type {TrustedTypePolicy | undefined} */ var overlayTrustedTypesPolicy; ansi_html_community__WEBPACK_IMPORTED_MODULE_0___default().setColors(colors); /** * @param {string | null} trustedTypesPolicyName */ function createContainer(trustedTypesPolicyName) { // Enable Trusted Types if they are available in the current browser. if (window.trustedTypes) { overlayTrustedTypesPolicy = window.trustedTypes.createPolicy(trustedTypesPolicyName || "webpack-dev-server#overlay", { createHTML: function createHTML(value) { return value; } }); } iframeContainerElement = document.createElement("iframe"); iframeContainerElement.id = "webpack-dev-server-client-overlay"; iframeContainerElement.src = "about:blank"; iframeContainerElement.style.position = "fixed"; iframeContainerElement.style.left = 0; iframeContainerElement.style.top = 0; iframeContainerElement.style.right = 0; iframeContainerElement.style.bottom = 0; iframeContainerElement.style.width = "100vw"; iframeContainerElement.style.height = "100vh"; iframeContainerElement.style.border = "none"; iframeContainerElement.style.zIndex = 9999999999; iframeContainerElement.onload = function () { containerElement = /** @type {Document} */ /** @type {HTMLIFrameElement} */ iframeContainerElement.contentDocument.createElement("div"); containerElement.id = "webpack-dev-server-client-overlay-div"; containerElement.style.position = "fixed"; containerElement.style.boxSizing = "border-box"; containerElement.style.left = 0; containerElement.style.top = 0; containerElement.style.right = 0; containerElement.style.bottom = 0; containerElement.style.width = "100vw"; containerElement.style.height = "100vh"; containerElement.style.backgroundColor = "rgba(0, 0, 0, 0.85)"; containerElement.style.color = "#E8E8E8"; containerElement.style.fontFamily = "Menlo, Consolas, monospace"; containerElement.style.fontSize = "large"; containerElement.style.padding = "2rem"; containerElement.style.lineHeight = "1.2"; containerElement.style.whiteSpace = "pre-wrap"; containerElement.style.overflow = "auto"; var headerElement = document.createElement("span"); headerElement.innerText = "Compiled with problems:"; var closeButtonElement = document.createElement("button"); closeButtonElement.innerText = "X"; closeButtonElement.style.background = "transparent"; closeButtonElement.style.border = "none"; closeButtonElement.style.fontSize = "20px"; closeButtonElement.style.fontWeight = "bold"; closeButtonElement.style.color = "white"; closeButtonElement.style.cursor = "pointer"; closeButtonElement.style.cssFloat = "right"; // @ts-ignore closeButtonElement.style.styleFloat = "right"; closeButtonElement.addEventListener("click", function () { hide(); }); containerElement.appendChild(headerElement); containerElement.appendChild(closeButtonElement); containerElement.appendChild(document.createElement("br")); containerElement.appendChild(document.createElement("br")); /** @type {Document} */ /** @type {HTMLIFrameElement} */ iframeContainerElement.contentDocument.body.appendChild(containerElement); onLoadQueue.forEach(function (onLoad) { onLoad( /** @type {HTMLDivElement} */ containerElement); }); onLoadQueue = []; /** @type {HTMLIFrameElement} */ iframeContainerElement.onload = null; }; document.body.appendChild(iframeContainerElement); } /** * @param {(element: HTMLDivElement) => void} callback * @param {string | null} trustedTypesPolicyName */ function ensureOverlayExists(callback, trustedTypesPolicyName) { if (containerElement) { // Everything is ready, call the callback right away. callback(containerElement); return; } onLoadQueue.push(callback); if (iframeContainerElement) { return; } createContainer(trustedTypesPolicyName); } // Successful compilation. function hide() { if (!iframeContainerElement) { return; } // Clean up and reset internal state. document.body.removeChild(iframeContainerElement); iframeContainerElement = null; containerElement = null; } /** * @param {string} type * @param {string | { file?: string, moduleName?: string, loc?: string, message?: string }} item * @returns {{ header: string, body: string }} */ function formatProblem(type, item) { var header = type === "warning" ? "WARNING" : "ERROR"; var body = ""; if (typeof item === "string") { body += item; } else { var file = item.file || ""; // eslint-disable-next-line no-nested-ternary var moduleName = item.moduleName ? item.moduleName.indexOf("!") !== -1 ? "".concat(item.moduleName.replace(/^(\s|\S)*!/, ""), " (").concat(item.moduleName, ")") : "".concat(item.moduleName) : ""; var loc = item.loc; header += "".concat(moduleName || file ? " in ".concat(moduleName ? "".concat(moduleName).concat(file ? " (".concat(file, ")") : "") : file).concat(loc ? " ".concat(loc) : "") : ""); body += item.message || ""; } return { header: header, body: body }; } // Compilation with errors (e.g. syntax error or missing modules). /** * @param {string} type * @param {Array<string | { file?: string, moduleName?: string, loc?: string, message?: string }>} messages * @param {string | null} trustedTypesPolicyName */ function show(type, messages, trustedTypesPolicyName) { ensureOverlayExists(function () { messages.forEach(function (message) { var entryElement = document.createElement("div"); var typeElement = document.createElement("span"); var _formatProblem = formatProblem(type, message), header = _formatProblem.header, body = _formatProblem.body; typeElement.innerText = header; typeElement.style.color = "#".concat(colors.red); // Make it look similar to our terminal. var text = ansi_html_community__WEBPACK_IMPORTED_MODULE_0___default()((0,html_entities__WEBPACK_IMPORTED_MODULE_1__.encode)(body)); var messageTextNode = document.createElement("div"); messageTextNode.innerHTML = overlayTrustedTypesPolicy ? overlayTrustedTypesPolicy.createHTML(text) : text; entryElement.appendChild(typeElement); entryElement.appendChild(document.createElement("br")); entryElement.appendChild(document.createElement("br")); entryElement.appendChild(messageTextNode); entryElement.appendChild(document.createElement("br")); entryElement.appendChild(document.createElement("br")); /** @type {HTMLDivElement} */ containerElement.appendChild(entryElement); }); }, trustedTypesPolicyName); } /***/ }), /***/ 9178: /*!**********************************************************!*\ !*** ./node_modules/webpack-dev-server/client/socket.js ***! \**********************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "client": () => (/* binding */ client), /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony import */ var _clients_WebSocketClient_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./clients/WebSocketClient.js */ 5218); /* harmony import */ var _utils_log_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utils/log.js */ 4664); /* provided dependency */ var __webpack_dev_server_client__ = __webpack_require__(/*! ./node_modules/webpack-dev-server/client/clients/WebSocketClient.js */ 5218); /* global __webpack_dev_server_client__ */ // this WebsocketClient is here as a default fallback, in case the client is not injected /* eslint-disable camelcase */ var Client = // eslint-disable-next-line no-nested-ternary typeof __webpack_dev_server_client__ !== "undefined" ? typeof __webpack_dev_server_client__.default !== "undefined" ? __webpack_dev_server_client__.default : __webpack_dev_server_client__ : _clients_WebSocketClient_js__WEBPACK_IMPORTED_MODULE_0__["default"]; /* eslint-enable camelcase */ var retries = 0; var maxRetries = 10; // Initialized client is exported so external consumers can utilize the same instance // It is mutable to enforce singleton // eslint-disable-next-line import/no-mutable-exports var client = null; /** * @param {string} url * @param {{ [handler: string]: (data?: any, params?: any) => any }} handlers * @param {number} [reconnect] */ var socket = function initSocket(url, handlers, reconnect) { client = new Client(url); client.onOpen(function () { retries = 0; if (typeof reconnect !== "undefined") { maxRetries = reconnect; } }); client.onClose(function () { if (retries === 0) { handlers.close(); } // Try to reconnect. client = null; // After 10 retries stop trying, to prevent logspam. if (retries < maxRetries) { // Exponentially increase timeout to reconnect. // Respectfully copied from the package `got`. // eslint-disable-next-line no-restricted-properties var retryInMs = 1000 * Math.pow(2, retries) + Math.random() * 100; retries += 1; _utils_log_js__WEBPACK_IMPORTED_MODULE_1__.log.info("Trying to reconnect..."); setTimeout(function () { socket(url, handlers, reconnect); }, retryInMs); } }); client.onMessage( /** * @param {any} data */ function (data) { var message = JSON.parse(data); if (handlers[message.type]) { handlers[message.type](message.data, message.params); } }); }; /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (socket); /***/ }), /***/ 7308: /*!*************************************************************************!*\ !*** ./node_modules/webpack-dev-server/client/utils/createSocketURL.js ***! \*************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /** * @param {{ protocol?: string, auth?: string, hostname?: string, port?: string, pathname?: string, search?: string, hash?: string, slashes?: boolean }} objURL * @returns {string} */ function format(objURL) { var protocol = objURL.protocol || ""; if (protocol && protocol.substr(-1) !== ":") { protocol += ":"; } var auth = objURL.auth || ""; if (auth) { auth = encodeURIComponent(auth); auth = auth.replace(/%3A/i, ":"); auth += "@"; } var host = ""; if (objURL.hostname) { host = auth + (objURL.hostname.indexOf(":") === -1 ? objURL.hostname : "[".concat(objURL.hostname, "]")); if (objURL.port) { host += ":".concat(objURL.port); } } var pathname = objURL.pathname || ""; if (objURL.slashes) { host = "//".concat(host || ""); if (pathname && pathname.charAt(0) !== "/") { pathname = "/".concat(pathname); } } else if (!host) { host = ""; } var search = objURL.search || ""; if (search && search.charAt(0) !== "?") { search = "?".concat(search); } var hash = objURL.hash || ""; if (hash && hash.charAt(0) !== "#") { hash = "#".concat(hash); } pathname = pathname.replace(/[?#]/g, /** * @param {string} match * @returns {string} */ function (match) { return encodeURIComponent(match); }); search = search.replace("#", "%23"); return "".concat(protocol).concat(host).concat(pathname).concat(search).concat(hash); } /** * @param {URL & { fromCurrentScript?: boolean }} parsedURL * @returns {string} */ function createSocketURL(parsedURL) { var hostname = parsedURL.hostname; // Node.js module parses it as `::` // `new URL(urlString, [baseURLString])` parses it as '[::]' var isInAddrAny = hostname === "0.0.0.0" || hostname === "::" || hostname === "[::]"; // why do we need this check? // hostname n/a for file protocol (example, when using electron, ionic) // see: https://github.com/webpack/webpack-dev-server/pull/384 if (isInAddrAny && self.location.hostname && self.location.protocol.indexOf("http") === 0) { hostname = self.location.hostname; } var socketURLProtocol = parsedURL.protocol || self.location.protocol; // When https is used in the app, secure web sockets are always necessary because the browser doesn't accept non-secure web sockets. if (socketURLProtocol === "auto:" || hostname && isInAddrAny && self.location.protocol === "https:") { socketURLProtocol = self.location.protocol; } socketURLProtocol = socketURLProtocol.replace(/^(?:http|.+-extension|file)/i, "ws"); var socketURLAuth = ""; // `new URL(urlString, [baseURLstring])` doesn't have `auth` property // Parse authentication credentials in case we need them if (parsedURL.username) { socketURLAuth = parsedURL.username; // Since HTTP basic authentication does not allow empty username, // we only include password if the username is not empty. if (parsedURL.password) { // Result: <username>:<password> socketURLAuth = socketURLAuth.concat(":", parsedURL.password); } } // In case the host is a raw IPv6 address, it can be enclosed in // the brackets as the brackets are needed in the final URL string. // Need to remove those as url.format blindly adds its own set of brackets // if the host string contains colons. That would lead to non-working // double brackets (e.g. [[::]]) host // // All of these web socket url params are optionally passed in through resourceQuery, // so we need to fall back to the default if they are not provided var socketURLHostname = (hostname || self.location.hostname || "localhost").replace(/^\[(.*)\]$/, "$1"); var socketURLPort = parsedURL.port; if (!socketURLPort || socketURLPort === "0") { socketURLPort = self.location.port; } // If path is provided it'll be passed in via the resourceQuery as a // query param so it has to be parsed out of the querystring in order for the // client to open the socket to the correct location. var socketURLPathname = "/ws"; if (parsedURL.pathname && !parsedURL.fromCurrentScript) { socketURLPathname = parsedURL.pathname; } return format({ protocol: socketURLProtocol, auth: socketURLAuth, hostname: socketURLHostname, port: socketURLPort, pathname: socketURLPathname, slashes: true }); } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (createSocketURL); /***/ }), /***/ 6671: /*!********************************************************************************!*\ !*** ./node_modules/webpack-dev-server/client/utils/getCurrentScriptSource.js ***! \********************************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /** * @returns {string} */ function getCurrentScriptSource() { // `document.currentScript` is the most accurate way to find the current script, // but is not supported in all browsers. if (document.currentScript) { return document.currentScript.getAttribute("src"); } // Fallback to getting all scripts running in the document. var scriptElements = document.scripts || []; var scriptElementsWithSrc = Array.prototype.filter.call(scriptElements, function (element) { return element.getAttribute("src"); }); if (scriptElementsWithSrc.length > 0) { var currentScript = scriptElementsWithSrc[scriptElementsWithSrc.length - 1]; return currentScript.getAttribute("src"); } // Fail as there was no script to use. throw new Error("[webpack-dev-server] Failed to get current script source."); } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (getCurrentScriptSource); /***/ }), /***/ 4664: /*!*************************************************************!*\ !*** ./node_modules/webpack-dev-server/client/utils/log.js ***! \*************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "log": () => (/* binding */ log), /* harmony export */ "logEnabledFeatures": () => (/* binding */ logEnabledFeatures), /* harmony export */ "setLogLevel": () => (/* binding */ setLogLevel) /* harmony export */ }); /* harmony import */ var _modules_logger_index_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../modules/logger/index.js */ 3431); /* harmony import */ var _modules_logger_index_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_modules_logger_index_js__WEBPACK_IMPORTED_MODULE_0__); var name = "webpack-dev-server"; // default level is set on the client side, so it does not need // to be set by the CLI or API var defaultLevel = "info"; // options new options, merge with old options /** * @param {false | true | "none" | "error" | "warn" | "info" | "log" | "verbose"} level * @returns {void} */ function setLogLevel(level) { _modules_logger_index_js__WEBPACK_IMPORTED_MODULE_0___default().configureDefaultLogger({ level: level }); } setLogLevel(defaultLevel); var log = _modules_logger_index_js__WEBPACK_IMPORTED_MODULE_0___default().getLogger(name); var logEnabledFeatures = function logEnabledFeatures(features) { var enabledFeatures = Object.keys(features); if (!features || enabledFeatures.length === 0) { return; } var logString = "Server started:"; // Server started: Hot Module Replacement enabled, Live Reloading enabled, Overlay disabled. for (var i = 0; i < enabledFeatures.length; i++) { var key = enabledFeatures[i]; logString += " ".concat(key, " ").concat(features[key] ? "enabled" : "disabled", ","); } // replace last comma with a period logString = logString.slice(0, -1).concat("."); log.info(logString); }; /***/ }), /***/ 8587: /*!******************************************************************!*\ !*** ./node_modules/webpack-dev-server/client/utils/parseURL.js ***! \******************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony import */ var _getCurrentScriptSource_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./getCurrentScriptSource.js */ 6671); /** * @param {string} resourceQuery * @returns {{ [key: string]: string | boolean }} */ function parseURL(resourceQuery) { /** @type {{ [key: string]: string }} */ var options = {}; if (typeof resourceQuery === "string" && resourceQuery !== "") { var searchParams = resourceQuery.slice(1).split("&"); for (var i = 0; i < searchParams.length; i++) { var pair = searchParams[i].split("="); options[pair[0]] = decodeURIComponent(pair[1]); } } else { // Else, get the url from the <script> this file was called with. var scriptSource = (0,_getCurrentScriptSource_js__WEBPACK_IMPORTED_MODULE_0__["default"])(); var scriptSourceURL; try { // The placeholder `baseURL` with `window.location.href`, // is to allow parsing of path-relative or protocol-relative URLs, // and will have no effect if `scriptSource` is a fully valid URL. scriptSourceURL = new URL(scriptSource, self.location.href); } catch (error) {// URL parsing failed, do nothing. // We will still proceed to see if we can recover using `resourceQuery` } if (scriptSourceURL) { options = scriptSourceURL; options.fromCurrentScript = true; } } return options; } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (parseURL); /***/ }), /***/ 4163: /*!*******************************************************************!*\ !*** ./node_modules/webpack-dev-server/client/utils/reloadApp.js ***! \*******************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* harmony import */ var webpack_hot_emitter_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! webpack/hot/emitter.js */ 6226); /* harmony import */ var webpack_hot_emitter_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(webpack_hot_emitter_js__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _log_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./log.js */ 4664); /** @typedef {import("../index").Options} Options /** @typedef {import("../index").Status} Status /** * @param {Options} options * @param {Status} status */ function reloadApp(_ref, status) { var hot = _ref.hot, liveReload = _ref.liveReload; if (status.isUnloading) { return; } var currentHash = status.currentHash, previousHash = status.previousHash; var isInitial = currentHash.indexOf( /** @type {string} */ previousHash) >= 0; if (isInitial) { return; } /** * @param {Window} rootWindow * @param {number} intervalId */ function applyReload(rootWindow, intervalId) { clearInterval(intervalId); _log_js__WEBPACK_IMPORTED_MODULE_1__.log.info("App updated. Reloading..."); rootWindow.location.reload(); } var search = self.location.search.toLowerCase(); var allowToHot = search.indexOf("webpack-dev-server-hot=false") === -1; var allowToLiveReload = search.indexOf("webpack-dev-server-live-reload=false") === -1; if (hot && allowToHot) { _log_js__WEBPACK_IMPORTED_MODULE_1__.log.info("App hot update..."); webpack_hot_emitter_js__WEBPACK_IMPORTED_MODULE_0___default().emit("webpackHotUpdate", status.currentHash); if (typeof self !== "undefined" && self.window) { // broadcast update to window self.postMessage("webpackHotUpdate".concat(status.currentHash), "*"); } } // allow refreshing the page only if liveReload isn't disabled else if (liveReload && allowToLiveReload) { var rootWindow = self; // use parent window for reload (in case we're in an iframe with no valid src) var intervalId = self.setInterval(function () { if (rootWindow.location.protocol !== "about:") { // reload immediately if protocol is valid applyReload(rootWindow, intervalId); } else { rootWindow = rootWindow.parent; if (rootWindow.parent === rootWindow) { // if parent equals current window we've reached the root which would continue forever, so trigger a reload anyways applyReload(rootWindow, intervalId); } } }); } } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (reloadApp); /***/ }), /***/ 430: /*!*********************************************************************!*\ !*** ./node_modules/webpack-dev-server/client/utils/sendMessage.js ***! \*********************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /* global __resourceQuery WorkerGlobalScope */ // Send messages to the outside, so plugins can consume it. /** * @param {string} type * @param {any} [data] */ function sendMsg(type, data) { if (typeof self !== "undefined" && (typeof WorkerGlobalScope === "undefined" || !(self instanceof WorkerGlobalScope))) { self.postMessage({ type: "webpack".concat(type), data: data }, "*"); } } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (sendMsg); /***/ }), /***/ 8931: /*!*******************************************************************!*\ !*** ./node_modules/webpack-dev-server/client/utils/stripAnsi.js ***! \*******************************************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); var ansiRegex = new RegExp(["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|"), "g"); /** * * Strip [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) from a string. * Adapted from code originally released by Sindre Sorhus * Licensed the MIT License * * @param {string} string * @return {string} */ function stripAnsi(string) { if (typeof string !== "string") { throw new TypeError("Expected a `string`, got `".concat(typeof string, "`")); } return string.replace(ansiRegex, ""); } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (stripAnsi); /***/ }), /***/ 6226: /*!*********************************************!*\ !*** ./node_modules/webpack/hot/emitter.js ***! \*********************************************/ /***/ ((module, __unused_webpack_exports, __webpack_require__) => { var EventEmitter = __webpack_require__(/*! events */ 3358); module.exports = new EventEmitter(); /***/ }), /***/ 1877: /*!*****************************************!*\ !*** ./node_modules/webpack/hot/log.js ***! \*****************************************/ /***/ ((module) => { var logLevel = "info"; function dummy() {} function shouldLog(level) { var shouldLog = logLevel === "info" && level === "info" || ["info", "warning"].indexOf(logLevel) >= 0 && level === "warning" || ["info", "warning", "error"].indexOf(logLevel) >= 0 && level === "error"; return shouldLog; } function logGroup(logFn) { return function (level, msg) { if (shouldLog(level)) { logFn(msg); } }; } module.exports = function (level, msg) { if (shouldLog(level)) { if (level === "info") { console.log(msg); } else if (level === "warning") { console.warn(msg); } else if (level === "error") { console.error(msg); } } }; /* eslint-disable node/no-unsupported-features/node-builtins */ var group = console.group || dummy; var groupCollapsed = console.groupCollapsed || dummy; var groupEnd = console.groupEnd || dummy; /* eslint-enable node/no-unsupported-features/node-builtins */ module.exports.group = logGroup(group); module.exports.groupCollapsed = logGroup(groupCollapsed); module.exports.groupEnd = logGroup(groupEnd); module.exports.setLogLevel = function (level) { logLevel = level; }; module.exports.formatError = function (err) { var message = err.message; var stack = err.stack; if (!stack) { return message; } else if (stack.indexOf(message) < 0) { return message + "\n" + stack; } else { return stack; } }; /***/ }), /***/ 4666: /*!**********************************************************!*\ !*** ./node_modules/@angular/common/fesm2020/common.mjs ***! \**********************************************************/ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "APP_BASE_HREF": () => (/* binding */ APP_BASE_HREF), /* harmony export */ "AsyncPipe": () => (/* binding */ AsyncPipe), /* harmony export */ "BrowserPlatformLocation": () => (/* binding */ BrowserPlatformLocation), /* harmony export */ "CommonModule": () => (/* binding */ CommonModule), /* harmony export */ "CurrencyPipe": () => (/* binding */ CurrencyPipe), /* harmony export */ "DATE_PIPE_DEFAULT_OPTIONS": () => (/* binding */ DATE_PIPE_DEFAULT_OPTIONS), /* harmony export */ "DATE_PIPE_DEFAULT_TIMEZONE": () => (/* binding */ DATE_PIPE_DEFAULT_TIMEZONE), /* harmony export */ "DOCUMENT": () => (/* binding */ DOCUMENT), /* harmony export */ "DatePipe": () => (/* binding */ DatePipe), /* harmony export */ "DecimalPipe": () => (/* binding */ DecimalPipe), /* harmony export */ "FormStyle": () => (/* binding */ FormStyle), /* harmony export */ "FormatWidth": () => (/* binding */ FormatWidth), /* harmony export */ "HashLocationStrategy": () => (/* binding */ HashLocationStrategy), /* harmony export */ "I18nPluralPipe": () => (/* binding */ I18nPluralPipe), /* harmony export */ "I18nSelectPipe": () => (/* binding */ I18nSelectPipe), /* harmony export */ "IMAGE_CONFIG": () => (/* binding */ IMAGE_CONFIG), /* harmony export */ "IMAGE_LOADER": () => (/* binding */ IMAGE_LOADER), /* harmony export */ "JsonPipe": () => (/* binding */ JsonPipe), /* harmony export */ "KeyValuePipe": () => (/* binding */ KeyValuePipe), /* harmony export */ "LOCATION_INITIALIZED": () => (/* binding */ LOCATION_INITIALIZED), /* harmony export */ "Location": () => (/* binding */ Location), /* harmony export */ "LocationStrategy": () => (/* binding */ LocationStrategy), /* harmony export */ "LowerCasePipe": () => (/* binding */ LowerCasePipe), /* harmony export */ "NgClass": () => (/* binding */ NgClass), /* harmony export */ "NgComponentOutlet": () => (/* binding */ NgComponentOutlet), /* harmony export */ "NgFor": () => (/* binding */ NgForOf), /* harmony export */ "NgForOf": () => (/* binding */ NgForOf), /* harmony export */ "NgForOfContext": () => (/* binding */ NgForOfContext), /* harmony export */ "NgIf": () => (/* binding */ NgIf), /* harmony export */ "NgIfContext": () => (/* binding */ NgIfContext), /* harmony export */ "NgLocaleLocalization": () => (/* binding */ NgLocaleLocalization), /* harmony export */ "NgLocalization": () => (/* binding */ NgLocalization), /* harmony export */ "NgOptimizedImage": () => (/* binding */ NgOptimizedImage), /* harmony export */ "NgPlural": () => (/* binding */ NgPlural), /* harmony export */ "NgPluralCase": () => (/* binding */ NgPluralCase), /* harmony export */ "NgStyle": () => (/* binding */ NgStyle), /* harmony export */ "NgSwitch": () => (/* binding */ NgSwitch), /* harmony export */ "NgSwitchCase": () => (/* binding */ NgSwitchCase), /* harmony export */ "NgSwitchDefault": () => (/* binding */ NgSwitchDefault), /* harmony export */ "NgTemplateOutlet": () => (/* binding */ NgTemplateOutlet), /* harmony export */ "NumberFormatStyle": () => (/* binding */ NumberFormatStyle), /* harmony export */ "NumberSymbol": () => (/* binding */ NumberSymbol), /* harmony export */ "PRECONNECT_CHECK_BLOCKLIST": () => (/* binding */ PRECONNECT_CHECK_BLOCKLIST), /* harmony export */ "PathLocationStrategy": () => (/* binding */ PathLocationStrategy), /* harmony export */ "PercentPipe": () => (/* binding */ PercentPipe), /* harmony export */ "PlatformLocation": () => (/* binding */ PlatformLocation), /* harmony export */ "Plural": () => (/* binding */ Plural), /* harmony export */ "SlicePipe": () => (/* binding */ SlicePipe), /* harmony export */ "TitleCasePipe": () => (/* binding */ TitleCasePipe), /* harmony export */ "TranslationWidth": () => (/* binding */ TranslationWidth), /* harmony export */ "UpperCasePipe": () => (/* binding */ UpperCasePipe), /* harmony export */ "VERSION": () => (/* binding */ VERSION), /* harmony export */ "ViewportScroller": () => (/* binding */ ViewportScroller), /* harmony export */ "WeekDay": () => (/* binding */ WeekDay), /* harmony export */ "XhrFactory": () => (/* binding */ XhrFactory), /* harmony export */ "formatCurrency": () => (/* binding */ formatCurrency), /* harmony export */ "formatDate": () => (/* binding */ formatDate), /* harmony export */ "formatNumber": () => (/* binding */ formatNumber), /* harmony export */ "formatPercent": () => (/* binding */ formatPercent), /* harmony export */ "getCurrencySymbol": () => (/* binding */ getCurrencySymbol), /* harmony export */ "getLocaleCurrencyCode": () => (/* binding */ getLocaleCurrencyCode), /* harmony export */ "getLocaleCurrencyName": () => (/* binding */ getLocaleCurrencyName), /* harmony export */ "getLocaleCurrencySymbol": () => (/* binding */ getLocaleCurrencySymbol), /* harmony export */ "getLocaleDateFormat": () => (/* binding */ getLocaleDateFormat), /* harmony export */ "getLocaleDateTimeFormat": () => (/* binding */ getLocaleDateTimeFormat), /* harmony export */ "getLocaleDayNames": () => (/* binding */ getLocaleDayNames), /* harmony export */ "getLocaleDayPeriods": () => (/* binding */ getLocaleDayPeriods), /* harmony export */ "getLocaleDirection": () => (/* binding */ getLocaleDirection), /* harmony export */ "getLocaleEraNames": () => (/* binding */ getLocaleEraNames), /* harmony export */ "getLocaleExtraDayPeriodRules": () => (/* binding */ getLocaleExtraDayPeriodRules), /* harmony export */ "getLocaleExtraDayPeriods": () => (/* binding */ getLocaleExtraDayPeriods), /* harmony export */ "getLocaleFirstDayOfWeek": () => (/* binding */ getLocaleFirstDayOfWeek), /* harmony export */ "getLocaleId": () => (/* binding */ getLocaleId), /* harmony export */ "getLocaleMonthNames": () => (/* binding */ getLocaleMonthNames), /* harmony export */ "getLocaleNumberFormat": () => (/* binding */ getLocaleNumberFormat), /* harmony export */ "getLocaleNumberSymbol": () => (/* binding */ getLocaleNumberSymbol), /* harmony export */ "getLocalePluralCase": () => (/* binding */ getLocalePluralCase), /* harmony export */ "getLocaleTimeFormat": () => (/* binding */ getLocaleTimeFormat), /* harmony export */ "getLocaleWeekEndRange": () => (/* binding */ getLocaleWeekEndRange), /* harmony export */ "getNumberOfCurrencyDigits": () => (/* binding */ getNumberOfCurrencyDigits), /* harmony export */ "isPlatformBrowser": () => (/* binding */ isPlatformBrowser), /* harmony export */ "isPlatformServer": () => (/* binding */ isPlatformServer), /* harmony export */ "isPlatformWorkerApp": () => (/* binding */ isPlatformWorkerApp), /* harmony export */ "isPlatformWorkerUi": () => (/* binding */ isPlatformWorkerUi), /* harmony export */ "provideCloudflareLoader": () => (/* binding */ provideCloudflareLoader), /* harmony export */ "provideCloudinaryLoader": () => (/* binding */ provideCloudinaryLoader), /* harmony export */ "provideImageKitLoader": () => (/* binding */ provideImageKitLoader), /* harmony export */ "provideImgixLoader": () => (/* binding */ provideImgixLoader), /* harmony export */ "registerLocaleData": () => (/* binding */ registerLocaleData), /* harmony export */ "ɵDomAdapter": () => (/* binding */ DomAdapter), /* harmony export */ "ɵNullViewportScroller": () => (/* binding */ NullViewportScroller), /* harmony export */ "ɵPLATFORM_BROWSER_ID": () => (/* binding */ PLATFORM_BROWSER_ID), /* harmony export */ "ɵPLATFORM_SERVER_ID": () => (/* binding */ PLATFORM_SERVER_ID), /* harmony export */ "ɵPLATFORM_WORKER_APP_ID": () => (/* binding */ PLATFORM_WORKER_APP_ID), /* harmony export */ "ɵPLATFORM_WORKER_UI_ID": () => (/* binding */ PLATFORM_WORKER_UI_ID), /* harmony export */ "ɵgetDOM": () => (/* binding */ getDOM), /* harmony export */ "ɵparseCookieValue": () => (/* binding */ parseCookieValue), /* harmony export */ "ɵsetRootDomAdapter": () => (/* binding */ setRootDomAdapter) /* harmony export */ }); /* harmony import */ var _angular_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @angular/core */ 2560); /** * @license Angular v15.2.9 * (c) 2010-2022 Google LLC. https://angular.io/ * License: MIT */ let _DOM = null; function getDOM() { return _DOM; } function setRootDomAdapter(adapter) { if (!_DOM) { _DOM = adapter; } } /* tslint:disable:requireParameterType */ /** * Provides DOM operations in an environment-agnostic way. * * @security Tread carefully! Interacting with the DOM directly is dangerous and * can introduce XSS risks. */ class DomAdapter {} /** * A DI Token representing the main rendering context. * In a browser and SSR this is the DOM Document. * When using SSR, that document is created by [Domino](https://github.com/angular/domino). * * @publicApi */ const DOCUMENT = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('DocumentToken'); /** * This class should not be used directly by an application developer. Instead, use * {@link Location}. * * `PlatformLocation` encapsulates all calls to DOM APIs, which allows the Router to be * platform-agnostic. * This means that we can have different implementation of `PlatformLocation` for the different * platforms that Angular supports. For example, `@angular/platform-browser` provides an * implementation specific to the browser environment, while `@angular/platform-server` provides * one suitable for use with server-side rendering. * * The `PlatformLocation` class is used directly by all implementations of {@link LocationStrategy} * when they need to interact with the DOM APIs like pushState, popState, etc. * * {@link LocationStrategy} in turn is used by the {@link Location} service which is used directly * by the {@link Router} in order to navigate between routes. Since all interactions between {@link * Router} / * {@link Location} / {@link LocationStrategy} and DOM APIs flow through the `PlatformLocation` * class, they are all platform-agnostic. * * @publicApi */ class PlatformLocation { historyGo(relativePosition) { throw new Error('Not implemented'); } } PlatformLocation.ɵfac = function PlatformLocation_Factory(t) { return new (t || PlatformLocation)(); }; PlatformLocation.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: PlatformLocation, factory: function () { return useBrowserPlatformLocation(); }, providedIn: 'platform' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](PlatformLocation, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'platform', // See #23917 useFactory: useBrowserPlatformLocation }] }], null, null); })(); function useBrowserPlatformLocation() { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"])(BrowserPlatformLocation); } /** * @description * Indicates when a location is initialized. * * @publicApi */ const LOCATION_INITIALIZED = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('Location Initialized'); /** * `PlatformLocation` encapsulates all of the direct calls to platform APIs. * This class should not be used directly by an application developer. Instead, use * {@link Location}. * * @publicApi */ class BrowserPlatformLocation extends PlatformLocation { constructor(_doc) { super(); this._doc = _doc; this._location = window.location; this._history = window.history; } getBaseHrefFromDOM() { return getDOM().getBaseHref(this._doc); } onPopState(fn) { const window = getDOM().getGlobalEventTarget(this._doc, 'window'); window.addEventListener('popstate', fn, false); return () => window.removeEventListener('popstate', fn); } onHashChange(fn) { const window = getDOM().getGlobalEventTarget(this._doc, 'window'); window.addEventListener('hashchange', fn, false); return () => window.removeEventListener('hashchange', fn); } get href() { return this._location.href; } get protocol() { return this._location.protocol; } get hostname() { return this._location.hostname; } get port() { return this._location.port; } get pathname() { return this._location.pathname; } get search() { return this._location.search; } get hash() { return this._location.hash; } set pathname(newPath) { this._location.pathname = newPath; } pushState(state, title, url) { if (supportsState()) { this._history.pushState(state, title, url); } else { this._location.hash = url; } } replaceState(state, title, url) { if (supportsState()) { this._history.replaceState(state, title, url); } else { this._location.hash = url; } } forward() { this._history.forward(); } back() { this._history.back(); } historyGo(relativePosition = 0) { this._history.go(relativePosition); } getState() { return this._history.state; } } BrowserPlatformLocation.ɵfac = function BrowserPlatformLocation_Factory(t) { return new (t || BrowserPlatformLocation)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](DOCUMENT)); }; BrowserPlatformLocation.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: BrowserPlatformLocation, factory: function () { return createBrowserPlatformLocation(); }, providedIn: 'platform' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](BrowserPlatformLocation, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'platform', // See #23917 useFactory: createBrowserPlatformLocation }] }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [DOCUMENT] }] }]; }, null); })(); function supportsState() { return !!window.history.pushState; } function createBrowserPlatformLocation() { return new BrowserPlatformLocation((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"])(DOCUMENT)); } /** * Joins two parts of a URL with a slash if needed. * * @param start URL string * @param end URL string * * * @returns The joined URL string. */ function joinWithSlash(start, end) { if (start.length == 0) { return end; } if (end.length == 0) { return start; } let slashes = 0; if (start.endsWith('/')) { slashes++; } if (end.startsWith('/')) { slashes++; } if (slashes == 2) { return start + end.substring(1); } if (slashes == 1) { return start + end; } return start + '/' + end; } /** * Removes a trailing slash from a URL string if needed. * Looks for the first occurrence of either `#`, `?`, or the end of the * line as `/` characters and removes the trailing slash if one exists. * * @param url URL string. * * @returns The URL string, modified if needed. */ function stripTrailingSlash(url) { const match = url.match(/#|\?|$/); const pathEndIdx = match && match.index || url.length; const droppedSlashIdx = pathEndIdx - (url[pathEndIdx - 1] === '/' ? 1 : 0); return url.slice(0, droppedSlashIdx) + url.slice(pathEndIdx); } /** * Normalizes URL parameters by prepending with `?` if needed. * * @param params String of URL parameters. * * @returns The normalized URL parameters string. */ function normalizeQueryParams(params) { return params && params[0] !== '?' ? '?' + params : params; } /** * Enables the `Location` service to read route state from the browser's URL. * Angular provides two strategies: * `HashLocationStrategy` and `PathLocationStrategy`. * * Applications should use the `Router` or `Location` services to * interact with application route state. * * For instance, `HashLocationStrategy` produces URLs like * <code class="no-auto-link">http://example.com#/foo</code>, * and `PathLocationStrategy` produces * <code class="no-auto-link">http://example.com/foo</code> as an equivalent URL. * * See these two classes for more. * * @publicApi */ class LocationStrategy { historyGo(relativePosition) { throw new Error('Not implemented'); } } LocationStrategy.ɵfac = function LocationStrategy_Factory(t) { return new (t || LocationStrategy)(); }; LocationStrategy.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: LocationStrategy, factory: function () { return (() => (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(PathLocationStrategy))(); }, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](LocationStrategy, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root', useFactory: () => (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(PathLocationStrategy) }] }], null, null); })(); /** * A predefined [DI token](guide/glossary#di-token) for the base href * to be used with the `PathLocationStrategy`. * The base href is the URL prefix that should be preserved when generating * and recognizing URLs. * * @usageNotes * * The following example shows how to use this token to configure the root app injector * with a base href value, so that the DI framework can supply the dependency anywhere in the app. * * ```typescript * import {Component, NgModule} from '@angular/core'; * import {APP_BASE_HREF} from '@angular/common'; * * @NgModule({ * providers: [{provide: APP_BASE_HREF, useValue: '/my/app'}] * }) * class AppModule {} * ``` * * @publicApi */ const APP_BASE_HREF = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('appBaseHref'); /** * @description * A {@link LocationStrategy} used to configure the {@link Location} service to * represent its state in the * [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) of the * browser's URL. * * If you're using `PathLocationStrategy`, you may provide a {@link APP_BASE_HREF} * or add a `<base href>` element to the document to override the default. * * For instance, if you provide an `APP_BASE_HREF` of `'/my/app/'` and call * `location.go('/foo')`, the browser's URL will become * `example.com/my/app/foo`. To ensure all relative URIs resolve correctly, * the `<base href>` and/or `APP_BASE_HREF` should end with a `/`. * * Similarly, if you add `<base href='/my/app/'/>` to the document and call * `location.go('/foo')`, the browser's URL will become * `example.com/my/app/foo`. * * Note that when using `PathLocationStrategy`, neither the query nor * the fragment in the `<base href>` will be preserved, as outlined * by the [RFC](https://tools.ietf.org/html/rfc3986#section-5.2.2). * * @usageNotes * * ### Example * * {@example common/location/ts/path_location_component.ts region='LocationComponent'} * * @publicApi */ class PathLocationStrategy extends LocationStrategy { constructor(_platformLocation, href) { super(); this._platformLocation = _platformLocation; this._removeListenerFns = []; this._baseHref = href ?? this._platformLocation.getBaseHrefFromDOM() ?? (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(DOCUMENT).location?.origin ?? ''; } /** @nodoc */ ngOnDestroy() { while (this._removeListenerFns.length) { this._removeListenerFns.pop()(); } } onPopState(fn) { this._removeListenerFns.push(this._platformLocation.onPopState(fn), this._platformLocation.onHashChange(fn)); } getBaseHref() { return this._baseHref; } prepareExternalUrl(internal) { return joinWithSlash(this._baseHref, internal); } path(includeHash = false) { const pathname = this._platformLocation.pathname + normalizeQueryParams(this._platformLocation.search); const hash = this._platformLocation.hash; return hash && includeHash ? `${pathname}${hash}` : pathname; } pushState(state, title, url, queryParams) { const externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams)); this._platformLocation.pushState(state, title, externalUrl); } replaceState(state, title, url, queryParams) { const externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams)); this._platformLocation.replaceState(state, title, externalUrl); } forward() { this._platformLocation.forward(); } back() { this._platformLocation.back(); } getState() { return this._platformLocation.getState(); } historyGo(relativePosition = 0) { this._platformLocation.historyGo?.(relativePosition); } } PathLocationStrategy.ɵfac = function PathLocationStrategy_Factory(t) { return new (t || PathLocationStrategy)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](PlatformLocation), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](APP_BASE_HREF, 8)); }; PathLocationStrategy.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: PathLocationStrategy, factory: PathLocationStrategy.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](PathLocationStrategy, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], function () { return [{ type: PlatformLocation }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [APP_BASE_HREF] }] }]; }, null); })(); /** * @description * A {@link LocationStrategy} used to configure the {@link Location} service to * represent its state in the * [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) * of the browser's URL. * * For instance, if you call `location.go('/foo')`, the browser's URL will become * `example.com#/foo`. * * @usageNotes * * ### Example * * {@example common/location/ts/hash_location_component.ts region='LocationComponent'} * * @publicApi */ class HashLocationStrategy extends LocationStrategy { constructor(_platformLocation, _baseHref) { super(); this._platformLocation = _platformLocation; this._baseHref = ''; this._removeListenerFns = []; if (_baseHref != null) { this._baseHref = _baseHref; } } /** @nodoc */ ngOnDestroy() { while (this._removeListenerFns.length) { this._removeListenerFns.pop()(); } } onPopState(fn) { this._removeListenerFns.push(this._platformLocation.onPopState(fn), this._platformLocation.onHashChange(fn)); } getBaseHref() { return this._baseHref; } path(includeHash = false) { // the hash value is always prefixed with a `#` // and if it is empty then it will stay empty let path = this._platformLocation.hash; if (path == null) path = '#'; return path.length > 0 ? path.substring(1) : path; } prepareExternalUrl(internal) { const url = joinWithSlash(this._baseHref, internal); return url.length > 0 ? '#' + url : url; } pushState(state, title, path, queryParams) { let url = this.prepareExternalUrl(path + normalizeQueryParams(queryParams)); if (url.length == 0) { url = this._platformLocation.pathname; } this._platformLocation.pushState(state, title, url); } replaceState(state, title, path, queryParams) { let url = this.prepareExternalUrl(path + normalizeQueryParams(queryParams)); if (url.length == 0) { url = this._platformLocation.pathname; } this._platformLocation.replaceState(state, title, url); } forward() { this._platformLocation.forward(); } back() { this._platformLocation.back(); } getState() { return this._platformLocation.getState(); } historyGo(relativePosition = 0) { this._platformLocation.historyGo?.(relativePosition); } } HashLocationStrategy.ɵfac = function HashLocationStrategy_Factory(t) { return new (t || HashLocationStrategy)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](PlatformLocation), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](APP_BASE_HREF, 8)); }; HashLocationStrategy.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: HashLocationStrategy, factory: HashLocationStrategy.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](HashLocationStrategy, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable }], function () { return [{ type: PlatformLocation }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [APP_BASE_HREF] }] }]; }, null); })(); /** * @description * * A service that applications can use to interact with a browser's URL. * * Depending on the `LocationStrategy` used, `Location` persists * to the URL's path or the URL's hash segment. * * @usageNotes * * It's better to use the `Router.navigate()` service to trigger route changes. Use * `Location` only if you need to interact with or create normalized URLs outside of * routing. * * `Location` is responsible for normalizing the URL against the application's base href. * A normalized URL is absolute from the URL host, includes the application's base href, and has no * trailing slash: * - `/my/app/user/123` is normalized * - `my/app/user/123` **is not** normalized * - `/my/app/user/123/` **is not** normalized * * ### Example * * <code-example path='common/location/ts/path_location_component.ts' * region='LocationComponent'></code-example> * * @publicApi */ class Location { constructor(locationStrategy) { /** @internal */ this._subject = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.EventEmitter(); /** @internal */ this._urlChangeListeners = []; /** @internal */ this._urlChangeSubscription = null; this._locationStrategy = locationStrategy; const baseHref = this._locationStrategy.getBaseHref(); // Note: This class's interaction with base HREF does not fully follow the rules // outlined in the spec https://www.freesoft.org/CIE/RFC/1808/18.htm. // Instead of trying to fix individual bugs with more and more code, we should // investigate using the URL constructor and providing the base as a second // argument. // https://developer.mozilla.org/en-US/docs/Web/API/URL/URL#parameters this._basePath = _stripOrigin(stripTrailingSlash(_stripIndexHtml(baseHref))); this._locationStrategy.onPopState(ev => { this._subject.emit({ 'url': this.path(true), 'pop': true, 'state': ev.state, 'type': ev.type }); }); } /** @nodoc */ ngOnDestroy() { this._urlChangeSubscription?.unsubscribe(); this._urlChangeListeners = []; } /** * Normalizes the URL path for this location. * * @param includeHash True to include an anchor fragment in the path. * * @returns The normalized URL path. */ // TODO: vsavkin. Remove the boolean flag and always include hash once the deprecated router is // removed. path(includeHash = false) { return this.normalize(this._locationStrategy.path(includeHash)); } /** * Reports the current state of the location history. * @returns The current value of the `history.state` object. */ getState() { return this._locationStrategy.getState(); } /** * Normalizes the given path and compares to the current normalized path. * * @param path The given URL path. * @param query Query parameters. * * @returns True if the given URL path is equal to the current normalized path, false * otherwise. */ isCurrentPathEqualTo(path, query = '') { return this.path() == this.normalize(path + normalizeQueryParams(query)); } /** * Normalizes a URL path by stripping any trailing slashes. * * @param url String representing a URL. * * @returns The normalized URL string. */ normalize(url) { return Location.stripTrailingSlash(_stripBasePath(this._basePath, _stripIndexHtml(url))); } /** * Normalizes an external URL path. * If the given URL doesn't begin with a leading slash (`'/'`), adds one * before normalizing. Adds a hash if `HashLocationStrategy` is * in use, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use. * * @param url String representing a URL. * * @returns A normalized platform-specific URL. */ prepareExternalUrl(url) { if (url && url[0] !== '/') { url = '/' + url; } return this._locationStrategy.prepareExternalUrl(url); } // TODO: rename this method to pushState /** * Changes the browser's URL to a normalized version of a given URL, and pushes a * new item onto the platform's history. * * @param path URL path to normalize. * @param query Query parameters. * @param state Location history state. * */ go(path, query = '', state = null) { this._locationStrategy.pushState(state, '', path, query); this._notifyUrlChangeListeners(this.prepareExternalUrl(path + normalizeQueryParams(query)), state); } /** * Changes the browser's URL to a normalized version of the given URL, and replaces * the top item on the platform's history stack. * * @param path URL path to normalize. * @param query Query parameters. * @param state Location history state. */ replaceState(path, query = '', state = null) { this._locationStrategy.replaceState(state, '', path, query); this._notifyUrlChangeListeners(this.prepareExternalUrl(path + normalizeQueryParams(query)), state); } /** * Navigates forward in the platform's history. */ forward() { this._locationStrategy.forward(); } /** * Navigates back in the platform's history. */ back() { this._locationStrategy.back(); } /** * Navigate to a specific page from session history, identified by its relative position to the * current page. * * @param relativePosition Position of the target page in the history relative to the current * page. * A negative value moves backwards, a positive value moves forwards, e.g. `location.historyGo(2)` * moves forward two pages and `location.historyGo(-2)` moves back two pages. When we try to go * beyond what's stored in the history session, we stay in the current page. Same behaviour occurs * when `relativePosition` equals 0. * @see https://developer.mozilla.org/en-US/docs/Web/API/History_API#Moving_to_a_specific_point_in_history */ historyGo(relativePosition = 0) { this._locationStrategy.historyGo?.(relativePosition); } /** * Registers a URL change listener. Use to catch updates performed by the Angular * framework that are not detectible through "popstate" or "hashchange" events. * * @param fn The change handler function, which take a URL and a location history state. * @returns A function that, when executed, unregisters a URL change listener. */ onUrlChange(fn) { this._urlChangeListeners.push(fn); if (!this._urlChangeSubscription) { this._urlChangeSubscription = this.subscribe(v => { this._notifyUrlChangeListeners(v.url, v.state); }); } return () => { const fnIndex = this._urlChangeListeners.indexOf(fn); this._urlChangeListeners.splice(fnIndex, 1); if (this._urlChangeListeners.length === 0) { this._urlChangeSubscription?.unsubscribe(); this._urlChangeSubscription = null; } }; } /** @internal */ _notifyUrlChangeListeners(url = '', state) { this._urlChangeListeners.forEach(fn => fn(url, state)); } /** * Subscribes to the platform's `popState` events. * * Note: `Location.go()` does not trigger the `popState` event in the browser. Use * `Location.onUrlChange()` to subscribe to URL changes instead. * * @param value Event that is triggered when the state history changes. * @param exception The exception to throw. * * @see [onpopstate](https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate) * * @returns Subscribed events. */ subscribe(onNext, onThrow, onReturn) { return this._subject.subscribe({ next: onNext, error: onThrow, complete: onReturn }); } } /** * Normalizes URL parameters by prepending with `?` if needed. * * @param params String of URL parameters. * * @returns The normalized URL parameters string. */ Location.normalizeQueryParams = normalizeQueryParams; /** * Joins two parts of a URL with a slash if needed. * * @param start URL string * @param end URL string * * * @returns The joined URL string. */ Location.joinWithSlash = joinWithSlash; /** * Removes a trailing slash from a URL string if needed. * Looks for the first occurrence of either `#`, `?`, or the end of the * line as `/` characters and removes the trailing slash if one exists. * * @param url URL string. * * @returns The URL string, modified if needed. */ Location.stripTrailingSlash = stripTrailingSlash; Location.ɵfac = function Location_Factory(t) { return new (t || Location)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](LocationStrategy)); }; Location.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: Location, factory: function () { return createLocation(); }, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](Location, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root', // See #23917 useFactory: createLocation }] }], function () { return [{ type: LocationStrategy }]; }, null); })(); function createLocation() { return new Location((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"])(LocationStrategy)); } function _stripBasePath(basePath, url) { if (!basePath || !url.startsWith(basePath)) { return url; } const strippedUrl = url.substring(basePath.length); if (strippedUrl === '' || ['/', ';', '?', '#'].includes(strippedUrl[0])) { return strippedUrl; } return url; } function _stripIndexHtml(url) { return url.replace(/\/index.html$/, ''); } function _stripOrigin(baseHref) { // DO NOT REFACTOR! Previously, this check looked like this: // `/^(https?:)?\/\//.test(baseHref)`, but that resulted in // syntactically incorrect code after Closure Compiler minification. // This was likely caused by a bug in Closure Compiler, but // for now, the check is rewritten to use `new RegExp` instead. const isAbsoluteUrl = new RegExp('^(https?:)?//').test(baseHref); if (isAbsoluteUrl) { const [, pathname] = baseHref.split(/\/\/[^\/]+/); return pathname; } return baseHref; } /** @internal */ const CURRENCIES_EN = { "ADP": [undefined, undefined, 0], "AFN": [undefined, "؋", 0], "ALL": [undefined, undefined, 0], "AMD": [undefined, "֏", 2], "AOA": [undefined, "Kz"], "ARS": [undefined, "$"], "AUD": ["A$", "$"], "AZN": [undefined, "₼"], "BAM": [undefined, "KM"], "BBD": [undefined, "$"], "BDT": [undefined, "৳"], "BHD": [undefined, undefined, 3], "BIF": [undefined, undefined, 0], "BMD": [undefined, "$"], "BND": [undefined, "$"], "BOB": [undefined, "Bs"], "BRL": ["R$"], "BSD": [undefined, "$"], "BWP": [undefined, "P"], "BYN": [undefined, undefined, 2], "BYR": [undefined, undefined, 0], "BZD": [undefined, "$"], "CAD": ["CA$", "$", 2], "CHF": [undefined, undefined, 2], "CLF": [undefined, undefined, 4], "CLP": [undefined, "$", 0], "CNY": ["CN¥", "¥"], "COP": [undefined, "$", 2], "CRC": [undefined, "₡", 2], "CUC": [undefined, "$"], "CUP": [undefined, "$"], "CZK": [undefined, "Kč", 2], "DJF": [undefined, undefined, 0], "DKK": [undefined, "kr", 2], "DOP": [undefined, "$"], "EGP": [undefined, "E£"], "ESP": [undefined, "₧", 0], "EUR": ["€"], "FJD": [undefined, "$"], "FKP": [undefined, "£"], "GBP": ["£"], "GEL": [undefined, "₾"], "GHS": [undefined, "GH₵"], "GIP": [undefined, "£"], "GNF": [undefined, "FG", 0], "GTQ": [undefined, "Q"], "GYD": [undefined, "$", 2], "HKD": ["HK$", "$"], "HNL": [undefined, "L"], "HRK": [undefined, "kn"], "HUF": [undefined, "Ft", 2], "IDR": [undefined, "Rp", 2], "ILS": ["₪"], "INR": ["₹"], "IQD": [undefined, undefined, 0], "IRR": [undefined, undefined, 0], "ISK": [undefined, "kr", 0], "ITL": [undefined, undefined, 0], "JMD": [undefined, "$"], "JOD": [undefined, undefined, 3], "JPY": ["¥", undefined, 0], "KHR": [undefined, "៛"], "KMF": [undefined, "CF", 0], "KPW": [undefined, "₩", 0], "KRW": ["₩", undefined, 0], "KWD": [undefined, undefined, 3], "KYD": [undefined, "$"], "KZT": [undefined, "₸"], "LAK": [undefined, "₭", 0], "LBP": [undefined, "L£", 0], "LKR": [undefined, "Rs"], "LRD": [undefined, "$"], "LTL": [undefined, "Lt"], "LUF": [undefined, undefined, 0], "LVL": [undefined, "Ls"], "LYD": [undefined, undefined, 3], "MGA": [undefined, "Ar", 0], "MGF": [undefined, undefined, 0], "MMK": [undefined, "K", 0], "MNT": [undefined, "₮", 2], "MRO": [undefined, undefined, 0], "MUR": [undefined, "Rs", 2], "MXN": ["MX$", "$"], "MYR": [undefined, "RM"], "NAD": [undefined, "$"], "NGN": [undefined, "₦"], "NIO": [undefined, "C$"], "NOK": [undefined, "kr", 2], "NPR": [undefined, "Rs"], "NZD": ["NZ$", "$"], "OMR": [undefined, undefined, 3], "PHP": ["₱"], "PKR": [undefined, "Rs", 2], "PLN": [undefined, "zł"], "PYG": [undefined, "₲", 0], "RON": [undefined, "lei"], "RSD": [undefined, undefined, 0], "RUB": [undefined, "₽"], "RWF": [undefined, "RF", 0], "SBD": [undefined, "$"], "SEK": [undefined, "kr", 2], "SGD": [undefined, "$"], "SHP": [undefined, "£"], "SLE": [undefined, undefined, 2], "SLL": [undefined, undefined, 0], "SOS": [undefined, undefined, 0], "SRD": [undefined, "$"], "SSP": [undefined, "£"], "STD": [undefined, undefined, 0], "STN": [undefined, "Db"], "SYP": [undefined, "£", 0], "THB": [undefined, "฿"], "TMM": [undefined, undefined, 0], "TND": [undefined, undefined, 3], "TOP": [undefined, "T$"], "TRL": [undefined, undefined, 0], "TRY": [undefined, "₺"], "TTD": [undefined, "$"], "TWD": ["NT$", "$", 2], "TZS": [undefined, undefined, 2], "UAH": [undefined, "₴"], "UGX": [undefined, undefined, 0], "USD": ["$"], "UYI": [undefined, undefined, 0], "UYU": [undefined, "$"], "UYW": [undefined, undefined, 4], "UZS": [undefined, undefined, 2], "VEF": [undefined, "Bs", 2], "VND": ["₫", undefined, 0], "VUV": [undefined, undefined, 0], "XAF": ["FCFA", undefined, 0], "XCD": ["EC$", "$"], "XOF": ["F CFA", undefined, 0], "XPF": ["CFPF", undefined, 0], "XXX": ["¤"], "YER": [undefined, undefined, 0], "ZAR": [undefined, "R"], "ZMK": [undefined, undefined, 0], "ZMW": [undefined, "ZK"], "ZWD": [undefined, undefined, 0] }; /** * Format styles that can be used to represent numbers. * @see `getLocaleNumberFormat()`. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ var NumberFormatStyle; (function (NumberFormatStyle) { NumberFormatStyle[NumberFormatStyle["Decimal"] = 0] = "Decimal"; NumberFormatStyle[NumberFormatStyle["Percent"] = 1] = "Percent"; NumberFormatStyle[NumberFormatStyle["Currency"] = 2] = "Currency"; NumberFormatStyle[NumberFormatStyle["Scientific"] = 3] = "Scientific"; })(NumberFormatStyle || (NumberFormatStyle = {})); /** * Plurality cases used for translating plurals to different languages. * * @see `NgPlural` * @see `NgPluralCase` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ var Plural; (function (Plural) { Plural[Plural["Zero"] = 0] = "Zero"; Plural[Plural["One"] = 1] = "One"; Plural[Plural["Two"] = 2] = "Two"; Plural[Plural["Few"] = 3] = "Few"; Plural[Plural["Many"] = 4] = "Many"; Plural[Plural["Other"] = 5] = "Other"; })(Plural || (Plural = {})); /** * Context-dependant translation forms for strings. * Typically the standalone version is for the nominative form of the word, * and the format version is used for the genitive case. * @see [CLDR website](http://cldr.unicode.org/translation/date-time-1/date-time#TOC-Standalone-vs.-Format-Styles) * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ var FormStyle; (function (FormStyle) { FormStyle[FormStyle["Format"] = 0] = "Format"; FormStyle[FormStyle["Standalone"] = 1] = "Standalone"; })(FormStyle || (FormStyle = {})); /** * String widths available for translations. * The specific character widths are locale-specific. * Examples are given for the word "Sunday" in English. * * @publicApi */ var TranslationWidth; (function (TranslationWidth) { /** 1 character for `en-US`. For example: 'S' */ TranslationWidth[TranslationWidth["Narrow"] = 0] = "Narrow"; /** 3 characters for `en-US`. For example: 'Sun' */ TranslationWidth[TranslationWidth["Abbreviated"] = 1] = "Abbreviated"; /** Full length for `en-US`. For example: "Sunday" */ TranslationWidth[TranslationWidth["Wide"] = 2] = "Wide"; /** 2 characters for `en-US`, For example: "Su" */ TranslationWidth[TranslationWidth["Short"] = 3] = "Short"; })(TranslationWidth || (TranslationWidth = {})); /** * String widths available for date-time formats. * The specific character widths are locale-specific. * Examples are given for `en-US`. * * @see `getLocaleDateFormat()` * @see `getLocaleTimeFormat()` * @see `getLocaleDateTimeFormat()` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * @publicApi */ var FormatWidth; (function (FormatWidth) { /** * For `en-US`, 'M/d/yy, h:mm a'` * (Example: `6/15/15, 9:03 AM`) */ FormatWidth[FormatWidth["Short"] = 0] = "Short"; /** * For `en-US`, `'MMM d, y, h:mm:ss a'` * (Example: `Jun 15, 2015, 9:03:01 AM`) */ FormatWidth[FormatWidth["Medium"] = 1] = "Medium"; /** * For `en-US`, `'MMMM d, y, h:mm:ss a z'` * (Example: `June 15, 2015 at 9:03:01 AM GMT+1`) */ FormatWidth[FormatWidth["Long"] = 2] = "Long"; /** * For `en-US`, `'EEEE, MMMM d, y, h:mm:ss a zzzz'` * (Example: `Monday, June 15, 2015 at 9:03:01 AM GMT+01:00`) */ FormatWidth[FormatWidth["Full"] = 3] = "Full"; })(FormatWidth || (FormatWidth = {})); /** * Symbols that can be used to replace placeholders in number patterns. * Examples are based on `en-US` values. * * @see `getLocaleNumberSymbol()` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ var NumberSymbol; (function (NumberSymbol) { /** * Decimal separator. * For `en-US`, the dot character. * Example: 2,345`.`67 */ NumberSymbol[NumberSymbol["Decimal"] = 0] = "Decimal"; /** * Grouping separator, typically for thousands. * For `en-US`, the comma character. * Example: 2`,`345.67 */ NumberSymbol[NumberSymbol["Group"] = 1] = "Group"; /** * List-item separator. * Example: "one, two, and three" */ NumberSymbol[NumberSymbol["List"] = 2] = "List"; /** * Sign for percentage (out of 100). * Example: 23.4% */ NumberSymbol[NumberSymbol["PercentSign"] = 3] = "PercentSign"; /** * Sign for positive numbers. * Example: +23 */ NumberSymbol[NumberSymbol["PlusSign"] = 4] = "PlusSign"; /** * Sign for negative numbers. * Example: -23 */ NumberSymbol[NumberSymbol["MinusSign"] = 5] = "MinusSign"; /** * Computer notation for exponential value (n times a power of 10). * Example: 1.2E3 */ NumberSymbol[NumberSymbol["Exponential"] = 6] = "Exponential"; /** * Human-readable format of exponential. * Example: 1.2x103 */ NumberSymbol[NumberSymbol["SuperscriptingExponent"] = 7] = "SuperscriptingExponent"; /** * Sign for permille (out of 1000). * Example: 23.4‰ */ NumberSymbol[NumberSymbol["PerMille"] = 8] = "PerMille"; /** * Infinity, can be used with plus and minus. * Example: ∞, +∞, -∞ */ NumberSymbol[NumberSymbol["Infinity"] = 9] = "Infinity"; /** * Not a number. * Example: NaN */ NumberSymbol[NumberSymbol["NaN"] = 10] = "NaN"; /** * Symbol used between time units. * Example: 10:52 */ NumberSymbol[NumberSymbol["TimeSeparator"] = 11] = "TimeSeparator"; /** * Decimal separator for currency values (fallback to `Decimal`). * Example: $2,345.67 */ NumberSymbol[NumberSymbol["CurrencyDecimal"] = 12] = "CurrencyDecimal"; /** * Group separator for currency values (fallback to `Group`). * Example: $2,345.67 */ NumberSymbol[NumberSymbol["CurrencyGroup"] = 13] = "CurrencyGroup"; })(NumberSymbol || (NumberSymbol = {})); /** * The value for each day of the week, based on the `en-US` locale * * @publicApi */ var WeekDay; (function (WeekDay) { WeekDay[WeekDay["Sunday"] = 0] = "Sunday"; WeekDay[WeekDay["Monday"] = 1] = "Monday"; WeekDay[WeekDay["Tuesday"] = 2] = "Tuesday"; WeekDay[WeekDay["Wednesday"] = 3] = "Wednesday"; WeekDay[WeekDay["Thursday"] = 4] = "Thursday"; WeekDay[WeekDay["Friday"] = 5] = "Friday"; WeekDay[WeekDay["Saturday"] = 6] = "Saturday"; })(WeekDay || (WeekDay = {})); /** * Retrieves the locale ID from the currently loaded locale. * The loaded locale could be, for example, a global one rather than a regional one. * @param locale A locale code, such as `fr-FR`. * @returns The locale code. For example, `fr`. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleId(locale) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale)[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].LocaleId]; } /** * Retrieves day period strings for the given locale. * * @param locale A locale code for the locale format rules to use. * @param formStyle The required grammatical form. * @param width The required character width. * @returns An array of localized period strings. For example, `[AM, PM]` for `en-US`. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleDayPeriods(locale, formStyle, width) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); const amPmData = [data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].DayPeriodsFormat], data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].DayPeriodsStandalone]]; const amPm = getLastDefinedValue(amPmData, formStyle); return getLastDefinedValue(amPm, width); } /** * Retrieves days of the week for the given locale, using the Gregorian calendar. * * @param locale A locale code for the locale format rules to use. * @param formStyle The required grammatical form. * @param width The required character width. * @returns An array of localized name strings. * For example,`[Sunday, Monday, ... Saturday]` for `en-US`. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleDayNames(locale, formStyle, width) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); const daysData = [data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].DaysFormat], data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].DaysStandalone]]; const days = getLastDefinedValue(daysData, formStyle); return getLastDefinedValue(days, width); } /** * Retrieves months of the year for the given locale, using the Gregorian calendar. * * @param locale A locale code for the locale format rules to use. * @param formStyle The required grammatical form. * @param width The required character width. * @returns An array of localized name strings. * For example, `[January, February, ...]` for `en-US`. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleMonthNames(locale, formStyle, width) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); const monthsData = [data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].MonthsFormat], data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].MonthsStandalone]]; const months = getLastDefinedValue(monthsData, formStyle); return getLastDefinedValue(months, width); } /** * Retrieves Gregorian-calendar eras for the given locale. * @param locale A locale code for the locale format rules to use. * @param width The required character width. * @returns An array of localized era strings. * For example, `[AD, BC]` for `en-US`. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleEraNames(locale, width) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); const erasData = data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].Eras]; return getLastDefinedValue(erasData, width); } /** * Retrieves the first day of the week for the given locale. * * @param locale A locale code for the locale format rules to use. * @returns A day index number, using the 0-based week-day index for `en-US` * (Sunday = 0, Monday = 1, ...). * For example, for `fr-FR`, returns 1 to indicate that the first day is Monday. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleFirstDayOfWeek(locale) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].FirstDayOfWeek]; } /** * Range of week days that are considered the week-end for the given locale. * * @param locale A locale code for the locale format rules to use. * @returns The range of day values, `[startDay, endDay]`. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleWeekEndRange(locale) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].WeekendRange]; } /** * Retrieves a localized date-value formatting string. * * @param locale A locale code for the locale format rules to use. * @param width The format type. * @returns The localized formatting string. * @see `FormatWidth` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleDateFormat(locale, width) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); return getLastDefinedValue(data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].DateFormat], width); } /** * Retrieves a localized time-value formatting string. * * @param locale A locale code for the locale format rules to use. * @param width The format type. * @returns The localized formatting string. * @see `FormatWidth` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * @publicApi */ function getLocaleTimeFormat(locale, width) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); return getLastDefinedValue(data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].TimeFormat], width); } /** * Retrieves a localized date-time formatting string. * * @param locale A locale code for the locale format rules to use. * @param width The format type. * @returns The localized formatting string. * @see `FormatWidth` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleDateTimeFormat(locale, width) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); const dateTimeFormatData = data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].DateTimeFormat]; return getLastDefinedValue(dateTimeFormatData, width); } /** * Retrieves a localized number symbol that can be used to replace placeholders in number formats. * @param locale The locale code. * @param symbol The symbol to localize. * @returns The character for the localized symbol. * @see `NumberSymbol` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleNumberSymbol(locale, symbol) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); const res = data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].NumberSymbols][symbol]; if (typeof res === 'undefined') { if (symbol === NumberSymbol.CurrencyDecimal) { return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].NumberSymbols][NumberSymbol.Decimal]; } else if (symbol === NumberSymbol.CurrencyGroup) { return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].NumberSymbols][NumberSymbol.Group]; } } return res; } /** * Retrieves a number format for a given locale. * * Numbers are formatted using patterns, like `#,###.00`. For example, the pattern `#,###.00` * when used to format the number 12345.678 could result in "12'345,678". That would happen if the * grouping separator for your language is an apostrophe, and the decimal separator is a comma. * * <b>Important:</b> The characters `.` `,` `0` `#` (and others below) are special placeholders * that stand for the decimal separator, and so on, and are NOT real characters. * You must NOT "translate" the placeholders. For example, don't change `.` to `,` even though in * your language the decimal point is written with a comma. The symbols should be replaced by the * local equivalents, using the appropriate `NumberSymbol` for your language. * * Here are the special characters used in number patterns: * * | Symbol | Meaning | * |--------|---------| * | . | Replaced automatically by the character used for the decimal point. | * | , | Replaced by the "grouping" (thousands) separator. | * | 0 | Replaced by a digit (or zero if there aren't enough digits). | * | # | Replaced by a digit (or nothing if there aren't enough). | * | ¤ | Replaced by a currency symbol, such as $ or USD. | * | % | Marks a percent format. The % symbol may change position, but must be retained. | * | E | Marks a scientific format. The E symbol may change position, but must be retained. | * | ' | Special characters used as literal characters are quoted with ASCII single quotes. | * * @param locale A locale code for the locale format rules to use. * @param type The type of numeric value to be formatted (such as `Decimal` or `Currency`.) * @returns The localized format string. * @see `NumberFormatStyle` * @see [CLDR website](http://cldr.unicode.org/translation/number-patterns) * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleNumberFormat(locale, type) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].NumberFormats][type]; } /** * Retrieves the symbol used to represent the currency for the main country * corresponding to a given locale. For example, '$' for `en-US`. * * @param locale A locale code for the locale format rules to use. * @returns The localized symbol character, * or `null` if the main country cannot be determined. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleCurrencySymbol(locale) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].CurrencySymbol] || null; } /** * Retrieves the name of the currency for the main country corresponding * to a given locale. For example, 'US Dollar' for `en-US`. * @param locale A locale code for the locale format rules to use. * @returns The currency name, * or `null` if the main country cannot be determined. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleCurrencyName(locale) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].CurrencyName] || null; } /** * Retrieves the default currency code for the given locale. * * The default is defined as the first currency which is still in use. * * @param locale The code of the locale whose currency code we want. * @returns The code of the default currency for the given locale. * * @publicApi */ function getLocaleCurrencyCode(locale) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵgetLocaleCurrencyCode"])(locale); } /** * Retrieves the currency values for a given locale. * @param locale A locale code for the locale format rules to use. * @returns The currency values. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) */ function getLocaleCurrencies(locale) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].Currencies]; } /** * @alias core/ɵgetLocalePluralCase * @publicApi */ const getLocalePluralCase = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵgetLocalePluralCase"]; function checkFullData(data) { if (!data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].ExtraData]) { throw new Error(`Missing extra locale data for the locale "${data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].LocaleId]}". Use "registerLocaleData" to load new data. See the "I18n guide" on angular.io to know more.`); } } /** * Retrieves locale-specific rules used to determine which day period to use * when more than one period is defined for a locale. * * There is a rule for each defined day period. The * first rule is applied to the first day period and so on. * Fall back to AM/PM when no rules are available. * * A rule can specify a period as time range, or as a single time value. * * This functionality is only available when you have loaded the full locale data. * See the ["I18n guide"](guide/i18n-common-format-data-locale). * * @param locale A locale code for the locale format rules to use. * @returns The rules for the locale, a single time value or array of *from-time, to-time*, * or null if no periods are available. * * @see `getLocaleExtraDayPeriods()` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleExtraDayPeriodRules(locale) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); checkFullData(data); const rules = data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].ExtraData][2 /* ɵExtraLocaleDataIndex.ExtraDayPeriodsRules */] || []; return rules.map(rule => { if (typeof rule === 'string') { return extractTime(rule); } return [extractTime(rule[0]), extractTime(rule[1])]; }); } /** * Retrieves locale-specific day periods, which indicate roughly how a day is broken up * in different languages. * For example, for `en-US`, periods are morning, noon, afternoon, evening, and midnight. * * This functionality is only available when you have loaded the full locale data. * See the ["I18n guide"](guide/i18n-common-format-data-locale). * * @param locale A locale code for the locale format rules to use. * @param formStyle The required grammatical form. * @param width The required character width. * @returns The translated day-period strings. * @see `getLocaleExtraDayPeriodRules()` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLocaleExtraDayPeriods(locale, formStyle, width) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); checkFullData(data); const dayPeriodsData = [data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].ExtraData][0 /* ɵExtraLocaleDataIndex.ExtraDayPeriodFormats */], data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].ExtraData][1 /* ɵExtraLocaleDataIndex.ExtraDayPeriodStandalone */]]; const dayPeriods = getLastDefinedValue(dayPeriodsData, formStyle) || []; return getLastDefinedValue(dayPeriods, width) || []; } /** * Retrieves the writing direction of a specified locale * @param locale A locale code for the locale format rules to use. * @publicApi * @returns 'rtl' or 'ltr' * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) */ function getLocaleDirection(locale) { const data = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵfindLocaleData"])(locale); return data[_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵLocaleDataIndex"].Directionality]; } /** * Retrieves the first value that is defined in an array, going backwards from an index position. * * To avoid repeating the same data (as when the "format" and "standalone" forms are the same) * add the first value to the locale data arrays, and add other values only if they are different. * * @param data The data array to retrieve from. * @param index A 0-based index into the array to start from. * @returns The value immediately before the given index position. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getLastDefinedValue(data, index) { for (let i = index; i > -1; i--) { if (typeof data[i] !== 'undefined') { return data[i]; } } throw new Error('Locale data API: locale data undefined'); } /** * Extracts the hours and minutes from a string like "15:45" */ function extractTime(time) { const [h, m] = time.split(':'); return { hours: +h, minutes: +m }; } /** * Retrieves the currency symbol for a given currency code. * * For example, for the default `en-US` locale, the code `USD` can * be represented by the narrow symbol `$` or the wide symbol `US$`. * * @param code The currency code. * @param format The format, `wide` or `narrow`. * @param locale A locale code for the locale format rules to use. * * @returns The symbol, or the currency code if no symbol is available. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getCurrencySymbol(code, format, locale = 'en') { const currency = getLocaleCurrencies(locale)[code] || CURRENCIES_EN[code] || []; const symbolNarrow = currency[1 /* ɵCurrencyIndex.SymbolNarrow */]; if (format === 'narrow' && typeof symbolNarrow === 'string') { return symbolNarrow; } return currency[0 /* ɵCurrencyIndex.Symbol */] || code; } // Most currencies have cents, that's why the default is 2 const DEFAULT_NB_OF_CURRENCY_DIGITS = 2; /** * Reports the number of decimal digits for a given currency. * The value depends upon the presence of cents in that particular currency. * * @param code The currency code. * @returns The number of decimal digits, typically 0 or 2. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function getNumberOfCurrencyDigits(code) { let digits; const currency = CURRENCIES_EN[code]; if (currency) { digits = currency[2 /* ɵCurrencyIndex.NbOfDigits */]; } return typeof digits === 'number' ? digits : DEFAULT_NB_OF_CURRENCY_DIGITS; } const ISO8601_DATE_REGEX = /^(\d{4,})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; // 1 2 3 4 5 6 7 8 9 10 11 const NAMED_FORMATS = {}; const DATE_FORMATS_SPLIT = /((?:[^BEGHLMOSWYZabcdhmswyz']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|Y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|c{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/; var ZoneWidth; (function (ZoneWidth) { ZoneWidth[ZoneWidth["Short"] = 0] = "Short"; ZoneWidth[ZoneWidth["ShortGMT"] = 1] = "ShortGMT"; ZoneWidth[ZoneWidth["Long"] = 2] = "Long"; ZoneWidth[ZoneWidth["Extended"] = 3] = "Extended"; })(ZoneWidth || (ZoneWidth = {})); var DateType; (function (DateType) { DateType[DateType["FullYear"] = 0] = "FullYear"; DateType[DateType["Month"] = 1] = "Month"; DateType[DateType["Date"] = 2] = "Date"; DateType[DateType["Hours"] = 3] = "Hours"; DateType[DateType["Minutes"] = 4] = "Minutes"; DateType[DateType["Seconds"] = 5] = "Seconds"; DateType[DateType["FractionalSeconds"] = 6] = "FractionalSeconds"; DateType[DateType["Day"] = 7] = "Day"; })(DateType || (DateType = {})); var TranslationType; (function (TranslationType) { TranslationType[TranslationType["DayPeriods"] = 0] = "DayPeriods"; TranslationType[TranslationType["Days"] = 1] = "Days"; TranslationType[TranslationType["Months"] = 2] = "Months"; TranslationType[TranslationType["Eras"] = 3] = "Eras"; })(TranslationType || (TranslationType = {})); /** * @ngModule CommonModule * @description * * Formats a date according to locale rules. * * @param value The date to format, as a Date, or a number (milliseconds since UTC epoch) * or an [ISO date-time string](https://www.w3.org/TR/NOTE-datetime). * @param format The date-time components to include. See `DatePipe` for details. * @param locale A locale code for the locale format rules to use. * @param timezone The time zone. A time zone offset from GMT (such as `'+0430'`), * or a standard UTC/GMT or continental US time zone abbreviation. * If not specified, uses host system settings. * * @returns The formatted date string. * * @see `DatePipe` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function formatDate(value, format, locale, timezone) { let date = toDate(value); const namedFormat = getNamedFormat(locale, format); format = namedFormat || format; let parts = []; let match; while (format) { match = DATE_FORMATS_SPLIT.exec(format); if (match) { parts = parts.concat(match.slice(1)); const part = parts.pop(); if (!part) { break; } format = part; } else { parts.push(format); break; } } let dateTimezoneOffset = date.getTimezoneOffset(); if (timezone) { dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); date = convertTimezoneToLocal(date, timezone, true); } let text = ''; parts.forEach(value => { const dateFormatter = getDateFormatter(value); text += dateFormatter ? dateFormatter(date, locale, dateTimezoneOffset) : value === '\'\'' ? '\'' : value.replace(/(^'|'$)/g, '').replace(/''/g, '\''); }); return text; } /** * Create a new Date object with the given date value, and the time set to midnight. * * We cannot use `new Date(year, month, date)` because it maps years between 0 and 99 to 1900-1999. * See: https://github.com/angular/angular/issues/40377 * * Note that this function returns a Date object whose time is midnight in the current locale's * timezone. In the future we might want to change this to be midnight in UTC, but this would be a * considerable breaking change. */ function createDate(year, month, date) { // The `newDate` is set to midnight (UTC) on January 1st 1970. // - In PST this will be December 31st 1969 at 4pm. // - In GMT this will be January 1st 1970 at 1am. // Note that they even have different years, dates and months! const newDate = new Date(0); // `setFullYear()` allows years like 0001 to be set correctly. This function does not // change the internal time of the date. // Consider calling `setFullYear(2019, 8, 20)` (September 20, 2019). // - In PST this will now be September 20, 2019 at 4pm // - In GMT this will now be September 20, 2019 at 1am newDate.setFullYear(year, month, date); // We want the final date to be at local midnight, so we reset the time. // - In PST this will now be September 20, 2019 at 12am // - In GMT this will now be September 20, 2019 at 12am newDate.setHours(0, 0, 0); return newDate; } function getNamedFormat(locale, format) { const localeId = getLocaleId(locale); NAMED_FORMATS[localeId] = NAMED_FORMATS[localeId] || {}; if (NAMED_FORMATS[localeId][format]) { return NAMED_FORMATS[localeId][format]; } let formatValue = ''; switch (format) { case 'shortDate': formatValue = getLocaleDateFormat(locale, FormatWidth.Short); break; case 'mediumDate': formatValue = getLocaleDateFormat(locale, FormatWidth.Medium); break; case 'longDate': formatValue = getLocaleDateFormat(locale, FormatWidth.Long); break; case 'fullDate': formatValue = getLocaleDateFormat(locale, FormatWidth.Full); break; case 'shortTime': formatValue = getLocaleTimeFormat(locale, FormatWidth.Short); break; case 'mediumTime': formatValue = getLocaleTimeFormat(locale, FormatWidth.Medium); break; case 'longTime': formatValue = getLocaleTimeFormat(locale, FormatWidth.Long); break; case 'fullTime': formatValue = getLocaleTimeFormat(locale, FormatWidth.Full); break; case 'short': const shortTime = getNamedFormat(locale, 'shortTime'); const shortDate = getNamedFormat(locale, 'shortDate'); formatValue = formatDateTime(getLocaleDateTimeFormat(locale, FormatWidth.Short), [shortTime, shortDate]); break; case 'medium': const mediumTime = getNamedFormat(locale, 'mediumTime'); const mediumDate = getNamedFormat(locale, 'mediumDate'); formatValue = formatDateTime(getLocaleDateTimeFormat(locale, FormatWidth.Medium), [mediumTime, mediumDate]); break; case 'long': const longTime = getNamedFormat(locale, 'longTime'); const longDate = getNamedFormat(locale, 'longDate'); formatValue = formatDateTime(getLocaleDateTimeFormat(locale, FormatWidth.Long), [longTime, longDate]); break; case 'full': const fullTime = getNamedFormat(locale, 'fullTime'); const fullDate = getNamedFormat(locale, 'fullDate'); formatValue = formatDateTime(getLocaleDateTimeFormat(locale, FormatWidth.Full), [fullTime, fullDate]); break; } if (formatValue) { NAMED_FORMATS[localeId][format] = formatValue; } return formatValue; } function formatDateTime(str, opt_values) { if (opt_values) { str = str.replace(/\{([^}]+)}/g, function (match, key) { return opt_values != null && key in opt_values ? opt_values[key] : match; }); } return str; } function padNumber(num, digits, minusSign = '-', trim, negWrap) { let neg = ''; if (num < 0 || negWrap && num <= 0) { if (negWrap) { num = -num + 1; } else { num = -num; neg = minusSign; } } let strNum = String(num); while (strNum.length < digits) { strNum = '0' + strNum; } if (trim) { strNum = strNum.slice(strNum.length - digits); } return neg + strNum; } function formatFractionalSeconds(milliseconds, digits) { const strMs = padNumber(milliseconds, 3); return strMs.substring(0, digits); } /** * Returns a date formatter that transforms a date into its locale digit representation */ function dateGetter(name, size, offset = 0, trim = false, negWrap = false) { return function (date, locale) { let part = getDatePart(name, date); if (offset > 0 || part > -offset) { part += offset; } if (name === DateType.Hours) { if (part === 0 && offset === -12) { part = 12; } } else if (name === DateType.FractionalSeconds) { return formatFractionalSeconds(part, size); } const localeMinus = getLocaleNumberSymbol(locale, NumberSymbol.MinusSign); return padNumber(part, size, localeMinus, trim, negWrap); }; } function getDatePart(part, date) { switch (part) { case DateType.FullYear: return date.getFullYear(); case DateType.Month: return date.getMonth(); case DateType.Date: return date.getDate(); case DateType.Hours: return date.getHours(); case DateType.Minutes: return date.getMinutes(); case DateType.Seconds: return date.getSeconds(); case DateType.FractionalSeconds: return date.getMilliseconds(); case DateType.Day: return date.getDay(); default: throw new Error(`Unknown DateType value "${part}".`); } } /** * Returns a date formatter that transforms a date into its locale string representation */ function dateStrGetter(name, width, form = FormStyle.Format, extended = false) { return function (date, locale) { return getDateTranslation(date, locale, name, width, form, extended); }; } /** * Returns the locale translation of a date for a given form, type and width */ function getDateTranslation(date, locale, name, width, form, extended) { switch (name) { case TranslationType.Months: return getLocaleMonthNames(locale, form, width)[date.getMonth()]; case TranslationType.Days: return getLocaleDayNames(locale, form, width)[date.getDay()]; case TranslationType.DayPeriods: const currentHours = date.getHours(); const currentMinutes = date.getMinutes(); if (extended) { const rules = getLocaleExtraDayPeriodRules(locale); const dayPeriods = getLocaleExtraDayPeriods(locale, form, width); const index = rules.findIndex(rule => { if (Array.isArray(rule)) { // morning, afternoon, evening, night const [from, to] = rule; const afterFrom = currentHours >= from.hours && currentMinutes >= from.minutes; const beforeTo = currentHours < to.hours || currentHours === to.hours && currentMinutes < to.minutes; // We must account for normal rules that span a period during the day (e.g. 6am-9am) // where `from` is less (earlier) than `to`. But also rules that span midnight (e.g. // 10pm - 5am) where `from` is greater (later!) than `to`. // // In the first case the current time must be BOTH after `from` AND before `to` // (e.g. 8am is after 6am AND before 10am). // // In the second case the current time must be EITHER after `from` OR before `to` // (e.g. 4am is before 5am but not after 10pm; and 11pm is not before 5am but it is // after 10pm). if (from.hours < to.hours) { if (afterFrom && beforeTo) { return true; } } else if (afterFrom || beforeTo) { return true; } } else { // noon or midnight if (rule.hours === currentHours && rule.minutes === currentMinutes) { return true; } } return false; }); if (index !== -1) { return dayPeriods[index]; } } // if no rules for the day periods, we use am/pm by default return getLocaleDayPeriods(locale, form, width)[currentHours < 12 ? 0 : 1]; case TranslationType.Eras: return getLocaleEraNames(locale, width)[date.getFullYear() <= 0 ? 0 : 1]; default: // This default case is not needed by TypeScript compiler, as the switch is exhaustive. // However Closure Compiler does not understand that and reports an error in typed mode. // The `throw new Error` below works around the problem, and the unexpected: never variable // makes sure tsc still checks this code is unreachable. const unexpected = name; throw new Error(`unexpected translation type ${unexpected}`); } } /** * Returns a date formatter that transforms a date and an offset into a timezone with ISO8601 or * GMT format depending on the width (eg: short = +0430, short:GMT = GMT+4, long = GMT+04:30, * extended = +04:30) */ function timeZoneGetter(width) { return function (date, locale, offset) { const zone = -1 * offset; const minusSign = getLocaleNumberSymbol(locale, NumberSymbol.MinusSign); const hours = zone > 0 ? Math.floor(zone / 60) : Math.ceil(zone / 60); switch (width) { case ZoneWidth.Short: return (zone >= 0 ? '+' : '') + padNumber(hours, 2, minusSign) + padNumber(Math.abs(zone % 60), 2, minusSign); case ZoneWidth.ShortGMT: return 'GMT' + (zone >= 0 ? '+' : '') + padNumber(hours, 1, minusSign); case ZoneWidth.Long: return 'GMT' + (zone >= 0 ? '+' : '') + padNumber(hours, 2, minusSign) + ':' + padNumber(Math.abs(zone % 60), 2, minusSign); case ZoneWidth.Extended: if (offset === 0) { return 'Z'; } else { return (zone >= 0 ? '+' : '') + padNumber(hours, 2, minusSign) + ':' + padNumber(Math.abs(zone % 60), 2, minusSign); } default: throw new Error(`Unknown zone width "${width}"`); } }; } const JANUARY = 0; const THURSDAY = 4; function getFirstThursdayOfYear(year) { const firstDayOfYear = createDate(year, JANUARY, 1).getDay(); return createDate(year, 0, 1 + (firstDayOfYear <= THURSDAY ? THURSDAY : THURSDAY + 7) - firstDayOfYear); } function getThursdayThisWeek(datetime) { return createDate(datetime.getFullYear(), datetime.getMonth(), datetime.getDate() + (THURSDAY - datetime.getDay())); } function weekGetter(size, monthBased = false) { return function (date, locale) { let result; if (monthBased) { const nbDaysBefore1stDayOfMonth = new Date(date.getFullYear(), date.getMonth(), 1).getDay() - 1; const today = date.getDate(); result = 1 + Math.floor((today + nbDaysBefore1stDayOfMonth) / 7); } else { const thisThurs = getThursdayThisWeek(date); // Some days of a year are part of next year according to ISO 8601. // Compute the firstThurs from the year of this week's Thursday const firstThurs = getFirstThursdayOfYear(thisThurs.getFullYear()); const diff = thisThurs.getTime() - firstThurs.getTime(); result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week } return padNumber(result, size, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign)); }; } /** * Returns a date formatter that provides the week-numbering year for the input date. */ function weekNumberingYearGetter(size, trim = false) { return function (date, locale) { const thisThurs = getThursdayThisWeek(date); const weekNumberingYear = thisThurs.getFullYear(); return padNumber(weekNumberingYear, size, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign), trim); }; } const DATE_FORMATS = {}; // Based on CLDR formats: // See complete list: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table // See also explanations: http://cldr.unicode.org/translation/date-time // TODO(ocombe): support all missing cldr formats: U, Q, D, F, e, j, J, C, A, v, V, X, x function getDateFormatter(format) { if (DATE_FORMATS[format]) { return DATE_FORMATS[format]; } let formatter; switch (format) { // Era name (AD/BC) case 'G': case 'GG': case 'GGG': formatter = dateStrGetter(TranslationType.Eras, TranslationWidth.Abbreviated); break; case 'GGGG': formatter = dateStrGetter(TranslationType.Eras, TranslationWidth.Wide); break; case 'GGGGG': formatter = dateStrGetter(TranslationType.Eras, TranslationWidth.Narrow); break; // 1 digit representation of the year, e.g. (AD 1 => 1, AD 199 => 199) case 'y': formatter = dateGetter(DateType.FullYear, 1, 0, false, true); break; // 2 digit representation of the year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) case 'yy': formatter = dateGetter(DateType.FullYear, 2, 0, true, true); break; // 3 digit representation of the year, padded (000-999). (e.g. AD 2001 => 01, AD 2010 => 10) case 'yyy': formatter = dateGetter(DateType.FullYear, 3, 0, false, true); break; // 4 digit representation of the year (e.g. AD 1 => 0001, AD 2010 => 2010) case 'yyyy': formatter = dateGetter(DateType.FullYear, 4, 0, false, true); break; // 1 digit representation of the week-numbering year, e.g. (AD 1 => 1, AD 199 => 199) case 'Y': formatter = weekNumberingYearGetter(1); break; // 2 digit representation of the week-numbering year, padded (00-99). (e.g. AD 2001 => 01, AD // 2010 => 10) case 'YY': formatter = weekNumberingYearGetter(2, true); break; // 3 digit representation of the week-numbering year, padded (000-999). (e.g. AD 1 => 001, AD // 2010 => 2010) case 'YYY': formatter = weekNumberingYearGetter(3); break; // 4 digit representation of the week-numbering year (e.g. AD 1 => 0001, AD 2010 => 2010) case 'YYYY': formatter = weekNumberingYearGetter(4); break; // Month of the year (1-12), numeric case 'M': case 'L': formatter = dateGetter(DateType.Month, 1, 1); break; case 'MM': case 'LL': formatter = dateGetter(DateType.Month, 2, 1); break; // Month of the year (January, ...), string, format case 'MMM': formatter = dateStrGetter(TranslationType.Months, TranslationWidth.Abbreviated); break; case 'MMMM': formatter = dateStrGetter(TranslationType.Months, TranslationWidth.Wide); break; case 'MMMMM': formatter = dateStrGetter(TranslationType.Months, TranslationWidth.Narrow); break; // Month of the year (January, ...), string, standalone case 'LLL': formatter = dateStrGetter(TranslationType.Months, TranslationWidth.Abbreviated, FormStyle.Standalone); break; case 'LLLL': formatter = dateStrGetter(TranslationType.Months, TranslationWidth.Wide, FormStyle.Standalone); break; case 'LLLLL': formatter = dateStrGetter(TranslationType.Months, TranslationWidth.Narrow, FormStyle.Standalone); break; // Week of the year (1, ... 52) case 'w': formatter = weekGetter(1); break; case 'ww': formatter = weekGetter(2); break; // Week of the month (1, ...) case 'W': formatter = weekGetter(1, true); break; // Day of the month (1-31) case 'd': formatter = dateGetter(DateType.Date, 1); break; case 'dd': formatter = dateGetter(DateType.Date, 2); break; // Day of the Week StandAlone (1, 1, Mon, Monday, M, Mo) case 'c': case 'cc': formatter = dateGetter(DateType.Day, 1); break; case 'ccc': formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Abbreviated, FormStyle.Standalone); break; case 'cccc': formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Wide, FormStyle.Standalone); break; case 'ccccc': formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Narrow, FormStyle.Standalone); break; case 'cccccc': formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Short, FormStyle.Standalone); break; // Day of the Week case 'E': case 'EE': case 'EEE': formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Abbreviated); break; case 'EEEE': formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Wide); break; case 'EEEEE': formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Narrow); break; case 'EEEEEE': formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Short); break; // Generic period of the day (am-pm) case 'a': case 'aa': case 'aaa': formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Abbreviated); break; case 'aaaa': formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Wide); break; case 'aaaaa': formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Narrow); break; // Extended period of the day (midnight, at night, ...), standalone case 'b': case 'bb': case 'bbb': formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Abbreviated, FormStyle.Standalone, true); break; case 'bbbb': formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Wide, FormStyle.Standalone, true); break; case 'bbbbb': formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Narrow, FormStyle.Standalone, true); break; // Extended period of the day (midnight, night, ...), standalone case 'B': case 'BB': case 'BBB': formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Abbreviated, FormStyle.Format, true); break; case 'BBBB': formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Wide, FormStyle.Format, true); break; case 'BBBBB': formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Narrow, FormStyle.Format, true); break; // Hour in AM/PM, (1-12) case 'h': formatter = dateGetter(DateType.Hours, 1, -12); break; case 'hh': formatter = dateGetter(DateType.Hours, 2, -12); break; // Hour of the day (0-23) case 'H': formatter = dateGetter(DateType.Hours, 1); break; // Hour in day, padded (00-23) case 'HH': formatter = dateGetter(DateType.Hours, 2); break; // Minute of the hour (0-59) case 'm': formatter = dateGetter(DateType.Minutes, 1); break; case 'mm': formatter = dateGetter(DateType.Minutes, 2); break; // Second of the minute (0-59) case 's': formatter = dateGetter(DateType.Seconds, 1); break; case 'ss': formatter = dateGetter(DateType.Seconds, 2); break; // Fractional second case 'S': formatter = dateGetter(DateType.FractionalSeconds, 1); break; case 'SS': formatter = dateGetter(DateType.FractionalSeconds, 2); break; case 'SSS': formatter = dateGetter(DateType.FractionalSeconds, 3); break; // Timezone ISO8601 short format (-0430) case 'Z': case 'ZZ': case 'ZZZ': formatter = timeZoneGetter(ZoneWidth.Short); break; // Timezone ISO8601 extended format (-04:30) case 'ZZZZZ': formatter = timeZoneGetter(ZoneWidth.Extended); break; // Timezone GMT short format (GMT+4) case 'O': case 'OO': case 'OOO': // Should be location, but fallback to format O instead because we don't have the data yet case 'z': case 'zz': case 'zzz': formatter = timeZoneGetter(ZoneWidth.ShortGMT); break; // Timezone GMT long format (GMT+0430) case 'OOOO': case 'ZZZZ': // Should be location, but fallback to format O instead because we don't have the data yet case 'zzzz': formatter = timeZoneGetter(ZoneWidth.Long); break; default: return null; } DATE_FORMATS[format] = formatter; return formatter; } function timezoneToOffset(timezone, fallback) { // Support: IE 11 only, Edge 13-15+ // IE/Edge do not "understand" colon (`:`) in timezone timezone = timezone.replace(/:/g, ''); const requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; } function addDateMinutes(date, minutes) { date = new Date(date.getTime()); date.setMinutes(date.getMinutes() + minutes); return date; } function convertTimezoneToLocal(date, timezone, reverse) { const reverseValue = reverse ? -1 : 1; const dateTimezoneOffset = date.getTimezoneOffset(); const timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); return addDateMinutes(date, reverseValue * (timezoneOffset - dateTimezoneOffset)); } /** * Converts a value to date. * * Supported input formats: * - `Date` * - number: timestamp * - string: numeric (e.g. "1234"), ISO and date strings in a format supported by * [Date.parse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse). * Note: ISO strings without time return a date without timeoffset. * * Throws if unable to convert to a date. */ function toDate(value) { if (isDate(value)) { return value; } if (typeof value === 'number' && !isNaN(value)) { return new Date(value); } if (typeof value === 'string') { value = value.trim(); if (/^(\d{4}(-\d{1,2}(-\d{1,2})?)?)$/.test(value)) { /* For ISO Strings without time the day, month and year must be extracted from the ISO String before Date creation to avoid time offset and errors in the new Date. If we only replace '-' with ',' in the ISO String ("2015,01,01"), and try to create a new date, some browsers (e.g. IE 9) will throw an invalid Date error. If we leave the '-' ("2015-01-01") and try to create a new Date("2015-01-01") the timeoffset is applied. Note: ISO months are 0 for January, 1 for February, ... */ const [y, m = 1, d = 1] = value.split('-').map(val => +val); return createDate(y, m - 1, d); } const parsedNb = parseFloat(value); // any string that only contains numbers, like "1234" but not like "1234hello" if (!isNaN(value - parsedNb)) { return new Date(parsedNb); } let match; if (match = value.match(ISO8601_DATE_REGEX)) { return isoStringToDate(match); } } const date = new Date(value); if (!isDate(date)) { throw new Error(`Unable to convert "${value}" into a date`); } return date; } /** * Converts a date in ISO8601 to a Date. * Used instead of `Date.parse` because of browser discrepancies. */ function isoStringToDate(match) { const date = new Date(0); let tzHour = 0; let tzMin = 0; // match[8] means that the string contains "Z" (UTC) or a timezone like "+01:00" or "+0100" const dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear; const timeSetter = match[8] ? date.setUTCHours : date.setHours; // if there is a timezone defined like "+01:00" or "+0100" if (match[9]) { tzHour = Number(match[9] + match[10]); tzMin = Number(match[9] + match[11]); } dateSetter.call(date, Number(match[1]), Number(match[2]) - 1, Number(match[3])); const h = Number(match[4] || 0) - tzHour; const m = Number(match[5] || 0) - tzMin; const s = Number(match[6] || 0); // The ECMAScript specification (https://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.11) // defines that `DateTime` milliseconds should always be rounded down, so that `999.9ms` // becomes `999ms`. const ms = Math.floor(parseFloat('0.' + (match[7] || 0)) * 1000); timeSetter.call(date, h, m, s, ms); return date; } function isDate(value) { return value instanceof Date && !isNaN(value.valueOf()); } const NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/; const MAX_DIGITS = 22; const DECIMAL_SEP = '.'; const ZERO_CHAR = '0'; const PATTERN_SEP = ';'; const GROUP_SEP = ','; const DIGIT_CHAR = '#'; const CURRENCY_CHAR = '¤'; const PERCENT_CHAR = '%'; /** * Transforms a number to a locale string based on a style and a format. */ function formatNumberToLocaleString(value, pattern, locale, groupSymbol, decimalSymbol, digitsInfo, isPercent = false) { let formattedText = ''; let isZero = false; if (!isFinite(value)) { formattedText = getLocaleNumberSymbol(locale, NumberSymbol.Infinity); } else { let parsedNumber = parseNumber(value); if (isPercent) { parsedNumber = toPercent(parsedNumber); } let minInt = pattern.minInt; let minFraction = pattern.minFrac; let maxFraction = pattern.maxFrac; if (digitsInfo) { const parts = digitsInfo.match(NUMBER_FORMAT_REGEXP); if (parts === null) { throw new Error(`${digitsInfo} is not a valid digit info`); } const minIntPart = parts[1]; const minFractionPart = parts[3]; const maxFractionPart = parts[5]; if (minIntPart != null) { minInt = parseIntAutoRadix(minIntPart); } if (minFractionPart != null) { minFraction = parseIntAutoRadix(minFractionPart); } if (maxFractionPart != null) { maxFraction = parseIntAutoRadix(maxFractionPart); } else if (minFractionPart != null && minFraction > maxFraction) { maxFraction = minFraction; } } roundNumber(parsedNumber, minFraction, maxFraction); let digits = parsedNumber.digits; let integerLen = parsedNumber.integerLen; const exponent = parsedNumber.exponent; let decimals = []; isZero = digits.every(d => !d); // pad zeros for small numbers for (; integerLen < minInt; integerLen++) { digits.unshift(0); } // pad zeros for small numbers for (; integerLen < 0; integerLen++) { digits.unshift(0); } // extract decimals digits if (integerLen > 0) { decimals = digits.splice(integerLen, digits.length); } else { decimals = digits; digits = [0]; } // format the integer digits with grouping separators const groups = []; if (digits.length >= pattern.lgSize) { groups.unshift(digits.splice(-pattern.lgSize, digits.length).join('')); } while (digits.length > pattern.gSize) { groups.unshift(digits.splice(-pattern.gSize, digits.length).join('')); } if (digits.length) { groups.unshift(digits.join('')); } formattedText = groups.join(getLocaleNumberSymbol(locale, groupSymbol)); // append the decimal digits if (decimals.length) { formattedText += getLocaleNumberSymbol(locale, decimalSymbol) + decimals.join(''); } if (exponent) { formattedText += getLocaleNumberSymbol(locale, NumberSymbol.Exponential) + '+' + exponent; } } if (value < 0 && !isZero) { formattedText = pattern.negPre + formattedText + pattern.negSuf; } else { formattedText = pattern.posPre + formattedText + pattern.posSuf; } return formattedText; } /** * @ngModule CommonModule * @description * * Formats a number as currency using locale rules. * * @param value The number to format. * @param locale A locale code for the locale format rules to use. * @param currency A string containing the currency symbol or its name, * such as "$" or "Canadian Dollar". Used in output string, but does not affect the operation * of the function. * @param currencyCode The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) * currency code, such as `USD` for the US dollar and `EUR` for the euro. * Used to determine the number of digits in the decimal part. * @param digitsInfo Decimal representation options, specified by a string in the following format: * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. * * @returns The formatted currency value. * * @see `formatNumber()` * @see `DecimalPipe` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function formatCurrency(value, locale, currency, currencyCode, digitsInfo) { const format = getLocaleNumberFormat(locale, NumberFormatStyle.Currency); const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign)); pattern.minFrac = getNumberOfCurrencyDigits(currencyCode); pattern.maxFrac = pattern.minFrac; const res = formatNumberToLocaleString(value, pattern, locale, NumberSymbol.CurrencyGroup, NumberSymbol.CurrencyDecimal, digitsInfo); return res.replace(CURRENCY_CHAR, currency) // if we have 2 time the currency character, the second one is ignored .replace(CURRENCY_CHAR, '') // If there is a spacing between currency character and the value and // the currency character is suppressed by passing an empty string, the // spacing character would remain as part of the string. Then we // should remove it. .trim(); } /** * @ngModule CommonModule * @description * * Formats a number as a percentage according to locale rules. * * @param value The number to format. * @param locale A locale code for the locale format rules to use. * @param digitsInfo Decimal representation options, specified by a string in the following format: * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. * * @returns The formatted percentage value. * * @see `formatNumber()` * @see `DecimalPipe` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * @publicApi * */ function formatPercent(value, locale, digitsInfo) { const format = getLocaleNumberFormat(locale, NumberFormatStyle.Percent); const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign)); const res = formatNumberToLocaleString(value, pattern, locale, NumberSymbol.Group, NumberSymbol.Decimal, digitsInfo, true); return res.replace(new RegExp(PERCENT_CHAR, 'g'), getLocaleNumberSymbol(locale, NumberSymbol.PercentSign)); } /** * @ngModule CommonModule * @description * * Formats a number as text, with group sizing, separator, and other * parameters based on the locale. * * @param value The number to format. * @param locale A locale code for the locale format rules to use. * @param digitsInfo Decimal representation options, specified by a string in the following format: * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details. * * @returns The formatted text string. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) * * @publicApi */ function formatNumber(value, locale, digitsInfo) { const format = getLocaleNumberFormat(locale, NumberFormatStyle.Decimal); const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign)); return formatNumberToLocaleString(value, pattern, locale, NumberSymbol.Group, NumberSymbol.Decimal, digitsInfo); } function parseNumberFormat(format, minusSign = '-') { const p = { minInt: 1, minFrac: 0, maxFrac: 0, posPre: '', posSuf: '', negPre: '', negSuf: '', gSize: 0, lgSize: 0 }; const patternParts = format.split(PATTERN_SEP); const positive = patternParts[0]; const negative = patternParts[1]; const positiveParts = positive.indexOf(DECIMAL_SEP) !== -1 ? positive.split(DECIMAL_SEP) : [positive.substring(0, positive.lastIndexOf(ZERO_CHAR) + 1), positive.substring(positive.lastIndexOf(ZERO_CHAR) + 1)], integer = positiveParts[0], fraction = positiveParts[1] || ''; p.posPre = integer.substring(0, integer.indexOf(DIGIT_CHAR)); for (let i = 0; i < fraction.length; i++) { const ch = fraction.charAt(i); if (ch === ZERO_CHAR) { p.minFrac = p.maxFrac = i + 1; } else if (ch === DIGIT_CHAR) { p.maxFrac = i + 1; } else { p.posSuf += ch; } } const groups = integer.split(GROUP_SEP); p.gSize = groups[1] ? groups[1].length : 0; p.lgSize = groups[2] || groups[1] ? (groups[2] || groups[1]).length : 0; if (negative) { const trunkLen = positive.length - p.posPre.length - p.posSuf.length, pos = negative.indexOf(DIGIT_CHAR); p.negPre = negative.substring(0, pos).replace(/'/g, ''); p.negSuf = negative.slice(pos + trunkLen).replace(/'/g, ''); } else { p.negPre = minusSign + p.posPre; p.negSuf = p.posSuf; } return p; } // Transforms a parsed number into a percentage by multiplying it by 100 function toPercent(parsedNumber) { // if the number is 0, don't do anything if (parsedNumber.digits[0] === 0) { return parsedNumber; } // Getting the current number of decimals const fractionLen = parsedNumber.digits.length - parsedNumber.integerLen; if (parsedNumber.exponent) { parsedNumber.exponent += 2; } else { if (fractionLen === 0) { parsedNumber.digits.push(0, 0); } else if (fractionLen === 1) { parsedNumber.digits.push(0); } parsedNumber.integerLen += 2; } return parsedNumber; } /** * Parses a number. * Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/ */ function parseNumber(num) { let numStr = Math.abs(num) + ''; let exponent = 0, digits, integerLen; let i, j, zeros; // Decimal point? if ((integerLen = numStr.indexOf(DECIMAL_SEP)) > -1) { numStr = numStr.replace(DECIMAL_SEP, ''); } // Exponential form? if ((i = numStr.search(/e/i)) > 0) { // Work out the exponent. if (integerLen < 0) integerLen = i; integerLen += +numStr.slice(i + 1); numStr = numStr.substring(0, i); } else if (integerLen < 0) { // There was no decimal point or exponent so it is an integer. integerLen = numStr.length; } // Count the number of leading zeros. for (i = 0; numStr.charAt(i) === ZERO_CHAR; i++) {/* empty */ } if (i === (zeros = numStr.length)) { // The digits are all zero. digits = [0]; integerLen = 1; } else { // Count the number of trailing zeros zeros--; while (numStr.charAt(zeros) === ZERO_CHAR) zeros--; // Trailing zeros are insignificant so ignore them integerLen -= i; digits = []; // Convert string to array of digits without leading/trailing zeros. for (j = 0; i <= zeros; i++, j++) { digits[j] = Number(numStr.charAt(i)); } } // If the number overflows the maximum allowed digits then use an exponent. if (integerLen > MAX_DIGITS) { digits = digits.splice(0, MAX_DIGITS - 1); exponent = integerLen - 1; integerLen = 1; } return { digits, exponent, integerLen }; } /** * Round the parsed number to the specified number of decimal places * This function changes the parsedNumber in-place */ function roundNumber(parsedNumber, minFrac, maxFrac) { if (minFrac > maxFrac) { throw new Error(`The minimum number of digits after fraction (${minFrac}) is higher than the maximum (${maxFrac}).`); } let digits = parsedNumber.digits; let fractionLen = digits.length - parsedNumber.integerLen; const fractionSize = Math.min(Math.max(minFrac, fractionLen), maxFrac); // The index of the digit to where rounding is to occur let roundAt = fractionSize + parsedNumber.integerLen; let digit = digits[roundAt]; if (roundAt > 0) { // Drop fractional digits beyond `roundAt` digits.splice(Math.max(parsedNumber.integerLen, roundAt)); // Set non-fractional digits beyond `roundAt` to 0 for (let j = roundAt; j < digits.length; j++) { digits[j] = 0; } } else { // We rounded to zero so reset the parsedNumber fractionLen = Math.max(0, fractionLen); parsedNumber.integerLen = 1; digits.length = Math.max(1, roundAt = fractionSize + 1); digits[0] = 0; for (let i = 1; i < roundAt; i++) digits[i] = 0; } if (digit >= 5) { if (roundAt - 1 < 0) { for (let k = 0; k > roundAt; k--) { digits.unshift(0); parsedNumber.integerLen++; } digits.unshift(1); parsedNumber.integerLen++; } else { digits[roundAt - 1]++; } } // Pad out with zeros to get the required fraction length for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0); let dropTrailingZeros = fractionSize !== 0; // Minimal length = nb of decimals required + current nb of integers // Any number besides that is optional and can be removed if it's a trailing 0 const minLen = minFrac + parsedNumber.integerLen; // Do any carrying, e.g. a digit was rounded up to 10 const carry = digits.reduceRight(function (carry, d, i, digits) { d = d + carry; digits[i] = d < 10 ? d : d - 10; // d % 10 if (dropTrailingZeros) { // Do not keep meaningless fractional trailing zeros (e.g. 15.52000 --> 15.52) if (digits[i] === 0 && i >= minLen) { digits.pop(); } else { dropTrailingZeros = false; } } return d >= 10 ? 1 : 0; // Math.floor(d / 10); }, 0); if (carry) { digits.unshift(carry); parsedNumber.integerLen++; } } function parseIntAutoRadix(text) { const result = parseInt(text); if (isNaN(result)) { throw new Error('Invalid integer literal when parsing ' + text); } return result; } /** * @publicApi */ class NgLocalization {} NgLocalization.ɵfac = function NgLocalization_Factory(t) { return new (t || NgLocalization)(); }; NgLocalization.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: NgLocalization, factory: function NgLocalization_Factory(t) { let r = null; if (t) { r = new t(); } else { r = (locale => new NgLocaleLocalization(locale))(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID)); } return r; }, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgLocalization, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root', useFactory: locale => new NgLocaleLocalization(locale), deps: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID] }] }], null, null); })(); /** * Returns the plural category for a given value. * - "=value" when the case exists, * - the plural category otherwise */ function getPluralCategory(value, cases, ngLocalization, locale) { let key = `=${value}`; if (cases.indexOf(key) > -1) { return key; } key = ngLocalization.getPluralCategory(value, locale); if (cases.indexOf(key) > -1) { return key; } if (cases.indexOf('other') > -1) { return 'other'; } throw new Error(`No plural message found for value "${value}"`); } /** * Returns the plural case based on the locale * * @publicApi */ class NgLocaleLocalization extends NgLocalization { constructor(locale) { super(); this.locale = locale; } getPluralCategory(value, locale) { const plural = getLocalePluralCase(locale || this.locale)(value); switch (plural) { case Plural.Zero: return 'zero'; case Plural.One: return 'one'; case Plural.Two: return 'two'; case Plural.Few: return 'few'; case Plural.Many: return 'many'; default: return 'other'; } } } NgLocaleLocalization.ɵfac = function NgLocaleLocalization_Factory(t) { return new (t || NgLocaleLocalization)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID)); }; NgLocaleLocalization.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: NgLocaleLocalization, factory: NgLocaleLocalization.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgLocaleLocalization, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID] }] }]; }, null); })(); /** * Register global data to be used internally by Angular. See the * ["I18n guide"](guide/i18n-common-format-data-locale) to know how to import additional locale * data. * * The signature registerLocaleData(data: any, extraData?: any) is deprecated since v5.1 * * @publicApi */ function registerLocaleData(data, localeId, extraData) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵregisterLocaleData"])(data, localeId, extraData); } function parseCookieValue(cookieStr, name) { name = encodeURIComponent(name); for (const cookie of cookieStr.split(';')) { const eqIndex = cookie.indexOf('='); const [cookieName, cookieValue] = eqIndex == -1 ? [cookie, ''] : [cookie.slice(0, eqIndex), cookie.slice(eqIndex + 1)]; if (cookieName.trim() === name) { return decodeURIComponent(cookieValue); } } return null; } const WS_REGEXP = /\s+/; const EMPTY_ARRAY = []; /** * @ngModule CommonModule * * @usageNotes * ``` * <some-element [ngClass]="'first second'">...</some-element> * * <some-element [ngClass]="['first', 'second']">...</some-element> * * <some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element> * * <some-element [ngClass]="stringExp|arrayExp|objExp">...</some-element> * * <some-element [ngClass]="{'class1 class2 class3' : true}">...</some-element> * ``` * * @description * * Adds and removes CSS classes on an HTML element. * * The CSS classes are updated as follows, depending on the type of the expression evaluation: * - `string` - the CSS classes listed in the string (space delimited) are added, * - `Array` - the CSS classes declared as Array elements are added, * - `Object` - keys are CSS classes that get added when the expression given in the value * evaluates to a truthy value, otherwise they are removed. * * @publicApi */ class NgClass { constructor( // leaving references to differs in place since flex layout is extending NgClass... _iterableDiffers, _keyValueDiffers, _ngEl, _renderer) { this._iterableDiffers = _iterableDiffers; this._keyValueDiffers = _keyValueDiffers; this._ngEl = _ngEl; this._renderer = _renderer; this.initialClasses = EMPTY_ARRAY; this.stateMap = new Map(); } set klass(value) { this.initialClasses = value != null ? value.trim().split(WS_REGEXP) : EMPTY_ARRAY; } set ngClass(value) { this.rawClass = typeof value === 'string' ? value.trim().split(WS_REGEXP) : value; } /* The NgClass directive uses the custom change detection algorithm for its inputs. The custom algorithm is necessary since inputs are represented as complex object or arrays that need to be deeply-compared. This algorithm is perf-sensitive since NgClass is used very frequently and its poor performance might negatively impact runtime performance of the entire change detection cycle. The design of this algorithm is making sure that: - there is no unnecessary DOM manipulation (CSS classes are added / removed from the DOM only when needed), even if references to bound objects change; - there is no memory allocation if nothing changes (even relatively modest memory allocation during the change detection cycle can result in GC pauses for some of the CD cycles). The algorithm works by iterating over the set of bound classes, staring with [class] binding and then going over [ngClass] binding. For each CSS class name: - check if it was seen before (this information is tracked in the state map) and if its value changed; - mark it as "touched" - names that are not marked are not present in the latest set of binding and we can remove such class name from the internal data structures; After iteration over all the CSS class names we've got data structure with all the information necessary to synchronize changes to the DOM - it is enough to iterate over the state map, flush changes to the DOM and reset internal data structures so those are ready for the next change detection cycle. */ ngDoCheck() { // classes from the [class] binding for (const klass of this.initialClasses) { this._updateState(klass, true); } // classes from the [ngClass] binding const rawClass = this.rawClass; if (Array.isArray(rawClass) || rawClass instanceof Set) { for (const klass of rawClass) { this._updateState(klass, true); } } else if (rawClass != null) { for (const klass of Object.keys(rawClass)) { this._updateState(klass, Boolean(rawClass[klass])); } } this._applyStateDiff(); } _updateState(klass, nextEnabled) { const state = this.stateMap.get(klass); if (state !== undefined) { if (state.enabled !== nextEnabled) { state.changed = true; state.enabled = nextEnabled; } state.touched = true; } else { this.stateMap.set(klass, { enabled: nextEnabled, changed: true, touched: true }); } } _applyStateDiff() { for (const stateEntry of this.stateMap) { const klass = stateEntry[0]; const state = stateEntry[1]; if (state.changed) { this._toggleClass(klass, state.enabled); state.changed = false; } else if (!state.touched) { // A class that was previously active got removed from the new collection of classes - // remove from the DOM as well. if (state.enabled) { this._toggleClass(klass, false); } this.stateMap.delete(klass); } state.touched = false; } } _toggleClass(klass, enabled) { if (ngDevMode) { if (typeof klass !== 'string') { throw new Error(`NgClass can only toggle CSS classes expressed as strings, got ${(0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵstringify"])(klass)}`); } } klass = klass.trim(); if (klass.length > 0) { klass.split(WS_REGEXP).forEach(klass => { if (enabled) { this._renderer.addClass(this._ngEl.nativeElement, klass); } else { this._renderer.removeClass(this._ngEl.nativeElement, klass); } }); } } } NgClass.ɵfac = function NgClass_Factory(t) { return new (t || NgClass)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.IterableDiffers), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.KeyValueDiffers), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2)); }; NgClass.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgClass, selectors: [["", "ngClass", ""]], inputs: { klass: ["class", "klass"], ngClass: "ngClass" }, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgClass, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[ngClass]', standalone: true }] }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.IterableDiffers }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.KeyValueDiffers }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2 }]; }, { klass: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['class'] }], ngClass: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['ngClass'] }] }); })(); /** * Instantiates a {@link Component} type and inserts its Host View into the current View. * `NgComponentOutlet` provides a declarative approach for dynamic component creation. * * `NgComponentOutlet` requires a component type, if a falsy value is set the view will clear and * any existing component will be destroyed. * * @usageNotes * * ### Fine tune control * * You can control the component creation process by using the following optional attributes: * * * `ngComponentOutletInjector`: Optional custom {@link Injector} that will be used as parent for * the Component. Defaults to the injector of the current view container. * * * `ngComponentOutletContent`: Optional list of projectable nodes to insert into the content * section of the component, if it exists. * * * `ngComponentOutletNgModule`: Optional NgModule class reference to allow loading another * module dynamically, then loading a component from that module. * * * `ngComponentOutletNgModuleFactory`: Deprecated config option that allows providing optional * NgModule factory to allow loading another module dynamically, then loading a component from that * module. Use `ngComponentOutletNgModule` instead. * * ### Syntax * * Simple * ``` * <ng-container *ngComponentOutlet="componentTypeExpression"></ng-container> * ``` * * Customized injector/content * ``` * <ng-container *ngComponentOutlet="componentTypeExpression; * injector: injectorExpression; * content: contentNodesExpression;"> * </ng-container> * ``` * * Customized NgModule reference * ``` * <ng-container *ngComponentOutlet="componentTypeExpression; * ngModule: ngModuleClass;"> * </ng-container> * ``` * * ### A simple example * * {@example common/ngComponentOutlet/ts/module.ts region='SimpleExample'} * * A more complete example with additional options: * * {@example common/ngComponentOutlet/ts/module.ts region='CompleteExample'} * * @publicApi * @ngModule CommonModule */ class NgComponentOutlet { constructor(_viewContainerRef) { this._viewContainerRef = _viewContainerRef; this.ngComponentOutlet = null; } /** @nodoc */ ngOnChanges(changes) { const { _viewContainerRef: viewContainerRef, ngComponentOutletNgModule: ngModule, ngComponentOutletNgModuleFactory: ngModuleFactory } = this; viewContainerRef.clear(); this._componentRef = undefined; if (this.ngComponentOutlet) { const injector = this.ngComponentOutletInjector || viewContainerRef.parentInjector; if (changes['ngComponentOutletNgModule'] || changes['ngComponentOutletNgModuleFactory']) { if (this._moduleRef) this._moduleRef.destroy(); if (ngModule) { this._moduleRef = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.createNgModule)(ngModule, getParentInjector(injector)); } else if (ngModuleFactory) { this._moduleRef = ngModuleFactory.create(getParentInjector(injector)); } else { this._moduleRef = undefined; } } this._componentRef = viewContainerRef.createComponent(this.ngComponentOutlet, { index: viewContainerRef.length, injector, ngModuleRef: this._moduleRef, projectableNodes: this.ngComponentOutletContent }); } } /** @nodoc */ ngOnDestroy() { if (this._moduleRef) this._moduleRef.destroy(); } } NgComponentOutlet.ɵfac = function NgComponentOutlet_Factory(t) { return new (t || NgComponentOutlet)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef)); }; NgComponentOutlet.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgComponentOutlet, selectors: [["", "ngComponentOutlet", ""]], inputs: { ngComponentOutlet: "ngComponentOutlet", ngComponentOutletInjector: "ngComponentOutletInjector", ngComponentOutletContent: "ngComponentOutletContent", ngComponentOutletNgModule: "ngComponentOutletNgModule", ngComponentOutletNgModuleFactory: "ngComponentOutletNgModuleFactory" }, standalone: true, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵNgOnChangesFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgComponentOutlet, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[ngComponentOutlet]', standalone: true }] }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef }]; }, { ngComponentOutlet: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], ngComponentOutletInjector: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], ngComponentOutletContent: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], ngComponentOutletNgModule: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], ngComponentOutletNgModuleFactory: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); // Helper function that returns an Injector instance of a parent NgModule. function getParentInjector(injector) { const parentNgModule = injector.get(_angular_core__WEBPACK_IMPORTED_MODULE_0__.NgModuleRef); return parentNgModule.injector; } const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode; /** * @publicApi */ class NgForOfContext { constructor($implicit, ngForOf, index, count) { this.$implicit = $implicit; this.ngForOf = ngForOf; this.index = index; this.count = count; } get first() { return this.index === 0; } get last() { return this.index === this.count - 1; } get even() { return this.index % 2 === 0; } get odd() { return !this.even; } } /** * A [structural directive](guide/structural-directives) that renders * a template for each item in a collection. * The directive is placed on an element, which becomes the parent * of the cloned templates. * * The `ngForOf` directive is generally used in the * [shorthand form](guide/structural-directives#asterisk) `*ngFor`. * In this form, the template to be rendered for each iteration is the content * of an anchor element containing the directive. * * The following example shows the shorthand syntax with some options, * contained in an `<li>` element. * * ``` * <li *ngFor="let item of items; index as i; trackBy: trackByFn">...</li> * ``` * * The shorthand form expands into a long form that uses the `ngForOf` selector * on an `<ng-template>` element. * The content of the `<ng-template>` element is the `<li>` element that held the * short-form directive. * * Here is the expanded version of the short-form example. * * ``` * <ng-template ngFor let-item [ngForOf]="items" let-i="index" [ngForTrackBy]="trackByFn"> * <li>...</li> * </ng-template> * ``` * * Angular automatically expands the shorthand syntax as it compiles the template. * The context for each embedded view is logically merged to the current component * context according to its lexical position. * * When using the shorthand syntax, Angular allows only [one structural directive * on an element](guide/structural-directives#one-per-element). * If you want to iterate conditionally, for example, * put the `*ngIf` on a container element that wraps the `*ngFor` element. * For further discussion, see * [Structural Directives](guide/structural-directives#one-per-element). * * @usageNotes * * ### Local variables * * `NgForOf` provides exported values that can be aliased to local variables. * For example: * * ``` * <li *ngFor="let user of users; index as i; first as isFirst"> * {{i}}/{{users.length}}. {{user}} <span *ngIf="isFirst">default</span> * </li> * ``` * * The following exported values can be aliased to local variables: * * - `$implicit: T`: The value of the individual items in the iterable (`ngForOf`). * - `ngForOf: NgIterable<T>`: The value of the iterable expression. Useful when the expression is * more complex then a property access, for example when using the async pipe (`userStreams | * async`). * - `index: number`: The index of the current item in the iterable. * - `count: number`: The length of the iterable. * - `first: boolean`: True when the item is the first item in the iterable. * - `last: boolean`: True when the item is the last item in the iterable. * - `even: boolean`: True when the item has an even index in the iterable. * - `odd: boolean`: True when the item has an odd index in the iterable. * * ### Change propagation * * When the contents of the iterator changes, `NgForOf` makes the corresponding changes to the DOM: * * * When an item is added, a new instance of the template is added to the DOM. * * When an item is removed, its template instance is removed from the DOM. * * When items are reordered, their respective templates are reordered in the DOM. * * Angular uses object identity to track insertions and deletions within the iterator and reproduce * those changes in the DOM. This has important implications for animations and any stateful * controls that are present, such as `<input>` elements that accept user input. Inserted rows can * be animated in, deleted rows can be animated out, and unchanged rows retain any unsaved state * such as user input. * For more on animations, see [Transitions and Triggers](guide/transition-and-triggers). * * The identities of elements in the iterator can change while the data does not. * This can happen, for example, if the iterator is produced from an RPC to the server, and that * RPC is re-run. Even if the data hasn't changed, the second response produces objects with * different identities, and Angular must tear down the entire DOM and rebuild it (as if all old * elements were deleted and all new elements inserted). * * To avoid this expensive operation, you can customize the default tracking algorithm. * by supplying the `trackBy` option to `NgForOf`. * `trackBy` takes a function that has two arguments: `index` and `item`. * If `trackBy` is given, Angular tracks changes by the return value of the function. * * @see [Structural Directives](guide/structural-directives) * @ngModule CommonModule * @publicApi */ class NgForOf { /** * The value of the iterable expression, which can be used as a * [template input variable](guide/structural-directives#shorthand). */ set ngForOf(ngForOf) { this._ngForOf = ngForOf; this._ngForOfDirty = true; } /** * Specifies a custom `TrackByFunction` to compute the identity of items in an iterable. * * If a custom `TrackByFunction` is not provided, `NgForOf` will use the item's [object * identity](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) * as the key. * * `NgForOf` uses the computed key to associate items in an iterable with DOM elements * it produces for these items. * * A custom `TrackByFunction` is useful to provide good user experience in cases when items in an * iterable rendered using `NgForOf` have a natural identifier (for example, custom ID or a * primary key), and this iterable could be updated with new object instances that still * represent the same underlying entity (for example, when data is re-fetched from the server, * and the iterable is recreated and re-rendered, but most of the data is still the same). * * @see `TrackByFunction` */ set ngForTrackBy(fn) { if (NG_DEV_MODE && fn != null && typeof fn !== 'function') { console.warn(`trackBy must be a function, but received ${JSON.stringify(fn)}. ` + `See https://angular.io/api/common/NgForOf#change-propagation for more information.`); } this._trackByFn = fn; } get ngForTrackBy() { return this._trackByFn; } constructor(_viewContainer, _template, _differs) { this._viewContainer = _viewContainer; this._template = _template; this._differs = _differs; this._ngForOf = null; this._ngForOfDirty = true; this._differ = null; } /** * A reference to the template that is stamped out for each item in the iterable. * @see [template reference variable](guide/template-reference-variables) */ set ngForTemplate(value) { // TODO(TS2.1): make TemplateRef<Partial<NgForRowOf<T>>> once we move to TS v2.1 // The current type is too restrictive; a template that just uses index, for example, // should be acceptable. if (value) { this._template = value; } } /** * Applies the changes when needed. * @nodoc */ ngDoCheck() { if (this._ngForOfDirty) { this._ngForOfDirty = false; // React on ngForOf changes only once all inputs have been initialized const value = this._ngForOf; if (!this._differ && value) { if (NG_DEV_MODE) { try { // CAUTION: this logic is duplicated for production mode below, as the try-catch // is only present in development builds. this._differ = this._differs.find(value).create(this.ngForTrackBy); } catch { let errorMessage = `Cannot find a differ supporting object '${value}' of type '` + `${getTypeName(value)}'. NgFor only supports binding to Iterables, such as Arrays.`; if (typeof value === 'object') { errorMessage += ' Did you mean to use the keyvalue pipe?'; } throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](-2200 /* RuntimeErrorCode.NG_FOR_MISSING_DIFFER */, errorMessage); } } else { // CAUTION: this logic is duplicated for development mode above, as the try-catch // is only present in development builds. this._differ = this._differs.find(value).create(this.ngForTrackBy); } } } if (this._differ) { const changes = this._differ.diff(this._ngForOf); if (changes) this._applyChanges(changes); } } _applyChanges(changes) { const viewContainer = this._viewContainer; changes.forEachOperation((item, adjustedPreviousIndex, currentIndex) => { if (item.previousIndex == null) { // NgForOf is never "null" or "undefined" here because the differ detected // that a new item needs to be inserted from the iterable. This implies that // there is an iterable value for "_ngForOf". viewContainer.createEmbeddedView(this._template, new NgForOfContext(item.item, this._ngForOf, -1, -1), currentIndex === null ? undefined : currentIndex); } else if (currentIndex == null) { viewContainer.remove(adjustedPreviousIndex === null ? undefined : adjustedPreviousIndex); } else if (adjustedPreviousIndex !== null) { const view = viewContainer.get(adjustedPreviousIndex); viewContainer.move(view, currentIndex); applyViewChange(view, item); } }); for (let i = 0, ilen = viewContainer.length; i < ilen; i++) { const viewRef = viewContainer.get(i); const context = viewRef.context; context.index = i; context.count = ilen; context.ngForOf = this._ngForOf; } changes.forEachIdentityChange(record => { const viewRef = viewContainer.get(record.currentIndex); applyViewChange(viewRef, record); }); } /** * Asserts the correct type of the context for the template that `NgForOf` will render. * * The presence of this method is a signal to the Ivy template type-check compiler that the * `NgForOf` structural directive renders its template with a specific context type. */ static ngTemplateContextGuard(dir, ctx) { return true; } } NgForOf.ɵfac = function NgForOf_Factory(t) { return new (t || NgForOf)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.IterableDiffers)); }; NgForOf.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgForOf, selectors: [["", "ngFor", "", "ngForOf", ""]], inputs: { ngForOf: "ngForOf", ngForTrackBy: "ngForTrackBy", ngForTemplate: "ngForTemplate" }, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgForOf, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[ngFor][ngForOf]', standalone: true }] }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.IterableDiffers }]; }, { ngForOf: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], ngForTrackBy: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], ngForTemplate: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); function applyViewChange(view, record) { view.context.$implicit = record.item; } function getTypeName(type) { return type['name'] || typeof type; } /** * A structural directive that conditionally includes a template based on the value of * an expression coerced to Boolean. * When the expression evaluates to true, Angular renders the template * provided in a `then` clause, and when false or null, * Angular renders the template provided in an optional `else` clause. The default * template for the `else` clause is blank. * * A [shorthand form](guide/structural-directives#asterisk) of the directive, * `*ngIf="condition"`, is generally used, provided * as an attribute of the anchor element for the inserted template. * Angular expands this into a more explicit version, in which the anchor element * is contained in an `<ng-template>` element. * * Simple form with shorthand syntax: * * ``` * <div *ngIf="condition">Content to render when condition is true.</div> * ``` * * Simple form with expanded syntax: * * ``` * <ng-template [ngIf]="condition"><div>Content to render when condition is * true.</div></ng-template> * ``` * * Form with an "else" block: * * ``` * <div *ngIf="condition; else elseBlock">Content to render when condition is true.</div> * <ng-template #elseBlock>Content to render when condition is false.</ng-template> * ``` * * Shorthand form with "then" and "else" blocks: * * ``` * <div *ngIf="condition; then thenBlock else elseBlock"></div> * <ng-template #thenBlock>Content to render when condition is true.</ng-template> * <ng-template #elseBlock>Content to render when condition is false.</ng-template> * ``` * * Form with storing the value locally: * * ``` * <div *ngIf="condition as value; else elseBlock">{{value}}</div> * <ng-template #elseBlock>Content to render when value is null.</ng-template> * ``` * * @usageNotes * * The `*ngIf` directive is most commonly used to conditionally show an inline template, * as seen in the following example. * The default `else` template is blank. * * {@example common/ngIf/ts/module.ts region='NgIfSimple'} * * ### Showing an alternative template using `else` * * To display a template when `expression` evaluates to false, use an `else` template * binding as shown in the following example. * The `else` binding points to an `<ng-template>` element labeled `#elseBlock`. * The template can be defined anywhere in the component view, but is typically placed right after * `ngIf` for readability. * * {@example common/ngIf/ts/module.ts region='NgIfElse'} * * ### Using an external `then` template * * In the previous example, the then-clause template is specified inline, as the content of the * tag that contains the `ngIf` directive. You can also specify a template that is defined * externally, by referencing a labeled `<ng-template>` element. When you do this, you can * change which template to use at runtime, as shown in the following example. * * {@example common/ngIf/ts/module.ts region='NgIfThenElse'} * * ### Storing a conditional result in a variable * * You might want to show a set of properties from the same object. If you are waiting * for asynchronous data, the object can be undefined. * In this case, you can use `ngIf` and store the result of the condition in a local * variable as shown in the following example. * * {@example common/ngIf/ts/module.ts region='NgIfAs'} * * This code uses only one `AsyncPipe`, so only one subscription is created. * The conditional statement stores the result of `userStream|async` in the local variable `user`. * You can then bind the local `user` repeatedly. * * The conditional displays the data only if `userStream` returns a value, * so you don't need to use the * safe-navigation-operator (`?.`) * to guard against null values when accessing properties. * You can display an alternative template while waiting for the data. * * ### Shorthand syntax * * The shorthand syntax `*ngIf` expands into two separate template specifications * for the "then" and "else" clauses. For example, consider the following shorthand statement, * that is meant to show a loading page while waiting for data to be loaded. * * ``` * <div class="hero-list" *ngIf="heroes else loading"> * ... * </div> * * <ng-template #loading> * <div>Loading...</div> * </ng-template> * ``` * * You can see that the "else" clause references the `<ng-template>` * with the `#loading` label, and the template for the "then" clause * is provided as the content of the anchor element. * * However, when Angular expands the shorthand syntax, it creates * another `<ng-template>` tag, with `ngIf` and `ngIfElse` directives. * The anchor element containing the template for the "then" clause becomes * the content of this unlabeled `<ng-template>` tag. * * ``` * <ng-template [ngIf]="heroes" [ngIfElse]="loading"> * <div class="hero-list"> * ... * </div> * </ng-template> * * <ng-template #loading> * <div>Loading...</div> * </ng-template> * ``` * * The presence of the implicit template object has implications for the nesting of * structural directives. For more on this subject, see * [Structural Directives](guide/structural-directives#one-per-element). * * @ngModule CommonModule * @publicApi */ class NgIf { constructor(_viewContainer, templateRef) { this._viewContainer = _viewContainer; this._context = new NgIfContext(); this._thenTemplateRef = null; this._elseTemplateRef = null; this._thenViewRef = null; this._elseViewRef = null; this._thenTemplateRef = templateRef; } /** * The Boolean expression to evaluate as the condition for showing a template. */ set ngIf(condition) { this._context.$implicit = this._context.ngIf = condition; this._updateView(); } /** * A template to show if the condition expression evaluates to true. */ set ngIfThen(templateRef) { assertTemplate('ngIfThen', templateRef); this._thenTemplateRef = templateRef; this._thenViewRef = null; // clear previous view if any. this._updateView(); } /** * A template to show if the condition expression evaluates to false. */ set ngIfElse(templateRef) { assertTemplate('ngIfElse', templateRef); this._elseTemplateRef = templateRef; this._elseViewRef = null; // clear previous view if any. this._updateView(); } _updateView() { if (this._context.$implicit) { if (!this._thenViewRef) { this._viewContainer.clear(); this._elseViewRef = null; if (this._thenTemplateRef) { this._thenViewRef = this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context); } } } else { if (!this._elseViewRef) { this._viewContainer.clear(); this._thenViewRef = null; if (this._elseTemplateRef) { this._elseViewRef = this._viewContainer.createEmbeddedView(this._elseTemplateRef, this._context); } } } } /** * Asserts the correct type of the context for the template that `NgIf` will render. * * The presence of this method is a signal to the Ivy template type-check compiler that the * `NgIf` structural directive renders its template with a specific context type. */ static ngTemplateContextGuard(dir, ctx) { return true; } } NgIf.ɵfac = function NgIf_Factory(t) { return new (t || NgIf)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef)); }; NgIf.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgIf, selectors: [["", "ngIf", ""]], inputs: { ngIf: "ngIf", ngIfThen: "ngIfThen", ngIfElse: "ngIfElse" }, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgIf, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[ngIf]', standalone: true }] }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef }]; }, { ngIf: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], ngIfThen: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], ngIfElse: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /** * @publicApi */ class NgIfContext { constructor() { this.$implicit = null; this.ngIf = null; } } function assertTemplate(property, templateRef) { const isTemplateRefOrNull = !!(!templateRef || templateRef.createEmbeddedView); if (!isTemplateRefOrNull) { throw new Error(`${property} must be a TemplateRef, but received '${(0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵstringify"])(templateRef)}'.`); } } class SwitchView { constructor(_viewContainerRef, _templateRef) { this._viewContainerRef = _viewContainerRef; this._templateRef = _templateRef; this._created = false; } create() { this._created = true; this._viewContainerRef.createEmbeddedView(this._templateRef); } destroy() { this._created = false; this._viewContainerRef.clear(); } enforceState(created) { if (created && !this._created) { this.create(); } else if (!created && this._created) { this.destroy(); } } } /** * @ngModule CommonModule * * @description * The `[ngSwitch]` directive on a container specifies an expression to match against. * The expressions to match are provided by `ngSwitchCase` directives on views within the container. * - Every view that matches is rendered. * - If there are no matches, a view with the `ngSwitchDefault` directive is rendered. * - Elements within the `[NgSwitch]` statement but outside of any `NgSwitchCase` * or `ngSwitchDefault` directive are preserved at the location. * * @usageNotes * Define a container element for the directive, and specify the switch expression * to match against as an attribute: * * ``` * <container-element [ngSwitch]="switch_expression"> * ``` * * Within the container, `*ngSwitchCase` statements specify the match expressions * as attributes. Include `*ngSwitchDefault` as the final case. * * ``` * <container-element [ngSwitch]="switch_expression"> * <some-element *ngSwitchCase="match_expression_1">...</some-element> * ... * <some-element *ngSwitchDefault>...</some-element> * </container-element> * ``` * * ### Usage Examples * * The following example shows how to use more than one case to display the same view: * * ``` * <container-element [ngSwitch]="switch_expression"> * <!-- the same view can be shown in more than one case --> * <some-element *ngSwitchCase="match_expression_1">...</some-element> * <some-element *ngSwitchCase="match_expression_2">...</some-element> * <some-other-element *ngSwitchCase="match_expression_3">...</some-other-element> * <!--default case when there are no matches --> * <some-element *ngSwitchDefault>...</some-element> * </container-element> * ``` * * The following example shows how cases can be nested: * ``` * <container-element [ngSwitch]="switch_expression"> * <some-element *ngSwitchCase="match_expression_1">...</some-element> * <some-element *ngSwitchCase="match_expression_2">...</some-element> * <some-other-element *ngSwitchCase="match_expression_3">...</some-other-element> * <ng-container *ngSwitchCase="match_expression_3"> * <!-- use a ng-container to group multiple root nodes --> * <inner-element></inner-element> * <inner-other-element></inner-other-element> * </ng-container> * <some-element *ngSwitchDefault>...</some-element> * </container-element> * ``` * * @publicApi * @see `NgSwitchCase` * @see `NgSwitchDefault` * @see [Structural Directives](guide/structural-directives) * */ class NgSwitch { constructor() { this._defaultViews = []; this._defaultUsed = false; this._caseCount = 0; this._lastCaseCheckIndex = 0; this._lastCasesMatched = false; } set ngSwitch(newValue) { this._ngSwitch = newValue; if (this._caseCount === 0) { this._updateDefaultCases(true); } } /** @internal */ _addCase() { return this._caseCount++; } /** @internal */ _addDefault(view) { this._defaultViews.push(view); } /** @internal */ _matchCase(value) { const matched = value == this._ngSwitch; this._lastCasesMatched = this._lastCasesMatched || matched; this._lastCaseCheckIndex++; if (this._lastCaseCheckIndex === this._caseCount) { this._updateDefaultCases(!this._lastCasesMatched); this._lastCaseCheckIndex = 0; this._lastCasesMatched = false; } return matched; } _updateDefaultCases(useDefault) { if (this._defaultViews.length > 0 && useDefault !== this._defaultUsed) { this._defaultUsed = useDefault; for (const defaultView of this._defaultViews) { defaultView.enforceState(useDefault); } } } } NgSwitch.ɵfac = function NgSwitch_Factory(t) { return new (t || NgSwitch)(); }; NgSwitch.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgSwitch, selectors: [["", "ngSwitch", ""]], inputs: { ngSwitch: "ngSwitch" }, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgSwitch, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[ngSwitch]', standalone: true }] }], null, { ngSwitch: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /** * @ngModule CommonModule * * @description * Provides a switch case expression to match against an enclosing `ngSwitch` expression. * When the expressions match, the given `NgSwitchCase` template is rendered. * If multiple match expressions match the switch expression value, all of them are displayed. * * @usageNotes * * Within a switch container, `*ngSwitchCase` statements specify the match expressions * as attributes. Include `*ngSwitchDefault` as the final case. * * ``` * <container-element [ngSwitch]="switch_expression"> * <some-element *ngSwitchCase="match_expression_1">...</some-element> * ... * <some-element *ngSwitchDefault>...</some-element> * </container-element> * ``` * * Each switch-case statement contains an in-line HTML template or template reference * that defines the subtree to be selected if the value of the match expression * matches the value of the switch expression. * * Unlike JavaScript, which uses strict equality, Angular uses loose equality. * This means that the empty string, `""` matches 0. * * @publicApi * @see `NgSwitch` * @see `NgSwitchDefault` * */ class NgSwitchCase { constructor(viewContainer, templateRef, ngSwitch) { this.ngSwitch = ngSwitch; if ((typeof ngDevMode === 'undefined' || ngDevMode) && !ngSwitch) { throwNgSwitchProviderNotFoundError('ngSwitchCase', 'NgSwitchCase'); } ngSwitch._addCase(); this._view = new SwitchView(viewContainer, templateRef); } /** * Performs case matching. For internal use only. * @nodoc */ ngDoCheck() { this._view.enforceState(this.ngSwitch._matchCase(this.ngSwitchCase)); } } NgSwitchCase.ɵfac = function NgSwitchCase_Factory(t) { return new (t || NgSwitchCase)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NgSwitch, 9)); }; NgSwitchCase.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgSwitchCase, selectors: [["", "ngSwitchCase", ""]], inputs: { ngSwitchCase: "ngSwitchCase" }, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgSwitchCase, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[ngSwitchCase]', standalone: true }] }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef }, { type: NgSwitch, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Host }] }]; }, { ngSwitchCase: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /** * @ngModule CommonModule * * @description * * Creates a view that is rendered when no `NgSwitchCase` expressions * match the `NgSwitch` expression. * This statement should be the final case in an `NgSwitch`. * * @publicApi * @see `NgSwitch` * @see `NgSwitchCase` * */ class NgSwitchDefault { constructor(viewContainer, templateRef, ngSwitch) { if ((typeof ngDevMode === 'undefined' || ngDevMode) && !ngSwitch) { throwNgSwitchProviderNotFoundError('ngSwitchDefault', 'NgSwitchDefault'); } ngSwitch._addDefault(new SwitchView(viewContainer, templateRef)); } } NgSwitchDefault.ɵfac = function NgSwitchDefault_Factory(t) { return new (t || NgSwitchDefault)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NgSwitch, 9)); }; NgSwitchDefault.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgSwitchDefault, selectors: [["", "ngSwitchDefault", ""]], standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgSwitchDefault, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[ngSwitchDefault]', standalone: true }] }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef }, { type: NgSwitch, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Host }] }]; }, null); })(); function throwNgSwitchProviderNotFoundError(attrName, directiveName) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2000 /* RuntimeErrorCode.PARENT_NG_SWITCH_NOT_FOUND */, `An element with the "${attrName}" attribute ` + `(matching the "${directiveName}" directive) must be located inside an element with the "ngSwitch" attribute ` + `(matching "NgSwitch" directive)`); } /** * @ngModule CommonModule * * @usageNotes * ``` * <some-element [ngPlural]="value"> * <ng-template ngPluralCase="=0">there is nothing</ng-template> * <ng-template ngPluralCase="=1">there is one</ng-template> * <ng-template ngPluralCase="few">there are a few</ng-template> * </some-element> * ``` * * @description * * Adds / removes DOM sub-trees based on a numeric value. Tailored for pluralization. * * Displays DOM sub-trees that match the switch expression value, or failing that, DOM sub-trees * that match the switch expression's pluralization category. * * To use this directive you must provide a container element that sets the `[ngPlural]` attribute * to a switch expression. Inner elements with a `[ngPluralCase]` will display based on their * expression: * - if `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value * matches the switch expression exactly, * - otherwise, the view will be treated as a "category match", and will only display if exact * value matches aren't found and the value maps to its category for the defined locale. * * See http://cldr.unicode.org/index/cldr-spec/plural-rules * * @publicApi */ class NgPlural { constructor(_localization) { this._localization = _localization; this._caseViews = {}; } set ngPlural(value) { this._updateView(value); } addCase(value, switchView) { this._caseViews[value] = switchView; } _updateView(switchValue) { this._clearViews(); const cases = Object.keys(this._caseViews); const key = getPluralCategory(switchValue, cases, this._localization); this._activateView(this._caseViews[key]); } _clearViews() { if (this._activeView) this._activeView.destroy(); } _activateView(view) { if (view) { this._activeView = view; this._activeView.create(); } } } NgPlural.ɵfac = function NgPlural_Factory(t) { return new (t || NgPlural)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NgLocalization)); }; NgPlural.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgPlural, selectors: [["", "ngPlural", ""]], inputs: { ngPlural: "ngPlural" }, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgPlural, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[ngPlural]', standalone: true }] }], function () { return [{ type: NgLocalization }]; }, { ngPlural: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /** * @ngModule CommonModule * * @description * * Creates a view that will be added/removed from the parent {@link NgPlural} when the * given expression matches the plural expression according to CLDR rules. * * @usageNotes * ``` * <some-element [ngPlural]="value"> * <ng-template ngPluralCase="=0">...</ng-template> * <ng-template ngPluralCase="other">...</ng-template> * </some-element> *``` * * See {@link NgPlural} for more details and example. * * @publicApi */ class NgPluralCase { constructor(value, template, viewContainer, ngPlural) { this.value = value; const isANumber = !isNaN(Number(value)); ngPlural.addCase(isANumber ? `=${value}` : value, new SwitchView(viewContainer, template)); } } NgPluralCase.ɵfac = function NgPluralCase_Factory(t) { return new (t || NgPluralCase)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinjectAttribute"]('ngPluralCase'), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NgPlural, 1)); }; NgPluralCase.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgPluralCase, selectors: [["", "ngPluralCase", ""]], standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgPluralCase, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[ngPluralCase]', standalone: true }] }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Attribute, args: ['ngPluralCase'] }] }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.TemplateRef }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef }, { type: NgPlural, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Host }] }]; }, null); })(); /** * @ngModule CommonModule * * @usageNotes * * Set the font of the containing element to the result of an expression. * * ``` * <some-element [ngStyle]="{'font-style': styleExp}">...</some-element> * ``` * * Set the width of the containing element to a pixel value returned by an expression. * * ``` * <some-element [ngStyle]="{'max-width.px': widthExp}">...</some-element> * ``` * * Set a collection of style values using an expression that returns key-value pairs. * * ``` * <some-element [ngStyle]="objExp">...</some-element> * ``` * * @description * * An attribute directive that updates styles for the containing HTML element. * Sets one or more style properties, specified as colon-separated key-value pairs. * The key is a style name, with an optional `.<unit>` suffix * (such as 'top.px', 'font-style.em'). * The value is an expression to be evaluated. * The resulting non-null value, expressed in the given unit, * is assigned to the given style property. * If the result of evaluation is null, the corresponding style is removed. * * @publicApi */ class NgStyle { constructor(_ngEl, _differs, _renderer) { this._ngEl = _ngEl; this._differs = _differs; this._renderer = _renderer; this._ngStyle = null; this._differ = null; } set ngStyle(values) { this._ngStyle = values; if (!this._differ && values) { this._differ = this._differs.find(values).create(); } } ngDoCheck() { if (this._differ) { const changes = this._differ.diff(this._ngStyle); if (changes) { this._applyChanges(changes); } } } _setStyle(nameAndUnit, value) { const [name, unit] = nameAndUnit.split('.'); const flags = name.indexOf('-') === -1 ? undefined : _angular_core__WEBPACK_IMPORTED_MODULE_0__.RendererStyleFlags2.DashCase; if (value != null) { this._renderer.setStyle(this._ngEl.nativeElement, name, unit ? `${value}${unit}` : value, flags); } else { this._renderer.removeStyle(this._ngEl.nativeElement, name, flags); } } _applyChanges(changes) { changes.forEachRemovedItem(record => this._setStyle(record.key, null)); changes.forEachAddedItem(record => this._setStyle(record.key, record.currentValue)); changes.forEachChangedItem(record => this._setStyle(record.key, record.currentValue)); } } NgStyle.ɵfac = function NgStyle_Factory(t) { return new (t || NgStyle)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.KeyValueDiffers), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2)); }; NgStyle.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgStyle, selectors: [["", "ngStyle", ""]], inputs: { ngStyle: "ngStyle" }, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgStyle, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[ngStyle]', standalone: true }] }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.KeyValueDiffers }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2 }]; }, { ngStyle: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['ngStyle'] }] }); })(); /** * @ngModule CommonModule * * @description * * Inserts an embedded view from a prepared `TemplateRef`. * * You can attach a context object to the `EmbeddedViewRef` by setting `[ngTemplateOutletContext]`. * `[ngTemplateOutletContext]` should be an object, the object's keys will be available for binding * by the local template `let` declarations. * * @usageNotes * ``` * <ng-container *ngTemplateOutlet="templateRefExp; context: contextExp"></ng-container> * ``` * * Using the key `$implicit` in the context object will set its value as default. * * ### Example * * {@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'} * * @publicApi */ class NgTemplateOutlet { constructor(_viewContainerRef) { this._viewContainerRef = _viewContainerRef; this._viewRef = null; /** * A context object to attach to the {@link EmbeddedViewRef}. This should be an * object, the object's keys will be available for binding by the local template `let` * declarations. * Using the key `$implicit` in the context object will set its value as default. */ this.ngTemplateOutletContext = null; /** * A string defining the template reference and optionally the context object for the template. */ this.ngTemplateOutlet = null; /** Injector to be used within the embedded view. */ this.ngTemplateOutletInjector = null; } /** @nodoc */ ngOnChanges(changes) { if (changes['ngTemplateOutlet'] || changes['ngTemplateOutletInjector']) { const viewContainerRef = this._viewContainerRef; if (this._viewRef) { viewContainerRef.remove(viewContainerRef.indexOf(this._viewRef)); } if (this.ngTemplateOutlet) { const { ngTemplateOutlet: template, ngTemplateOutletContext: context, ngTemplateOutletInjector: injector } = this; this._viewRef = viewContainerRef.createEmbeddedView(template, context, injector ? { injector } : undefined); } else { this._viewRef = null; } } else if (this._viewRef && changes['ngTemplateOutletContext'] && this.ngTemplateOutletContext) { this._viewRef.context = this.ngTemplateOutletContext; } } } NgTemplateOutlet.ɵfac = function NgTemplateOutlet_Factory(t) { return new (t || NgTemplateOutlet)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef)); }; NgTemplateOutlet.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgTemplateOutlet, selectors: [["", "ngTemplateOutlet", ""]], inputs: { ngTemplateOutletContext: "ngTemplateOutletContext", ngTemplateOutlet: "ngTemplateOutlet", ngTemplateOutletInjector: "ngTemplateOutletInjector" }, standalone: true, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵNgOnChangesFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgTemplateOutlet, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[ngTemplateOutlet]', standalone: true }] }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef }]; }, { ngTemplateOutletContext: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], ngTemplateOutlet: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], ngTemplateOutletInjector: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /** * A collection of Angular directives that are likely to be used in each and every Angular * application. */ const COMMON_DIRECTIVES = [NgClass, NgComponentOutlet, NgForOf, NgIf, NgTemplateOutlet, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgPlural, NgPluralCase]; function invalidPipeArgumentError(type, value) { return new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2100 /* RuntimeErrorCode.INVALID_PIPE_ARGUMENT */, ngDevMode && `InvalidPipeArgument: '${value}' for pipe '${(0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵstringify"])(type)}'`); } class SubscribableStrategy { createSubscription(async, updateLatestValue) { return async.subscribe({ next: updateLatestValue, error: e => { throw e; } }); } dispose(subscription) { subscription.unsubscribe(); } } class PromiseStrategy { createSubscription(async, updateLatestValue) { return async.then(updateLatestValue, e => { throw e; }); } dispose(subscription) {} } const _promiseStrategy = new PromiseStrategy(); const _subscribableStrategy = new SubscribableStrategy(); /** * @ngModule CommonModule * @description * * Unwraps a value from an asynchronous primitive. * * The `async` pipe subscribes to an `Observable` or `Promise` and returns the latest value it has * emitted. When a new value is emitted, the `async` pipe marks the component to be checked for * changes. When the component gets destroyed, the `async` pipe unsubscribes automatically to avoid * potential memory leaks. When the reference of the expression changes, the `async` pipe * automatically unsubscribes from the old `Observable` or `Promise` and subscribes to the new one. * * @usageNotes * * ### Examples * * This example binds a `Promise` to the view. Clicking the `Resolve` button resolves the * promise. * * {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'} * * It's also possible to use `async` with Observables. The example below binds the `time` Observable * to the view. The Observable continuously updates the view with the current time. * * {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'} * * @publicApi */ class AsyncPipe { constructor(ref) { this._latestValue = null; this._subscription = null; this._obj = null; this._strategy = null; // Assign `ref` into `this._ref` manually instead of declaring `_ref` in the constructor // parameter list, as the type of `this._ref` includes `null` unlike the type of `ref`. this._ref = ref; } ngOnDestroy() { if (this._subscription) { this._dispose(); } // Clear the `ChangeDetectorRef` and its association with the view data, to mitigate // potential memory leaks in Observables that could otherwise cause the view data to // be retained. // https://github.com/angular/angular/issues/17624 this._ref = null; } transform(obj) { if (!this._obj) { if (obj) { this._subscribe(obj); } return this._latestValue; } if (obj !== this._obj) { this._dispose(); return this.transform(obj); } return this._latestValue; } _subscribe(obj) { this._obj = obj; this._strategy = this._selectStrategy(obj); this._subscription = this._strategy.createSubscription(obj, value => this._updateLatestValue(obj, value)); } _selectStrategy(obj) { if ((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵisPromise"])(obj)) { return _promiseStrategy; } if ((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵisSubscribable"])(obj)) { return _subscribableStrategy; } throw invalidPipeArgumentError(AsyncPipe, obj); } _dispose() { // Note: `dispose` is only called if a subscription has been initialized before, indicating // that `this._strategy` is also available. this._strategy.dispose(this._subscription); this._latestValue = null; this._subscription = null; this._obj = null; } _updateLatestValue(async, value) { if (async === this._obj) { this._latestValue = value; // Note: `this._ref` is only cleared in `ngOnDestroy` so is known to be available when a // value is being updated. this._ref.markForCheck(); } } } AsyncPipe.ɵfac = function AsyncPipe_Factory(t) { return new (t || AsyncPipe)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ChangeDetectorRef, 16)); }; AsyncPipe.ɵpipe = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ name: "async", type: AsyncPipe, pure: false, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](AsyncPipe, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, args: [{ name: 'async', pure: false, standalone: true }] }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ChangeDetectorRef }]; }, null); })(); /** * Transforms text to all lower case. * * @see `UpperCasePipe` * @see `TitleCasePipe` * @usageNotes * * The following example defines a view that allows the user to enter * text, and then uses the pipe to convert the input text to all lower case. * * <code-example path="common/pipes/ts/lowerupper_pipe.ts" region='LowerUpperPipe'></code-example> * * @ngModule CommonModule * @publicApi */ class LowerCasePipe { transform(value) { if (value == null) return null; if (typeof value !== 'string') { throw invalidPipeArgumentError(LowerCasePipe, value); } return value.toLowerCase(); } } LowerCasePipe.ɵfac = function LowerCasePipe_Factory(t) { return new (t || LowerCasePipe)(); }; LowerCasePipe.ɵpipe = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ name: "lowercase", type: LowerCasePipe, pure: true, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](LowerCasePipe, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, args: [{ name: 'lowercase', standalone: true }] }], null, null); })(); // // Regex below matches any Unicode word and number compatible with ES5. In ES2018 the same result // can be achieved by using /[0-9\p{L}]\S*/gu and also known as Unicode Property Escapes // (https://2ality.com/2017/07/regexp-unicode-property-escapes.html). Since there is no // transpilation of this functionality down to ES5 without external tool, the only solution is // to use already transpiled form. Example can be found here - // https://mothereff.in/regexpu#input=var+regex+%3D+%2F%5B0-9%5Cp%7BL%7D%5D%5CS*%2Fgu%3B%0A%0A&unicodePropertyEscape=1 // const unicodeWordMatch = /(?:[0-9A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088E\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7CA\uA7D0\uA7D1\uA7D3\uA7D5-\uA7D9\uA7F2-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF40\uDF42-\uDF49\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDD70-\uDD7A\uDD7C-\uDD8A\uDD8C-\uDD92\uDD94\uDD95\uDD97-\uDDA1\uDDA3-\uDDB1\uDDB3-\uDDB9\uDDBB\uDDBC\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDD00-\uDD23\uDE80-\uDEA9\uDEB0\uDEB1\uDF00-\uDF1C\uDF27\uDF30-\uDF45\uDF70-\uDF81\uDFB0-\uDFC4\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC71\uDC72\uDC75\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDEB8\uDF00-\uDF1A\uDF40-\uDF46]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCDF\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEB0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDEE0-\uDEF2\uDFB0]|\uD808[\uDC00-\uDF99]|\uD809[\uDC80-\uDD43]|\uD80B[\uDF90-\uDFF0]|[\uD80C\uD81C-\uD820\uD822\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879\uD880-\uD883][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE70-\uDEBE\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDE40-\uDE7F\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3]|\uD821[\uDC00-\uDFF7]|\uD823[\uDC00-\uDCD5\uDD00-\uDD08]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD82C[\uDC00-\uDD22\uDD50-\uDD52\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD837[\uDF00-\uDF1E]|\uD838[\uDD00-\uDD2C\uDD37-\uDD3D\uDD4E\uDE90-\uDEAD\uDEC0-\uDEEB]|\uD839[\uDFE0-\uDFE6\uDFE8-\uDFEB\uDFED\uDFEE\uDFF0-\uDFFE]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43\uDD4B]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF38\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A])\S*/g; /** * Transforms text to title case. * Capitalizes the first letter of each word and transforms the * rest of the word to lower case. * Words are delimited by any whitespace character, such as a space, tab, or line-feed character. * * @see `LowerCasePipe` * @see `UpperCasePipe` * * @usageNotes * The following example shows the result of transforming various strings into title case. * * <code-example path="common/pipes/ts/titlecase_pipe.ts" region='TitleCasePipe'></code-example> * * @ngModule CommonModule * @publicApi */ class TitleCasePipe { transform(value) { if (value == null) return null; if (typeof value !== 'string') { throw invalidPipeArgumentError(TitleCasePipe, value); } return value.replace(unicodeWordMatch, txt => txt[0].toUpperCase() + txt.slice(1).toLowerCase()); } } TitleCasePipe.ɵfac = function TitleCasePipe_Factory(t) { return new (t || TitleCasePipe)(); }; TitleCasePipe.ɵpipe = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ name: "titlecase", type: TitleCasePipe, pure: true, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](TitleCasePipe, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, args: [{ name: 'titlecase', standalone: true }] }], null, null); })(); /** * Transforms text to all upper case. * @see `LowerCasePipe` * @see `TitleCasePipe` * * @ngModule CommonModule * @publicApi */ class UpperCasePipe { transform(value) { if (value == null) return null; if (typeof value !== 'string') { throw invalidPipeArgumentError(UpperCasePipe, value); } return value.toUpperCase(); } } UpperCasePipe.ɵfac = function UpperCasePipe_Factory(t) { return new (t || UpperCasePipe)(); }; UpperCasePipe.ɵpipe = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ name: "uppercase", type: UpperCasePipe, pure: true, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](UpperCasePipe, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, args: [{ name: 'uppercase', standalone: true }] }], null, null); })(); /** * The default date format of Angular date pipe, which corresponds to the following format: * `'MMM d,y'` (e.g. `Jun 15, 2015`) */ const DEFAULT_DATE_FORMAT = 'mediumDate'; /** * Optionally-provided default timezone to use for all instances of `DatePipe` (such as `'+0430'`). * If the value isn't provided, the `DatePipe` will use the end-user's local system timezone. * * @deprecated use DATE_PIPE_DEFAULT_OPTIONS token to configure DatePipe */ const DATE_PIPE_DEFAULT_TIMEZONE = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('DATE_PIPE_DEFAULT_TIMEZONE'); /** * DI token that allows to provide default configuration for the `DatePipe` instances in an * application. The value is an object which can include the following fields: * - `dateFormat`: configures the default date format. If not provided, the `DatePipe` * will use the 'mediumDate' as a value. * - `timezone`: configures the default timezone. If not provided, the `DatePipe` will * use the end-user's local system timezone. * * @see `DatePipeConfig` * * @usageNotes * * Various date pipe default values can be overwritten by providing this token with * the value that has this interface. * * For example: * * Override the default date format by providing a value using the token: * ```typescript * providers: [ * {provide: DATE_PIPE_DEFAULT_OPTIONS, useValue: {dateFormat: 'shortDate'}} * ] * ``` * * Override the default timezone by providing a value using the token: * ```typescript * providers: [ * {provide: DATE_PIPE_DEFAULT_OPTIONS, useValue: {timezone: '-1200'}} * ] * ``` */ const DATE_PIPE_DEFAULT_OPTIONS = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('DATE_PIPE_DEFAULT_OPTIONS'); // clang-format off /** * @ngModule CommonModule * @description * * Formats a date value according to locale rules. * * `DatePipe` is executed only when it detects a pure change to the input value. * A pure change is either a change to a primitive input value * (such as `String`, `Number`, `Boolean`, or `Symbol`), * or a changed object reference (such as `Date`, `Array`, `Function`, or `Object`). * * Note that mutating a `Date` object does not cause the pipe to be rendered again. * To ensure that the pipe is executed, you must create a new `Date` object. * * Only the `en-US` locale data comes with Angular. To localize dates * in another language, you must import the corresponding locale data. * See the [I18n guide](guide/i18n-common-format-data-locale) for more information. * * The time zone of the formatted value can be specified either by passing it in as the second * parameter of the pipe, or by setting the default through the `DATE_PIPE_DEFAULT_OPTIONS` * injection token. The value that is passed in as the second parameter takes precedence over * the one defined using the injection token. * * @see `formatDate()` * * * @usageNotes * * The result of this pipe is not reevaluated when the input is mutated. To avoid the need to * reformat the date on every change-detection cycle, treat the date as an immutable object * and change the reference when the pipe needs to run again. * * ### Pre-defined format options * * | Option | Equivalent to | Examples (given in `en-US` locale) | * |---------------|-------------------------------------|-------------------------------------------------| * | `'short'` | `'M/d/yy, h:mm a'` | `6/15/15, 9:03 AM` | * | `'medium'` | `'MMM d, y, h:mm:ss a'` | `Jun 15, 2015, 9:03:01 AM` | * | `'long'` | `'MMMM d, y, h:mm:ss a z'` | `June 15, 2015 at 9:03:01 AM GMT+1` | * | `'full'` | `'EEEE, MMMM d, y, h:mm:ss a zzzz'` | `Monday, June 15, 2015 at 9:03:01 AM GMT+01:00` | * | `'shortDate'` | `'M/d/yy'` | `6/15/15` | * | `'mediumDate'`| `'MMM d, y'` | `Jun 15, 2015` | * | `'longDate'` | `'MMMM d, y'` | `June 15, 2015` | * | `'fullDate'` | `'EEEE, MMMM d, y'` | `Monday, June 15, 2015` | * | `'shortTime'` | `'h:mm a'` | `9:03 AM` | * | `'mediumTime'`| `'h:mm:ss a'` | `9:03:01 AM` | * | `'longTime'` | `'h:mm:ss a z'` | `9:03:01 AM GMT+1` | * | `'fullTime'` | `'h:mm:ss a zzzz'` | `9:03:01 AM GMT+01:00` | * * ### Custom format options * * You can construct a format string using symbols to specify the components * of a date-time value, as described in the following table. * Format details depend on the locale. * Fields marked with (*) are only available in the extra data set for the given locale. * * | Field type | Format | Description | Example Value | * |-------------------- |-------------|---------------------------------------------------------------|------------------------------------------------------------| * | Era | G, GG & GGG | Abbreviated | AD | * | | GGGG | Wide | Anno Domini | * | | GGGGG | Narrow | A | * | Year | y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | * | | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | * | | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | * | | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | * | Week-numbering year | Y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | * | | YY | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | * | | YYY | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | * | | YYYY | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | * | Month | M | Numeric: 1 digit | 9, 12 | * | | MM | Numeric: 2 digits + zero padded | 09, 12 | * | | MMM | Abbreviated | Sep | * | | MMMM | Wide | September | * | | MMMMM | Narrow | S | * | Month standalone | L | Numeric: 1 digit | 9, 12 | * | | LL | Numeric: 2 digits + zero padded | 09, 12 | * | | LLL | Abbreviated | Sep | * | | LLLL | Wide | September | * | | LLLLL | Narrow | S | * | Week of year | w | Numeric: minimum digits | 1... 53 | * | | ww | Numeric: 2 digits + zero padded | 01... 53 | * | Week of month | W | Numeric: 1 digit | 1... 5 | * | Day of month | d | Numeric: minimum digits | 1 | * | | dd | Numeric: 2 digits + zero padded | 01 | * | Week day | E, EE & EEE | Abbreviated | Tue | * | | EEEE | Wide | Tuesday | * | | EEEEE | Narrow | T | * | | EEEEEE | Short | Tu | * | Week day standalone | c, cc | Numeric: 1 digit | 2 | * | | ccc | Abbreviated | Tue | * | | cccc | Wide | Tuesday | * | | ccccc | Narrow | T | * | | cccccc | Short | Tu | * | Period | a, aa & aaa | Abbreviated | am/pm or AM/PM | * | | aaaa | Wide (fallback to `a` when missing) | ante meridiem/post meridiem | * | | aaaaa | Narrow | a/p | * | Period* | B, BB & BBB | Abbreviated | mid. | * | | BBBB | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | * | | BBBBB | Narrow | md | * | Period standalone* | b, bb & bbb | Abbreviated | mid. | * | | bbbb | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | * | | bbbbb | Narrow | md | * | Hour 1-12 | h | Numeric: minimum digits | 1, 12 | * | | hh | Numeric: 2 digits + zero padded | 01, 12 | * | Hour 0-23 | H | Numeric: minimum digits | 0, 23 | * | | HH | Numeric: 2 digits + zero padded | 00, 23 | * | Minute | m | Numeric: minimum digits | 8, 59 | * | | mm | Numeric: 2 digits + zero padded | 08, 59 | * | Second | s | Numeric: minimum digits | 0... 59 | * | | ss | Numeric: 2 digits + zero padded | 00... 59 | * | Fractional seconds | S | Numeric: 1 digit | 0... 9 | * | | SS | Numeric: 2 digits + zero padded | 00... 99 | * | | SSS | Numeric: 3 digits + zero padded (= milliseconds) | 000... 999 | * | Zone | z, zz & zzz | Short specific non location format (fallback to O) | GMT-8 | * | | zzzz | Long specific non location format (fallback to OOOO) | GMT-08:00 | * | | Z, ZZ & ZZZ | ISO8601 basic format | -0800 | * | | ZZZZ | Long localized GMT format | GMT-8:00 | * | | ZZZZZ | ISO8601 extended format + Z indicator for offset 0 (= XXXXX) | -08:00 | * | | O, OO & OOO | Short localized GMT format | GMT-8 | * | | OOOO | Long localized GMT format | GMT-08:00 | * * * ### Format examples * * These examples transform a date into various formats, * assuming that `dateObj` is a JavaScript `Date` object for * year: 2015, month: 6, day: 15, hour: 21, minute: 43, second: 11, * given in the local time for the `en-US` locale. * * ``` * {{ dateObj | date }} // output is 'Jun 15, 2015' * {{ dateObj | date:'medium' }} // output is 'Jun 15, 2015, 9:43:11 PM' * {{ dateObj | date:'shortTime' }} // output is '9:43 PM' * {{ dateObj | date:'mm:ss' }} // output is '43:11' * ``` * * ### Usage example * * The following component uses a date pipe to display the current date in different formats. * * ``` * @Component({ * selector: 'date-pipe', * template: `<div> * <p>Today is {{today | date}}</p> * <p>Or if you prefer, {{today | date:'fullDate'}}</p> * <p>The time is {{today | date:'h:mm a z'}}</p> * </div>` * }) * // Get the current date and time as a date-time value. * export class DatePipeComponent { * today: number = Date.now(); * } * ``` * * @publicApi */ // clang-format on class DatePipe { constructor(locale, defaultTimezone, defaultOptions) { this.locale = locale; this.defaultTimezone = defaultTimezone; this.defaultOptions = defaultOptions; } transform(value, format, timezone, locale) { if (value == null || value === '' || value !== value) return null; try { const _format = format ?? this.defaultOptions?.dateFormat ?? DEFAULT_DATE_FORMAT; const _timezone = timezone ?? this.defaultOptions?.timezone ?? this.defaultTimezone ?? undefined; return formatDate(value, _format, locale || this.locale, _timezone); } catch (error) { throw invalidPipeArgumentError(DatePipe, error.message); } } } DatePipe.ɵfac = function DatePipe_Factory(t) { return new (t || DatePipe)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID, 16), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](DATE_PIPE_DEFAULT_TIMEZONE, 24), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](DATE_PIPE_DEFAULT_OPTIONS, 24)); }; DatePipe.ɵpipe = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ name: "date", type: DatePipe, pure: true, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](DatePipe, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, args: [{ name: 'date', pure: true, standalone: true }] }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [DATE_PIPE_DEFAULT_TIMEZONE] }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [DATE_PIPE_DEFAULT_OPTIONS] }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }] }]; }, null); })(); const _INTERPOLATION_REGEXP = /#/g; /** * @ngModule CommonModule * @description * * Maps a value to a string that pluralizes the value according to locale rules. * * @usageNotes * * ### Example * * {@example common/pipes/ts/i18n_pipe.ts region='I18nPluralPipeComponent'} * * @publicApi */ class I18nPluralPipe { constructor(_localization) { this._localization = _localization; } /** * @param value the number to be formatted * @param pluralMap an object that mimics the ICU format, see * https://unicode-org.github.io/icu/userguide/format_parse/messages/. * @param locale a `string` defining the locale to use (uses the current {@link LOCALE_ID} by * default). */ transform(value, pluralMap, locale) { if (value == null) return ''; if (typeof pluralMap !== 'object' || pluralMap === null) { throw invalidPipeArgumentError(I18nPluralPipe, pluralMap); } const key = getPluralCategory(value, Object.keys(pluralMap), this._localization, locale); return pluralMap[key].replace(_INTERPOLATION_REGEXP, value.toString()); } } I18nPluralPipe.ɵfac = function I18nPluralPipe_Factory(t) { return new (t || I18nPluralPipe)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NgLocalization, 16)); }; I18nPluralPipe.ɵpipe = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ name: "i18nPlural", type: I18nPluralPipe, pure: true, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](I18nPluralPipe, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, args: [{ name: 'i18nPlural', pure: true, standalone: true }] }], function () { return [{ type: NgLocalization }]; }, null); })(); /** * @ngModule CommonModule * @description * * Generic selector that displays the string that matches the current value. * * If none of the keys of the `mapping` match the `value`, then the content * of the `other` key is returned when present, otherwise an empty string is returned. * * @usageNotes * * ### Example * * {@example common/pipes/ts/i18n_pipe.ts region='I18nSelectPipeComponent'} * * @publicApi */ class I18nSelectPipe { /** * @param value a string to be internationalized. * @param mapping an object that indicates the text that should be displayed * for different values of the provided `value`. */ transform(value, mapping) { if (value == null) return ''; if (typeof mapping !== 'object' || typeof value !== 'string') { throw invalidPipeArgumentError(I18nSelectPipe, mapping); } if (mapping.hasOwnProperty(value)) { return mapping[value]; } if (mapping.hasOwnProperty('other')) { return mapping['other']; } return ''; } } I18nSelectPipe.ɵfac = function I18nSelectPipe_Factory(t) { return new (t || I18nSelectPipe)(); }; I18nSelectPipe.ɵpipe = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ name: "i18nSelect", type: I18nSelectPipe, pure: true, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](I18nSelectPipe, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, args: [{ name: 'i18nSelect', pure: true, standalone: true }] }], null, null); })(); /** * @ngModule CommonModule * @description * * Converts a value into its JSON-format representation. Useful for debugging. * * @usageNotes * * The following component uses a JSON pipe to convert an object * to JSON format, and displays the string in both formats for comparison. * * {@example common/pipes/ts/json_pipe.ts region='JsonPipe'} * * @publicApi */ class JsonPipe { /** * @param value A value of any type to convert into a JSON-format string. */ transform(value) { return JSON.stringify(value, null, 2); } } JsonPipe.ɵfac = function JsonPipe_Factory(t) { return new (t || JsonPipe)(); }; JsonPipe.ɵpipe = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ name: "json", type: JsonPipe, pure: false, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](JsonPipe, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, args: [{ name: 'json', pure: false, standalone: true }] }], null, null); })(); function makeKeyValuePair(key, value) { return { key: key, value: value }; } /** * @ngModule CommonModule * @description * * Transforms Object or Map into an array of key value pairs. * * The output array will be ordered by keys. * By default the comparator will be by Unicode point value. * You can optionally pass a compareFn if your keys are complex types. * * @usageNotes * ### Examples * * This examples show how an Object or a Map can be iterated by ngFor with the use of this * keyvalue pipe. * * {@example common/pipes/ts/keyvalue_pipe.ts region='KeyValuePipe'} * * @publicApi */ class KeyValuePipe { constructor(differs) { this.differs = differs; this.keyValues = []; this.compareFn = defaultComparator; } transform(input, compareFn = defaultComparator) { if (!input || !(input instanceof Map) && typeof input !== 'object') { return null; } if (!this.differ) { // make a differ for whatever type we've been passed in this.differ = this.differs.find(input).create(); } const differChanges = this.differ.diff(input); const compareFnChanged = compareFn !== this.compareFn; if (differChanges) { this.keyValues = []; differChanges.forEachItem(r => { this.keyValues.push(makeKeyValuePair(r.key, r.currentValue)); }); } if (differChanges || compareFnChanged) { this.keyValues.sort(compareFn); this.compareFn = compareFn; } return this.keyValues; } } KeyValuePipe.ɵfac = function KeyValuePipe_Factory(t) { return new (t || KeyValuePipe)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.KeyValueDiffers, 16)); }; KeyValuePipe.ɵpipe = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ name: "keyvalue", type: KeyValuePipe, pure: false, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](KeyValuePipe, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, args: [{ name: 'keyvalue', pure: false, standalone: true }] }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.KeyValueDiffers }]; }, null); })(); function defaultComparator(keyValueA, keyValueB) { const a = keyValueA.key; const b = keyValueB.key; // if same exit with 0; if (a === b) return 0; // make sure that undefined are at the end of the sort. if (a === undefined) return 1; if (b === undefined) return -1; // make sure that nulls are at the end of the sort. if (a === null) return 1; if (b === null) return -1; if (typeof a == 'string' && typeof b == 'string') { return a < b ? -1 : 1; } if (typeof a == 'number' && typeof b == 'number') { return a - b; } if (typeof a == 'boolean' && typeof b == 'boolean') { return a < b ? -1 : 1; } // `a` and `b` are of different types. Compare their string values. const aString = String(a); const bString = String(b); return aString == bString ? 0 : aString < bString ? -1 : 1; } /** * @ngModule CommonModule * @description * * Formats a value according to digit options and locale rules. * Locale determines group sizing and separator, * decimal point character, and other locale-specific configurations. * * @see `formatNumber()` * * @usageNotes * * ### digitsInfo * * The value's decimal representation is specified by the `digitsInfo` * parameter, written in the following format:<br> * * ``` * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits} * ``` * * - `minIntegerDigits`: * The minimum number of integer digits before the decimal point. * Default is 1. * * - `minFractionDigits`: * The minimum number of digits after the decimal point. * Default is 0. * * - `maxFractionDigits`: * The maximum number of digits after the decimal point. * Default is 3. * * If the formatted value is truncated it will be rounded using the "to-nearest" method: * * ``` * {{3.6 | number: '1.0-0'}} * <!--will output '4'--> * * {{-3.6 | number:'1.0-0'}} * <!--will output '-4'--> * ``` * * ### locale * * `locale` will format a value according to locale rules. * Locale determines group sizing and separator, * decimal point character, and other locale-specific configurations. * * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. * * See [Setting your app locale](guide/i18n-common-locale-id). * * ### Example * * The following code shows how the pipe transforms values * according to various format specifications, * where the caller's default locale is `en-US`. * * <code-example path="common/pipes/ts/number_pipe.ts" region='NumberPipe'></code-example> * * @publicApi */ class DecimalPipe { constructor(_locale) { this._locale = _locale; } /** * @param value The value to be formatted. * @param digitsInfo Sets digit and decimal representation. * [See more](#digitsinfo). * @param locale Specifies what locale format rules to use. * [See more](#locale). */ transform(value, digitsInfo, locale) { if (!isValue(value)) return null; locale = locale || this._locale; try { const num = strToNumber(value); return formatNumber(num, locale, digitsInfo); } catch (error) { throw invalidPipeArgumentError(DecimalPipe, error.message); } } } DecimalPipe.ɵfac = function DecimalPipe_Factory(t) { return new (t || DecimalPipe)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID, 16)); }; DecimalPipe.ɵpipe = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ name: "number", type: DecimalPipe, pure: true, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](DecimalPipe, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, args: [{ name: 'number', standalone: true }] }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID] }] }]; }, null); })(); /** * @ngModule CommonModule * @description * * Transforms a number to a percentage * string, formatted according to locale rules that determine group sizing and * separator, decimal-point character, and other locale-specific * configurations. * * @see `formatPercent()` * * @usageNotes * The following code shows how the pipe transforms numbers * into text strings, according to various format specifications, * where the caller's default locale is `en-US`. * * <code-example path="common/pipes/ts/percent_pipe.ts" region='PercentPipe'></code-example> * * @publicApi */ class PercentPipe { constructor(_locale) { this._locale = _locale; } /** * * @param value The number to be formatted as a percentage. * @param digitsInfo Decimal representation options, specified by a string * in the following format:<br> * <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>. * - `minIntegerDigits`: The minimum number of integer digits before the decimal point. * Default is `1`. * - `minFractionDigits`: The minimum number of digits after the decimal point. * Default is `0`. * - `maxFractionDigits`: The maximum number of digits after the decimal point. * Default is `0`. * @param locale A locale code for the locale format rules to use. * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. * See [Setting your app locale](guide/i18n-common-locale-id). */ transform(value, digitsInfo, locale) { if (!isValue(value)) return null; locale = locale || this._locale; try { const num = strToNumber(value); return formatPercent(num, locale, digitsInfo); } catch (error) { throw invalidPipeArgumentError(PercentPipe, error.message); } } } PercentPipe.ɵfac = function PercentPipe_Factory(t) { return new (t || PercentPipe)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID, 16)); }; PercentPipe.ɵpipe = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ name: "percent", type: PercentPipe, pure: true, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](PercentPipe, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, args: [{ name: 'percent', standalone: true }] }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID] }] }]; }, null); })(); /** * @ngModule CommonModule * @description * * Transforms a number to a currency string, formatted according to locale rules * that determine group sizing and separator, decimal-point character, * and other locale-specific configurations. * * * @see `getCurrencySymbol()` * @see `formatCurrency()` * * @usageNotes * The following code shows how the pipe transforms numbers * into text strings, according to various format specifications, * where the caller's default locale is `en-US`. * * <code-example path="common/pipes/ts/currency_pipe.ts" region='CurrencyPipe'></code-example> * * @publicApi */ class CurrencyPipe { constructor(_locale, _defaultCurrencyCode = 'USD') { this._locale = _locale; this._defaultCurrencyCode = _defaultCurrencyCode; } /** * * @param value The number to be formatted as currency. * @param currencyCode The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code, * such as `USD` for the US dollar and `EUR` for the euro. The default currency code can be * configured using the `DEFAULT_CURRENCY_CODE` injection token. * @param display The format for the currency indicator. One of the following: * - `code`: Show the code (such as `USD`). * - `symbol`(default): Show the symbol (such as `$`). * - `symbol-narrow`: Use the narrow symbol for locales that have two symbols for their * currency. * For example, the Canadian dollar CAD has the symbol `CA$` and the symbol-narrow `$`. If the * locale has no narrow symbol, uses the standard symbol for the locale. * - String: Use the given string value instead of a code or a symbol. * For example, an empty string will suppress the currency & symbol. * - Boolean (marked deprecated in v5): `true` for symbol and false for `code`. * * @param digitsInfo Decimal representation options, specified by a string * in the following format:<br> * <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>. * - `minIntegerDigits`: The minimum number of integer digits before the decimal point. * Default is `1`. * - `minFractionDigits`: The minimum number of digits after the decimal point. * Default is `2`. * - `maxFractionDigits`: The maximum number of digits after the decimal point. * Default is `2`. * If not provided, the number will be formatted with the proper amount of digits, * depending on what the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) specifies. * For example, the Canadian dollar has 2 digits, whereas the Chilean peso has none. * @param locale A locale code for the locale format rules to use. * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default. * See [Setting your app locale](guide/i18n-common-locale-id). */ transform(value, currencyCode = this._defaultCurrencyCode, display = 'symbol', digitsInfo, locale) { if (!isValue(value)) return null; locale = locale || this._locale; if (typeof display === 'boolean') { if ((typeof ngDevMode === 'undefined' || ngDevMode) && console && console.warn) { console.warn(`Warning: the currency pipe has been changed in Angular v5. The symbolDisplay option (third parameter) is now a string instead of a boolean. The accepted values are "code", "symbol" or "symbol-narrow".`); } display = display ? 'symbol' : 'code'; } let currency = currencyCode || this._defaultCurrencyCode; if (display !== 'code') { if (display === 'symbol' || display === 'symbol-narrow') { currency = getCurrencySymbol(currency, display === 'symbol' ? 'wide' : 'narrow', locale); } else { currency = display; } } try { const num = strToNumber(value); return formatCurrency(num, locale, currency, currencyCode, digitsInfo); } catch (error) { throw invalidPipeArgumentError(CurrencyPipe, error.message); } } } CurrencyPipe.ɵfac = function CurrencyPipe_Factory(t) { return new (t || CurrencyPipe)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID, 16), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.DEFAULT_CURRENCY_CODE, 16)); }; CurrencyPipe.ɵpipe = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ name: "currency", type: CurrencyPipe, pure: true, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](CurrencyPipe, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, args: [{ name: 'currency', standalone: true }] }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.LOCALE_ID] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.DEFAULT_CURRENCY_CODE] }] }]; }, null); })(); function isValue(value) { return !(value == null || value === '' || value !== value); } /** * Transforms a string into a number (if needed). */ function strToNumber(value) { // Convert strings to numbers if (typeof value === 'string' && !isNaN(Number(value) - parseFloat(value))) { return Number(value); } if (typeof value !== 'number') { throw new Error(`${value} is not a number`); } return value; } /** * @ngModule CommonModule * @description * * Creates a new `Array` or `String` containing a subset (slice) of the elements. * * @usageNotes * * All behavior is based on the expected behavior of the JavaScript API `Array.prototype.slice()` * and `String.prototype.slice()`. * * When operating on an `Array`, the returned `Array` is always a copy even when all * the elements are being returned. * * When operating on a blank value, the pipe returns the blank value. * * ### List Example * * This `ngFor` example: * * {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_list'} * * produces the following: * * ```html * <li>b</li> * <li>c</li> * ``` * * ### String Examples * * {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_string'} * * @publicApi */ class SlicePipe { transform(value, start, end) { if (value == null) return null; if (!this.supports(value)) { throw invalidPipeArgumentError(SlicePipe, value); } return value.slice(start, end); } supports(obj) { return typeof obj === 'string' || Array.isArray(obj); } } SlicePipe.ɵfac = function SlicePipe_Factory(t) { return new (t || SlicePipe)(); }; SlicePipe.ɵpipe = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefinePipe"]({ name: "slice", type: SlicePipe, pure: false, standalone: true }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](SlicePipe, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Pipe, args: [{ name: 'slice', pure: false, standalone: true }] }], null, null); })(); /** * @module * @description * This module provides a set of common Pipes. */ /** * A collection of Angular pipes that are likely to be used in each and every application. */ const COMMON_PIPES = [AsyncPipe, UpperCasePipe, LowerCasePipe, JsonPipe, SlicePipe, DecimalPipe, PercentPipe, TitleCasePipe, CurrencyPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, KeyValuePipe]; // Note: This does not contain the location providers, // as they need some platform specific implementations to work. /** * Exports all the basic Angular directives and pipes, * such as `NgIf`, `NgForOf`, `DecimalPipe`, and so on. * Re-exported by `BrowserModule`, which is included automatically in the root * `AppModule` when you create a new app with the CLI `new` command. * * @publicApi */ class CommonModule {} CommonModule.ɵfac = function CommonModule_Factory(t) { return new (t || CommonModule)(); }; CommonModule.ɵmod = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineNgModule"]({ type: CommonModule }); CommonModule.ɵinj = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjector"]({}); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](CommonModule, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.NgModule, args: [{ imports: [COMMON_DIRECTIVES, COMMON_PIPES], exports: [COMMON_DIRECTIVES, COMMON_PIPES] }] }], null, null); })(); const PLATFORM_BROWSER_ID = 'browser'; const PLATFORM_SERVER_ID = 'server'; const PLATFORM_WORKER_APP_ID = 'browserWorkerApp'; const PLATFORM_WORKER_UI_ID = 'browserWorkerUi'; /** * Returns whether a platform id represents a browser platform. * @publicApi */ function isPlatformBrowser(platformId) { return platformId === PLATFORM_BROWSER_ID; } /** * Returns whether a platform id represents a server platform. * @publicApi */ function isPlatformServer(platformId) { return platformId === PLATFORM_SERVER_ID; } /** * Returns whether a platform id represents a web worker app platform. * @publicApi */ function isPlatformWorkerApp(platformId) { return platformId === PLATFORM_WORKER_APP_ID; } /** * Returns whether a platform id represents a web worker UI platform. * @publicApi */ function isPlatformWorkerUi(platformId) { return platformId === PLATFORM_WORKER_UI_ID; } /** * @module * @description * Entry point for all public APIs of the common package. */ /** * @publicApi */ const VERSION = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.Version('15.2.9'); /** * Defines a scroll position manager. Implemented by `BrowserViewportScroller`. * * @publicApi */ class ViewportScroller {} // De-sugared tree-shakable injection // See #23917 /** @nocollapse */ ViewportScroller.ɵprov = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"])({ token: ViewportScroller, providedIn: 'root', factory: () => new BrowserViewportScroller((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"])(DOCUMENT), window) }); /** * Manages the scroll position for a browser window. */ class BrowserViewportScroller { constructor(document, window) { this.document = document; this.window = window; this.offset = () => [0, 0]; } /** * Configures the top offset used when scrolling to an anchor. * @param offset A position in screen coordinates (a tuple with x and y values) * or a function that returns the top offset position. * */ setOffset(offset) { if (Array.isArray(offset)) { this.offset = () => offset; } else { this.offset = offset; } } /** * Retrieves the current scroll position. * @returns The position in screen coordinates. */ getScrollPosition() { if (this.supportsScrolling()) { return [this.window.pageXOffset, this.window.pageYOffset]; } else { return [0, 0]; } } /** * Sets the scroll position. * @param position The new position in screen coordinates. */ scrollToPosition(position) { if (this.supportsScrolling()) { this.window.scrollTo(position[0], position[1]); } } /** * Scrolls to an element and attempts to focus the element. * * Note that the function name here is misleading in that the target string may be an ID for a * non-anchor element. * * @param target The ID of an element or name of the anchor. * * @see https://html.spec.whatwg.org/#the-indicated-part-of-the-document * @see https://html.spec.whatwg.org/#scroll-to-fragid */ scrollToAnchor(target) { if (!this.supportsScrolling()) { return; } const elSelected = findAnchorFromDocument(this.document, target); if (elSelected) { this.scrollToElement(elSelected); // After scrolling to the element, the spec dictates that we follow the focus steps for the // target. Rather than following the robust steps, simply attempt focus. // // @see https://html.spec.whatwg.org/#get-the-focusable-area // @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/focus // @see https://html.spec.whatwg.org/#focusable-area elSelected.focus(); } } /** * Disables automatic scroll restoration provided by the browser. */ setHistoryScrollRestoration(scrollRestoration) { if (this.supportScrollRestoration()) { const history = this.window.history; if (history && history.scrollRestoration) { history.scrollRestoration = scrollRestoration; } } } /** * Scrolls to an element using the native offset and the specified offset set on this scroller. * * The offset can be used when we know that there is a floating header and scrolling naively to an * element (ex: `scrollIntoView`) leaves the element hidden behind the floating header. */ scrollToElement(el) { const rect = el.getBoundingClientRect(); const left = rect.left + this.window.pageXOffset; const top = rect.top + this.window.pageYOffset; const offset = this.offset(); this.window.scrollTo(left - offset[0], top - offset[1]); } /** * We only support scroll restoration when we can get a hold of window. * This means that we do not support this behavior when running in a web worker. * * Lifting this restriction right now would require more changes in the dom adapter. * Since webworkers aren't widely used, we will lift it once RouterScroller is * battle-tested. */ supportScrollRestoration() { try { if (!this.supportsScrolling()) { return false; } // The `scrollRestoration` property could be on the `history` instance or its prototype. const scrollRestorationDescriptor = getScrollRestorationProperty(this.window.history) || getScrollRestorationProperty(Object.getPrototypeOf(this.window.history)); // We can write to the `scrollRestoration` property if it is a writable data field or it has a // setter function. return !!scrollRestorationDescriptor && !!(scrollRestorationDescriptor.writable || scrollRestorationDescriptor.set); } catch { return false; } } supportsScrolling() { try { return !!this.window && !!this.window.scrollTo && 'pageXOffset' in this.window; } catch { return false; } } } function getScrollRestorationProperty(obj) { return Object.getOwnPropertyDescriptor(obj, 'scrollRestoration'); } function findAnchorFromDocument(document, target) { const documentResult = document.getElementById(target) || document.getElementsByName(target)[0]; if (documentResult) { return documentResult; } // `getElementById` and `getElementsByName` won't pierce through the shadow DOM so we // have to traverse the DOM manually and do the lookup through the shadow roots. if (typeof document.createTreeWalker === 'function' && document.body && (document.body.createShadowRoot || document.body.attachShadow)) { const treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT); let currentNode = treeWalker.currentNode; while (currentNode) { const shadowRoot = currentNode.shadowRoot; if (shadowRoot) { // Note that `ShadowRoot` doesn't support `getElementsByName` // so we have to fall back to `querySelector`. const result = shadowRoot.getElementById(target) || shadowRoot.querySelector(`[name="${target}"]`); if (result) { return result; } } currentNode = treeWalker.nextNode(); } } return null; } /** * Provides an empty implementation of the viewport scroller. */ class NullViewportScroller { /** * Empty implementation */ setOffset(offset) {} /** * Empty implementation */ getScrollPosition() { return [0, 0]; } /** * Empty implementation */ scrollToPosition(position) {} /** * Empty implementation */ scrollToAnchor(anchor) {} /** * Empty implementation */ setHistoryScrollRestoration(scrollRestoration) {} } /** * A wrapper around the `XMLHttpRequest` constructor. * * @publicApi */ class XhrFactory {} // Converts a string that represents a URL into a URL class instance. function getUrl(src, win) { // Don't use a base URL is the URL is absolute. return isAbsoluteUrl(src) ? new URL(src) : new URL(src, win.location.href); } // Checks whether a URL is absolute (i.e. starts with `http://` or `https://`). function isAbsoluteUrl(src) { return /^https?:\/\//.test(src); } // Given a URL, extract the hostname part. // If a URL is a relative one - the URL is returned as is. function extractHostname(url) { return isAbsoluteUrl(url) ? new URL(url).hostname : url; } function isValidPath(path) { const isString = typeof path === 'string'; if (!isString || path.trim() === '') { return false; } // Calling new URL() will throw if the path string is malformed try { const url = new URL(path); return true; } catch { return false; } } function normalizePath(path) { return path.endsWith('/') ? path.slice(0, -1) : path; } function normalizeSrc(src) { return src.startsWith('/') ? src.slice(1) : src; } /** * Noop image loader that does no transformation to the original src and just returns it as is. * This loader is used as a default one if more specific logic is not provided in an app config. * * @see `ImageLoader` * @see `NgOptimizedImage` */ const noopImageLoader = config => config.src; /** * Injection token that configures the image loader function. * * @see `ImageLoader` * @see `NgOptimizedImage` * @publicApi */ const IMAGE_LOADER = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('ImageLoader', { providedIn: 'root', factory: () => noopImageLoader }); /** * Internal helper function that makes it easier to introduce custom image loaders for the * `NgOptimizedImage` directive. It is enough to specify a URL builder function to obtain full DI * configuration for a given loader: a DI token corresponding to the actual loader function, plus DI * tokens managing preconnect check functionality. * @param buildUrlFn a function returning a full URL based on loader's configuration * @param exampleUrls example of full URLs for a given loader (used in error messages) * @returns a set of DI providers corresponding to the configured image loader */ function createImageLoader(buildUrlFn, exampleUrls) { return function provideImageLoader(path) { if (!isValidPath(path)) { throwInvalidPathError(path, exampleUrls || []); } // The trailing / is stripped (if provided) to make URL construction (concatenation) easier in // the individual loader functions. path = normalizePath(path); const loaderFn = config => { if (isAbsoluteUrl(config.src)) { // Image loader functions expect an image file name (e.g. `my-image.png`) // or a relative path + a file name (e.g. `/a/b/c/my-image.png`) as an input, // so the final absolute URL can be constructed. // When an absolute URL is provided instead - the loader can not // build a final URL, thus the error is thrown to indicate that. throwUnexpectedAbsoluteUrlError(path, config.src); } return buildUrlFn(path, { ...config, src: normalizeSrc(config.src) }); }; const providers = [{ provide: IMAGE_LOADER, useValue: loaderFn }]; return providers; }; } function throwInvalidPathError(path, exampleUrls) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2959 /* RuntimeErrorCode.INVALID_LOADER_ARGUMENTS */, ngDevMode && `Image loader has detected an invalid path (\`${path}\`). ` + `To fix this, supply a path using one of the following formats: ${exampleUrls.join(' or ')}`); } function throwUnexpectedAbsoluteUrlError(path, url) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2959 /* RuntimeErrorCode.INVALID_LOADER_ARGUMENTS */, ngDevMode && `Image loader has detected a \`<img>\` tag with an invalid \`ngSrc\` attribute: ${url}. ` + `This image loader expects \`ngSrc\` to be a relative URL - ` + `however the provided value is an absolute URL. ` + `To fix this, provide \`ngSrc\` as a path relative to the base URL ` + `configured for this loader (\`${path}\`).`); } /** * Function that generates an ImageLoader for [Cloudflare Image * Resizing](https://developers.cloudflare.com/images/image-resizing/) and turns it into an Angular * provider. Note: Cloudflare has multiple image products - this provider is specifically for * Cloudflare Image Resizing; it will not work with Cloudflare Images or Cloudflare Polish. * * @param path Your domain name, e.g. https://mysite.com * @returns Provider that provides an ImageLoader function * * @publicApi */ const provideCloudflareLoader = createImageLoader(createCloudflareUrl, ngDevMode ? ['https://<ZONE>/cdn-cgi/image/<OPTIONS>/<SOURCE-IMAGE>'] : undefined); function createCloudflareUrl(path, config) { let params = `format=auto`; if (config.width) { params += `,width=${config.width}`; } // Cloudflare image URLs format: // https://developers.cloudflare.com/images/image-resizing/url-format/ return `${path}/cdn-cgi/image/${params}/${config.src}`; } /** * Name and URL tester for Cloudinary. */ const cloudinaryLoaderInfo = { name: 'Cloudinary', testUrl: isCloudinaryUrl }; const CLOUDINARY_LOADER_REGEX = /https?\:\/\/[^\/]+\.cloudinary\.com\/.+/; /** * Tests whether a URL is from Cloudinary CDN. */ function isCloudinaryUrl(url) { return CLOUDINARY_LOADER_REGEX.test(url); } /** * Function that generates an ImageLoader for Cloudinary and turns it into an Angular provider. * * @param path Base URL of your Cloudinary images * This URL should match one of the following formats: * https://res.cloudinary.com/mysite * https://mysite.cloudinary.com * https://subdomain.mysite.com * @returns Set of providers to configure the Cloudinary loader. * * @publicApi */ const provideCloudinaryLoader = createImageLoader(createCloudinaryUrl, ngDevMode ? ['https://res.cloudinary.com/mysite', 'https://mysite.cloudinary.com', 'https://subdomain.mysite.com'] : undefined); function createCloudinaryUrl(path, config) { // Cloudinary image URLformat: // https://cloudinary.com/documentation/image_transformations#transformation_url_structure // Example of a Cloudinary image URL: // https://res.cloudinary.com/mysite/image/upload/c_scale,f_auto,q_auto,w_600/marketing/tile-topics-m.png let params = `f_auto,q_auto`; // sets image format and quality to "auto" if (config.width) { params += `,w_${config.width}`; } return `${path}/image/upload/${params}/${config.src}`; } /** * Name and URL tester for ImageKit. */ const imageKitLoaderInfo = { name: 'ImageKit', testUrl: isImageKitUrl }; const IMAGE_KIT_LOADER_REGEX = /https?\:\/\/[^\/]+\.imagekit\.io\/.+/; /** * Tests whether a URL is from ImageKit CDN. */ function isImageKitUrl(url) { return IMAGE_KIT_LOADER_REGEX.test(url); } /** * Function that generates an ImageLoader for ImageKit and turns it into an Angular provider. * * @param path Base URL of your ImageKit images * This URL should match one of the following formats: * https://ik.imagekit.io/myaccount * https://subdomain.mysite.com * @returns Set of providers to configure the ImageKit loader. * * @publicApi */ const provideImageKitLoader = createImageLoader(createImagekitUrl, ngDevMode ? ['https://ik.imagekit.io/mysite', 'https://subdomain.mysite.com'] : undefined); function createImagekitUrl(path, config) { // Example of an ImageKit image URL: // https://ik.imagekit.io/demo/tr:w-300,h-300/medium_cafe_B1iTdD0C.jpg const { src, width } = config; let urlSegments; if (width) { const params = `tr:w-${width}`; urlSegments = [path, params, src]; } else { urlSegments = [path, src]; } return urlSegments.join('/'); } /** * Name and URL tester for Imgix. */ const imgixLoaderInfo = { name: 'Imgix', testUrl: isImgixUrl }; const IMGIX_LOADER_REGEX = /https?\:\/\/[^\/]+\.imgix\.net\/.+/; /** * Tests whether a URL is from Imgix CDN. */ function isImgixUrl(url) { return IMGIX_LOADER_REGEX.test(url); } /** * Function that generates an ImageLoader for Imgix and turns it into an Angular provider. * * @param path path to the desired Imgix origin, * e.g. https://somepath.imgix.net or https://images.mysite.com * @returns Set of providers to configure the Imgix loader. * * @publicApi */ const provideImgixLoader = createImageLoader(createImgixUrl, ngDevMode ? ['https://somepath.imgix.net/'] : undefined); function createImgixUrl(path, config) { const url = new URL(`${path}/${config.src}`); // This setting ensures the smallest allowable format is set. url.searchParams.set('auto', 'format'); if (config.width) { url.searchParams.set('w', config.width.toString()); } return url.href; } // Assembles directive details string, useful for error messages. function imgDirectiveDetails(ngSrc, includeNgSrc = true) { const ngSrcInfo = includeNgSrc ? `(activated on an <img> element with the \`ngSrc="${ngSrc}"\`) ` : ''; return `The NgOptimizedImage directive ${ngSrcInfo}has detected that`; } /** * Asserts that the application is in development mode. Throws an error if the application is in * production mode. This assert can be used to make sure that there is no dev-mode code invoked in * the prod mode accidentally. */ function assertDevMode(checkName) { if (!ngDevMode) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2958 /* RuntimeErrorCode.UNEXPECTED_DEV_MODE_CHECK_IN_PROD_MODE */, `Unexpected invocation of the ${checkName} in the prod mode. ` + `Please make sure that the prod mode is enabled for production builds.`); } } /** * Observer that detects whether an image with `NgOptimizedImage` * is treated as a Largest Contentful Paint (LCP) element. If so, * asserts that the image has the `priority` attribute. * * Note: this is a dev-mode only class and it does not appear in prod bundles, * thus there is no `ngDevMode` use in the code. * * Based on https://web.dev/lcp/#measure-lcp-in-javascript. */ class LCPImageObserver { constructor() { // Map of full image URLs -> original `ngSrc` values. this.images = new Map(); // Keep track of images for which `console.warn` was produced. this.alreadyWarned = new Set(); this.window = null; this.observer = null; assertDevMode('LCP checker'); const win = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(DOCUMENT).defaultView; if (typeof win !== 'undefined' && typeof PerformanceObserver !== 'undefined') { this.window = win; this.observer = this.initPerformanceObserver(); } } /** * Inits PerformanceObserver and subscribes to LCP events. * Based on https://web.dev/lcp/#measure-lcp-in-javascript */ initPerformanceObserver() { const observer = new PerformanceObserver(entryList => { const entries = entryList.getEntries(); if (entries.length === 0) return; // We use the latest entry produced by the `PerformanceObserver` as the best // signal on which element is actually an LCP one. As an example, the first image to load on // a page, by virtue of being the only thing on the page so far, is often a LCP candidate // and gets reported by PerformanceObserver, but isn't necessarily the LCP element. const lcpElement = entries[entries.length - 1]; // Cast to `any` due to missing `element` on the `LargestContentfulPaint` type of entry. // See https://developer.mozilla.org/en-US/docs/Web/API/LargestContentfulPaint const imgSrc = lcpElement.element?.src ?? ''; // Exclude `data:` and `blob:` URLs, since they are not supported by the directive. if (imgSrc.startsWith('data:') || imgSrc.startsWith('blob:')) return; const imgNgSrc = this.images.get(imgSrc); if (imgNgSrc && !this.alreadyWarned.has(imgSrc)) { this.alreadyWarned.add(imgSrc); logMissingPriorityWarning(imgSrc); } }); observer.observe({ type: 'largest-contentful-paint', buffered: true }); return observer; } registerImage(rewrittenSrc, originalNgSrc) { if (!this.observer) return; this.images.set(getUrl(rewrittenSrc, this.window).href, originalNgSrc); } unregisterImage(rewrittenSrc) { if (!this.observer) return; this.images.delete(getUrl(rewrittenSrc, this.window).href); } ngOnDestroy() { if (!this.observer) return; this.observer.disconnect(); this.images.clear(); this.alreadyWarned.clear(); } } LCPImageObserver.ɵfac = function LCPImageObserver_Factory(t) { return new (t || LCPImageObserver)(); }; LCPImageObserver.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: LCPImageObserver, factory: LCPImageObserver.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](LCPImageObserver, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], function () { return []; }, null); })(); function logMissingPriorityWarning(ngSrc) { const directiveDetails = imgDirectiveDetails(ngSrc); console.warn((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵformatRuntimeError"])(2955 /* RuntimeErrorCode.LCP_IMG_MISSING_PRIORITY */, `${directiveDetails} this image is the Largest Contentful Paint (LCP) ` + `element but was not marked "priority". This image should be marked ` + `"priority" in order to prioritize its loading. ` + `To fix this, add the "priority" attribute.`)); } // Set of origins that are always excluded from the preconnect checks. const INTERNAL_PRECONNECT_CHECK_BLOCKLIST = new Set(['localhost', '127.0.0.1', '0.0.0.0']); /** * Injection token to configure which origins should be excluded * from the preconnect checks. It can either be a single string or an array of strings * to represent a group of origins, for example: * * ```typescript * {provide: PRECONNECT_CHECK_BLOCKLIST, useValue: 'https://your-domain.com'} * ``` * * or: * * ```typescript * {provide: PRECONNECT_CHECK_BLOCKLIST, * useValue: ['https://your-domain-1.com', 'https://your-domain-2.com']} * ``` * * @publicApi */ const PRECONNECT_CHECK_BLOCKLIST = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('PRECONNECT_CHECK_BLOCKLIST'); /** * Contains the logic to detect whether an image, marked with the "priority" attribute * has a corresponding `<link rel="preconnect">` tag in the `document.head`. * * Note: this is a dev-mode only class, which should not appear in prod bundles, * thus there is no `ngDevMode` use in the code. */ class PreconnectLinkChecker { constructor() { this.document = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(DOCUMENT); /** * Set of <link rel="preconnect"> tags found on this page. * The `null` value indicates that there was no DOM query operation performed. */ this.preconnectLinks = null; /* * Keep track of all already seen origin URLs to avoid repeating the same check. */ this.alreadySeen = new Set(); this.window = null; this.blocklist = new Set(INTERNAL_PRECONNECT_CHECK_BLOCKLIST); assertDevMode('preconnect link checker'); const win = this.document.defaultView; if (typeof win !== 'undefined') { this.window = win; } const blocklist = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(PRECONNECT_CHECK_BLOCKLIST, { optional: true }); if (blocklist) { this.populateBlocklist(blocklist); } } populateBlocklist(origins) { if (Array.isArray(origins)) { deepForEach(origins, origin => { this.blocklist.add(extractHostname(origin)); }); } else { this.blocklist.add(extractHostname(origins)); } } /** * Checks that a preconnect resource hint exists in the head for the * given src. * * @param rewrittenSrc src formatted with loader * @param originalNgSrc ngSrc value */ assertPreconnect(rewrittenSrc, originalNgSrc) { if (!this.window) return; const imgUrl = getUrl(rewrittenSrc, this.window); if (this.blocklist.has(imgUrl.hostname) || this.alreadySeen.has(imgUrl.origin)) return; // Register this origin as seen, so we don't check it again later. this.alreadySeen.add(imgUrl.origin); if (!this.preconnectLinks) { // Note: we query for preconnect links only *once* and cache the results // for the entire lifespan of an application, since it's unlikely that the // list would change frequently. This allows to make sure there are no // performance implications of making extra DOM lookups for each image. this.preconnectLinks = this.queryPreconnectLinks(); } if (!this.preconnectLinks.has(imgUrl.origin)) { console.warn((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵformatRuntimeError"])(2956 /* RuntimeErrorCode.PRIORITY_IMG_MISSING_PRECONNECT_TAG */, `${imgDirectiveDetails(originalNgSrc)} there is no preconnect tag present for this ` + `image. Preconnecting to the origin(s) that serve priority images ensures that these ` + `images are delivered as soon as possible. To fix this, please add the following ` + `element into the <head> of the document:\n` + ` <link rel="preconnect" href="${imgUrl.origin}">`)); } } queryPreconnectLinks() { const preconnectUrls = new Set(); const selector = 'link[rel=preconnect]'; const links = Array.from(this.document.querySelectorAll(selector)); for (let link of links) { const url = getUrl(link.href, this.window); preconnectUrls.add(url.origin); } return preconnectUrls; } ngOnDestroy() { this.preconnectLinks?.clear(); this.alreadySeen.clear(); } } PreconnectLinkChecker.ɵfac = function PreconnectLinkChecker_Factory(t) { return new (t || PreconnectLinkChecker)(); }; PreconnectLinkChecker.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: PreconnectLinkChecker, factory: PreconnectLinkChecker.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](PreconnectLinkChecker, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], function () { return []; }, null); })(); /** * Invokes a callback for each element in the array. Also invokes a callback * recursively for each nested array. */ function deepForEach(input, fn) { for (let value of input) { Array.isArray(value) ? deepForEach(value, fn) : fn(value); } } /** * In SSR scenarios, a preload `<link>` element is generated for priority images. * Having a large number of preload tags may negatively affect the performance, * so we warn developers (by throwing an error) if the number of preloaded images * is above a certain threshold. This const specifies this threshold. */ const DEFAULT_PRELOADED_IMAGES_LIMIT = 5; /** * Helps to keep track of priority images that already have a corresponding * preload tag (to avoid generating multiple preload tags with the same URL). * * This Set tracks the original src passed into the `ngSrc` input not the src after it has been * run through the specified `IMAGE_LOADER`. */ const PRELOADED_IMAGES = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('NG_OPTIMIZED_PRELOADED_IMAGES', { providedIn: 'root', factory: () => new Set() }); /** * @description Contains the logic needed to track and add preload link tags to the `<head>` tag. It * will also track what images have already had preload link tags added so as to not duplicate link * tags. * * In dev mode this service will validate that the number of preloaded images does not exceed the * configured default preloaded images limit: {@link DEFAULT_PRELOADED_IMAGES_LIMIT}. */ class PreloadLinkCreator { constructor() { this.preloadedImages = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(PRELOADED_IMAGES); this.document = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(DOCUMENT); } /** * @description Add a preload `<link>` to the `<head>` of the `index.html` that is served from the * server while using Angular Universal and SSR to kick off image loads for high priority images. * * The `sizes` (passed in from the user) and `srcset` (parsed and formatted from `ngSrcset`) * properties used to set the corresponding attributes, `imagesizes` and `imagesrcset` * respectively, on the preload `<link>` tag so that the correctly sized image is preloaded from * the CDN. * * {@link https://web.dev/preload-responsive-images/#imagesrcset-and-imagesizes} * * @param renderer The `Renderer2` passed in from the directive * @param src The original src of the image that is set on the `ngSrc` input. * @param srcset The parsed and formatted srcset created from the `ngSrcset` input * @param sizes The value of the `sizes` attribute passed in to the `<img>` tag */ createPreloadLinkTag(renderer, src, srcset, sizes) { if (ngDevMode) { if (this.preloadedImages.size >= DEFAULT_PRELOADED_IMAGES_LIMIT) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2961 /* RuntimeErrorCode.TOO_MANY_PRELOADED_IMAGES */, ngDevMode && `The \`NgOptimizedImage\` directive has detected that more than ` + `${DEFAULT_PRELOADED_IMAGES_LIMIT} images were marked as priority. ` + `This might negatively affect an overall performance of the page. ` + `To fix this, remove the "priority" attribute from images with less priority.`); } } if (this.preloadedImages.has(src)) { return; } this.preloadedImages.add(src); const preload = renderer.createElement('link'); renderer.setAttribute(preload, 'as', 'image'); renderer.setAttribute(preload, 'href', src); renderer.setAttribute(preload, 'rel', 'preload'); renderer.setAttribute(preload, 'fetchpriority', 'high'); if (sizes) { renderer.setAttribute(preload, 'imageSizes', sizes); } if (srcset) { renderer.setAttribute(preload, 'imageSrcset', srcset); } renderer.appendChild(this.document.head, preload); } } PreloadLinkCreator.ɵfac = function PreloadLinkCreator_Factory(t) { return new (t || PreloadLinkCreator)(); }; PreloadLinkCreator.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: PreloadLinkCreator, factory: PreloadLinkCreator.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](PreloadLinkCreator, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], null, null); })(); /** * When a Base64-encoded image is passed as an input to the `NgOptimizedImage` directive, * an error is thrown. The image content (as a string) might be very long, thus making * it hard to read an error message if the entire string is included. This const defines * the number of characters that should be included into the error message. The rest * of the content is truncated. */ const BASE64_IMG_MAX_LENGTH_IN_ERROR = 50; /** * RegExpr to determine whether a src in a srcset is using width descriptors. * Should match something like: "100w, 200w". */ const VALID_WIDTH_DESCRIPTOR_SRCSET = /^((\s*\d+w\s*(,|$)){1,})$/; /** * RegExpr to determine whether a src in a srcset is using density descriptors. * Should match something like: "1x, 2x, 50x". Also supports decimals like "1.5x, 1.50x". */ const VALID_DENSITY_DESCRIPTOR_SRCSET = /^((\s*\d+(\.\d+)?x\s*(,|$)){1,})$/; /** * Srcset values with a density descriptor higher than this value will actively * throw an error. Such densities are not permitted as they cause image sizes * to be unreasonably large and slow down LCP. */ const ABSOLUTE_SRCSET_DENSITY_CAP = 3; /** * Used only in error message text to communicate best practices, as we will * only throw based on the slightly more conservative ABSOLUTE_SRCSET_DENSITY_CAP. */ const RECOMMENDED_SRCSET_DENSITY_CAP = 2; /** * Used in generating automatic density-based srcsets */ const DENSITY_SRCSET_MULTIPLIERS = [1, 2]; /** * Used to determine which breakpoints to use on full-width images */ const VIEWPORT_BREAKPOINT_CUTOFF = 640; /** * Used to determine whether two aspect ratios are similar in value. */ const ASPECT_RATIO_TOLERANCE = .1; /** * Used to determine whether the image has been requested at an overly * large size compared to the actual rendered image size (after taking * into account a typical device pixel ratio). In pixels. */ const OVERSIZED_IMAGE_TOLERANCE = 1000; /** * Used to limit automatic srcset generation of very large sources for * fixed-size images. In pixels. */ const FIXED_SRCSET_WIDTH_LIMIT = 1920; const FIXED_SRCSET_HEIGHT_LIMIT = 1080; /** Info about built-in loaders we can test for. */ const BUILT_IN_LOADERS = [imgixLoaderInfo, imageKitLoaderInfo, cloudinaryLoaderInfo]; const defaultConfig = { breakpoints: [16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840] }; /** * Injection token that configures the image optimized image functionality. * * @see `NgOptimizedImage` * @publicApi * @developerPreview */ const IMAGE_CONFIG = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('ImageConfig', { providedIn: 'root', factory: () => defaultConfig }); /** * Directive that improves image loading performance by enforcing best practices. * * `NgOptimizedImage` ensures that the loading of the Largest Contentful Paint (LCP) image is * prioritized by: * - Automatically setting the `fetchpriority` attribute on the `<img>` tag * - Lazy loading non-priority images by default * - Asserting that there is a corresponding preconnect link tag in the document head * * In addition, the directive: * - Generates appropriate asset URLs if a corresponding `ImageLoader` function is provided * - Automatically generates a srcset * - Requires that `width` and `height` are set * - Warns if `width` or `height` have been set incorrectly * - Warns if the image will be visually distorted when rendered * * @usageNotes * The `NgOptimizedImage` directive is marked as [standalone](guide/standalone-components) and can * be imported directly. * * Follow the steps below to enable and use the directive: * 1. Import it into the necessary NgModule or a standalone Component. * 2. Optionally provide an `ImageLoader` if you use an image hosting service. * 3. Update the necessary `<img>` tags in templates and replace `src` attributes with `ngSrc`. * Using a `ngSrc` allows the directive to control when the `src` gets set, which triggers an image * download. * * Step 1: import the `NgOptimizedImage` directive. * * ```typescript * import { NgOptimizedImage } from '@angular/common'; * * // Include it into the necessary NgModule * @NgModule({ * imports: [NgOptimizedImage], * }) * class AppModule {} * * // ... or a standalone Component * @Component({ * standalone: true * imports: [NgOptimizedImage], * }) * class MyStandaloneComponent {} * ``` * * Step 2: configure a loader. * * To use the **default loader**: no additional code changes are necessary. The URL returned by the * generic loader will always match the value of "src". In other words, this loader applies no * transformations to the resource URL and the value of the `ngSrc` attribute will be used as is. * * To use an existing loader for a **third-party image service**: add the provider factory for your * chosen service to the `providers` array. In the example below, the Imgix loader is used: * * ```typescript * import {provideImgixLoader} from '@angular/common'; * * // Call the function and add the result to the `providers` array: * providers: [ * provideImgixLoader("https://my.base.url/"), * ], * ``` * * The `NgOptimizedImage` directive provides the following functions: * - `provideCloudflareLoader` * - `provideCloudinaryLoader` * - `provideImageKitLoader` * - `provideImgixLoader` * * If you use a different image provider, you can create a custom loader function as described * below. * * To use a **custom loader**: provide your loader function as a value for the `IMAGE_LOADER` DI * token. * * ```typescript * import {IMAGE_LOADER, ImageLoaderConfig} from '@angular/common'; * * // Configure the loader using the `IMAGE_LOADER` token. * providers: [ * { * provide: IMAGE_LOADER, * useValue: (config: ImageLoaderConfig) => { * return `https://example.com/${config.src}-${config.width}.jpg}`; * } * }, * ], * ``` * * Step 3: update `<img>` tags in templates to use `ngSrc` instead of `src`. * * ``` * <img ngSrc="logo.png" width="200" height="100"> * ``` * * @publicApi */ class NgOptimizedImage { constructor() { this.imageLoader = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(IMAGE_LOADER); this.config = processConfig((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(IMAGE_CONFIG)); this.renderer = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2); this.imgElement = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef).nativeElement; this.injector = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__.Injector); this.isServer = isPlatformServer((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__.PLATFORM_ID)); this.preloadLinkChecker = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(PreloadLinkCreator); // a LCP image observer - should be injected only in the dev mode this.lcpObserver = ngDevMode ? this.injector.get(LCPImageObserver) : null; /** * Calculate the rewritten `src` once and store it. * This is needed to avoid repetitive calculations and make sure the directive cleanup in the * `ngOnDestroy` does not rely on the `IMAGE_LOADER` logic (which in turn can rely on some other * instance that might be already destroyed). */ this._renderedSrc = null; this._priority = false; this._disableOptimizedSrcset = false; this._fill = false; } /** * For responsive images: the intrinsic width of the image in pixels. * For fixed size images: the desired rendered width of the image in pixels. */ set width(value) { ngDevMode && assertGreaterThanZero(this, value, 'width'); this._width = inputToInteger(value); } get width() { return this._width; } /** * For responsive images: the intrinsic height of the image in pixels. * For fixed size images: the desired rendered height of the image in pixels.* The intrinsic * height of the image in pixels. */ set height(value) { ngDevMode && assertGreaterThanZero(this, value, 'height'); this._height = inputToInteger(value); } get height() { return this._height; } /** * Indicates whether this image should have a high priority. */ set priority(value) { this._priority = inputToBoolean(value); } get priority() { return this._priority; } /** * Disables automatic srcset generation for this image. */ set disableOptimizedSrcset(value) { this._disableOptimizedSrcset = inputToBoolean(value); } get disableOptimizedSrcset() { return this._disableOptimizedSrcset; } /** * Sets the image to "fill mode", which eliminates the height/width requirement and adds * styles such that the image fills its containing element. * * @developerPreview */ set fill(value) { this._fill = inputToBoolean(value); } get fill() { return this._fill; } /** @nodoc */ ngOnInit() { if (ngDevMode) { assertNonEmptyInput(this, 'ngSrc', this.ngSrc); assertValidNgSrcset(this, this.ngSrcset); assertNoConflictingSrc(this); if (this.ngSrcset) { assertNoConflictingSrcset(this); } assertNotBase64Image(this); assertNotBlobUrl(this); if (this.fill) { assertEmptyWidthAndHeight(this); assertNonZeroRenderedHeight(this, this.imgElement, this.renderer); } else { assertNonEmptyWidthAndHeight(this); // Only check for distorted images when not in fill mode, where // images may be intentionally stretched, cropped or letterboxed. assertNoImageDistortion(this, this.imgElement, this.renderer); } assertValidLoadingInput(this); if (!this.ngSrcset) { assertNoComplexSizes(this); } assertNotMissingBuiltInLoader(this.ngSrc, this.imageLoader); assertNoNgSrcsetWithoutLoader(this, this.imageLoader); assertNoLoaderParamsWithoutLoader(this, this.imageLoader); if (this.priority) { const checker = this.injector.get(PreconnectLinkChecker); checker.assertPreconnect(this.getRewrittenSrc(), this.ngSrc); } else { // Monitor whether an image is an LCP element only in case // the `priority` attribute is missing. Otherwise, an image // has the necessary settings and no extra checks are required. if (this.lcpObserver !== null) { const ngZone = this.injector.get(_angular_core__WEBPACK_IMPORTED_MODULE_0__.NgZone); ngZone.runOutsideAngular(() => { this.lcpObserver.registerImage(this.getRewrittenSrc(), this.ngSrc); }); } } } this.setHostAttributes(); } setHostAttributes() { // Must set width/height explicitly in case they are bound (in which case they will // only be reflected and not found by the browser) if (this.fill) { if (!this.sizes) { this.sizes = '100vw'; } } else { this.setHostAttribute('width', this.width.toString()); this.setHostAttribute('height', this.height.toString()); } this.setHostAttribute('loading', this.getLoadingBehavior()); this.setHostAttribute('fetchpriority', this.getFetchPriority()); // The `data-ng-img` attribute flags an image as using the directive, to allow // for analysis of the directive's performance. this.setHostAttribute('ng-img', 'true'); // The `src` and `srcset` attributes should be set last since other attributes // could affect the image's loading behavior. const rewrittenSrc = this.getRewrittenSrc(); this.setHostAttribute('src', rewrittenSrc); let rewrittenSrcset = undefined; if (this.sizes) { this.setHostAttribute('sizes', this.sizes); } if (this.ngSrcset) { rewrittenSrcset = this.getRewrittenSrcset(); } else if (this.shouldGenerateAutomaticSrcset()) { rewrittenSrcset = this.getAutomaticSrcset(); } if (rewrittenSrcset) { this.setHostAttribute('srcset', rewrittenSrcset); } if (this.isServer && this.priority) { this.preloadLinkChecker.createPreloadLinkTag(this.renderer, rewrittenSrc, rewrittenSrcset, this.sizes); } } /** @nodoc */ ngOnChanges(changes) { if (ngDevMode) { assertNoPostInitInputChange(this, changes, ['ngSrc', 'ngSrcset', 'width', 'height', 'priority', 'fill', 'loading', 'sizes', 'loaderParams', 'disableOptimizedSrcset']); } } callImageLoader(configWithoutCustomParams) { let augmentedConfig = configWithoutCustomParams; if (this.loaderParams) { augmentedConfig.loaderParams = this.loaderParams; } return this.imageLoader(augmentedConfig); } getLoadingBehavior() { if (!this.priority && this.loading !== undefined) { return this.loading; } return this.priority ? 'eager' : 'lazy'; } getFetchPriority() { return this.priority ? 'high' : 'auto'; } getRewrittenSrc() { // ImageLoaderConfig supports setting a width property. However, we're not setting width here // because if the developer uses rendered width instead of intrinsic width in the HTML width // attribute, the image requested may be too small for 2x+ screens. if (!this._renderedSrc) { const imgConfig = { src: this.ngSrc }; // Cache calculated image src to reuse it later in the code. this._renderedSrc = this.callImageLoader(imgConfig); } return this._renderedSrc; } getRewrittenSrcset() { const widthSrcSet = VALID_WIDTH_DESCRIPTOR_SRCSET.test(this.ngSrcset); const finalSrcs = this.ngSrcset.split(',').filter(src => src !== '').map(srcStr => { srcStr = srcStr.trim(); const width = widthSrcSet ? parseFloat(srcStr) : parseFloat(srcStr) * this.width; return `${this.callImageLoader({ src: this.ngSrc, width })} ${srcStr}`; }); return finalSrcs.join(', '); } getAutomaticSrcset() { if (this.sizes) { return this.getResponsiveSrcset(); } else { return this.getFixedSrcset(); } } getResponsiveSrcset() { const { breakpoints } = this.config; let filteredBreakpoints = breakpoints; if (this.sizes?.trim() === '100vw') { // Since this is a full-screen-width image, our srcset only needs to include // breakpoints with full viewport widths. filteredBreakpoints = breakpoints.filter(bp => bp >= VIEWPORT_BREAKPOINT_CUTOFF); } const finalSrcs = filteredBreakpoints.map(bp => `${this.callImageLoader({ src: this.ngSrc, width: bp })} ${bp}w`); return finalSrcs.join(', '); } getFixedSrcset() { const finalSrcs = DENSITY_SRCSET_MULTIPLIERS.map(multiplier => `${this.callImageLoader({ src: this.ngSrc, width: this.width * multiplier })} ${multiplier}x`); return finalSrcs.join(', '); } shouldGenerateAutomaticSrcset() { return !this._disableOptimizedSrcset && !this.srcset && this.imageLoader !== noopImageLoader && !(this.width > FIXED_SRCSET_WIDTH_LIMIT || this.height > FIXED_SRCSET_HEIGHT_LIMIT); } /** @nodoc */ ngOnDestroy() { if (ngDevMode) { if (!this.priority && this._renderedSrc !== null && this.lcpObserver !== null) { this.lcpObserver.unregisterImage(this._renderedSrc); } } } setHostAttribute(name, value) { this.renderer.setAttribute(this.imgElement, name, value); } } NgOptimizedImage.ɵfac = function NgOptimizedImage_Factory(t) { return new (t || NgOptimizedImage)(); }; NgOptimizedImage.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgOptimizedImage, selectors: [["img", "ngSrc", ""]], hostVars: 8, hostBindings: function NgOptimizedImage_HostBindings(rf, ctx) { if (rf & 2) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵstyleProp"]("position", ctx.fill ? "absolute" : null)("width", ctx.fill ? "100%" : null)("height", ctx.fill ? "100%" : null)("inset", ctx.fill ? "0px" : null); } }, inputs: { ngSrc: "ngSrc", ngSrcset: "ngSrcset", sizes: "sizes", width: "width", height: "height", loading: "loading", priority: "priority", loaderParams: "loaderParams", disableOptimizedSrcset: "disableOptimizedSrcset", fill: "fill", src: "src", srcset: "srcset" }, standalone: true, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵNgOnChangesFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgOptimizedImage, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ standalone: true, selector: 'img[ngSrc]', host: { '[style.position]': 'fill ? "absolute" : null', '[style.width]': 'fill ? "100%" : null', '[style.height]': 'fill ? "100%" : null', '[style.inset]': 'fill ? "0px" : null' } }] }], null, { ngSrc: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], ngSrcset: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], sizes: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], width: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], height: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], loading: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], priority: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], loaderParams: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], disableOptimizedSrcset: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], fill: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], src: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], srcset: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /***** Helpers *****/ /** * Convert input value to integer. */ function inputToInteger(value) { return typeof value === 'string' ? parseInt(value, 10) : value; } /** * Convert input value to boolean. */ function inputToBoolean(value) { return value != null && `${value}` !== 'false'; } /** * Sorts provided config breakpoints and uses defaults. */ function processConfig(config) { let sortedBreakpoints = {}; if (config.breakpoints) { sortedBreakpoints.breakpoints = config.breakpoints.sort((a, b) => a - b); } return Object.assign({}, defaultConfig, config, sortedBreakpoints); } /***** Assert functions *****/ /** * Verifies that there is no `src` set on a host element. */ function assertNoConflictingSrc(dir) { if (dir.src) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2950 /* RuntimeErrorCode.UNEXPECTED_SRC_ATTR */, `${imgDirectiveDetails(dir.ngSrc)} both \`src\` and \`ngSrc\` have been set. ` + `Supplying both of these attributes breaks lazy loading. ` + `The NgOptimizedImage directive sets \`src\` itself based on the value of \`ngSrc\`. ` + `To fix this, please remove the \`src\` attribute.`); } } /** * Verifies that there is no `srcset` set on a host element. */ function assertNoConflictingSrcset(dir) { if (dir.srcset) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2951 /* RuntimeErrorCode.UNEXPECTED_SRCSET_ATTR */, `${imgDirectiveDetails(dir.ngSrc)} both \`srcset\` and \`ngSrcset\` have been set. ` + `Supplying both of these attributes breaks lazy loading. ` + `The NgOptimizedImage directive sets \`srcset\` itself based on the value of ` + `\`ngSrcset\`. To fix this, please remove the \`srcset\` attribute.`); } } /** * Verifies that the `ngSrc` is not a Base64-encoded image. */ function assertNotBase64Image(dir) { let ngSrc = dir.ngSrc.trim(); if (ngSrc.startsWith('data:')) { if (ngSrc.length > BASE64_IMG_MAX_LENGTH_IN_ERROR) { ngSrc = ngSrc.substring(0, BASE64_IMG_MAX_LENGTH_IN_ERROR) + '...'; } throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc, false)} \`ngSrc\` is a Base64-encoded string ` + `(${ngSrc}). NgOptimizedImage does not support Base64-encoded strings. ` + `To fix this, disable the NgOptimizedImage directive for this element ` + `by removing \`ngSrc\` and using a standard \`src\` attribute instead.`); } } /** * Verifies that the 'sizes' only includes responsive values. */ function assertNoComplexSizes(dir) { let sizes = dir.sizes; if (sizes?.match(/((\)|,)\s|^)\d+px/)) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc, false)} \`sizes\` was set to a string including ` + `pixel values. For automatic \`srcset\` generation, \`sizes\` must only include responsive ` + `values, such as \`sizes="50vw"\` or \`sizes="(min-width: 768px) 50vw, 100vw"\`. ` + `To fix this, modify the \`sizes\` attribute, or provide your own \`ngSrcset\` value directly.`); } } /** * Verifies that the `ngSrc` is not a Blob URL. */ function assertNotBlobUrl(dir) { const ngSrc = dir.ngSrc.trim(); if (ngSrc.startsWith('blob:')) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} \`ngSrc\` was set to a blob URL (${ngSrc}). ` + `Blob URLs are not supported by the NgOptimizedImage directive. ` + `To fix this, disable the NgOptimizedImage directive for this element ` + `by removing \`ngSrc\` and using a regular \`src\` attribute instead.`); } } /** * Verifies that the input is set to a non-empty string. */ function assertNonEmptyInput(dir, name, value) { const isString = typeof value === 'string'; const isEmptyString = isString && value.trim() === ''; if (!isString || isEmptyString) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} \`${name}\` has an invalid value ` + `(\`${value}\`). To fix this, change the value to a non-empty string.`); } } /** * Verifies that the `ngSrcset` is in a valid format, e.g. "100w, 200w" or "1x, 2x". */ function assertValidNgSrcset(dir, value) { if (value == null) return; assertNonEmptyInput(dir, 'ngSrcset', value); const stringVal = value; const isValidWidthDescriptor = VALID_WIDTH_DESCRIPTOR_SRCSET.test(stringVal); const isValidDensityDescriptor = VALID_DENSITY_DESCRIPTOR_SRCSET.test(stringVal); if (isValidDensityDescriptor) { assertUnderDensityCap(dir, stringVal); } const isValidSrcset = isValidWidthDescriptor || isValidDensityDescriptor; if (!isValidSrcset) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} \`ngSrcset\` has an invalid value (\`${value}\`). ` + `To fix this, supply \`ngSrcset\` using a comma-separated list of one or more width ` + `descriptors (e.g. "100w, 200w") or density descriptors (e.g. "1x, 2x").`); } } function assertUnderDensityCap(dir, value) { const underDensityCap = value.split(',').every(num => num === '' || parseFloat(num) <= ABSOLUTE_SRCSET_DENSITY_CAP); if (!underDensityCap) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the \`ngSrcset\` contains an unsupported image density:` + `\`${value}\`. NgOptimizedImage generally recommends a max image density of ` + `${RECOMMENDED_SRCSET_DENSITY_CAP}x but supports image densities up to ` + `${ABSOLUTE_SRCSET_DENSITY_CAP}x. The human eye cannot distinguish between image densities ` + `greater than ${RECOMMENDED_SRCSET_DENSITY_CAP}x - which makes them unnecessary for ` + `most use cases. Images that will be pinch-zoomed are typically the primary use case for ` + `${ABSOLUTE_SRCSET_DENSITY_CAP}x images. Please remove the high density descriptor and try again.`); } } /** * Creates a `RuntimeError` instance to represent a situation when an input is set after * the directive has initialized. */ function postInitInputChangeError(dir, inputName) { let reason; if (inputName === 'width' || inputName === 'height') { reason = `Changing \`${inputName}\` may result in different attribute value ` + `applied to the underlying image element and cause layout shifts on a page.`; } else { reason = `Changing the \`${inputName}\` would have no effect on the underlying ` + `image element, because the resource loading has already occurred.`; } return new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2953 /* RuntimeErrorCode.UNEXPECTED_INPUT_CHANGE */, `${imgDirectiveDetails(dir.ngSrc)} \`${inputName}\` was updated after initialization. ` + `The NgOptimizedImage directive will not react to this input change. ${reason} ` + `To fix this, either switch \`${inputName}\` to a static value ` + `or wrap the image element in an *ngIf that is gated on the necessary value.`); } /** * Verify that none of the listed inputs has changed. */ function assertNoPostInitInputChange(dir, changes, inputs) { inputs.forEach(input => { const isUpdated = changes.hasOwnProperty(input); if (isUpdated && !changes[input].isFirstChange()) { if (input === 'ngSrc') { // When the `ngSrc` input changes, we detect that only in the // `ngOnChanges` hook, thus the `ngSrc` is already set. We use // `ngSrc` in the error message, so we use a previous value, but // not the updated one in it. dir = { ngSrc: changes[input].previousValue }; } throw postInitInputChangeError(dir, input); } }); } /** * Verifies that a specified input is a number greater than 0. */ function assertGreaterThanZero(dir, inputValue, inputName) { const validNumber = typeof inputValue === 'number' && inputValue > 0; const validString = typeof inputValue === 'string' && /^\d+$/.test(inputValue.trim()) && parseInt(inputValue) > 0; if (!validNumber && !validString) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} \`${inputName}\` has an invalid value ` + `(\`${inputValue}\`). To fix this, provide \`${inputName}\` ` + `as a number greater than 0.`); } } /** * Verifies that the rendered image is not visually distorted. Effectively this is checking: * - Whether the "width" and "height" attributes reflect the actual dimensions of the image. * - Whether image styling is "correct" (see below for a longer explanation). */ function assertNoImageDistortion(dir, img, renderer) { const removeListenerFn = renderer.listen(img, 'load', () => { removeListenerFn(); const computedStyle = window.getComputedStyle(img); let renderedWidth = parseFloat(computedStyle.getPropertyValue('width')); let renderedHeight = parseFloat(computedStyle.getPropertyValue('height')); const boxSizing = computedStyle.getPropertyValue('box-sizing'); if (boxSizing === 'border-box') { const paddingTop = computedStyle.getPropertyValue('padding-top'); const paddingRight = computedStyle.getPropertyValue('padding-right'); const paddingBottom = computedStyle.getPropertyValue('padding-bottom'); const paddingLeft = computedStyle.getPropertyValue('padding-left'); renderedWidth -= parseFloat(paddingRight) + parseFloat(paddingLeft); renderedHeight -= parseFloat(paddingTop) + parseFloat(paddingBottom); } const renderedAspectRatio = renderedWidth / renderedHeight; const nonZeroRenderedDimensions = renderedWidth !== 0 && renderedHeight !== 0; const intrinsicWidth = img.naturalWidth; const intrinsicHeight = img.naturalHeight; const intrinsicAspectRatio = intrinsicWidth / intrinsicHeight; const suppliedWidth = dir.width; const suppliedHeight = dir.height; const suppliedAspectRatio = suppliedWidth / suppliedHeight; // Tolerance is used to account for the impact of subpixel rendering. // Due to subpixel rendering, the rendered, intrinsic, and supplied // aspect ratios of a correctly configured image may not exactly match. // For example, a `width=4030 height=3020` image might have a rendered // size of "1062w, 796.48h". (An aspect ratio of 1.334... vs. 1.333...) const inaccurateDimensions = Math.abs(suppliedAspectRatio - intrinsicAspectRatio) > ASPECT_RATIO_TOLERANCE; const stylingDistortion = nonZeroRenderedDimensions && Math.abs(intrinsicAspectRatio - renderedAspectRatio) > ASPECT_RATIO_TOLERANCE; if (inaccurateDimensions) { console.warn((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵformatRuntimeError"])(2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the aspect ratio of the image does not match ` + `the aspect ratio indicated by the width and height attributes. ` + `\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h ` + `(aspect-ratio: ${intrinsicAspectRatio}). \nSupplied width and height attributes: ` + `${suppliedWidth}w x ${suppliedHeight}h (aspect-ratio: ${suppliedAspectRatio}). ` + `\nTo fix this, update the width and height attributes.`)); } else if (stylingDistortion) { console.warn((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵformatRuntimeError"])(2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the aspect ratio of the rendered image ` + `does not match the image's intrinsic aspect ratio. ` + `\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h ` + `(aspect-ratio: ${intrinsicAspectRatio}). \nRendered image size: ` + `${renderedWidth}w x ${renderedHeight}h (aspect-ratio: ` + `${renderedAspectRatio}). \nThis issue can occur if "width" and "height" ` + `attributes are added to an image without updating the corresponding ` + `image styling. To fix this, adjust image styling. In most cases, ` + `adding "height: auto" or "width: auto" to the image styling will fix ` + `this issue.`)); } else if (!dir.ngSrcset && nonZeroRenderedDimensions) { // If `ngSrcset` hasn't been set, sanity check the intrinsic size. const recommendedWidth = RECOMMENDED_SRCSET_DENSITY_CAP * renderedWidth; const recommendedHeight = RECOMMENDED_SRCSET_DENSITY_CAP * renderedHeight; const oversizedWidth = intrinsicWidth - recommendedWidth >= OVERSIZED_IMAGE_TOLERANCE; const oversizedHeight = intrinsicHeight - recommendedHeight >= OVERSIZED_IMAGE_TOLERANCE; if (oversizedWidth || oversizedHeight) { console.warn((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵformatRuntimeError"])(2960 /* RuntimeErrorCode.OVERSIZED_IMAGE */, `${imgDirectiveDetails(dir.ngSrc)} the intrinsic image is significantly ` + `larger than necessary. ` + `\nRendered image size: ${renderedWidth}w x ${renderedHeight}h. ` + `\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h. ` + `\nRecommended intrinsic image size: ${recommendedWidth}w x ${recommendedHeight}h. ` + `\nNote: Recommended intrinsic image size is calculated assuming a maximum DPR of ` + `${RECOMMENDED_SRCSET_DENSITY_CAP}. To improve loading time, resize the image ` + `or consider using the "ngSrcset" and "sizes" attributes.`)); } } }); } /** * Verifies that a specified input is set. */ function assertNonEmptyWidthAndHeight(dir) { let missingAttributes = []; if (dir.width === undefined) missingAttributes.push('width'); if (dir.height === undefined) missingAttributes.push('height'); if (missingAttributes.length > 0) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2954 /* RuntimeErrorCode.REQUIRED_INPUT_MISSING */, `${imgDirectiveDetails(dir.ngSrc)} these required attributes ` + `are missing: ${missingAttributes.map(attr => `"${attr}"`).join(', ')}. ` + `Including "width" and "height" attributes will prevent image-related layout shifts. ` + `To fix this, include "width" and "height" attributes on the image tag or turn on ` + `"fill" mode with the \`fill\` attribute.`); } } /** * Verifies that width and height are not set. Used in fill mode, where those attributes don't make * sense. */ function assertEmptyWidthAndHeight(dir) { if (dir.width || dir.height) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the attributes \`height\` and/or \`width\` are present ` + `along with the \`fill\` attribute. Because \`fill\` mode causes an image to fill its containing ` + `element, the size attributes have no effect and should be removed.`); } } /** * Verifies that the rendered image has a nonzero height. If the image is in fill mode, provides * guidance that this can be caused by the containing element's CSS position property. */ function assertNonZeroRenderedHeight(dir, img, renderer) { const removeListenerFn = renderer.listen(img, 'load', () => { removeListenerFn(); const renderedHeight = img.clientHeight; if (dir.fill && renderedHeight === 0) { console.warn((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵformatRuntimeError"])(2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the height of the fill-mode image is zero. ` + `This is likely because the containing element does not have the CSS 'position' ` + `property set to one of the following: "relative", "fixed", or "absolute". ` + `To fix this problem, make sure the container element has the CSS 'position' ` + `property defined and the height of the element is not zero.`)); } }); } /** * Verifies that the `loading` attribute is set to a valid input & * is not used on priority images. */ function assertValidLoadingInput(dir) { if (dir.loading && dir.priority) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the \`loading\` attribute ` + `was used on an image that was marked "priority". ` + `Setting \`loading\` on priority images is not allowed ` + `because these images will always be eagerly loaded. ` + `To fix this, remove the “loading” attribute from the priority image.`); } const validInputs = ['auto', 'eager', 'lazy']; if (typeof dir.loading === 'string' && !validInputs.includes(dir.loading)) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the \`loading\` attribute ` + `has an invalid value (\`${dir.loading}\`). ` + `To fix this, provide a valid value ("lazy", "eager", or "auto").`); } } /** * Warns if NOT using a loader (falling back to the generic loader) and * the image appears to be hosted on one of the image CDNs for which * we do have a built-in image loader. Suggests switching to the * built-in loader. * * @param ngSrc Value of the ngSrc attribute * @param imageLoader ImageLoader provided */ function assertNotMissingBuiltInLoader(ngSrc, imageLoader) { if (imageLoader === noopImageLoader) { let builtInLoaderName = ''; for (const loader of BUILT_IN_LOADERS) { if (loader.testUrl(ngSrc)) { builtInLoaderName = loader.name; break; } } if (builtInLoaderName) { console.warn((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵformatRuntimeError"])(2962 /* RuntimeErrorCode.MISSING_BUILTIN_LOADER */, `NgOptimizedImage: It looks like your images may be hosted on the ` + `${builtInLoaderName} CDN, but your app is not using Angular's ` + `built-in loader for that CDN. We recommend switching to use ` + `the built-in by calling \`provide${builtInLoaderName}Loader()\` ` + `in your \`providers\` and passing it your instance's base URL. ` + `If you don't want to use the built-in loader, define a custom ` + `loader function using IMAGE_LOADER to silence this warning.`)); } } } /** * Warns if ngSrcset is present and no loader is configured (i.e. the default one is being used). */ function assertNoNgSrcsetWithoutLoader(dir, imageLoader) { if (dir.ngSrcset && imageLoader === noopImageLoader) { console.warn((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵformatRuntimeError"])(2963 /* RuntimeErrorCode.MISSING_NECESSARY_LOADER */, `${imgDirectiveDetails(dir.ngSrc)} the \`ngSrcset\` attribute is present but ` + `no image loader is configured (i.e. the default one is being used), ` + `which would result in the same image being used for all configured sizes. ` + `To fix this, provide a loader or remove the \`ngSrcset\` attribute from the image.`)); } } /** * Warns if loaderParams is present and no loader is configured (i.e. the default one is being * used). */ function assertNoLoaderParamsWithoutLoader(dir, imageLoader) { if (dir.loaderParams && imageLoader === noopImageLoader) { console.warn((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵformatRuntimeError"])(2963 /* RuntimeErrorCode.MISSING_NECESSARY_LOADER */, `${imgDirectiveDetails(dir.ngSrc)} the \`loaderParams\` attribute is present but ` + `no image loader is configured (i.e. the default one is being used), ` + `which means that the loaderParams data will not be consumed and will not affect the URL. ` + `To fix this, provide a custom loader or remove the \`loaderParams\` attribute from the image.`)); } } // These exports represent the set of symbols exposed as a public API. /** * @module * @description * Entry point for all public APIs of the common package. */ /** * @module * @description * Entry point for all public APIs of this package. */ // This file only reexports content of the `src` folder. Keep it that way. // This file is not used to build this module. It is only used during editing /** * Generated bundle index. Do not edit. */ /***/ }), /***/ 2560: /*!******************************************************!*\ !*** ./node_modules/@angular/core/fesm2020/core.mjs ***! \******************************************************/ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "ANALYZE_FOR_ENTRY_COMPONENTS": () => (/* binding */ ANALYZE_FOR_ENTRY_COMPONENTS), /* harmony export */ "ANIMATION_MODULE_TYPE": () => (/* binding */ ANIMATION_MODULE_TYPE), /* harmony export */ "APP_BOOTSTRAP_LISTENER": () => (/* binding */ APP_BOOTSTRAP_LISTENER), /* harmony export */ "APP_ID": () => (/* binding */ APP_ID), /* harmony export */ "APP_INITIALIZER": () => (/* binding */ APP_INITIALIZER), /* harmony export */ "ApplicationInitStatus": () => (/* binding */ ApplicationInitStatus), /* harmony export */ "ApplicationModule": () => (/* binding */ ApplicationModule), /* harmony export */ "ApplicationRef": () => (/* binding */ ApplicationRef), /* harmony export */ "Attribute": () => (/* binding */ Attribute), /* harmony export */ "COMPILER_OPTIONS": () => (/* binding */ COMPILER_OPTIONS), /* harmony export */ "CUSTOM_ELEMENTS_SCHEMA": () => (/* binding */ CUSTOM_ELEMENTS_SCHEMA), /* harmony export */ "ChangeDetectionStrategy": () => (/* binding */ ChangeDetectionStrategy), /* harmony export */ "ChangeDetectorRef": () => (/* binding */ ChangeDetectorRef), /* harmony export */ "Compiler": () => (/* binding */ Compiler), /* harmony export */ "CompilerFactory": () => (/* binding */ CompilerFactory), /* harmony export */ "Component": () => (/* binding */ Component), /* harmony export */ "ComponentFactory": () => (/* binding */ ComponentFactory$1), /* harmony export */ "ComponentFactoryResolver": () => (/* binding */ ComponentFactoryResolver$1), /* harmony export */ "ComponentRef": () => (/* binding */ ComponentRef$1), /* harmony export */ "ContentChild": () => (/* binding */ ContentChild), /* harmony export */ "ContentChildren": () => (/* binding */ ContentChildren), /* harmony export */ "DEFAULT_CURRENCY_CODE": () => (/* binding */ DEFAULT_CURRENCY_CODE), /* harmony export */ "DebugElement": () => (/* binding */ DebugElement), /* harmony export */ "DebugEventListener": () => (/* binding */ DebugEventListener), /* harmony export */ "DebugNode": () => (/* binding */ DebugNode), /* harmony export */ "DefaultIterableDiffer": () => (/* binding */ DefaultIterableDiffer), /* harmony export */ "Directive": () => (/* binding */ Directive), /* harmony export */ "ENVIRONMENT_INITIALIZER": () => (/* binding */ ENVIRONMENT_INITIALIZER), /* harmony export */ "ElementRef": () => (/* binding */ ElementRef), /* harmony export */ "EmbeddedViewRef": () => (/* binding */ EmbeddedViewRef), /* harmony export */ "EnvironmentInjector": () => (/* binding */ EnvironmentInjector), /* harmony export */ "ErrorHandler": () => (/* binding */ ErrorHandler), /* harmony export */ "EventEmitter": () => (/* binding */ EventEmitter), /* harmony export */ "Host": () => (/* binding */ Host), /* harmony export */ "HostBinding": () => (/* binding */ HostBinding), /* harmony export */ "HostListener": () => (/* binding */ HostListener), /* harmony export */ "INJECTOR": () => (/* binding */ INJECTOR), /* harmony export */ "Inject": () => (/* binding */ Inject), /* harmony export */ "InjectFlags": () => (/* binding */ InjectFlags), /* harmony export */ "Injectable": () => (/* binding */ Injectable), /* harmony export */ "InjectionToken": () => (/* binding */ InjectionToken), /* harmony export */ "Injector": () => (/* binding */ Injector), /* harmony export */ "Input": () => (/* binding */ Input), /* harmony export */ "IterableDiffers": () => (/* binding */ IterableDiffers), /* harmony export */ "KeyValueDiffers": () => (/* binding */ KeyValueDiffers), /* harmony export */ "LOCALE_ID": () => (/* binding */ LOCALE_ID), /* harmony export */ "MissingTranslationStrategy": () => (/* binding */ MissingTranslationStrategy), /* harmony export */ "ModuleWithComponentFactories": () => (/* binding */ ModuleWithComponentFactories), /* harmony export */ "NO_ERRORS_SCHEMA": () => (/* binding */ NO_ERRORS_SCHEMA), /* harmony export */ "NgModule": () => (/* binding */ NgModule), /* harmony export */ "NgModuleFactory": () => (/* binding */ NgModuleFactory$1), /* harmony export */ "NgModuleRef": () => (/* binding */ NgModuleRef$1), /* harmony export */ "NgProbeToken": () => (/* binding */ NgProbeToken), /* harmony export */ "NgZone": () => (/* binding */ NgZone), /* harmony export */ "Optional": () => (/* binding */ Optional), /* harmony export */ "Output": () => (/* binding */ Output), /* harmony export */ "PACKAGE_ROOT_URL": () => (/* binding */ PACKAGE_ROOT_URL), /* harmony export */ "PLATFORM_ID": () => (/* binding */ PLATFORM_ID), /* harmony export */ "PLATFORM_INITIALIZER": () => (/* binding */ PLATFORM_INITIALIZER), /* harmony export */ "Pipe": () => (/* binding */ Pipe), /* harmony export */ "PlatformRef": () => (/* binding */ PlatformRef), /* harmony export */ "Query": () => (/* binding */ Query), /* harmony export */ "QueryList": () => (/* binding */ QueryList), /* harmony export */ "ReflectiveInjector": () => (/* binding */ ReflectiveInjector), /* harmony export */ "ReflectiveKey": () => (/* binding */ ReflectiveKey), /* harmony export */ "Renderer2": () => (/* binding */ Renderer2), /* harmony export */ "RendererFactory2": () => (/* binding */ RendererFactory2), /* harmony export */ "RendererStyleFlags2": () => (/* binding */ RendererStyleFlags2), /* harmony export */ "ResolvedReflectiveFactory": () => (/* binding */ ResolvedReflectiveFactory), /* harmony export */ "Sanitizer": () => (/* binding */ Sanitizer), /* harmony export */ "SecurityContext": () => (/* binding */ SecurityContext), /* harmony export */ "Self": () => (/* binding */ Self), /* harmony export */ "SimpleChange": () => (/* binding */ SimpleChange), /* harmony export */ "SkipSelf": () => (/* binding */ SkipSelf), /* harmony export */ "TRANSLATIONS": () => (/* binding */ TRANSLATIONS), /* harmony export */ "TRANSLATIONS_FORMAT": () => (/* binding */ TRANSLATIONS_FORMAT), /* harmony export */ "TemplateRef": () => (/* binding */ TemplateRef), /* harmony export */ "Testability": () => (/* binding */ Testability), /* harmony export */ "TestabilityRegistry": () => (/* binding */ TestabilityRegistry), /* harmony export */ "Type": () => (/* binding */ Type), /* harmony export */ "VERSION": () => (/* binding */ VERSION), /* harmony export */ "Version": () => (/* binding */ Version), /* harmony export */ "ViewChild": () => (/* binding */ ViewChild), /* harmony export */ "ViewChildren": () => (/* binding */ ViewChildren), /* harmony export */ "ViewContainerRef": () => (/* binding */ ViewContainerRef), /* harmony export */ "ViewEncapsulation": () => (/* binding */ ViewEncapsulation$1), /* harmony export */ "ViewRef": () => (/* binding */ ViewRef), /* harmony export */ "asNativeElements": () => (/* binding */ asNativeElements), /* harmony export */ "assertPlatform": () => (/* binding */ assertPlatform), /* harmony export */ "createComponent": () => (/* binding */ createComponent), /* harmony export */ "createEnvironmentInjector": () => (/* binding */ createEnvironmentInjector), /* harmony export */ "createNgModule": () => (/* binding */ createNgModule), /* harmony export */ "createNgModuleRef": () => (/* binding */ createNgModuleRef), /* harmony export */ "createPlatform": () => (/* binding */ createPlatform), /* harmony export */ "createPlatformFactory": () => (/* binding */ createPlatformFactory), /* harmony export */ "defineInjectable": () => (/* binding */ defineInjectable), /* harmony export */ "destroyPlatform": () => (/* binding */ destroyPlatform), /* harmony export */ "enableProdMode": () => (/* binding */ enableProdMode), /* harmony export */ "forwardRef": () => (/* binding */ forwardRef), /* harmony export */ "getDebugNode": () => (/* binding */ getDebugNode), /* harmony export */ "getModuleFactory": () => (/* binding */ getModuleFactory), /* harmony export */ "getNgModuleById": () => (/* binding */ getNgModuleById), /* harmony export */ "getPlatform": () => (/* binding */ getPlatform), /* harmony export */ "importProvidersFrom": () => (/* binding */ importProvidersFrom), /* harmony export */ "inject": () => (/* binding */ inject), /* harmony export */ "isDevMode": () => (/* binding */ isDevMode), /* harmony export */ "isStandalone": () => (/* binding */ isStandalone), /* harmony export */ "makeEnvironmentProviders": () => (/* binding */ makeEnvironmentProviders), /* harmony export */ "platformCore": () => (/* binding */ platformCore), /* harmony export */ "reflectComponentType": () => (/* binding */ reflectComponentType), /* harmony export */ "resolveForwardRef": () => (/* binding */ resolveForwardRef), /* harmony export */ "setTestabilityGetter": () => (/* binding */ setTestabilityGetter), /* harmony export */ "ɵALLOW_MULTIPLE_PLATFORMS": () => (/* binding */ ALLOW_MULTIPLE_PLATFORMS), /* harmony export */ "ɵAPP_ID_RANDOM_PROVIDER": () => (/* binding */ APP_ID_RANDOM_PROVIDER), /* harmony export */ "ɵComponentFactory": () => (/* binding */ ComponentFactory$1), /* harmony export */ "ɵConsole": () => (/* binding */ Console), /* harmony export */ "ɵDEFAULT_LOCALE_ID": () => (/* binding */ DEFAULT_LOCALE_ID), /* harmony export */ "ɵINJECTOR_SCOPE": () => (/* binding */ INJECTOR_SCOPE), /* harmony export */ "ɵLContext": () => (/* binding */ LContext), /* harmony export */ "ɵLifecycleHooksFeature": () => (/* binding */ LifecycleHooksFeature), /* harmony export */ "ɵLocaleDataIndex": () => (/* binding */ LocaleDataIndex), /* harmony export */ "ɵNG_COMP_DEF": () => (/* binding */ NG_COMP_DEF), /* harmony export */ "ɵNG_DIR_DEF": () => (/* binding */ NG_DIR_DEF), /* harmony export */ "ɵNG_ELEMENT_ID": () => (/* binding */ NG_ELEMENT_ID), /* harmony export */ "ɵNG_INJ_DEF": () => (/* binding */ NG_INJ_DEF), /* harmony export */ "ɵNG_MOD_DEF": () => (/* binding */ NG_MOD_DEF), /* harmony export */ "ɵNG_PIPE_DEF": () => (/* binding */ NG_PIPE_DEF), /* harmony export */ "ɵNG_PROV_DEF": () => (/* binding */ NG_PROV_DEF), /* harmony export */ "ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR": () => (/* binding */ NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR), /* harmony export */ "ɵNO_CHANGE": () => (/* binding */ NO_CHANGE), /* harmony export */ "ɵNgModuleFactory": () => (/* binding */ NgModuleFactory), /* harmony export */ "ɵNoopNgZone": () => (/* binding */ NoopNgZone), /* harmony export */ "ɵReflectionCapabilities": () => (/* binding */ ReflectionCapabilities), /* harmony export */ "ɵRender3ComponentFactory": () => (/* binding */ ComponentFactory), /* harmony export */ "ɵRender3ComponentRef": () => (/* binding */ ComponentRef), /* harmony export */ "ɵRender3NgModuleRef": () => (/* binding */ NgModuleRef), /* harmony export */ "ɵRuntimeError": () => (/* binding */ RuntimeError), /* harmony export */ "ɵTESTABILITY": () => (/* binding */ TESTABILITY), /* harmony export */ "ɵTESTABILITY_GETTER": () => (/* binding */ TESTABILITY_GETTER), /* harmony export */ "ɵViewRef": () => (/* binding */ ViewRef$1), /* harmony export */ "ɵXSS_SECURITY_URL": () => (/* binding */ XSS_SECURITY_URL), /* harmony export */ "ɵ_sanitizeHtml": () => (/* binding */ _sanitizeHtml), /* harmony export */ "ɵ_sanitizeUrl": () => (/* binding */ _sanitizeUrl), /* harmony export */ "ɵallowSanitizationBypassAndThrow": () => (/* binding */ allowSanitizationBypassAndThrow), /* harmony export */ "ɵbypassSanitizationTrustHtml": () => (/* binding */ bypassSanitizationTrustHtml), /* harmony export */ "ɵbypassSanitizationTrustResourceUrl": () => (/* binding */ bypassSanitizationTrustResourceUrl), /* harmony export */ "ɵbypassSanitizationTrustScript": () => (/* binding */ bypassSanitizationTrustScript), /* harmony export */ "ɵbypassSanitizationTrustStyle": () => (/* binding */ bypassSanitizationTrustStyle), /* harmony export */ "ɵbypassSanitizationTrustUrl": () => (/* binding */ bypassSanitizationTrustUrl), /* harmony export */ "ɵclearResolutionOfComponentResourcesQueue": () => (/* binding */ clearResolutionOfComponentResourcesQueue), /* harmony export */ "ɵcoerceToBoolean": () => (/* binding */ coerceToBoolean), /* harmony export */ "ɵcompileComponent": () => (/* binding */ compileComponent), /* harmony export */ "ɵcompileDirective": () => (/* binding */ compileDirective), /* harmony export */ "ɵcompileNgModule": () => (/* binding */ compileNgModule), /* harmony export */ "ɵcompileNgModuleDefs": () => (/* binding */ compileNgModuleDefs), /* harmony export */ "ɵcompileNgModuleFactory": () => (/* binding */ compileNgModuleFactory), /* harmony export */ "ɵcompilePipe": () => (/* binding */ compilePipe), /* harmony export */ "ɵconvertToBitFlags": () => (/* binding */ convertToBitFlags), /* harmony export */ "ɵcreateInjector": () => (/* binding */ createInjector), /* harmony export */ "ɵdefaultIterableDiffers": () => (/* binding */ defaultIterableDiffers), /* harmony export */ "ɵdefaultKeyValueDiffers": () => (/* binding */ defaultKeyValueDiffers), /* harmony export */ "ɵdetectChanges": () => (/* binding */ detectChanges), /* harmony export */ "ɵdevModeEqual": () => (/* binding */ devModeEqual), /* harmony export */ "ɵfindLocaleData": () => (/* binding */ findLocaleData), /* harmony export */ "ɵflushModuleScopingQueueAsMuchAsPossible": () => (/* binding */ flushModuleScopingQueueAsMuchAsPossible), /* harmony export */ "ɵformatRuntimeError": () => (/* binding */ formatRuntimeError), /* harmony export */ "ɵgetDebugNode": () => (/* binding */ getDebugNode), /* harmony export */ "ɵgetDebugNodeR2": () => (/* binding */ getDebugNodeR2), /* harmony export */ "ɵgetDirectives": () => (/* binding */ getDirectives), /* harmony export */ "ɵgetHostElement": () => (/* binding */ getHostElement), /* harmony export */ "ɵgetInjectableDef": () => (/* binding */ getInjectableDef), /* harmony export */ "ɵgetLContext": () => (/* binding */ getLContext), /* harmony export */ "ɵgetLocaleCurrencyCode": () => (/* binding */ getLocaleCurrencyCode), /* harmony export */ "ɵgetLocalePluralCase": () => (/* binding */ getLocalePluralCase), /* harmony export */ "ɵgetSanitizationBypassType": () => (/* binding */ getSanitizationBypassType), /* harmony export */ "ɵgetUnknownElementStrictMode": () => (/* binding */ ɵgetUnknownElementStrictMode), /* harmony export */ "ɵgetUnknownPropertyStrictMode": () => (/* binding */ ɵgetUnknownPropertyStrictMode), /* harmony export */ "ɵglobal": () => (/* binding */ _global), /* harmony export */ "ɵinjectChangeDetectorRef": () => (/* binding */ injectChangeDetectorRef), /* harmony export */ "ɵinternalCreateApplication": () => (/* binding */ internalCreateApplication), /* harmony export */ "ɵisBoundToModule": () => (/* binding */ isBoundToModule), /* harmony export */ "ɵisEnvironmentProviders": () => (/* binding */ isEnvironmentProviders), /* harmony export */ "ɵisInjectable": () => (/* binding */ isInjectable), /* harmony export */ "ɵisNgModule": () => (/* binding */ isNgModule), /* harmony export */ "ɵisObservable": () => (/* binding */ isObservable), /* harmony export */ "ɵisPromise": () => (/* binding */ isPromise), /* harmony export */ "ɵisSubscribable": () => (/* binding */ isSubscribable), /* harmony export */ "ɵmakeDecorator": () => (/* binding */ makeDecorator), /* harmony export */ "ɵnoSideEffects": () => (/* binding */ noSideEffects), /* harmony export */ "ɵpatchComponentDefWithScope": () => (/* binding */ patchComponentDefWithScope), /* harmony export */ "ɵpublishDefaultGlobalUtils": () => (/* binding */ publishDefaultGlobalUtils$1), /* harmony export */ "ɵpublishGlobalUtil": () => (/* binding */ publishGlobalUtil), /* harmony export */ "ɵregisterLocaleData": () => (/* binding */ registerLocaleData), /* harmony export */ "ɵresetCompiledComponents": () => (/* binding */ resetCompiledComponents), /* harmony export */ "ɵresetJitOptions": () => (/* binding */ resetJitOptions), /* harmony export */ "ɵresolveComponentResources": () => (/* binding */ resolveComponentResources), /* harmony export */ "ɵsetAllowDuplicateNgModuleIdsForTest": () => (/* binding */ setAllowDuplicateNgModuleIdsForTest), /* harmony export */ "ɵsetClassMetadata": () => (/* binding */ setClassMetadata), /* harmony export */ "ɵsetCurrentInjector": () => (/* binding */ setCurrentInjector), /* harmony export */ "ɵsetDocument": () => (/* binding */ setDocument), /* harmony export */ "ɵsetLocaleId": () => (/* binding */ setLocaleId), /* harmony export */ "ɵsetUnknownElementStrictMode": () => (/* binding */ ɵsetUnknownElementStrictMode), /* harmony export */ "ɵsetUnknownPropertyStrictMode": () => (/* binding */ ɵsetUnknownPropertyStrictMode), /* harmony export */ "ɵstore": () => (/* binding */ store), /* harmony export */ "ɵstringify": () => (/* binding */ stringify), /* harmony export */ "ɵtransitiveScopesFor": () => (/* binding */ transitiveScopesFor), /* harmony export */ "ɵunregisterLocaleData": () => (/* binding */ unregisterAllLocaleData), /* harmony export */ "ɵunwrapSafeValue": () => (/* binding */ unwrapSafeValue), /* harmony export */ "ɵɵCopyDefinitionFeature": () => (/* binding */ ɵɵCopyDefinitionFeature), /* harmony export */ "ɵɵFactoryTarget": () => (/* binding */ FactoryTarget), /* harmony export */ "ɵɵHostDirectivesFeature": () => (/* binding */ ɵɵHostDirectivesFeature), /* harmony export */ "ɵɵInheritDefinitionFeature": () => (/* binding */ ɵɵInheritDefinitionFeature), /* harmony export */ "ɵɵNgOnChangesFeature": () => (/* binding */ ɵɵNgOnChangesFeature), /* harmony export */ "ɵɵProvidersFeature": () => (/* binding */ ɵɵProvidersFeature), /* harmony export */ "ɵɵStandaloneFeature": () => (/* binding */ ɵɵStandaloneFeature), /* harmony export */ "ɵɵadvance": () => (/* binding */ ɵɵadvance), /* harmony export */ "ɵɵattribute": () => (/* binding */ ɵɵattribute), /* harmony export */ "ɵɵattributeInterpolate1": () => (/* binding */ ɵɵattributeInterpolate1), /* harmony export */ "ɵɵattributeInterpolate2": () => (/* binding */ ɵɵattributeInterpolate2), /* harmony export */ "ɵɵattributeInterpolate3": () => (/* binding */ ɵɵattributeInterpolate3), /* harmony export */ "ɵɵattributeInterpolate4": () => (/* binding */ ɵɵattributeInterpolate4), /* harmony export */ "ɵɵattributeInterpolate5": () => (/* binding */ ɵɵattributeInterpolate5), /* harmony export */ "ɵɵattributeInterpolate6": () => (/* binding */ ɵɵattributeInterpolate6), /* harmony export */ "ɵɵattributeInterpolate7": () => (/* binding */ ɵɵattributeInterpolate7), /* harmony export */ "ɵɵattributeInterpolate8": () => (/* binding */ ɵɵattributeInterpolate8), /* harmony export */ "ɵɵattributeInterpolateV": () => (/* binding */ ɵɵattributeInterpolateV), /* harmony export */ "ɵɵclassMap": () => (/* binding */ ɵɵclassMap), /* harmony export */ "ɵɵclassMapInterpolate1": () => (/* binding */ ɵɵclassMapInterpolate1), /* harmony export */ "ɵɵclassMapInterpolate2": () => (/* binding */ ɵɵclassMapInterpolate2), /* harmony export */ "ɵɵclassMapInterpolate3": () => (/* binding */ ɵɵclassMapInterpolate3), /* harmony export */ "ɵɵclassMapInterpolate4": () => (/* binding */ ɵɵclassMapInterpolate4), /* harmony export */ "ɵɵclassMapInterpolate5": () => (/* binding */ ɵɵclassMapInterpolate5), /* harmony export */ "ɵɵclassMapInterpolate6": () => (/* binding */ ɵɵclassMapInterpolate6), /* harmony export */ "ɵɵclassMapInterpolate7": () => (/* binding */ ɵɵclassMapInterpolate7), /* harmony export */ "ɵɵclassMapInterpolate8": () => (/* binding */ ɵɵclassMapInterpolate8), /* harmony export */ "ɵɵclassMapInterpolateV": () => (/* binding */ ɵɵclassMapInterpolateV), /* harmony export */ "ɵɵclassProp": () => (/* binding */ ɵɵclassProp), /* harmony export */ "ɵɵcontentQuery": () => (/* binding */ ɵɵcontentQuery), /* harmony export */ "ɵɵdefineComponent": () => (/* binding */ ɵɵdefineComponent), /* harmony export */ "ɵɵdefineDirective": () => (/* binding */ ɵɵdefineDirective), /* harmony export */ "ɵɵdefineInjectable": () => (/* binding */ ɵɵdefineInjectable), /* harmony export */ "ɵɵdefineInjector": () => (/* binding */ ɵɵdefineInjector), /* harmony export */ "ɵɵdefineNgModule": () => (/* binding */ ɵɵdefineNgModule), /* harmony export */ "ɵɵdefinePipe": () => (/* binding */ ɵɵdefinePipe), /* harmony export */ "ɵɵdirectiveInject": () => (/* binding */ ɵɵdirectiveInject), /* harmony export */ "ɵɵdisableBindings": () => (/* binding */ ɵɵdisableBindings), /* harmony export */ "ɵɵelement": () => (/* binding */ ɵɵelement), /* harmony export */ "ɵɵelementContainer": () => (/* binding */ ɵɵelementContainer), /* harmony export */ "ɵɵelementContainerEnd": () => (/* binding */ ɵɵelementContainerEnd), /* harmony export */ "ɵɵelementContainerStart": () => (/* binding */ ɵɵelementContainerStart), /* harmony export */ "ɵɵelementEnd": () => (/* binding */ ɵɵelementEnd), /* harmony export */ "ɵɵelementStart": () => (/* binding */ ɵɵelementStart), /* harmony export */ "ɵɵenableBindings": () => (/* binding */ ɵɵenableBindings), /* harmony export */ "ɵɵgetCurrentView": () => (/* binding */ ɵɵgetCurrentView), /* harmony export */ "ɵɵgetInheritedFactory": () => (/* binding */ ɵɵgetInheritedFactory), /* harmony export */ "ɵɵhostProperty": () => (/* binding */ ɵɵhostProperty), /* harmony export */ "ɵɵi18n": () => (/* binding */ ɵɵi18n), /* harmony export */ "ɵɵi18nApply": () => (/* binding */ ɵɵi18nApply), /* harmony export */ "ɵɵi18nAttributes": () => (/* binding */ ɵɵi18nAttributes), /* harmony export */ "ɵɵi18nEnd": () => (/* binding */ ɵɵi18nEnd), /* harmony export */ "ɵɵi18nExp": () => (/* binding */ ɵɵi18nExp), /* harmony export */ "ɵɵi18nPostprocess": () => (/* binding */ ɵɵi18nPostprocess), /* harmony export */ "ɵɵi18nStart": () => (/* binding */ ɵɵi18nStart), /* harmony export */ "ɵɵinject": () => (/* binding */ ɵɵinject), /* harmony export */ "ɵɵinjectAttribute": () => (/* binding */ ɵɵinjectAttribute), /* harmony export */ "ɵɵinvalidFactory": () => (/* binding */ ɵɵinvalidFactory), /* harmony export */ "ɵɵinvalidFactoryDep": () => (/* binding */ ɵɵinvalidFactoryDep), /* harmony export */ "ɵɵlistener": () => (/* binding */ ɵɵlistener), /* harmony export */ "ɵɵloadQuery": () => (/* binding */ ɵɵloadQuery), /* harmony export */ "ɵɵnamespaceHTML": () => (/* binding */ ɵɵnamespaceHTML), /* harmony export */ "ɵɵnamespaceMathML": () => (/* binding */ ɵɵnamespaceMathML), /* harmony export */ "ɵɵnamespaceSVG": () => (/* binding */ ɵɵnamespaceSVG), /* harmony export */ "ɵɵnextContext": () => (/* binding */ ɵɵnextContext), /* harmony export */ "ɵɵngDeclareClassMetadata": () => (/* binding */ ɵɵngDeclareClassMetadata), /* harmony export */ "ɵɵngDeclareComponent": () => (/* binding */ ɵɵngDeclareComponent), /* harmony export */ "ɵɵngDeclareDirective": () => (/* binding */ ɵɵngDeclareDirective), /* harmony export */ "ɵɵngDeclareFactory": () => (/* binding */ ɵɵngDeclareFactory), /* harmony export */ "ɵɵngDeclareInjectable": () => (/* binding */ ɵɵngDeclareInjectable), /* harmony export */ "ɵɵngDeclareInjector": () => (/* binding */ ɵɵngDeclareInjector), /* harmony export */ "ɵɵngDeclareNgModule": () => (/* binding */ ɵɵngDeclareNgModule), /* harmony export */ "ɵɵngDeclarePipe": () => (/* binding */ ɵɵngDeclarePipe), /* harmony export */ "ɵɵpipe": () => (/* binding */ ɵɵpipe), /* harmony export */ "ɵɵpipeBind1": () => (/* binding */ ɵɵpipeBind1), /* harmony export */ "ɵɵpipeBind2": () => (/* binding */ ɵɵpipeBind2), /* harmony export */ "ɵɵpipeBind3": () => (/* binding */ ɵɵpipeBind3), /* harmony export */ "ɵɵpipeBind4": () => (/* binding */ ɵɵpipeBind4), /* harmony export */ "ɵɵpipeBindV": () => (/* binding */ ɵɵpipeBindV), /* harmony export */ "ɵɵprojection": () => (/* binding */ ɵɵprojection), /* harmony export */ "ɵɵprojectionDef": () => (/* binding */ ɵɵprojectionDef), /* harmony export */ "ɵɵproperty": () => (/* binding */ ɵɵproperty), /* harmony export */ "ɵɵpropertyInterpolate": () => (/* binding */ ɵɵpropertyInterpolate), /* harmony export */ "ɵɵpropertyInterpolate1": () => (/* binding */ ɵɵpropertyInterpolate1), /* harmony export */ "ɵɵpropertyInterpolate2": () => (/* binding */ ɵɵpropertyInterpolate2), /* harmony export */ "ɵɵpropertyInterpolate3": () => (/* binding */ ɵɵpropertyInterpolate3), /* harmony export */ "ɵɵpropertyInterpolate4": () => (/* binding */ ɵɵpropertyInterpolate4), /* harmony export */ "ɵɵpropertyInterpolate5": () => (/* binding */ ɵɵpropertyInterpolate5), /* harmony export */ "ɵɵpropertyInterpolate6": () => (/* binding */ ɵɵpropertyInterpolate6), /* harmony export */ "ɵɵpropertyInterpolate7": () => (/* binding */ ɵɵpropertyInterpolate7), /* harmony export */ "ɵɵpropertyInterpolate8": () => (/* binding */ ɵɵpropertyInterpolate8), /* harmony export */ "ɵɵpropertyInterpolateV": () => (/* binding */ ɵɵpropertyInterpolateV), /* harmony export */ "ɵɵpureFunction0": () => (/* binding */ ɵɵpureFunction0), /* harmony export */ "ɵɵpureFunction1": () => (/* binding */ ɵɵpureFunction1), /* harmony export */ "ɵɵpureFunction2": () => (/* binding */ ɵɵpureFunction2), /* harmony export */ "ɵɵpureFunction3": () => (/* binding */ ɵɵpureFunction3), /* harmony export */ "ɵɵpureFunction4": () => (/* binding */ ɵɵpureFunction4), /* harmony export */ "ɵɵpureFunction5": () => (/* binding */ ɵɵpureFunction5), /* harmony export */ "ɵɵpureFunction6": () => (/* binding */ ɵɵpureFunction6), /* harmony export */ "ɵɵpureFunction7": () => (/* binding */ ɵɵpureFunction7), /* harmony export */ "ɵɵpureFunction8": () => (/* binding */ ɵɵpureFunction8), /* harmony export */ "ɵɵpureFunctionV": () => (/* binding */ ɵɵpureFunctionV), /* harmony export */ "ɵɵqueryRefresh": () => (/* binding */ ɵɵqueryRefresh), /* harmony export */ "ɵɵreference": () => (/* binding */ ɵɵreference), /* harmony export */ "ɵɵregisterNgModuleType": () => (/* binding */ registerNgModuleType), /* harmony export */ "ɵɵresetView": () => (/* binding */ ɵɵresetView), /* harmony export */ "ɵɵresolveBody": () => (/* binding */ ɵɵresolveBody), /* harmony export */ "ɵɵresolveDocument": () => (/* binding */ ɵɵresolveDocument), /* harmony export */ "ɵɵresolveWindow": () => (/* binding */ ɵɵresolveWindow), /* harmony export */ "ɵɵrestoreView": () => (/* binding */ ɵɵrestoreView), /* harmony export */ "ɵɵsanitizeHtml": () => (/* binding */ ɵɵsanitizeHtml), /* harmony export */ "ɵɵsanitizeResourceUrl": () => (/* binding */ ɵɵsanitizeResourceUrl), /* harmony export */ "ɵɵsanitizeScript": () => (/* binding */ ɵɵsanitizeScript), /* harmony export */ "ɵɵsanitizeStyle": () => (/* binding */ ɵɵsanitizeStyle), /* harmony export */ "ɵɵsanitizeUrl": () => (/* binding */ ɵɵsanitizeUrl), /* harmony export */ "ɵɵsanitizeUrlOrResourceUrl": () => (/* binding */ ɵɵsanitizeUrlOrResourceUrl), /* harmony export */ "ɵɵsetComponentScope": () => (/* binding */ ɵɵsetComponentScope), /* harmony export */ "ɵɵsetNgModuleScope": () => (/* binding */ ɵɵsetNgModuleScope), /* harmony export */ "ɵɵstyleMap": () => (/* binding */ ɵɵstyleMap), /* harmony export */ "ɵɵstyleMapInterpolate1": () => (/* binding */ ɵɵstyleMapInterpolate1), /* harmony export */ "ɵɵstyleMapInterpolate2": () => (/* binding */ ɵɵstyleMapInterpolate2), /* harmony export */ "ɵɵstyleMapInterpolate3": () => (/* binding */ ɵɵstyleMapInterpolate3), /* harmony export */ "ɵɵstyleMapInterpolate4": () => (/* binding */ ɵɵstyleMapInterpolate4), /* harmony export */ "ɵɵstyleMapInterpolate5": () => (/* binding */ ɵɵstyleMapInterpolate5), /* harmony export */ "ɵɵstyleMapInterpolate6": () => (/* binding */ ɵɵstyleMapInterpolate6), /* harmony export */ "ɵɵstyleMapInterpolate7": () => (/* binding */ ɵɵstyleMapInterpolate7), /* harmony export */ "ɵɵstyleMapInterpolate8": () => (/* binding */ ɵɵstyleMapInterpolate8), /* harmony export */ "ɵɵstyleMapInterpolateV": () => (/* binding */ ɵɵstyleMapInterpolateV), /* harmony export */ "ɵɵstyleProp": () => (/* binding */ ɵɵstyleProp), /* harmony export */ "ɵɵstylePropInterpolate1": () => (/* binding */ ɵɵstylePropInterpolate1), /* harmony export */ "ɵɵstylePropInterpolate2": () => (/* binding */ ɵɵstylePropInterpolate2), /* harmony export */ "ɵɵstylePropInterpolate3": () => (/* binding */ ɵɵstylePropInterpolate3), /* harmony export */ "ɵɵstylePropInterpolate4": () => (/* binding */ ɵɵstylePropInterpolate4), /* harmony export */ "ɵɵstylePropInterpolate5": () => (/* binding */ ɵɵstylePropInterpolate5), /* harmony export */ "ɵɵstylePropInterpolate6": () => (/* binding */ ɵɵstylePropInterpolate6), /* harmony export */ "ɵɵstylePropInterpolate7": () => (/* binding */ ɵɵstylePropInterpolate7), /* harmony export */ "ɵɵstylePropInterpolate8": () => (/* binding */ ɵɵstylePropInterpolate8), /* harmony export */ "ɵɵstylePropInterpolateV": () => (/* binding */ ɵɵstylePropInterpolateV), /* harmony export */ "ɵɵsyntheticHostListener": () => (/* binding */ ɵɵsyntheticHostListener), /* harmony export */ "ɵɵsyntheticHostProperty": () => (/* binding */ ɵɵsyntheticHostProperty), /* harmony export */ "ɵɵtemplate": () => (/* binding */ ɵɵtemplate), /* harmony export */ "ɵɵtemplateRefExtractor": () => (/* binding */ ɵɵtemplateRefExtractor), /* harmony export */ "ɵɵtext": () => (/* binding */ ɵɵtext), /* harmony export */ "ɵɵtextInterpolate": () => (/* binding */ ɵɵtextInterpolate), /* harmony export */ "ɵɵtextInterpolate1": () => (/* binding */ ɵɵtextInterpolate1), /* harmony export */ "ɵɵtextInterpolate2": () => (/* binding */ ɵɵtextInterpolate2), /* harmony export */ "ɵɵtextInterpolate3": () => (/* binding */ ɵɵtextInterpolate3), /* harmony export */ "ɵɵtextInterpolate4": () => (/* binding */ ɵɵtextInterpolate4), /* harmony export */ "ɵɵtextInterpolate5": () => (/* binding */ ɵɵtextInterpolate5), /* harmony export */ "ɵɵtextInterpolate6": () => (/* binding */ ɵɵtextInterpolate6), /* harmony export */ "ɵɵtextInterpolate7": () => (/* binding */ ɵɵtextInterpolate7), /* harmony export */ "ɵɵtextInterpolate8": () => (/* binding */ ɵɵtextInterpolate8), /* harmony export */ "ɵɵtextInterpolateV": () => (/* binding */ ɵɵtextInterpolateV), /* harmony export */ "ɵɵtrustConstantHtml": () => (/* binding */ ɵɵtrustConstantHtml), /* harmony export */ "ɵɵtrustConstantResourceUrl": () => (/* binding */ ɵɵtrustConstantResourceUrl), /* harmony export */ "ɵɵvalidateIframeAttribute": () => (/* binding */ ɵɵvalidateIframeAttribute), /* harmony export */ "ɵɵviewQuery": () => (/* binding */ ɵɵviewQuery) /* harmony export */ }); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! rxjs */ 228); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! rxjs */ 6078); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! rxjs */ 833); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! rxjs */ 6646); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! rxjs/operators */ 1203); /** * @license Angular v15.2.9 * (c) 2010-2022 Google LLC. https://angular.io/ * License: MIT */ function getClosureSafeProperty(objWithPropertyToExtract) { for (let key in objWithPropertyToExtract) { if (objWithPropertyToExtract[key] === getClosureSafeProperty) { return key; } } throw Error('Could not find renamed property on target object.'); } /** * Sets properties on a target object from a source object, but only if * the property doesn't already exist on the target object. * @param target The target to set properties on * @param source The source of the property keys and values to set */ function fillProperties(target, source) { for (const key in source) { if (source.hasOwnProperty(key) && !target.hasOwnProperty(key)) { target[key] = source[key]; } } } function stringify(token) { if (typeof token === 'string') { return token; } if (Array.isArray(token)) { return '[' + token.map(stringify).join(', ') + ']'; } if (token == null) { return '' + token; } if (token.overriddenName) { return `${token.overriddenName}`; } if (token.name) { return `${token.name}`; } const res = token.toString(); if (res == null) { return '' + res; } const newLineIndex = res.indexOf('\n'); return newLineIndex === -1 ? res : res.substring(0, newLineIndex); } /** * Concatenates two strings with separator, allocating new strings only when necessary. * * @param before before string. * @param separator separator string. * @param after after string. * @returns concatenated string. */ function concatStringsWithSpace(before, after) { return before == null || before === '' ? after === null ? '' : after : after == null || after === '' ? before : before + ' ' + after; } const __forward_ref__ = getClosureSafeProperty({ __forward_ref__: getClosureSafeProperty }); /** * Allows to refer to references which are not yet defined. * * For instance, `forwardRef` is used when the `token` which we need to refer to for the purposes of * DI is declared, but not yet defined. It is also used when the `token` which we use when creating * a query is not yet defined. * * @usageNotes * ### Example * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref'} * @publicApi */ function forwardRef(forwardRefFn) { forwardRefFn.__forward_ref__ = forwardRef; forwardRefFn.toString = function () { return stringify(this()); }; return forwardRefFn; } /** * Lazily retrieves the reference value from a forwardRef. * * Acts as the identity function when given a non-forward-ref value. * * @usageNotes * ### Example * * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='resolve_forward_ref'} * * @see `forwardRef` * @publicApi */ function resolveForwardRef(type) { return isForwardRef(type) ? type() : type; } /** Checks whether a function is wrapped by a `forwardRef`. */ function isForwardRef(fn) { return typeof fn === 'function' && fn.hasOwnProperty(__forward_ref__) && fn.__forward_ref__ === forwardRef; } function isEnvironmentProviders(value) { return value && !!value.ɵproviders; } /** * Base URL for the error details page. * * Keep this constant in sync across: * - packages/compiler-cli/src/ngtsc/diagnostics/src/error_details_base_url.ts * - packages/core/src/error_details_base_url.ts */ const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors'; /** * URL for the XSS security documentation. */ const XSS_SECURITY_URL = 'https://g.co/ng/security#xss'; /** * Class that represents a runtime error. * Formats and outputs the error message in a consistent way. * * Example: * ``` * throw new RuntimeError( * RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED, * ngDevMode && 'Injector has already been destroyed.'); * ``` * * Note: the `message` argument contains a descriptive error message as a string in development * mode (when the `ngDevMode` is defined). In production mode (after tree-shaking pass), the * `message` argument becomes `false`, thus we account for it in the typings and the runtime logic. */ class RuntimeError extends Error { constructor(code, message) { super(formatRuntimeError(code, message)); this.code = code; } } /** * Called to format a runtime error. * See additional info on the `message` argument type in the `RuntimeError` class description. */ function formatRuntimeError(code, message) { // Error code might be a negative number, which is a special marker that instructs the logic to // generate a link to the error details page on angular.io. // We also prepend `0` to non-compile-time errors. const fullCode = `NG0${Math.abs(code)}`; let errorMessage = `${fullCode}${message ? ': ' + message.trim() : ''}`; if (ngDevMode && code < 0) { const addPeriodSeparator = !errorMessage.match(/[.,;!?]$/); const separator = addPeriodSeparator ? '.' : ''; errorMessage = `${errorMessage}${separator} Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/${fullCode}`; } return errorMessage; } /** * Used for stringify render output in Ivy. * Important! This function is very performance-sensitive and we should * be extra careful not to introduce megamorphic reads in it. * Check `core/test/render3/perf/render_stringify` for benchmarks and alternate implementations. */ function renderStringify(value) { if (typeof value === 'string') return value; if (value == null) return ''; // Use `String` so that it invokes the `toString` method of the value. Note that this // appears to be faster than calling `value.toString` (see `render_stringify` benchmark). return String(value); } /** * Used to stringify a value so that it can be displayed in an error message. * Important! This function contains a megamorphic read and should only be * used for error messages. */ function stringifyForError(value) { if (typeof value === 'function') return value.name || value.toString(); if (typeof value === 'object' && value != null && typeof value.type === 'function') { return value.type.name || value.type.toString(); } return renderStringify(value); } /** Called when directives inject each other (creating a circular dependency) */ function throwCyclicDependencyError(token, path) { const depPath = path ? `. Dependency path: ${path.join(' > ')} > ${token}` : ''; throw new RuntimeError(-200 /* RuntimeErrorCode.CYCLIC_DI_DEPENDENCY */, `Circular dependency in DI detected for ${token}${depPath}`); } function throwMixedMultiProviderError() { throw new Error(`Cannot mix multi providers and regular providers`); } function throwInvalidProviderError(ngModuleType, providers, provider) { if (ngModuleType && providers) { const providerDetail = providers.map(v => v == provider ? '?' + provider + '?' : '...'); throw new Error(`Invalid provider for the NgModule '${stringify(ngModuleType)}' - only instances of Provider and Type are allowed, got: [${providerDetail.join(', ')}]`); } else if (isEnvironmentProviders(provider)) { if (provider.ɵfromNgModule) { throw new RuntimeError(207 /* RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT */, `Invalid providers from 'importProvidersFrom' present in a non-environment injector. 'importProvidersFrom' can't be used for component providers.`); } else { throw new RuntimeError(207 /* RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT */, `Invalid providers present in a non-environment injector. 'EnvironmentProviders' can't be used for component providers.`); } } else { throw new Error('Invalid provider'); } } /** Throws an error when a token is not found in DI. */ function throwProviderNotFoundError(token, injectorName) { const injectorDetails = injectorName ? ` in ${injectorName}` : ''; throw new RuntimeError(-201 /* RuntimeErrorCode.PROVIDER_NOT_FOUND */, ngDevMode && `No provider for ${stringifyForError(token)} found${injectorDetails}`); } // The functions in this file verify that the assumptions we are making function assertNumber(actual, msg) { if (!(typeof actual === 'number')) { throwError(msg, typeof actual, 'number', '==='); } } function assertNumberInRange(actual, minInclusive, maxInclusive) { assertNumber(actual, 'Expected a number'); assertLessThanOrEqual(actual, maxInclusive, 'Expected number to be less than or equal to'); assertGreaterThanOrEqual(actual, minInclusive, 'Expected number to be greater than or equal to'); } function assertString(actual, msg) { if (!(typeof actual === 'string')) { throwError(msg, actual === null ? 'null' : typeof actual, 'string', '==='); } } function assertFunction(actual, msg) { if (!(typeof actual === 'function')) { throwError(msg, actual === null ? 'null' : typeof actual, 'function', '==='); } } function assertEqual(actual, expected, msg) { if (!(actual == expected)) { throwError(msg, actual, expected, '=='); } } function assertNotEqual(actual, expected, msg) { if (!(actual != expected)) { throwError(msg, actual, expected, '!='); } } function assertSame(actual, expected, msg) { if (!(actual === expected)) { throwError(msg, actual, expected, '==='); } } function assertNotSame(actual, expected, msg) { if (!(actual !== expected)) { throwError(msg, actual, expected, '!=='); } } function assertLessThan(actual, expected, msg) { if (!(actual < expected)) { throwError(msg, actual, expected, '<'); } } function assertLessThanOrEqual(actual, expected, msg) { if (!(actual <= expected)) { throwError(msg, actual, expected, '<='); } } function assertGreaterThan(actual, expected, msg) { if (!(actual > expected)) { throwError(msg, actual, expected, '>'); } } function assertGreaterThanOrEqual(actual, expected, msg) { if (!(actual >= expected)) { throwError(msg, actual, expected, '>='); } } function assertNotDefined(actual, msg) { if (actual != null) { throwError(msg, actual, null, '=='); } } function assertDefined(actual, msg) { if (actual == null) { throwError(msg, actual, null, '!='); } } function throwError(msg, actual, expected, comparison) { throw new Error(`ASSERTION ERROR: ${msg}` + (comparison == null ? '' : ` [Expected=> ${expected} ${comparison} ${actual} <=Actual]`)); } function assertDomNode(node) { // If we're in a worker, `Node` will not be defined. if (!(typeof Node !== 'undefined' && node instanceof Node) && !(typeof node === 'object' && node != null && node.constructor.name === 'WebWorkerRenderNode')) { throwError(`The provided value must be an instance of a DOM Node but got ${stringify(node)}`); } } function assertIndexInRange(arr, index) { assertDefined(arr, 'Array must be defined.'); const maxLen = arr.length; if (index < 0 || index >= maxLen) { throwError(`Index expected to be less than ${maxLen} but got ${index}`); } } function assertOneOf(value, ...validValues) { if (validValues.indexOf(value) !== -1) return true; throwError(`Expected value to be one of ${JSON.stringify(validValues)} but was ${JSON.stringify(value)}.`); } /** * Construct an injectable definition which defines how a token will be constructed by the DI * system, and in which injectors (if any) it will be available. * * This should be assigned to a static `ɵprov` field on a type, which will then be an * `InjectableType`. * * Options: * * `providedIn` determines which injectors will include the injectable, by either associating it * with an `@NgModule` or other `InjectorType`, or by specifying that this injectable should be * provided in the `'root'` injector, which will be the application-level injector in most apps. * * `factory` gives the zero argument function which will create an instance of the injectable. * The factory can call `inject` to access the `Injector` and request injection of dependencies. * * @codeGenApi * @publicApi This instruction has been emitted by ViewEngine for some time and is deployed to npm. */ function ɵɵdefineInjectable(opts) { return { token: opts.token, providedIn: opts.providedIn || null, factory: opts.factory, value: undefined }; } /** * @deprecated in v8, delete after v10. This API should be used only by generated code, and that * code should now use ɵɵdefineInjectable instead. * @publicApi */ const defineInjectable = ɵɵdefineInjectable; /** * Construct an `InjectorDef` which configures an injector. * * This should be assigned to a static injector def (`ɵinj`) field on a type, which will then be an * `InjectorType`. * * Options: * * * `providers`: an optional array of providers to add to the injector. Each provider must * either have a factory or point to a type which has a `ɵprov` static property (the * type must be an `InjectableType`). * * `imports`: an optional array of imports of other `InjectorType`s or `InjectorTypeWithModule`s * whose providers will also be added to the injector. Locally provided types will override * providers from imports. * * @codeGenApi */ function ɵɵdefineInjector(options) { return { providers: options.providers || [], imports: options.imports || [] }; } /** * Read the injectable def (`ɵprov`) for `type` in a way which is immune to accidentally reading * inherited value. * * @param type A type which may have its own (non-inherited) `ɵprov`. */ function getInjectableDef(type) { return getOwnDefinition(type, NG_PROV_DEF) || getOwnDefinition(type, NG_INJECTABLE_DEF); } function isInjectable(type) { return getInjectableDef(type) !== null; } /** * Return definition only if it is defined directly on `type` and is not inherited from a base * class of `type`. */ function getOwnDefinition(type, field) { return type.hasOwnProperty(field) ? type[field] : null; } /** * Read the injectable def (`ɵprov`) for `type` or read the `ɵprov` from one of its ancestors. * * @param type A type which may have `ɵprov`, via inheritance. * * @deprecated Will be removed in a future version of Angular, where an error will occur in the * scenario if we find the `ɵprov` on an ancestor only. */ function getInheritedInjectableDef(type) { const def = type && (type[NG_PROV_DEF] || type[NG_INJECTABLE_DEF]); if (def) { ngDevMode && console.warn(`DEPRECATED: DI is instantiating a token "${type.name}" that inherits its @Injectable decorator but does not provide one itself.\n` + `This will become an error in a future version of Angular. Please add @Injectable() to the "${type.name}" class.`); return def; } else { return null; } } /** * Read the injector def type in a way which is immune to accidentally reading inherited value. * * @param type type which may have an injector def (`ɵinj`) */ function getInjectorDef(type) { return type && (type.hasOwnProperty(NG_INJ_DEF) || type.hasOwnProperty(NG_INJECTOR_DEF)) ? type[NG_INJ_DEF] : null; } const NG_PROV_DEF = getClosureSafeProperty({ ɵprov: getClosureSafeProperty }); const NG_INJ_DEF = getClosureSafeProperty({ ɵinj: getClosureSafeProperty }); // We need to keep these around so we can read off old defs if new defs are unavailable const NG_INJECTABLE_DEF = getClosureSafeProperty({ ngInjectableDef: getClosureSafeProperty }); const NG_INJECTOR_DEF = getClosureSafeProperty({ ngInjectorDef: getClosureSafeProperty }); /** * Injection flags for DI. * * @publicApi * @deprecated use an options object for `inject` instead. */ var InjectFlags; (function (InjectFlags) { // TODO(alxhub): make this 'const' (and remove `InternalInjectFlags` enum) when ngc no longer // writes exports of it into ngfactory files. /** Check self and check parent injector if needed */ InjectFlags[InjectFlags["Default"] = 0] = "Default"; /** * Specifies that an injector should retrieve a dependency from any injector until reaching the * host element of the current component. (Only used with Element Injector) */ InjectFlags[InjectFlags["Host"] = 1] = "Host"; /** Don't ascend to ancestors of the node requesting injection. */ InjectFlags[InjectFlags["Self"] = 2] = "Self"; /** Skip the node that is requesting injection. */ InjectFlags[InjectFlags["SkipSelf"] = 4] = "SkipSelf"; /** Inject `defaultValue` instead if token not found. */ InjectFlags[InjectFlags["Optional"] = 8] = "Optional"; })(InjectFlags || (InjectFlags = {})); /** * Current implementation of inject. * * By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed * to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this * way for two reasons: * 1. `Injector` should not depend on ivy logic. * 2. To maintain tree shake-ability we don't want to bring in unnecessary code. */ let _injectImplementation; function getInjectImplementation() { return _injectImplementation; } /** * Sets the current inject implementation. */ function setInjectImplementation(impl) { const previous = _injectImplementation; _injectImplementation = impl; return previous; } /** * Injects `root` tokens in limp mode. * * If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to * `"root"`. This is known as the limp mode injection. In such case the value is stored in the * injectable definition. */ function injectRootLimpMode(token, notFoundValue, flags) { const injectableDef = getInjectableDef(token); if (injectableDef && injectableDef.providedIn == 'root') { return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() : injectableDef.value; } if (flags & InjectFlags.Optional) return null; if (notFoundValue !== undefined) return notFoundValue; throwProviderNotFoundError(stringify(token), 'Injector'); } /** * Assert that `_injectImplementation` is not `fn`. * * This is useful, to prevent infinite recursion. * * @param fn Function which it should not equal to */ function assertInjectImplementationNotEqual(fn) { ngDevMode && assertNotEqual(_injectImplementation, fn, 'Calling ɵɵinject would cause infinite recursion'); } // Always use __globalThis if available, which is the spec-defined global variable across all // environments, then fallback to __global first, because in Node tests both __global and // __window may be defined and _global should be __global in that case. Note: Typeof/Instanceof // checks are considered side-effects in Terser. We explicitly mark this as side-effect free: // https://github.com/terser/terser/issues/250. const _global = /* @__PURE__ */(() => typeof globalThis !== 'undefined' && globalThis || typeof global !== 'undefined' && global || typeof window !== 'undefined' && window || typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope && self)(); function ngDevModeResetPerfCounters() { const locationString = typeof location !== 'undefined' ? location.toString() : ''; const newCounters = { namedConstructors: locationString.indexOf('ngDevMode=namedConstructors') != -1, firstCreatePass: 0, tNode: 0, tView: 0, rendererCreateTextNode: 0, rendererSetText: 0, rendererCreateElement: 0, rendererAddEventListener: 0, rendererSetAttribute: 0, rendererRemoveAttribute: 0, rendererSetProperty: 0, rendererSetClassName: 0, rendererAddClass: 0, rendererRemoveClass: 0, rendererSetStyle: 0, rendererRemoveStyle: 0, rendererDestroy: 0, rendererDestroyNode: 0, rendererMoveNode: 0, rendererRemoveNode: 0, rendererAppendChild: 0, rendererInsertBefore: 0, rendererCreateComment: 0 }; // Make sure to refer to ngDevMode as ['ngDevMode'] for closure. const allowNgDevModeTrue = locationString.indexOf('ngDevMode=false') === -1; _global['ngDevMode'] = allowNgDevModeTrue && newCounters; return newCounters; } /** * This function checks to see if the `ngDevMode` has been set. If yes, * then we honor it, otherwise we default to dev mode with additional checks. * * The idea is that unless we are doing production build where we explicitly * set `ngDevMode == false` we should be helping the developer by providing * as much early warning and errors as possible. * * `ɵɵdefineComponent` is guaranteed to have been called before any component template functions * (and thus Ivy instructions), so a single initialization there is sufficient to ensure ngDevMode * is defined for the entire instruction set. * * When checking `ngDevMode` on toplevel, always init it before referencing it * (e.g. `((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode())`), otherwise you can * get a `ReferenceError` like in https://github.com/angular/angular/issues/31595. * * Details on possible values for `ngDevMode` can be found on its docstring. * * NOTE: * - changes to the `ngDevMode` name must be synced with `compiler-cli/src/tooling.ts`. */ function initNgDevMode() { // The below checks are to ensure that calling `initNgDevMode` multiple times does not // reset the counters. // If the `ngDevMode` is not an object, then it means we have not created the perf counters // yet. if (typeof ngDevMode === 'undefined' || ngDevMode) { if (typeof ngDevMode !== 'object') { ngDevModeResetPerfCounters(); } return typeof ngDevMode !== 'undefined' && !!ngDevMode; } return false; } const _THROW_IF_NOT_FOUND = {}; const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND; /* * Name of a property (that we patch onto DI decorator), which is used as an annotation of which * InjectFlag this decorator represents. This allows to avoid direct references to the DI decorators * in the code, thus making them tree-shakable. */ const DI_DECORATOR_FLAG = '__NG_DI_FLAG__'; const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath'; const NG_TOKEN_PATH = 'ngTokenPath'; const NEW_LINE = /\n/gm; const NO_NEW_LINE = 'ɵ'; const SOURCE = '__source'; /** * Current injector value used by `inject`. * - `undefined`: it is an error to call `inject` * - `null`: `inject` can be called but there is no injector (limp-mode). * - Injector instance: Use the injector for resolution. */ let _currentInjector = undefined; function setCurrentInjector(injector) { const former = _currentInjector; _currentInjector = injector; return former; } function injectInjectorOnly(token, flags = InjectFlags.Default) { if (_currentInjector === undefined) { throw new RuntimeError(-203 /* RuntimeErrorCode.MISSING_INJECTION_CONTEXT */, ngDevMode && `inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with \`EnvironmentInjector#runInContext\`.`); } else if (_currentInjector === null) { return injectRootLimpMode(token, undefined, flags); } else { return _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags); } } function ɵɵinject(token, flags = InjectFlags.Default) { return (getInjectImplementation() || injectInjectorOnly)(resolveForwardRef(token), flags); } /** * Throws an error indicating that a factory function could not be generated by the compiler for a * particular class. * * The name of the class is not mentioned here, but will be in the generated factory function name * and thus in the stack trace. * * @codeGenApi */ function ɵɵinvalidFactoryDep(index) { throw new RuntimeError(202 /* RuntimeErrorCode.INVALID_FACTORY_DEPENDENCY */, ngDevMode && `This constructor is not compatible with Angular Dependency Injection because its dependency at index ${index} of the parameter list is invalid. This can happen if the dependency type is a primitive like a string or if an ancestor of this class is missing an Angular decorator. Please check that 1) the type for the parameter at index ${index} is correct and 2) the correct Angular decorators are defined for this class and its ancestors.`); } /** * Injects a token from the currently active injector. * `inject` is only supported during instantiation of a dependency by the DI system. It can be used * during: * - Construction (via the `constructor`) of a class being instantiated by the DI system, such * as an `@Injectable` or `@Component`. * - In the initializer for fields of such classes. * - In the factory function specified for `useFactory` of a `Provider` or an `@Injectable`. * - In the `factory` function specified for an `InjectionToken`. * * @param token A token that represents a dependency that should be injected. * @param flags Optional flags that control how injection is executed. * The flags correspond to injection strategies that can be specified with * parameter decorators `@Host`, `@Self`, `@SkipSelf`, and `@Optional`. * @returns the injected value if operation is successful, `null` otherwise. * @throws if called outside of a supported context. * * @usageNotes * In practice the `inject()` calls are allowed in a constructor, a constructor parameter and a * field initializer: * * ```typescript * @Injectable({providedIn: 'root'}) * export class Car { * radio: Radio|undefined; * // OK: field initializer * spareTyre = inject(Tyre); * * constructor() { * // OK: constructor body * this.radio = inject(Radio); * } * } * ``` * * It is also legal to call `inject` from a provider's factory: * * ```typescript * providers: [ * {provide: Car, useFactory: () => { * // OK: a class factory * const engine = inject(Engine); * return new Car(engine); * }} * ] * ``` * * Calls to the `inject()` function outside of the class creation context will result in error. Most * notably, calls to `inject()` are disallowed after a class instance was created, in methods * (including lifecycle hooks): * * ```typescript * @Component({ ... }) * export class CarComponent { * ngOnInit() { * // ERROR: too late, the component instance was already created * const engine = inject(Engine); * engine.start(); * } * } * ``` * * @publicApi */ function inject(token, flags = InjectFlags.Default) { return ɵɵinject(token, convertToBitFlags(flags)); } // Converts object-based DI flags (`InjectOptions`) to bit flags (`InjectFlags`). function convertToBitFlags(flags) { if (typeof flags === 'undefined' || typeof flags === 'number') { return flags; } // While TypeScript doesn't accept it without a cast, bitwise OR with false-y values in // JavaScript is a no-op. We can use that for a very codesize-efficient conversion from // `InjectOptions` to `InjectFlags`. return 0 /* InternalInjectFlags.Default */ | ( // comment to force a line break in the formatter flags.optional && 8 /* InternalInjectFlags.Optional */) | (flags.host && 1 /* InternalInjectFlags.Host */) | (flags.self && 2 /* InternalInjectFlags.Self */) | (flags.skipSelf && 4 /* InternalInjectFlags.SkipSelf */); } function injectArgs(types) { const args = []; for (let i = 0; i < types.length; i++) { const arg = resolveForwardRef(types[i]); if (Array.isArray(arg)) { if (arg.length === 0) { throw new RuntimeError(900 /* RuntimeErrorCode.INVALID_DIFFER_INPUT */, ngDevMode && 'Arguments array must have arguments.'); } let type = undefined; let flags = InjectFlags.Default; for (let j = 0; j < arg.length; j++) { const meta = arg[j]; const flag = getInjectFlag(meta); if (typeof flag === 'number') { // Special case when we handle @Inject decorator. if (flag === -1 /* DecoratorFlags.Inject */) { type = meta.token; } else { flags |= flag; } } else { type = meta; } } args.push(ɵɵinject(type, flags)); } else { args.push(ɵɵinject(arg)); } } return args; } /** * Attaches a given InjectFlag to a given decorator using monkey-patching. * Since DI decorators can be used in providers `deps` array (when provider is configured using * `useFactory`) without initialization (e.g. `Host`) and as an instance (e.g. `new Host()`), we * attach the flag to make it available both as a static property and as a field on decorator * instance. * * @param decorator Provided DI decorator. * @param flag InjectFlag that should be applied. */ function attachInjectFlag(decorator, flag) { decorator[DI_DECORATOR_FLAG] = flag; decorator.prototype[DI_DECORATOR_FLAG] = flag; return decorator; } /** * Reads monkey-patched property that contains InjectFlag attached to a decorator. * * @param token Token that may contain monkey-patched DI flags property. */ function getInjectFlag(token) { return token[DI_DECORATOR_FLAG]; } function catchInjectorError(e, token, injectorErrorName, source) { const tokenPath = e[NG_TEMP_TOKEN_PATH]; if (token[SOURCE]) { tokenPath.unshift(token[SOURCE]); } e.message = formatError('\n' + e.message, tokenPath, injectorErrorName, source); e[NG_TOKEN_PATH] = tokenPath; e[NG_TEMP_TOKEN_PATH] = null; throw e; } function formatError(text, obj, injectorErrorName, source = null) { text = text && text.charAt(0) === '\n' && text.charAt(1) == NO_NEW_LINE ? text.slice(2) : text; let context = stringify(obj); if (Array.isArray(obj)) { context = obj.map(stringify).join(' -> '); } else if (typeof obj === 'object') { let parts = []; for (let key in obj) { if (obj.hasOwnProperty(key)) { let value = obj[key]; parts.push(key + ':' + (typeof value === 'string' ? JSON.stringify(value) : stringify(value))); } } context = `{${parts.join(', ')}}`; } return `${injectorErrorName}${source ? '(' + source + ')' : ''}[${context}]: ${text.replace(NEW_LINE, '\n ')}`; } /** * Convince closure compiler that the wrapped function has no side-effects. * * Closure compiler always assumes that `toString` has no side-effects. We use this quirk to * allow us to execute a function but have closure compiler mark the call as no-side-effects. * It is important that the return value for the `noSideEffects` function be assigned * to something which is retained otherwise the call to `noSideEffects` will be removed by closure * compiler. */ function noSideEffects(fn) { return { toString: fn }.toString(); } /** * The strategy that the default change detector uses to detect changes. * When set, takes effect the next time change detection is triggered. * * @see {@link ChangeDetectorRef#usage-notes Change detection usage} * * @publicApi */ var ChangeDetectionStrategy; (function (ChangeDetectionStrategy) { /** * Use the `CheckOnce` strategy, meaning that automatic change detection is deactivated * until reactivated by setting the strategy to `Default` (`CheckAlways`). * Change detection can still be explicitly invoked. * This strategy applies to all child directives and cannot be overridden. */ ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush"; /** * Use the default `CheckAlways` strategy, in which change detection is automatic until * explicitly deactivated. */ ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default"; })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {})); /** * Defines the CSS styles encapsulation policies for the {@link Component} decorator's * `encapsulation` option. * * See {@link Component#encapsulation encapsulation}. * * @usageNotes * ### Example * * {@example core/ts/metadata/encapsulation.ts region='longform'} * * @publicApi */ var ViewEncapsulation$1; (function (ViewEncapsulation) { // TODO: consider making `ViewEncapsulation` a `const enum` instead. See // https://github.com/angular/angular/issues/44119 for additional information. /** * Emulates a native Shadow DOM encapsulation behavior by adding a specific attribute to the * component's host element and applying the same attribute to all the CSS selectors provided * via {@link Component#styles styles} or {@link Component#styleUrls styleUrls}. * * This is the default option. */ ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated"; // Historically the 1 value was for `Native` encapsulation which has been removed as of v11. /** * Doesn't provide any sort of CSS style encapsulation, meaning that all the styles provided * via {@link Component#styles styles} or {@link Component#styleUrls styleUrls} are applicable * to any HTML element of the application regardless of their host Component. */ ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None"; /** * Uses the browser's native Shadow DOM API to encapsulate CSS styles, meaning that it creates * a ShadowRoot for the component's host element which is then used to encapsulate * all the Component's styling. */ ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom"; })(ViewEncapsulation$1 || (ViewEncapsulation$1 = {})); /** * This file contains reuseable "empty" symbols that can be used as default return values * in different parts of the rendering code. Because the same symbols are returned, this * allows for identity checks against these values to be consistently used by the framework * code. */ const EMPTY_OBJ = {}; const EMPTY_ARRAY = []; // freezing the values prevents any code from accidentally inserting new values in if ((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode()) { // These property accesses can be ignored because ngDevMode will be set to false // when optimizing code and the whole if statement will be dropped. // tslint:disable-next-line:no-toplevel-property-access Object.freeze(EMPTY_OBJ); // tslint:disable-next-line:no-toplevel-property-access Object.freeze(EMPTY_ARRAY); } const NG_COMP_DEF = getClosureSafeProperty({ ɵcmp: getClosureSafeProperty }); const NG_DIR_DEF = getClosureSafeProperty({ ɵdir: getClosureSafeProperty }); const NG_PIPE_DEF = getClosureSafeProperty({ ɵpipe: getClosureSafeProperty }); const NG_MOD_DEF = getClosureSafeProperty({ ɵmod: getClosureSafeProperty }); const NG_FACTORY_DEF = getClosureSafeProperty({ ɵfac: getClosureSafeProperty }); /** * If a directive is diPublic, bloomAdd sets a property on the type with this constant as * the key and the directive's unique ID as the value. This allows us to map directives to their * bloom filter bit for DI. */ // TODO(misko): This is wrong. The NG_ELEMENT_ID should never be minified. const NG_ELEMENT_ID = getClosureSafeProperty({ __NG_ELEMENT_ID__: getClosureSafeProperty }); /** Counter used to generate unique IDs for component definitions. */ let componentDefCount = 0; /** * Create a component definition object. * * * # Example * ``` * class MyComponent { * // Generated by Angular Template Compiler * // [Symbol] syntax will not be supported by TypeScript until v2.7 * static ɵcmp = defineComponent({ * ... * }); * } * ``` * @codeGenApi */ function ɵɵdefineComponent(componentDefinition) { return noSideEffects(() => { // Initialize ngDevMode. This must be the first statement in ɵɵdefineComponent. // See the `initNgDevMode` docstring for more information. (typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode(); const baseDef = getNgDirectiveDef(componentDefinition); const def = { ...baseDef, decls: componentDefinition.decls, vars: componentDefinition.vars, template: componentDefinition.template, consts: componentDefinition.consts || null, ngContentSelectors: componentDefinition.ngContentSelectors, onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush, directiveDefs: null, pipeDefs: null, dependencies: baseDef.standalone && componentDefinition.dependencies || null, getStandaloneInjector: null, data: componentDefinition.data || {}, encapsulation: componentDefinition.encapsulation || ViewEncapsulation$1.Emulated, id: `c${componentDefCount++}`, styles: componentDefinition.styles || EMPTY_ARRAY, _: null, schemas: componentDefinition.schemas || null, tView: null }; initFeatures(def); const dependencies = componentDefinition.dependencies; def.directiveDefs = extractDefListOrFactory(dependencies, /* pipeDef */false); def.pipeDefs = extractDefListOrFactory(dependencies, /* pipeDef */true); return def; }); } /** * Generated next to NgModules to monkey-patch directive and pipe references onto a component's * definition, when generating a direct reference in the component file would otherwise create an * import cycle. * * See [this explanation](https://hackmd.io/Odw80D0pR6yfsOjg_7XCJg?view) for more details. * * @codeGenApi */ function ɵɵsetComponentScope(type, directives, pipes) { const def = type.ɵcmp; def.directiveDefs = extractDefListOrFactory(directives, /* pipeDef */false); def.pipeDefs = extractDefListOrFactory(pipes, /* pipeDef */true); } function extractDirectiveDef(type) { return getComponentDef(type) || getDirectiveDef(type); } function nonNull(value) { return value !== null; } /** * @codeGenApi */ function ɵɵdefineNgModule(def) { return noSideEffects(() => { const res = { type: def.type, bootstrap: def.bootstrap || EMPTY_ARRAY, declarations: def.declarations || EMPTY_ARRAY, imports: def.imports || EMPTY_ARRAY, exports: def.exports || EMPTY_ARRAY, transitiveCompileScopes: null, schemas: def.schemas || null, id: def.id || null }; return res; }); } /** * Adds the module metadata that is necessary to compute the module's transitive scope to an * existing module definition. * * Scope metadata of modules is not used in production builds, so calls to this function can be * marked pure to tree-shake it from the bundle, allowing for all referenced declarations * to become eligible for tree-shaking as well. * * @codeGenApi */ function ɵɵsetNgModuleScope(type, scope) { return noSideEffects(() => { const ngModuleDef = getNgModuleDef(type, true); ngModuleDef.declarations = scope.declarations || EMPTY_ARRAY; ngModuleDef.imports = scope.imports || EMPTY_ARRAY; ngModuleDef.exports = scope.exports || EMPTY_ARRAY; }); } /** * Inverts an inputs or outputs lookup such that the keys, which were the * minified keys, are part of the values, and the values are parsed so that * the publicName of the property is the new key * * e.g. for * * ``` * class Comp { * @Input() * propName1: string; * * @Input('publicName2') * declaredPropName2: number; * } * ``` * * will be serialized as * * ``` * { * propName1: 'propName1', * declaredPropName2: ['publicName2', 'declaredPropName2'], * } * ``` * * which is than translated by the minifier as: * * ``` * { * minifiedPropName1: 'propName1', * minifiedPropName2: ['publicName2', 'declaredPropName2'], * } * ``` * * becomes: (public name => minifiedName) * * ``` * { * 'propName1': 'minifiedPropName1', * 'publicName2': 'minifiedPropName2', * } * ``` * * Optionally the function can take `secondary` which will result in: (public name => declared name) * * ``` * { * 'propName1': 'propName1', * 'publicName2': 'declaredPropName2', * } * ``` * */ function invertObject(obj, secondary) { if (obj == null) return EMPTY_OBJ; const newLookup = {}; for (const minifiedKey in obj) { if (obj.hasOwnProperty(minifiedKey)) { let publicName = obj[minifiedKey]; let declaredName = publicName; if (Array.isArray(publicName)) { declaredName = publicName[1]; publicName = publicName[0]; } newLookup[publicName] = minifiedKey; if (secondary) { secondary[publicName] = declaredName; } } } return newLookup; } /** * Create a directive definition object. * * # Example * ```ts * class MyDirective { * // Generated by Angular Template Compiler * // [Symbol] syntax will not be supported by TypeScript until v2.7 * static ɵdir = ɵɵdefineDirective({ * ... * }); * } * ``` * * @codeGenApi */ function ɵɵdefineDirective(directiveDefinition) { return noSideEffects(() => { const def = getNgDirectiveDef(directiveDefinition); initFeatures(def); return def; }); } /** * Create a pipe definition object. * * # Example * ``` * class MyPipe implements PipeTransform { * // Generated by Angular Template Compiler * static ɵpipe = definePipe({ * ... * }); * } * ``` * @param pipeDef Pipe definition generated by the compiler * * @codeGenApi */ function ɵɵdefinePipe(pipeDef) { return { type: pipeDef.type, name: pipeDef.name, factory: null, pure: pipeDef.pure !== false, standalone: pipeDef.standalone === true, onDestroy: pipeDef.type.prototype.ngOnDestroy || null }; } /** * The following getter methods retrieve the definition from the type. Currently the retrieval * honors inheritance, but in the future we may change the rule to require that definitions are * explicit. This would require some sort of migration strategy. */ function getComponentDef(type) { return type[NG_COMP_DEF] || null; } function getDirectiveDef(type) { return type[NG_DIR_DEF] || null; } function getPipeDef$1(type) { return type[NG_PIPE_DEF] || null; } /** * Checks whether a given Component, Directive or Pipe is marked as standalone. * This will return false if passed anything other than a Component, Directive, or Pipe class * See this guide for additional information: https://angular.io/guide/standalone-components * * @param type A reference to a Component, Directive or Pipe. * @publicApi */ function isStandalone(type) { const def = getComponentDef(type) || getDirectiveDef(type) || getPipeDef$1(type); return def !== null ? def.standalone : false; } function getNgModuleDef(type, throwNotFound) { const ngModuleDef = type[NG_MOD_DEF] || null; if (!ngModuleDef && throwNotFound === true) { throw new Error(`Type ${stringify(type)} does not have 'ɵmod' property.`); } return ngModuleDef; } function getNgDirectiveDef(directiveDefinition) { const declaredInputs = {}; return { type: directiveDefinition.type, providersResolver: null, factory: null, hostBindings: directiveDefinition.hostBindings || null, hostVars: directiveDefinition.hostVars || 0, hostAttrs: directiveDefinition.hostAttrs || null, contentQueries: directiveDefinition.contentQueries || null, declaredInputs, exportAs: directiveDefinition.exportAs || null, standalone: directiveDefinition.standalone === true, selectors: directiveDefinition.selectors || EMPTY_ARRAY, viewQuery: directiveDefinition.viewQuery || null, features: directiveDefinition.features || null, setInput: null, findHostDirectiveDefs: null, hostDirectives: null, inputs: invertObject(directiveDefinition.inputs, declaredInputs), outputs: invertObject(directiveDefinition.outputs) }; } function initFeatures(definition) { definition.features?.forEach(fn => fn(definition)); } function extractDefListOrFactory(dependencies, pipeDef) { if (!dependencies) { return null; } const defExtractor = pipeDef ? getPipeDef$1 : extractDirectiveDef; return () => (typeof dependencies === 'function' ? dependencies() : dependencies).map(dep => defExtractor(dep)).filter(nonNull); } // Below are constants for LView indices to help us look up LView members // without having to remember the specific indices. // Uglify will inline these when minifying so there shouldn't be a cost. const HOST = 0; const TVIEW = 1; const FLAGS = 2; const PARENT = 3; const NEXT = 4; const TRANSPLANTED_VIEWS_TO_REFRESH = 5; const T_HOST = 6; const CLEANUP = 7; const CONTEXT = 8; const INJECTOR$1 = 9; const RENDERER_FACTORY = 10; const RENDERER = 11; const SANITIZER = 12; const CHILD_HEAD = 13; const CHILD_TAIL = 14; // FIXME(misko): Investigate if the three declarations aren't all same thing. const DECLARATION_VIEW = 15; const DECLARATION_COMPONENT_VIEW = 16; const DECLARATION_LCONTAINER = 17; const PREORDER_HOOK_FLAGS = 18; const QUERIES = 19; const ID = 20; const EMBEDDED_VIEW_INJECTOR = 21; /** * Size of LView's header. Necessary to adjust for it when setting slots. * * IMPORTANT: `HEADER_OFFSET` should only be referred to the in the `ɵɵ*` instructions to translate * instruction index into `LView` index. All other indexes should be in the `LView` index space and * there should be no need to refer to `HEADER_OFFSET` anywhere else. */ const HEADER_OFFSET = 22; // Note: This hack is necessary so we don't erroneously get a circular dependency // failure based on types. const unusedValueExportToPlacateAjd$4 = 1; /** * Special location which allows easy identification of type. If we have an array which was * retrieved from the `LView` and that array has `true` at `TYPE` location, we know it is * `LContainer`. */ const TYPE = 1; /** * Below are constants for LContainer indices to help us look up LContainer members * without having to remember the specific indices. * Uglify will inline these when minifying so there shouldn't be a cost. */ /** * Flag to signify that this `LContainer` may have transplanted views which need to be change * detected. (see: `LView[DECLARATION_COMPONENT_VIEW])`. * * This flag, once set, is never unset for the `LContainer`. This means that when unset we can skip * a lot of work in `refreshEmbeddedViews`. But when set we still need to verify * that the `MOVED_VIEWS` are transplanted and on-push. */ const HAS_TRANSPLANTED_VIEWS = 2; // PARENT, NEXT, TRANSPLANTED_VIEWS_TO_REFRESH are indices 3, 4, and 5 // As we already have these constants in LView, we don't need to re-create them. // T_HOST is index 6 // We already have this constants in LView, we don't need to re-create it. const NATIVE = 7; const VIEW_REFS = 8; const MOVED_VIEWS = 9; /** * Size of LContainer's header. Represents the index after which all views in the * container will be inserted. We need to keep a record of current views so we know * which views are already in the DOM (and don't need to be re-added) and so we can * remove views from the DOM when they are no longer required. */ const CONTAINER_HEADER_OFFSET = 10; // Note: This hack is necessary so we don't erroneously get a circular dependency // failure based on types. const unusedValueExportToPlacateAjd$3 = 1; /** * True if `value` is `LView`. * @param value wrapped value of `RNode`, `LView`, `LContainer` */ function isLView(value) { return Array.isArray(value) && typeof value[TYPE] === 'object'; } /** * True if `value` is `LContainer`. * @param value wrapped value of `RNode`, `LView`, `LContainer` */ function isLContainer(value) { return Array.isArray(value) && value[TYPE] === true; } function isContentQueryHost(tNode) { return (tNode.flags & 4 /* TNodeFlags.hasContentQuery */) !== 0; } function isComponentHost(tNode) { return tNode.componentOffset > -1; } function isDirectiveHost(tNode) { return (tNode.flags & 1 /* TNodeFlags.isDirectiveHost */) === 1 /* TNodeFlags.isDirectiveHost */; } function isComponentDef(def) { return !!def.template; } function isRootView(target) { return (target[FLAGS] & 256 /* LViewFlags.IsRoot */) !== 0; } // [Assert functions do not constraint type when they are guarded by a truthy // expression.](https://github.com/microsoft/TypeScript/issues/37295) function assertTNodeForLView(tNode, lView) { assertTNodeForTView(tNode, lView[TVIEW]); } function assertTNodeForTView(tNode, tView) { assertTNode(tNode); tNode.hasOwnProperty('tView_') && assertEqual(tNode.tView_, tView, 'This TNode does not belong to this TView.'); } function assertTNode(tNode) { assertDefined(tNode, 'TNode must be defined'); if (!(tNode && typeof tNode === 'object' && tNode.hasOwnProperty('directiveStylingLast'))) { throwError('Not of type TNode, got: ' + tNode); } } function assertTIcu(tIcu) { assertDefined(tIcu, 'Expected TIcu to be defined'); if (!(typeof tIcu.currentCaseLViewIndex === 'number')) { throwError('Object is not of TIcu type.'); } } function assertComponentType(actual, msg = 'Type passed in is not ComponentType, it does not have \'ɵcmp\' property.') { if (!getComponentDef(actual)) { throwError(msg); } } function assertNgModuleType(actual, msg = 'Type passed in is not NgModuleType, it does not have \'ɵmod\' property.') { if (!getNgModuleDef(actual)) { throwError(msg); } } function assertCurrentTNodeIsParent(isParent) { assertEqual(isParent, true, 'currentTNode should be a parent'); } function assertHasParent(tNode) { assertDefined(tNode, 'currentTNode should exist!'); assertDefined(tNode.parent, 'currentTNode should have a parent'); } function assertLContainer(value) { assertDefined(value, 'LContainer must be defined'); assertEqual(isLContainer(value), true, 'Expecting LContainer'); } function assertLViewOrUndefined(value) { value && assertEqual(isLView(value), true, 'Expecting LView or undefined or null'); } function assertLView(value) { assertDefined(value, 'LView must be defined'); assertEqual(isLView(value), true, 'Expecting LView'); } function assertFirstCreatePass(tView, errMessage) { assertEqual(tView.firstCreatePass, true, errMessage || 'Should only be called in first create pass.'); } function assertFirstUpdatePass(tView, errMessage) { assertEqual(tView.firstUpdatePass, true, errMessage || 'Should only be called in first update pass.'); } /** * This is a basic sanity check that an object is probably a directive def. DirectiveDef is * an interface, so we can't do a direct instanceof check. */ function assertDirectiveDef(obj) { if (obj.type === undefined || obj.selectors == undefined || obj.inputs === undefined) { throwError(`Expected a DirectiveDef/ComponentDef and this object does not seem to have the expected shape.`); } } function assertIndexInDeclRange(lView, index) { const tView = lView[1]; assertBetween(HEADER_OFFSET, tView.bindingStartIndex, index); } function assertIndexInExpandoRange(lView, index) { const tView = lView[1]; assertBetween(tView.expandoStartIndex, lView.length, index); } function assertBetween(lower, upper, index) { if (!(lower <= index && index < upper)) { throwError(`Index out of range (expecting ${lower} <= ${index} < ${upper})`); } } function assertProjectionSlots(lView, errMessage) { assertDefined(lView[DECLARATION_COMPONENT_VIEW], 'Component views should exist.'); assertDefined(lView[DECLARATION_COMPONENT_VIEW][T_HOST].projection, errMessage || 'Components with projection nodes (<ng-content>) must have projection slots defined.'); } function assertParentView(lView, errMessage) { assertDefined(lView, errMessage || 'Component views should always have a parent view (component\'s host view)'); } /** * This is a basic sanity check that the `injectorIndex` seems to point to what looks like a * NodeInjector data structure. * * @param lView `LView` which should be checked. * @param injectorIndex index into the `LView` where the `NodeInjector` is expected. */ function assertNodeInjector(lView, injectorIndex) { assertIndexInExpandoRange(lView, injectorIndex); assertIndexInExpandoRange(lView, injectorIndex + 8 /* NodeInjectorOffset.PARENT */); assertNumber(lView[injectorIndex + 0], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 1], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 2], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 3], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 4], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 5], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 6], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 7], 'injectorIndex should point to a bloom filter'); assertNumber(lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */], 'injectorIndex should point to parent injector'); } function getFactoryDef(type, throwNotFound) { const hasFactoryDef = type.hasOwnProperty(NG_FACTORY_DEF); if (!hasFactoryDef && throwNotFound === true && ngDevMode) { throw new Error(`Type ${stringify(type)} does not have 'ɵfac' property.`); } return hasFactoryDef ? type[NG_FACTORY_DEF] : null; } /** * Represents a basic change from a previous to a new value for a single * property on a directive instance. Passed as a value in a * {@link SimpleChanges} object to the `ngOnChanges` hook. * * @see `OnChanges` * * @publicApi */ class SimpleChange { constructor(previousValue, currentValue, firstChange) { this.previousValue = previousValue; this.currentValue = currentValue; this.firstChange = firstChange; } /** * Check whether the new value is the first value assigned. */ isFirstChange() { return this.firstChange; } } /** * The NgOnChangesFeature decorates a component with support for the ngOnChanges * lifecycle hook, so it should be included in any component that implements * that hook. * * If the component or directive uses inheritance, the NgOnChangesFeature MUST * be included as a feature AFTER {@link InheritDefinitionFeature}, otherwise * inherited properties will not be propagated to the ngOnChanges lifecycle * hook. * * Example usage: * * ``` * static ɵcmp = defineComponent({ * ... * inputs: {name: 'publicName'}, * features: [NgOnChangesFeature] * }); * ``` * * @codeGenApi */ function ɵɵNgOnChangesFeature() { return NgOnChangesFeatureImpl; } function NgOnChangesFeatureImpl(definition) { if (definition.type.prototype.ngOnChanges) { definition.setInput = ngOnChangesSetInput; } return rememberChangeHistoryAndInvokeOnChangesHook; } // This option ensures that the ngOnChanges lifecycle hook will be inherited // from superclasses (in InheritDefinitionFeature). /** @nocollapse */ // tslint:disable-next-line:no-toplevel-property-access ɵɵNgOnChangesFeature.ngInherit = true; /** * This is a synthetic lifecycle hook which gets inserted into `TView.preOrderHooks` to simulate * `ngOnChanges`. * * The hook reads the `NgSimpleChangesStore` data from the component instance and if changes are * found it invokes `ngOnChanges` on the component instance. * * @param this Component instance. Because this function gets inserted into `TView.preOrderHooks`, * it is guaranteed to be called with component instance. */ function rememberChangeHistoryAndInvokeOnChangesHook() { const simpleChangesStore = getSimpleChangesStore(this); const current = simpleChangesStore?.current; if (current) { const previous = simpleChangesStore.previous; if (previous === EMPTY_OBJ) { simpleChangesStore.previous = current; } else { // New changes are copied to the previous store, so that we don't lose history for inputs // which were not changed this time for (let key in current) { previous[key] = current[key]; } } simpleChangesStore.current = null; this.ngOnChanges(current); } } function ngOnChangesSetInput(instance, value, publicName, privateName) { const declaredName = this.declaredInputs[publicName]; ngDevMode && assertString(declaredName, 'Name of input in ngOnChanges has to be a string'); const simpleChangesStore = getSimpleChangesStore(instance) || setSimpleChangesStore(instance, { previous: EMPTY_OBJ, current: null }); const current = simpleChangesStore.current || (simpleChangesStore.current = {}); const previous = simpleChangesStore.previous; const previousChange = previous[declaredName]; current[declaredName] = new SimpleChange(previousChange && previousChange.currentValue, value, previous === EMPTY_OBJ); instance[privateName] = value; } const SIMPLE_CHANGES_STORE = '__ngSimpleChanges__'; function getSimpleChangesStore(instance) { return instance[SIMPLE_CHANGES_STORE] || null; } function setSimpleChangesStore(instance, store) { return instance[SIMPLE_CHANGES_STORE] = store; } let profilerCallback = null; /** * Sets the callback function which will be invoked before and after performing certain actions at * runtime (for example, before and after running change detection). * * Warning: this function is *INTERNAL* and should not be relied upon in application's code. * The contract of the function might be changed in any release and/or the function can be removed * completely. * * @param profiler function provided by the caller or null value to disable profiling. */ const setProfiler = profiler => { profilerCallback = profiler; }; /** * Profiler function which wraps user code executed by the runtime. * * @param event ProfilerEvent corresponding to the execution context * @param instance component instance * @param hookOrListener lifecycle hook function or output listener. The value depends on the * execution context * @returns */ const profiler = function (event, instance, hookOrListener) { if (profilerCallback != null /* both `null` and `undefined` */) { profilerCallback(event, instance, hookOrListener); } }; const SVG_NAMESPACE = 'svg'; const MATH_ML_NAMESPACE = 'math'; /** * For efficiency reasons we often put several different data types (`RNode`, `LView`, `LContainer`) * in same location in `LView`. This is because we don't want to pre-allocate space for it * because the storage is sparse. This file contains utilities for dealing with such data types. * * How do we know what is stored at a given location in `LView`. * - `Array.isArray(value) === false` => `RNode` (The normal storage value) * - `Array.isArray(value) === true` => then the `value[0]` represents the wrapped value. * - `typeof value[TYPE] === 'object'` => `LView` * - This happens when we have a component at a given location * - `typeof value[TYPE] === true` => `LContainer` * - This happens when we have `LContainer` binding at a given location. * * * NOTE: it is assumed that `Array.isArray` and `typeof` operations are very efficient. */ /** * Returns `RNode`. * @param value wrapped value of `RNode`, `LView`, `LContainer` */ function unwrapRNode(value) { while (Array.isArray(value)) { value = value[HOST]; } return value; } /** * Returns `LView` or `null` if not found. * @param value wrapped value of `RNode`, `LView`, `LContainer` */ function unwrapLView(value) { while (Array.isArray(value)) { // This check is same as `isLView()` but we don't call at as we don't want to call // `Array.isArray()` twice and give JITer more work for inlining. if (typeof value[TYPE] === 'object') return value; value = value[HOST]; } return null; } /** * Retrieves an element value from the provided `viewData`, by unwrapping * from any containers, component views, or style contexts. */ function getNativeByIndex(index, lView) { ngDevMode && assertIndexInRange(lView, index); ngDevMode && assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Expected to be past HEADER_OFFSET'); return unwrapRNode(lView[index]); } /** * Retrieve an `RNode` for a given `TNode` and `LView`. * * This function guarantees in dev mode to retrieve a non-null `RNode`. * * @param tNode * @param lView */ function getNativeByTNode(tNode, lView) { ngDevMode && assertTNodeForLView(tNode, lView); ngDevMode && assertIndexInRange(lView, tNode.index); const node = unwrapRNode(lView[tNode.index]); return node; } /** * Retrieve an `RNode` or `null` for a given `TNode` and `LView`. * * Some `TNode`s don't have associated `RNode`s. For example `Projection` * * @param tNode * @param lView */ function getNativeByTNodeOrNull(tNode, lView) { const index = tNode === null ? -1 : tNode.index; if (index !== -1) { ngDevMode && assertTNodeForLView(tNode, lView); const node = unwrapRNode(lView[index]); return node; } return null; } // fixme(misko): The return Type should be `TNode|null` function getTNode(tView, index) { ngDevMode && assertGreaterThan(index, -1, 'wrong index for TNode'); ngDevMode && assertLessThan(index, tView.data.length, 'wrong index for TNode'); const tNode = tView.data[index]; ngDevMode && tNode !== null && assertTNode(tNode); return tNode; } /** Retrieves a value from any `LView` or `TData`. */ function load(view, index) { ngDevMode && assertIndexInRange(view, index); return view[index]; } function getComponentLViewByIndex(nodeIndex, hostView) { // Could be an LView or an LContainer. If LContainer, unwrap to find LView. ngDevMode && assertIndexInRange(hostView, nodeIndex); const slotValue = hostView[nodeIndex]; const lView = isLView(slotValue) ? slotValue : slotValue[HOST]; return lView; } /** Checks whether a given view is in creation mode */ function isCreationMode(view) { return (view[FLAGS] & 4 /* LViewFlags.CreationMode */) === 4 /* LViewFlags.CreationMode */; } /** * Returns a boolean for whether the view is attached to the change detection tree. * * Note: This determines whether a view should be checked, not whether it's inserted * into a container. For that, you'll want `viewAttachedToContainer` below. */ function viewAttachedToChangeDetector(view) { return (view[FLAGS] & 64 /* LViewFlags.Attached */) === 64 /* LViewFlags.Attached */; } /** Returns a boolean for whether the view is attached to a container. */ function viewAttachedToContainer(view) { return isLContainer(view[PARENT]); } function getConstant(consts, index) { if (index === null || index === undefined) return null; ngDevMode && assertIndexInRange(consts, index); return consts[index]; } /** * Resets the pre-order hook flags of the view. * @param lView the LView on which the flags are reset */ function resetPreOrderHookFlags(lView) { lView[PREORDER_HOOK_FLAGS] = 0; } /** * Updates the `TRANSPLANTED_VIEWS_TO_REFRESH` counter on the `LContainer` as well as the parents * whose * 1. counter goes from 0 to 1, indicating that there is a new child that has a view to refresh * or * 2. counter goes from 1 to 0, indicating there are no more descendant views to refresh */ function updateTransplantedViewCount(lContainer, amount) { lContainer[TRANSPLANTED_VIEWS_TO_REFRESH] += amount; let viewOrContainer = lContainer; let parent = lContainer[PARENT]; while (parent !== null && (amount === 1 && viewOrContainer[TRANSPLANTED_VIEWS_TO_REFRESH] === 1 || amount === -1 && viewOrContainer[TRANSPLANTED_VIEWS_TO_REFRESH] === 0)) { parent[TRANSPLANTED_VIEWS_TO_REFRESH] += amount; viewOrContainer = parent; parent = parent[PARENT]; } } const instructionState = { lFrame: createLFrame(null), bindingsEnabled: true }; /** * In this mode, any changes in bindings will throw an ExpressionChangedAfterChecked error. * * Necessary to support ChangeDetectorRef.checkNoChanges(). * * The `checkNoChanges` function is invoked only in ngDevMode=true and verifies that no unintended * changes exist in the change detector or its children. */ let _isInCheckNoChangesMode = false; /** * Returns true if the instruction state stack is empty. * * Intended to be called from tests only (tree shaken otherwise). */ function specOnlyIsInstructionStateEmpty() { return instructionState.lFrame.parent === null; } function getElementDepthCount() { return instructionState.lFrame.elementDepthCount; } function increaseElementDepthCount() { instructionState.lFrame.elementDepthCount++; } function decreaseElementDepthCount() { instructionState.lFrame.elementDepthCount--; } function getBindingsEnabled() { return instructionState.bindingsEnabled; } /** * Enables directive matching on elements. * * * Example: * ``` * <my-comp my-directive> * Should match component / directive. * </my-comp> * <div ngNonBindable> * <!-- ɵɵdisableBindings() --> * <my-comp my-directive> * Should not match component / directive because we are in ngNonBindable. * </my-comp> * <!-- ɵɵenableBindings() --> * </div> * ``` * * @codeGenApi */ function ɵɵenableBindings() { instructionState.bindingsEnabled = true; } /** * Disables directive matching on element. * * * Example: * ``` * <my-comp my-directive> * Should match component / directive. * </my-comp> * <div ngNonBindable> * <!-- ɵɵdisableBindings() --> * <my-comp my-directive> * Should not match component / directive because we are in ngNonBindable. * </my-comp> * <!-- ɵɵenableBindings() --> * </div> * ``` * * @codeGenApi */ function ɵɵdisableBindings() { instructionState.bindingsEnabled = false; } /** * Return the current `LView`. */ function getLView() { return instructionState.lFrame.lView; } /** * Return the current `TView`. */ function getTView() { return instructionState.lFrame.tView; } /** * Restores `contextViewData` to the given OpaqueViewState instance. * * Used in conjunction with the getCurrentView() instruction to save a snapshot * of the current view and restore it when listeners are invoked. This allows * walking the declaration view tree in listeners to get vars from parent views. * * @param viewToRestore The OpaqueViewState instance to restore. * @returns Context of the restored OpaqueViewState instance. * * @codeGenApi */ function ɵɵrestoreView(viewToRestore) { instructionState.lFrame.contextLView = viewToRestore; return viewToRestore[CONTEXT]; } /** * Clears the view set in `ɵɵrestoreView` from memory. Returns the passed in * value so that it can be used as a return value of an instruction. * * @codeGenApi */ function ɵɵresetView(value) { instructionState.lFrame.contextLView = null; return value; } function getCurrentTNode() { let currentTNode = getCurrentTNodePlaceholderOk(); while (currentTNode !== null && currentTNode.type === 64 /* TNodeType.Placeholder */) { currentTNode = currentTNode.parent; } return currentTNode; } function getCurrentTNodePlaceholderOk() { return instructionState.lFrame.currentTNode; } function getCurrentParentTNode() { const lFrame = instructionState.lFrame; const currentTNode = lFrame.currentTNode; return lFrame.isParent ? currentTNode : currentTNode.parent; } function setCurrentTNode(tNode, isParent) { ngDevMode && tNode && assertTNodeForTView(tNode, instructionState.lFrame.tView); const lFrame = instructionState.lFrame; lFrame.currentTNode = tNode; lFrame.isParent = isParent; } function isCurrentTNodeParent() { return instructionState.lFrame.isParent; } function setCurrentTNodeAsNotParent() { instructionState.lFrame.isParent = false; } function getContextLView() { const contextLView = instructionState.lFrame.contextLView; ngDevMode && assertDefined(contextLView, 'contextLView must be defined.'); return contextLView; } function isInCheckNoChangesMode() { !ngDevMode && throwError('Must never be called in production mode'); return _isInCheckNoChangesMode; } function setIsInCheckNoChangesMode(mode) { !ngDevMode && throwError('Must never be called in production mode'); _isInCheckNoChangesMode = mode; } // top level variables should not be exported for performance reasons (PERF_NOTES.md) function getBindingRoot() { const lFrame = instructionState.lFrame; let index = lFrame.bindingRootIndex; if (index === -1) { index = lFrame.bindingRootIndex = lFrame.tView.bindingStartIndex; } return index; } function getBindingIndex() { return instructionState.lFrame.bindingIndex; } function setBindingIndex(value) { return instructionState.lFrame.bindingIndex = value; } function nextBindingIndex() { return instructionState.lFrame.bindingIndex++; } function incrementBindingIndex(count) { const lFrame = instructionState.lFrame; const index = lFrame.bindingIndex; lFrame.bindingIndex = lFrame.bindingIndex + count; return index; } function isInI18nBlock() { return instructionState.lFrame.inI18n; } function setInI18nBlock(isInI18nBlock) { instructionState.lFrame.inI18n = isInI18nBlock; } /** * Set a new binding root index so that host template functions can execute. * * Bindings inside the host template are 0 index. But because we don't know ahead of time * how many host bindings we have we can't pre-compute them. For this reason they are all * 0 index and we just shift the root so that they match next available location in the LView. * * @param bindingRootIndex Root index for `hostBindings` * @param currentDirectiveIndex `TData[currentDirectiveIndex]` will point to the current directive * whose `hostBindings` are being processed. */ function setBindingRootForHostBindings(bindingRootIndex, currentDirectiveIndex) { const lFrame = instructionState.lFrame; lFrame.bindingIndex = lFrame.bindingRootIndex = bindingRootIndex; setCurrentDirectiveIndex(currentDirectiveIndex); } /** * When host binding is executing this points to the directive index. * `TView.data[getCurrentDirectiveIndex()]` is `DirectiveDef` * `LView[getCurrentDirectiveIndex()]` is directive instance. */ function getCurrentDirectiveIndex() { return instructionState.lFrame.currentDirectiveIndex; } /** * Sets an index of a directive whose `hostBindings` are being processed. * * @param currentDirectiveIndex `TData` index where current directive instance can be found. */ function setCurrentDirectiveIndex(currentDirectiveIndex) { instructionState.lFrame.currentDirectiveIndex = currentDirectiveIndex; } /** * Retrieve the current `DirectiveDef` which is active when `hostBindings` instruction is being * executed. * * @param tData Current `TData` where the `DirectiveDef` will be looked up at. */ function getCurrentDirectiveDef(tData) { const currentDirectiveIndex = instructionState.lFrame.currentDirectiveIndex; return currentDirectiveIndex === -1 ? null : tData[currentDirectiveIndex]; } function getCurrentQueryIndex() { return instructionState.lFrame.currentQueryIndex; } function setCurrentQueryIndex(value) { instructionState.lFrame.currentQueryIndex = value; } /** * Returns a `TNode` of the location where the current `LView` is declared at. * * @param lView an `LView` that we want to find parent `TNode` for. */ function getDeclarationTNode(lView) { const tView = lView[TVIEW]; // Return the declaration parent for embedded views if (tView.type === 2 /* TViewType.Embedded */) { ngDevMode && assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.'); return tView.declTNode; } // Components don't have `TView.declTNode` because each instance of component could be // inserted in different location, hence `TView.declTNode` is meaningless. // Falling back to `T_HOST` in case we cross component boundary. if (tView.type === 1 /* TViewType.Component */) { return lView[T_HOST]; } // Remaining TNode type is `TViewType.Root` which doesn't have a parent TNode. return null; } /** * This is a light weight version of the `enterView` which is needed by the DI system. * * @param lView `LView` location of the DI context. * @param tNode `TNode` for DI context * @param flags DI context flags. if `SkipSelf` flag is set than we walk up the declaration * tree from `tNode` until we find parent declared `TElementNode`. * @returns `true` if we have successfully entered DI associated with `tNode` (or with declared * `TNode` if `flags` has `SkipSelf`). Failing to enter DI implies that no associated * `NodeInjector` can be found and we should instead use `ModuleInjector`. * - If `true` than this call must be fallowed by `leaveDI` * - If `false` than this call failed and we should NOT call `leaveDI` */ function enterDI(lView, tNode, flags) { ngDevMode && assertLViewOrUndefined(lView); if (flags & InjectFlags.SkipSelf) { ngDevMode && assertTNodeForTView(tNode, lView[TVIEW]); let parentTNode = tNode; let parentLView = lView; while (true) { ngDevMode && assertDefined(parentTNode, 'Parent TNode should be defined'); parentTNode = parentTNode.parent; if (parentTNode === null && !(flags & InjectFlags.Host)) { parentTNode = getDeclarationTNode(parentLView); if (parentTNode === null) break; // In this case, a parent exists and is definitely an element. So it will definitely // have an existing lView as the declaration view, which is why we can assume it's defined. ngDevMode && assertDefined(parentLView, 'Parent LView should be defined'); parentLView = parentLView[DECLARATION_VIEW]; // In Ivy there are Comment nodes that correspond to ngIf and NgFor embedded directives // We want to skip those and look only at Elements and ElementContainers to ensure // we're looking at true parent nodes, and not content or other types. if (parentTNode.type & (2 /* TNodeType.Element */ | 8 /* TNodeType.ElementContainer */)) { break; } } else { break; } } if (parentTNode === null) { // If we failed to find a parent TNode this means that we should use module injector. return false; } else { tNode = parentTNode; lView = parentLView; } } ngDevMode && assertTNodeForLView(tNode, lView); const lFrame = instructionState.lFrame = allocLFrame(); lFrame.currentTNode = tNode; lFrame.lView = lView; return true; } /** * Swap the current lView with a new lView. * * For performance reasons we store the lView in the top level of the module. * This way we minimize the number of properties to read. Whenever a new view * is entered we have to store the lView for later, and when the view is * exited the state has to be restored * * @param newView New lView to become active * @returns the previously active lView; */ function enterView(newView) { ngDevMode && assertNotEqual(newView[0], newView[1], '????'); ngDevMode && assertLViewOrUndefined(newView); const newLFrame = allocLFrame(); if (ngDevMode) { assertEqual(newLFrame.isParent, true, 'Expected clean LFrame'); assertEqual(newLFrame.lView, null, 'Expected clean LFrame'); assertEqual(newLFrame.tView, null, 'Expected clean LFrame'); assertEqual(newLFrame.selectedIndex, -1, 'Expected clean LFrame'); assertEqual(newLFrame.elementDepthCount, 0, 'Expected clean LFrame'); assertEqual(newLFrame.currentDirectiveIndex, -1, 'Expected clean LFrame'); assertEqual(newLFrame.currentNamespace, null, 'Expected clean LFrame'); assertEqual(newLFrame.bindingRootIndex, -1, 'Expected clean LFrame'); assertEqual(newLFrame.currentQueryIndex, 0, 'Expected clean LFrame'); } const tView = newView[TVIEW]; instructionState.lFrame = newLFrame; ngDevMode && tView.firstChild && assertTNodeForTView(tView.firstChild, tView); newLFrame.currentTNode = tView.firstChild; newLFrame.lView = newView; newLFrame.tView = tView; newLFrame.contextLView = newView; newLFrame.bindingIndex = tView.bindingStartIndex; newLFrame.inI18n = false; } /** * Allocates next free LFrame. This function tries to reuse the `LFrame`s to lower memory pressure. */ function allocLFrame() { const currentLFrame = instructionState.lFrame; const childLFrame = currentLFrame === null ? null : currentLFrame.child; const newLFrame = childLFrame === null ? createLFrame(currentLFrame) : childLFrame; return newLFrame; } function createLFrame(parent) { const lFrame = { currentTNode: null, isParent: true, lView: null, tView: null, selectedIndex: -1, contextLView: null, elementDepthCount: 0, currentNamespace: null, currentDirectiveIndex: -1, bindingRootIndex: -1, bindingIndex: -1, currentQueryIndex: 0, parent: parent, child: null, inI18n: false }; parent !== null && (parent.child = lFrame); // link the new LFrame for reuse. return lFrame; } /** * A lightweight version of leave which is used with DI. * * This function only resets `currentTNode` and `LView` as those are the only properties * used with DI (`enterDI()`). * * NOTE: This function is reexported as `leaveDI`. However `leaveDI` has return type of `void` where * as `leaveViewLight` has `LFrame`. This is so that `leaveViewLight` can be used in `leaveView`. */ function leaveViewLight() { const oldLFrame = instructionState.lFrame; instructionState.lFrame = oldLFrame.parent; oldLFrame.currentTNode = null; oldLFrame.lView = null; return oldLFrame; } /** * This is a lightweight version of the `leaveView` which is needed by the DI system. * * NOTE: this function is an alias so that we can change the type of the function to have `void` * return type. */ const leaveDI = leaveViewLight; /** * Leave the current `LView` * * This pops the `LFrame` with the associated `LView` from the stack. * * IMPORTANT: We must zero out the `LFrame` values here otherwise they will be retained. This is * because for performance reasons we don't release `LFrame` but rather keep it for next use. */ function leaveView() { const oldLFrame = leaveViewLight(); oldLFrame.isParent = true; oldLFrame.tView = null; oldLFrame.selectedIndex = -1; oldLFrame.contextLView = null; oldLFrame.elementDepthCount = 0; oldLFrame.currentDirectiveIndex = -1; oldLFrame.currentNamespace = null; oldLFrame.bindingRootIndex = -1; oldLFrame.bindingIndex = -1; oldLFrame.currentQueryIndex = 0; } function nextContextImpl(level) { const contextLView = instructionState.lFrame.contextLView = walkUpViews(level, instructionState.lFrame.contextLView); return contextLView[CONTEXT]; } function walkUpViews(nestingLevel, currentView) { while (nestingLevel > 0) { ngDevMode && assertDefined(currentView[DECLARATION_VIEW], 'Declaration view should be defined if nesting level is greater than 0.'); currentView = currentView[DECLARATION_VIEW]; nestingLevel--; } return currentView; } /** * Gets the currently selected element index. * * Used with {@link property} instruction (and more in the future) to identify the index in the * current `LView` to act on. */ function getSelectedIndex() { return instructionState.lFrame.selectedIndex; } /** * Sets the most recent index passed to {@link select} * * Used with {@link property} instruction (and more in the future) to identify the index in the * current `LView` to act on. * * (Note that if an "exit function" was set earlier (via `setElementExitFn()`) then that will be * run if and when the provided `index` value is different from the current selected index value.) */ function setSelectedIndex(index) { ngDevMode && index !== -1 && assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Index must be past HEADER_OFFSET (or -1).'); ngDevMode && assertLessThan(index, instructionState.lFrame.lView.length, 'Can\'t set index passed end of LView'); instructionState.lFrame.selectedIndex = index; } /** * Gets the `tNode` that represents currently selected element. */ function getSelectedTNode() { const lFrame = instructionState.lFrame; return getTNode(lFrame.tView, lFrame.selectedIndex); } /** * Sets the namespace used to create elements to `'http://www.w3.org/2000/svg'` in global state. * * @codeGenApi */ function ɵɵnamespaceSVG() { instructionState.lFrame.currentNamespace = SVG_NAMESPACE; } /** * Sets the namespace used to create elements to `'http://www.w3.org/1998/MathML/'` in global state. * * @codeGenApi */ function ɵɵnamespaceMathML() { instructionState.lFrame.currentNamespace = MATH_ML_NAMESPACE; } /** * Sets the namespace used to create elements to `null`, which forces element creation to use * `createElement` rather than `createElementNS`. * * @codeGenApi */ function ɵɵnamespaceHTML() { namespaceHTMLInternal(); } /** * Sets the namespace used to create elements to `null`, which forces element creation to use * `createElement` rather than `createElementNS`. */ function namespaceHTMLInternal() { instructionState.lFrame.currentNamespace = null; } function getNamespace$1() { return instructionState.lFrame.currentNamespace; } /** * Adds all directive lifecycle hooks from the given `DirectiveDef` to the given `TView`. * * Must be run *only* on the first template pass. * * Sets up the pre-order hooks on the provided `tView`, * see {@link HookData} for details about the data structure. * * @param directiveIndex The index of the directive in LView * @param directiveDef The definition containing the hooks to setup in tView * @param tView The current TView */ function registerPreOrderHooks(directiveIndex, directiveDef, tView) { ngDevMode && assertFirstCreatePass(tView); const { ngOnChanges, ngOnInit, ngDoCheck } = directiveDef.type.prototype; if (ngOnChanges) { const wrappedOnChanges = NgOnChangesFeatureImpl(directiveDef); (tView.preOrderHooks ?? (tView.preOrderHooks = [])).push(directiveIndex, wrappedOnChanges); (tView.preOrderCheckHooks ?? (tView.preOrderCheckHooks = [])).push(directiveIndex, wrappedOnChanges); } if (ngOnInit) { (tView.preOrderHooks ?? (tView.preOrderHooks = [])).push(0 - directiveIndex, ngOnInit); } if (ngDoCheck) { (tView.preOrderHooks ?? (tView.preOrderHooks = [])).push(directiveIndex, ngDoCheck); (tView.preOrderCheckHooks ?? (tView.preOrderCheckHooks = [])).push(directiveIndex, ngDoCheck); } } /** * * Loops through the directives on the provided `tNode` and queues hooks to be * run that are not initialization hooks. * * Should be executed during `elementEnd()` and similar to * preserve hook execution order. Content, view, and destroy hooks for projected * components and directives must be called *before* their hosts. * * Sets up the content, view, and destroy hooks on the provided `tView`, * see {@link HookData} for details about the data structure. * * NOTE: This does not set up `onChanges`, `onInit` or `doCheck`, those are set up * separately at `elementStart`. * * @param tView The current TView * @param tNode The TNode whose directives are to be searched for hooks to queue */ function registerPostOrderHooks(tView, tNode) { ngDevMode && assertFirstCreatePass(tView); // It's necessary to loop through the directives at elementEnd() (rather than processing in // directiveCreate) so we can preserve the current hook order. Content, view, and destroy // hooks for projected components and directives must be called *before* their hosts. for (let i = tNode.directiveStart, end = tNode.directiveEnd; i < end; i++) { const directiveDef = tView.data[i]; ngDevMode && assertDefined(directiveDef, 'Expecting DirectiveDef'); const lifecycleHooks = directiveDef.type.prototype; const { ngAfterContentInit, ngAfterContentChecked, ngAfterViewInit, ngAfterViewChecked, ngOnDestroy } = lifecycleHooks; if (ngAfterContentInit) { (tView.contentHooks ?? (tView.contentHooks = [])).push(-i, ngAfterContentInit); } if (ngAfterContentChecked) { (tView.contentHooks ?? (tView.contentHooks = [])).push(i, ngAfterContentChecked); (tView.contentCheckHooks ?? (tView.contentCheckHooks = [])).push(i, ngAfterContentChecked); } if (ngAfterViewInit) { (tView.viewHooks ?? (tView.viewHooks = [])).push(-i, ngAfterViewInit); } if (ngAfterViewChecked) { (tView.viewHooks ?? (tView.viewHooks = [])).push(i, ngAfterViewChecked); (tView.viewCheckHooks ?? (tView.viewCheckHooks = [])).push(i, ngAfterViewChecked); } if (ngOnDestroy != null) { (tView.destroyHooks ?? (tView.destroyHooks = [])).push(i, ngOnDestroy); } } } /** * Executing hooks requires complex logic as we need to deal with 2 constraints. * * 1. Init hooks (ngOnInit, ngAfterContentInit, ngAfterViewInit) must all be executed once and only * once, across many change detection cycles. This must be true even if some hooks throw, or if * some recursively trigger a change detection cycle. * To solve that, it is required to track the state of the execution of these init hooks. * This is done by storing and maintaining flags in the view: the {@link InitPhaseState}, * and the index within that phase. They can be seen as a cursor in the following structure: * [[onInit1, onInit2], [afterContentInit1], [afterViewInit1, afterViewInit2, afterViewInit3]] * They are are stored as flags in LView[FLAGS]. * * 2. Pre-order hooks can be executed in batches, because of the select instruction. * To be able to pause and resume their execution, we also need some state about the hook's array * that is being processed: * - the index of the next hook to be executed * - the number of init hooks already found in the processed part of the array * They are are stored as flags in LView[PREORDER_HOOK_FLAGS]. */ /** * Executes pre-order check hooks ( OnChanges, DoChanges) given a view where all the init hooks were * executed once. This is a light version of executeInitAndCheckPreOrderHooks where we can skip read * / write of the init-hooks related flags. * @param lView The LView where hooks are defined * @param hooks Hooks to be run * @param nodeIndex 3 cases depending on the value: * - undefined: all hooks from the array should be executed (post-order case) * - null: execute hooks only from the saved index until the end of the array (pre-order case, when * flushing the remaining hooks) * - number: execute hooks only from the saved index until that node index exclusive (pre-order * case, when executing select(number)) */ function executeCheckHooks(lView, hooks, nodeIndex) { callHooks(lView, hooks, 3 /* InitPhaseState.InitPhaseCompleted */, nodeIndex); } /** * Executes post-order init and check hooks (one of AfterContentInit, AfterContentChecked, * AfterViewInit, AfterViewChecked) given a view where there are pending init hooks to be executed. * @param lView The LView where hooks are defined * @param hooks Hooks to be run * @param initPhase A phase for which hooks should be run * @param nodeIndex 3 cases depending on the value: * - undefined: all hooks from the array should be executed (post-order case) * - null: execute hooks only from the saved index until the end of the array (pre-order case, when * flushing the remaining hooks) * - number: execute hooks only from the saved index until that node index exclusive (pre-order * case, when executing select(number)) */ function executeInitAndCheckHooks(lView, hooks, initPhase, nodeIndex) { ngDevMode && assertNotEqual(initPhase, 3 /* InitPhaseState.InitPhaseCompleted */, 'Init pre-order hooks should not be called more than once'); if ((lView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) { callHooks(lView, hooks, initPhase, nodeIndex); } } function incrementInitPhaseFlags(lView, initPhase) { ngDevMode && assertNotEqual(initPhase, 3 /* InitPhaseState.InitPhaseCompleted */, 'Init hooks phase should not be incremented after all init hooks have been run.'); let flags = lView[FLAGS]; if ((flags & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) { flags &= 2047 /* LViewFlags.IndexWithinInitPhaseReset */; flags += 1 /* LViewFlags.InitPhaseStateIncrementer */; lView[FLAGS] = flags; } } /** * Calls lifecycle hooks with their contexts, skipping init hooks if it's not * the first LView pass * * @param currentView The current view * @param arr The array in which the hooks are found * @param initPhaseState the current state of the init phase * @param currentNodeIndex 3 cases depending on the value: * - undefined: all hooks from the array should be executed (post-order case) * - null: execute hooks only from the saved index until the end of the array (pre-order case, when * flushing the remaining hooks) * - number: execute hooks only from the saved index until that node index exclusive (pre-order * case, when executing select(number)) */ function callHooks(currentView, arr, initPhase, currentNodeIndex) { ngDevMode && assertEqual(isInCheckNoChangesMode(), false, 'Hooks should never be run when in check no changes mode.'); const startIndex = currentNodeIndex !== undefined ? currentView[PREORDER_HOOK_FLAGS] & 65535 /* PreOrderHookFlags.IndexOfTheNextPreOrderHookMaskMask */ : 0; const nodeIndexLimit = currentNodeIndex != null ? currentNodeIndex : -1; const max = arr.length - 1; // Stop the loop at length - 1, because we look for the hook at i + 1 let lastNodeIndexFound = 0; for (let i = startIndex; i < max; i++) { const hook = arr[i + 1]; if (typeof hook === 'number') { lastNodeIndexFound = arr[i]; if (currentNodeIndex != null && lastNodeIndexFound >= currentNodeIndex) { break; } } else { const isInitHook = arr[i] < 0; if (isInitHook) currentView[PREORDER_HOOK_FLAGS] += 65536 /* PreOrderHookFlags.NumberOfInitHooksCalledIncrementer */; if (lastNodeIndexFound < nodeIndexLimit || nodeIndexLimit == -1) { callHook(currentView, initPhase, arr, i); currentView[PREORDER_HOOK_FLAGS] = (currentView[PREORDER_HOOK_FLAGS] & 4294901760 /* PreOrderHookFlags.NumberOfInitHooksCalledMask */) + i + 2; } i++; } } } /** * Execute one hook against the current `LView`. * * @param currentView The current view * @param initPhaseState the current state of the init phase * @param arr The array in which the hooks are found * @param i The current index within the hook data array */ function callHook(currentView, initPhase, arr, i) { const isInitHook = arr[i] < 0; const hook = arr[i + 1]; const directiveIndex = isInitHook ? -arr[i] : arr[i]; const directive = currentView[directiveIndex]; if (isInitHook) { const indexWithintInitPhase = currentView[FLAGS] >> 11 /* LViewFlags.IndexWithinInitPhaseShift */; // The init phase state must be always checked here as it may have been recursively updated. if (indexWithintInitPhase < currentView[PREORDER_HOOK_FLAGS] >> 16 /* PreOrderHookFlags.NumberOfInitHooksCalledShift */ && (currentView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) { currentView[FLAGS] += 2048 /* LViewFlags.IndexWithinInitPhaseIncrementer */; profiler(4 /* ProfilerEvent.LifecycleHookStart */, directive, hook); try { hook.call(directive); } finally { profiler(5 /* ProfilerEvent.LifecycleHookEnd */, directive, hook); } } } else { profiler(4 /* ProfilerEvent.LifecycleHookStart */, directive, hook); try { hook.call(directive); } finally { profiler(5 /* ProfilerEvent.LifecycleHookEnd */, directive, hook); } } } const NO_PARENT_INJECTOR = -1; /** * Each injector is saved in 9 contiguous slots in `LView` and 9 contiguous slots in * `TView.data`. This allows us to store information about the current node's tokens (which * can be shared in `TView`) as well as the tokens of its ancestor nodes (which cannot be * shared, so they live in `LView`). * * Each of these slots (aside from the last slot) contains a bloom filter. This bloom filter * determines whether a directive is available on the associated node or not. This prevents us * from searching the directives array at this level unless it's probable the directive is in it. * * See: https://en.wikipedia.org/wiki/Bloom_filter for more about bloom filters. * * Because all injectors have been flattened into `LView` and `TViewData`, they cannot typed * using interfaces as they were previously. The start index of each `LInjector` and `TInjector` * will differ based on where it is flattened into the main array, so it's not possible to know * the indices ahead of time and save their types here. The interfaces are still included here * for documentation purposes. * * export interface LInjector extends Array<any> { * * // Cumulative bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE) * [0]: number; * * // Cumulative bloom for directive IDs 32-63 * [1]: number; * * // Cumulative bloom for directive IDs 64-95 * [2]: number; * * // Cumulative bloom for directive IDs 96-127 * [3]: number; * * // Cumulative bloom for directive IDs 128-159 * [4]: number; * * // Cumulative bloom for directive IDs 160 - 191 * [5]: number; * * // Cumulative bloom for directive IDs 192 - 223 * [6]: number; * * // Cumulative bloom for directive IDs 224 - 255 * [7]: number; * * // We need to store a reference to the injector's parent so DI can keep looking up * // the injector tree until it finds the dependency it's looking for. * [PARENT_INJECTOR]: number; * } * * export interface TInjector extends Array<any> { * * // Shared node bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE) * [0]: number; * * // Shared node bloom for directive IDs 32-63 * [1]: number; * * // Shared node bloom for directive IDs 64-95 * [2]: number; * * // Shared node bloom for directive IDs 96-127 * [3]: number; * * // Shared node bloom for directive IDs 128-159 * [4]: number; * * // Shared node bloom for directive IDs 160 - 191 * [5]: number; * * // Shared node bloom for directive IDs 192 - 223 * [6]: number; * * // Shared node bloom for directive IDs 224 - 255 * [7]: number; * * // Necessary to find directive indices for a particular node. * [TNODE]: TElementNode|TElementContainerNode|TContainerNode; * } */ /** * Factory for creating instances of injectors in the NodeInjector. * * This factory is complicated by the fact that it can resolve `multi` factories as well. * * NOTE: Some of the fields are optional which means that this class has two hidden classes. * - One without `multi` support (most common) * - One with `multi` values, (rare). * * Since VMs can cache up to 4 inline hidden classes this is OK. * * - Single factory: Only `resolving` and `factory` is defined. * - `providers` factory: `componentProviders` is a number and `index = -1`. * - `viewProviders` factory: `componentProviders` is a number and `index` points to `providers`. */ class NodeInjectorFactory { constructor( /** * Factory to invoke in order to create a new instance. */ factory, /** * Set to `true` if the token is declared in `viewProviders` (or if it is component). */ isViewProvider, injectImplementation) { this.factory = factory; /** * Marker set to true during factory invocation to see if we get into recursive loop. * Recursive loop causes an error to be displayed. */ this.resolving = false; ngDevMode && assertDefined(factory, 'Factory not specified'); ngDevMode && assertEqual(typeof factory, 'function', 'Expected factory function.'); this.canSeeViewProviders = isViewProvider; this.injectImpl = injectImplementation; } } function isFactory(obj) { return obj instanceof NodeInjectorFactory; } // Note: This hack is necessary so we don't erroneously get a circular dependency // failure based on types. const unusedValueExportToPlacateAjd$2 = 1; /** * Converts `TNodeType` into human readable text. * Make sure this matches with `TNodeType` */ function toTNodeTypeAsString(tNodeType) { let text = ''; tNodeType & 1 /* TNodeType.Text */ && (text += '|Text'); tNodeType & 2 /* TNodeType.Element */ && (text += '|Element'); tNodeType & 4 /* TNodeType.Container */ && (text += '|Container'); tNodeType & 8 /* TNodeType.ElementContainer */ && (text += '|ElementContainer'); tNodeType & 16 /* TNodeType.Projection */ && (text += '|Projection'); tNodeType & 32 /* TNodeType.Icu */ && (text += '|IcuContainer'); tNodeType & 64 /* TNodeType.Placeholder */ && (text += '|Placeholder'); return text.length > 0 ? text.substring(1) : text; } // Note: This hack is necessary so we don't erroneously get a circular dependency // failure based on types. const unusedValueExportToPlacateAjd$1 = 1; /** * Returns `true` if the `TNode` has a directive which has `@Input()` for `class` binding. * * ``` * <div my-dir [class]="exp"></div> * ``` * and * ``` * @Directive({ * }) * class MyDirective { * @Input() * class: string; * } * ``` * * In the above case it is necessary to write the reconciled styling information into the * directive's input. * * @param tNode */ function hasClassInput(tNode) { return (tNode.flags & 8 /* TNodeFlags.hasClassInput */) !== 0; } /** * Returns `true` if the `TNode` has a directive which has `@Input()` for `style` binding. * * ``` * <div my-dir [style]="exp"></div> * ``` * and * ``` * @Directive({ * }) * class MyDirective { * @Input() * class: string; * } * ``` * * In the above case it is necessary to write the reconciled styling information into the * directive's input. * * @param tNode */ function hasStyleInput(tNode) { return (tNode.flags & 16 /* TNodeFlags.hasStyleInput */) !== 0; } function assertTNodeType(tNode, expectedTypes, message) { assertDefined(tNode, 'should be called with a TNode'); if ((tNode.type & expectedTypes) === 0) { throwError(message || `Expected [${toTNodeTypeAsString(expectedTypes)}] but got ${toTNodeTypeAsString(tNode.type)}.`); } } function assertPureTNodeType(type) { if (!(type === 2 /* TNodeType.Element */ || // type === 1 /* TNodeType.Text */ || // type === 4 /* TNodeType.Container */ || // type === 8 /* TNodeType.ElementContainer */ || // type === 32 /* TNodeType.Icu */ || // type === 16 /* TNodeType.Projection */ || // type === 64 /* TNodeType.Placeholder */)) { throwError(`Expected TNodeType to have only a single type selected, but got ${toTNodeTypeAsString(type)}.`); } } /** * Assigns all attribute values to the provided element via the inferred renderer. * * This function accepts two forms of attribute entries: * * default: (key, value): * attrs = [key1, value1, key2, value2] * * namespaced: (NAMESPACE_MARKER, uri, name, value) * attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value] * * The `attrs` array can contain a mix of both the default and namespaced entries. * The "default" values are set without a marker, but if the function comes across * a marker value then it will attempt to set a namespaced value. If the marker is * not of a namespaced value then the function will quit and return the index value * where it stopped during the iteration of the attrs array. * * See [AttributeMarker] to understand what the namespace marker value is. * * Note that this instruction does not support assigning style and class values to * an element. See `elementStart` and `elementHostAttrs` to learn how styling values * are applied to an element. * @param renderer The renderer to be used * @param native The element that the attributes will be assigned to * @param attrs The attribute array of values that will be assigned to the element * @returns the index value that was last accessed in the attributes array */ function setUpAttributes(renderer, native, attrs) { let i = 0; while (i < attrs.length) { const value = attrs[i]; if (typeof value === 'number') { // only namespaces are supported. Other value types (such as style/class // entries) are not supported in this function. if (value !== 0 /* AttributeMarker.NamespaceURI */) { break; } // we just landed on the marker value ... therefore // we should skip to the next entry i++; const namespaceURI = attrs[i++]; const attrName = attrs[i++]; const attrVal = attrs[i++]; ngDevMode && ngDevMode.rendererSetAttribute++; renderer.setAttribute(native, attrName, attrVal, namespaceURI); } else { // attrName is string; const attrName = value; const attrVal = attrs[++i]; // Standard attributes ngDevMode && ngDevMode.rendererSetAttribute++; if (isAnimationProp(attrName)) { renderer.setProperty(native, attrName, attrVal); } else { renderer.setAttribute(native, attrName, attrVal); } i++; } } // another piece of code may iterate over the same attributes array. Therefore // it may be helpful to return the exact spot where the attributes array exited // whether by running into an unsupported marker or if all the static values were // iterated over. return i; } /** * Test whether the given value is a marker that indicates that the following * attribute values in a `TAttributes` array are only the names of attributes, * and not name-value pairs. * @param marker The attribute marker to test. * @returns true if the marker is a "name-only" marker (e.g. `Bindings`, `Template` or `I18n`). */ function isNameOnlyAttributeMarker(marker) { return marker === 3 /* AttributeMarker.Bindings */ || marker === 4 /* AttributeMarker.Template */ || marker === 6 /* AttributeMarker.I18n */; } function isAnimationProp(name) { // Perf note: accessing charCodeAt to check for the first character of a string is faster as // compared to accessing a character at index 0 (ex. name[0]). The main reason for this is that // charCodeAt doesn't allocate memory to return a substring. return name.charCodeAt(0) === 64 /* CharCode.AT_SIGN */; } /** * Merges `src` `TAttributes` into `dst` `TAttributes` removing any duplicates in the process. * * This merge function keeps the order of attrs same. * * @param dst Location of where the merged `TAttributes` should end up. * @param src `TAttributes` which should be appended to `dst` */ function mergeHostAttrs(dst, src) { if (src === null || src.length === 0) { // do nothing } else if (dst === null || dst.length === 0) { // We have source, but dst is empty, just make a copy. dst = src.slice(); } else { let srcMarker = -1 /* AttributeMarker.ImplicitAttributes */; for (let i = 0; i < src.length; i++) { const item = src[i]; if (typeof item === 'number') { srcMarker = item; } else { if (srcMarker === 0 /* AttributeMarker.NamespaceURI */) { // Case where we need to consume `key1`, `key2`, `value` items. } else if (srcMarker === -1 /* AttributeMarker.ImplicitAttributes */ || srcMarker === 2 /* AttributeMarker.Styles */) { // Case where we have to consume `key1` and `value` only. mergeHostAttribute(dst, srcMarker, item, null, src[++i]); } else { // Case where we have to consume `key1` only. mergeHostAttribute(dst, srcMarker, item, null, null); } } } } return dst; } /** * Append `key`/`value` to existing `TAttributes` taking region marker and duplicates into account. * * @param dst `TAttributes` to append to. * @param marker Region where the `key`/`value` should be added. * @param key1 Key to add to `TAttributes` * @param key2 Key to add to `TAttributes` (in case of `AttributeMarker.NamespaceURI`) * @param value Value to add or to overwrite to `TAttributes` Only used if `marker` is not Class. */ function mergeHostAttribute(dst, marker, key1, key2, value) { let i = 0; // Assume that new markers will be inserted at the end. let markerInsertPosition = dst.length; // scan until correct type. if (marker === -1 /* AttributeMarker.ImplicitAttributes */) { markerInsertPosition = -1; } else { while (i < dst.length) { const dstValue = dst[i++]; if (typeof dstValue === 'number') { if (dstValue === marker) { markerInsertPosition = -1; break; } else if (dstValue > marker) { // We need to save this as we want the markers to be inserted in specific order. markerInsertPosition = i - 1; break; } } } } // search until you find place of insertion while (i < dst.length) { const item = dst[i]; if (typeof item === 'number') { // since `i` started as the index after the marker, we did not find it if we are at the next // marker break; } else if (item === key1) { // We already have same token if (key2 === null) { if (value !== null) { dst[i + 1] = value; } return; } else if (key2 === dst[i + 1]) { dst[i + 2] = value; return; } } // Increment counter. i++; if (key2 !== null) i++; if (value !== null) i++; } // insert at location. if (markerInsertPosition !== -1) { dst.splice(markerInsertPosition, 0, marker); i = markerInsertPosition + 1; } dst.splice(i++, 0, key1); if (key2 !== null) { dst.splice(i++, 0, key2); } if (value !== null) { dst.splice(i++, 0, value); } } /// Parent Injector Utils /////////////////////////////////////////////////////////////// function hasParentInjector(parentLocation) { return parentLocation !== NO_PARENT_INJECTOR; } function getParentInjectorIndex(parentLocation) { ngDevMode && assertNumber(parentLocation, 'Number expected'); ngDevMode && assertNotEqual(parentLocation, -1, 'Not a valid state.'); const parentInjectorIndex = parentLocation & 32767 /* RelativeInjectorLocationFlags.InjectorIndexMask */; ngDevMode && assertGreaterThan(parentInjectorIndex, HEADER_OFFSET, 'Parent injector must be pointing past HEADER_OFFSET.'); return parentLocation & 32767 /* RelativeInjectorLocationFlags.InjectorIndexMask */; } function getParentInjectorViewOffset(parentLocation) { return parentLocation >> 16 /* RelativeInjectorLocationFlags.ViewOffsetShift */; } /** * Unwraps a parent injector location number to find the view offset from the current injector, * then walks up the declaration view tree until the view is found that contains the parent * injector. * * @param location The location of the parent injector, which contains the view offset * @param startView The LView instance from which to start walking up the view tree * @returns The LView instance that contains the parent injector */ function getParentInjectorView(location, startView) { let viewOffset = getParentInjectorViewOffset(location); let parentView = startView; // For most cases, the parent injector can be found on the host node (e.g. for component // or container), but we must keep the loop here to support the rarer case of deeply nested // <ng-template> tags or inline views, where the parent injector might live many views // above the child injector. while (viewOffset > 0) { parentView = parentView[DECLARATION_VIEW]; viewOffset--; } return parentView; } /** * Defines if the call to `inject` should include `viewProviders` in its resolution. * * This is set to true when we try to instantiate a component. This value is reset in * `getNodeInjectable` to a value which matches the declaration location of the token about to be * instantiated. This is done so that if we are injecting a token which was declared outside of * `viewProviders` we don't accidentally pull `viewProviders` in. * * Example: * * ``` * @Injectable() * class MyService { * constructor(public value: String) {} * } * * @Component({ * providers: [ * MyService, * {provide: String, value: 'providers' } * ] * viewProviders: [ * {provide: String, value: 'viewProviders'} * ] * }) * class MyComponent { * constructor(myService: MyService, value: String) { * // We expect that Component can see into `viewProviders`. * expect(value).toEqual('viewProviders'); * // `MyService` was not declared in `viewProviders` hence it can't see it. * expect(myService.value).toEqual('providers'); * } * } * * ``` */ let includeViewProviders = true; function setIncludeViewProviders(v) { const oldValue = includeViewProviders; includeViewProviders = v; return oldValue; } /** * The number of slots in each bloom filter (used by DI). The larger this number, the fewer * directives that will share slots, and thus, the fewer false positives when checking for * the existence of a directive. */ const BLOOM_SIZE = 256; const BLOOM_MASK = BLOOM_SIZE - 1; /** * The number of bits that is represented by a single bloom bucket. JS bit operations are 32 bits, * so each bucket represents 32 distinct tokens which accounts for log2(32) = 5 bits of a bloom hash * number. */ const BLOOM_BUCKET_BITS = 5; /** Counter used to generate unique IDs for directives. */ let nextNgElementId = 0; /** Value used when something wasn't found by an injector. */ const NOT_FOUND = {}; /** * Registers this directive as present in its node's injector by flipping the directive's * corresponding bit in the injector's bloom filter. * * @param injectorIndex The index of the node injector where this token should be registered * @param tView The TView for the injector's bloom filters * @param type The directive token to register */ function bloomAdd(injectorIndex, tView, type) { ngDevMode && assertEqual(tView.firstCreatePass, true, 'expected firstCreatePass to be true'); let id; if (typeof type === 'string') { id = type.charCodeAt(0) || 0; } else if (type.hasOwnProperty(NG_ELEMENT_ID)) { id = type[NG_ELEMENT_ID]; } // Set a unique ID on the directive type, so if something tries to inject the directive, // we can easily retrieve the ID and hash it into the bloom bit that should be checked. if (id == null) { id = type[NG_ELEMENT_ID] = nextNgElementId++; } // We only have BLOOM_SIZE (256) slots in our bloom filter (8 buckets * 32 bits each), // so all unique IDs must be modulo-ed into a number from 0 - 255 to fit into the filter. const bloomHash = id & BLOOM_MASK; // Create a mask that targets the specific bit associated with the directive. // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding // to bit positions 0 - 31 in a 32 bit integer. const mask = 1 << bloomHash; // Each bloom bucket in `tData` represents `BLOOM_BUCKET_BITS` number of bits of `bloomHash`. // Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset that the mask // should be written to. tView.data[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)] |= mask; } /** * Creates (or gets an existing) injector for a given element or container. * * @param tNode for which an injector should be retrieved / created. * @param lView View where the node is stored * @returns Node injector */ function getOrCreateNodeInjectorForNode(tNode, lView) { const existingInjectorIndex = getInjectorIndex(tNode, lView); if (existingInjectorIndex !== -1) { return existingInjectorIndex; } const tView = lView[TVIEW]; if (tView.firstCreatePass) { tNode.injectorIndex = lView.length; insertBloom(tView.data, tNode); // foundation for node bloom insertBloom(lView, null); // foundation for cumulative bloom insertBloom(tView.blueprint, null); } const parentLoc = getParentInjectorLocation(tNode, lView); const injectorIndex = tNode.injectorIndex; // If a parent injector can't be found, its location is set to -1. // In that case, we don't need to set up a cumulative bloom if (hasParentInjector(parentLoc)) { const parentIndex = getParentInjectorIndex(parentLoc); const parentLView = getParentInjectorView(parentLoc, lView); const parentData = parentLView[TVIEW].data; // Creates a cumulative bloom filter that merges the parent's bloom filter // and its own cumulative bloom (which contains tokens for all ancestors) for (let i = 0; i < 8 /* NodeInjectorOffset.BLOOM_SIZE */; i++) { lView[injectorIndex + i] = parentLView[parentIndex + i] | parentData[parentIndex + i]; } } lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */] = parentLoc; return injectorIndex; } function insertBloom(arr, footer) { arr.push(0, 0, 0, 0, 0, 0, 0, 0, footer); } function getInjectorIndex(tNode, lView) { if (tNode.injectorIndex === -1 || // If the injector index is the same as its parent's injector index, then the index has been // copied down from the parent node. No injector has been created yet on this node. tNode.parent && tNode.parent.injectorIndex === tNode.injectorIndex || // After the first template pass, the injector index might exist but the parent values // might not have been calculated yet for this instance lView[tNode.injectorIndex + 8 /* NodeInjectorOffset.PARENT */] === null) { return -1; } else { ngDevMode && assertIndexInRange(lView, tNode.injectorIndex); return tNode.injectorIndex; } } /** * Finds the index of the parent injector, with a view offset if applicable. Used to set the * parent injector initially. * * @returns Returns a number that is the combination of the number of LViews that we have to go up * to find the LView containing the parent inject AND the index of the injector within that LView. */ function getParentInjectorLocation(tNode, lView) { if (tNode.parent && tNode.parent.injectorIndex !== -1) { // If we have a parent `TNode` and there is an injector associated with it we are done, because // the parent injector is within the current `LView`. return tNode.parent.injectorIndex; // ViewOffset is 0 } // When parent injector location is computed it may be outside of the current view. (ie it could // be pointing to a declared parent location). This variable stores number of declaration parents // we need to walk up in order to find the parent injector location. let declarationViewOffset = 0; let parentTNode = null; let lViewCursor = lView; // The parent injector is not in the current `LView`. We will have to walk the declared parent // `LView` hierarchy and look for it. If we walk of the top, that means that there is no parent // `NodeInjector`. while (lViewCursor !== null) { parentTNode = getTNodeFromLView(lViewCursor); if (parentTNode === null) { // If we have no parent, than we are done. return NO_PARENT_INJECTOR; } ngDevMode && parentTNode && assertTNodeForLView(parentTNode, lViewCursor[DECLARATION_VIEW]); // Every iteration of the loop requires that we go to the declared parent. declarationViewOffset++; lViewCursor = lViewCursor[DECLARATION_VIEW]; if (parentTNode.injectorIndex !== -1) { // We found a NodeInjector which points to something. return parentTNode.injectorIndex | declarationViewOffset << 16 /* RelativeInjectorLocationFlags.ViewOffsetShift */; } } return NO_PARENT_INJECTOR; } /** * Makes a type or an injection token public to the DI system by adding it to an * injector's bloom filter. * * @param di The node injector in which a directive will be added * @param token The type or the injection token to be made public */ function diPublicInInjector(injectorIndex, tView, token) { bloomAdd(injectorIndex, tView, token); } /** * Inject static attribute value into directive constructor. * * This method is used with `factory` functions which are generated as part of * `defineDirective` or `defineComponent`. The method retrieves the static value * of an attribute. (Dynamic attributes are not supported since they are not resolved * at the time of injection and can change over time.) * * # Example * Given: * ``` * @Component(...) * class MyComponent { * constructor(@Attribute('title') title: string) { ... } * } * ``` * When instantiated with * ``` * <my-component title="Hello"></my-component> * ``` * * Then factory method generated is: * ``` * MyComponent.ɵcmp = defineComponent({ * factory: () => new MyComponent(injectAttribute('title')) * ... * }) * ``` * * @publicApi */ function injectAttributeImpl(tNode, attrNameToInject) { ngDevMode && assertTNodeType(tNode, 12 /* TNodeType.AnyContainer */ | 3 /* TNodeType.AnyRNode */); ngDevMode && assertDefined(tNode, 'expecting tNode'); if (attrNameToInject === 'class') { return tNode.classes; } if (attrNameToInject === 'style') { return tNode.styles; } const attrs = tNode.attrs; if (attrs) { const attrsLength = attrs.length; let i = 0; while (i < attrsLength) { const value = attrs[i]; // If we hit a `Bindings` or `Template` marker then we are done. if (isNameOnlyAttributeMarker(value)) break; // Skip namespaced attributes if (value === 0 /* AttributeMarker.NamespaceURI */) { // we skip the next two values // as namespaced attributes looks like // [..., AttributeMarker.NamespaceURI, 'http://someuri.com/test', 'test:exist', // 'existValue', ...] i = i + 2; } else if (typeof value === 'number') { // Skip to the first value of the marked attribute. i++; while (i < attrsLength && typeof attrs[i] === 'string') { i++; } } else if (value === attrNameToInject) { return attrs[i + 1]; } else { i = i + 2; } } } return null; } function notFoundValueOrThrow(notFoundValue, token, flags) { if (flags & InjectFlags.Optional || notFoundValue !== undefined) { return notFoundValue; } else { throwProviderNotFoundError(token, 'NodeInjector'); } } /** * Returns the value associated to the given token from the ModuleInjector or throws exception * * @param lView The `LView` that contains the `tNode` * @param token The token to look for * @param flags Injection flags * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional` * @returns the value from the injector or throws an exception */ function lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue) { if (flags & InjectFlags.Optional && notFoundValue === undefined) { // This must be set or the NullInjector will throw for optional deps notFoundValue = null; } if ((flags & (InjectFlags.Self | InjectFlags.Host)) === 0) { const moduleInjector = lView[INJECTOR$1]; // switch to `injectInjectorOnly` implementation for module injector, since module injector // should not have access to Component/Directive DI scope (that may happen through // `directiveInject` implementation) const previousInjectImplementation = setInjectImplementation(undefined); try { if (moduleInjector) { return moduleInjector.get(token, notFoundValue, flags & InjectFlags.Optional); } else { return injectRootLimpMode(token, notFoundValue, flags & InjectFlags.Optional); } } finally { setInjectImplementation(previousInjectImplementation); } } return notFoundValueOrThrow(notFoundValue, token, flags); } /** * Returns the value associated to the given token from the NodeInjectors => ModuleInjector. * * Look for the injector providing the token by walking up the node injector tree and then * the module injector tree. * * This function patches `token` with `__NG_ELEMENT_ID__` which contains the id for the bloom * filter. `-1` is reserved for injecting `Injector` (implemented by `NodeInjector`) * * @param tNode The Node where the search for the injector should start * @param lView The `LView` that contains the `tNode` * @param token The token to look for * @param flags Injection flags * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional` * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided */ function getOrCreateInjectable(tNode, lView, token, flags = InjectFlags.Default, notFoundValue) { if (tNode !== null) { // If the view or any of its ancestors have an embedded // view injector, we have to look it up there first. if (lView[FLAGS] & 1024 /* LViewFlags.HasEmbeddedViewInjector */) { const embeddedInjectorValue = lookupTokenUsingEmbeddedInjector(tNode, lView, token, flags, NOT_FOUND); if (embeddedInjectorValue !== NOT_FOUND) { return embeddedInjectorValue; } } // Otherwise try the node injector. const value = lookupTokenUsingNodeInjector(tNode, lView, token, flags, NOT_FOUND); if (value !== NOT_FOUND) { return value; } } // Finally, fall back to the module injector. return lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue); } /** * Returns the value associated to the given token from the node injector. * * @param tNode The Node where the search for the injector should start * @param lView The `LView` that contains the `tNode` * @param token The token to look for * @param flags Injection flags * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional` * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided */ function lookupTokenUsingNodeInjector(tNode, lView, token, flags, notFoundValue) { const bloomHash = bloomHashBitOrFactory(token); // If the ID stored here is a function, this is a special object like ElementRef or TemplateRef // so just call the factory function to create it. if (typeof bloomHash === 'function') { if (!enterDI(lView, tNode, flags)) { // Failed to enter DI, try module injector instead. If a token is injected with the @Host // flag, the module injector is not searched for that token in Ivy. return flags & InjectFlags.Host ? notFoundValueOrThrow(notFoundValue, token, flags) : lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue); } try { const value = bloomHash(flags); if (value == null && !(flags & InjectFlags.Optional)) { throwProviderNotFoundError(token); } else { return value; } } finally { leaveDI(); } } else if (typeof bloomHash === 'number') { // A reference to the previous injector TView that was found while climbing the element // injector tree. This is used to know if viewProviders can be accessed on the current // injector. let previousTView = null; let injectorIndex = getInjectorIndex(tNode, lView); let parentLocation = NO_PARENT_INJECTOR; let hostTElementNode = flags & InjectFlags.Host ? lView[DECLARATION_COMPONENT_VIEW][T_HOST] : null; // If we should skip this injector, or if there is no injector on this node, start by // searching the parent injector. if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) { parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) : lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */]; if (parentLocation === NO_PARENT_INJECTOR || !shouldSearchParent(flags, false)) { injectorIndex = -1; } else { previousTView = lView[TVIEW]; injectorIndex = getParentInjectorIndex(parentLocation); lView = getParentInjectorView(parentLocation, lView); } } // Traverse up the injector tree until we find a potential match or until we know there // *isn't* a match. while (injectorIndex !== -1) { ngDevMode && assertNodeInjector(lView, injectorIndex); // Check the current injector. If it matches, see if it contains token. const tView = lView[TVIEW]; ngDevMode && assertTNodeForLView(tView.data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */], lView); if (bloomHasToken(bloomHash, injectorIndex, tView.data)) { // At this point, we have an injector which *may* contain the token, so we step through // the providers and directives associated with the injector's corresponding node to get // the instance. const instance = searchTokensOnInjector(injectorIndex, lView, token, previousTView, flags, hostTElementNode); if (instance !== NOT_FOUND) { return instance; } } parentLocation = lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */]; if (parentLocation !== NO_PARENT_INJECTOR && shouldSearchParent(flags, lView[TVIEW].data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */] === hostTElementNode) && bloomHasToken(bloomHash, injectorIndex, lView)) { // The def wasn't found anywhere on this node, so it was a false positive. // Traverse up the tree and continue searching. previousTView = tView; injectorIndex = getParentInjectorIndex(parentLocation); lView = getParentInjectorView(parentLocation, lView); } else { // If we should not search parent OR If the ancestor bloom filter value does not have the // bit corresponding to the directive we can give up on traversing up to find the specific // injector. injectorIndex = -1; } } } return notFoundValue; } function searchTokensOnInjector(injectorIndex, lView, token, previousTView, flags, hostTElementNode) { const currentTView = lView[TVIEW]; const tNode = currentTView.data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */]; // First, we need to determine if view providers can be accessed by the starting element. // There are two possibilities const canAccessViewProviders = previousTView == null ? // 1) This is the first invocation `previousTView == null` which means that we are at the // `TNode` of where injector is starting to look. In such a case the only time we are allowed // to look into the ViewProviders is if: // - we are on a component // - AND the injector set `includeViewProviders` to true (implying that the token can see // ViewProviders because it is the Component or a Service which itself was declared in // ViewProviders) isComponentHost(tNode) && includeViewProviders : // 2) `previousTView != null` which means that we are now walking across the parent nodes. // In such a case we are only allowed to look into the ViewProviders if: // - We just crossed from child View to Parent View `previousTView != currentTView` // - AND the parent TNode is an Element. // This means that we just came from the Component's View and therefore are allowed to see // into the ViewProviders. previousTView != currentTView && (tNode.type & 3 /* TNodeType.AnyRNode */) !== 0; // This special case happens when there is a @host on the inject and when we are searching // on the host element node. const isHostSpecialCase = flags & InjectFlags.Host && hostTElementNode === tNode; const injectableIdx = locateDirectiveOrProvider(tNode, currentTView, token, canAccessViewProviders, isHostSpecialCase); if (injectableIdx !== null) { return getNodeInjectable(lView, currentTView, injectableIdx, tNode); } else { return NOT_FOUND; } } /** * Searches for the given token among the node's directives and providers. * * @param tNode TNode on which directives are present. * @param tView The tView we are currently processing * @param token Provider token or type of a directive to look for. * @param canAccessViewProviders Whether view providers should be considered. * @param isHostSpecialCase Whether the host special case applies. * @returns Index of a found directive or provider, or null when none found. */ function locateDirectiveOrProvider(tNode, tView, token, canAccessViewProviders, isHostSpecialCase) { const nodeProviderIndexes = tNode.providerIndexes; const tInjectables = tView.data; const injectablesStart = nodeProviderIndexes & 1048575 /* TNodeProviderIndexes.ProvidersStartIndexMask */; const directivesStart = tNode.directiveStart; const directiveEnd = tNode.directiveEnd; const cptViewProvidersCount = nodeProviderIndexes >> 20 /* TNodeProviderIndexes.CptViewProvidersCountShift */; const startingIndex = canAccessViewProviders ? injectablesStart : injectablesStart + cptViewProvidersCount; // When the host special case applies, only the viewProviders and the component are visible const endIndex = isHostSpecialCase ? injectablesStart + cptViewProvidersCount : directiveEnd; for (let i = startingIndex; i < endIndex; i++) { const providerTokenOrDef = tInjectables[i]; if (i < directivesStart && token === providerTokenOrDef || i >= directivesStart && providerTokenOrDef.type === token) { return i; } } if (isHostSpecialCase) { const dirDef = tInjectables[directivesStart]; if (dirDef && isComponentDef(dirDef) && dirDef.type === token) { return directivesStart; } } return null; } /** * Retrieve or instantiate the injectable from the `LView` at particular `index`. * * This function checks to see if the value has already been instantiated and if so returns the * cached `injectable`. Otherwise if it detects that the value is still a factory it * instantiates the `injectable` and caches the value. */ function getNodeInjectable(lView, tView, index, tNode) { let value = lView[index]; const tData = tView.data; if (isFactory(value)) { const factory = value; if (factory.resolving) { throwCyclicDependencyError(stringifyForError(tData[index])); } const previousIncludeViewProviders = setIncludeViewProviders(factory.canSeeViewProviders); factory.resolving = true; const previousInjectImplementation = factory.injectImpl ? setInjectImplementation(factory.injectImpl) : null; const success = enterDI(lView, tNode, InjectFlags.Default); ngDevMode && assertEqual(success, true, 'Because flags do not contain \`SkipSelf\' we expect this to always succeed.'); try { value = lView[index] = factory.factory(undefined, tData, lView, tNode); // This code path is hit for both directives and providers. // For perf reasons, we want to avoid searching for hooks on providers. // It does no harm to try (the hooks just won't exist), but the extra // checks are unnecessary and this is a hot path. So we check to see // if the index of the dependency is in the directive range for this // tNode. If it's not, we know it's a provider and skip hook registration. if (tView.firstCreatePass && index >= tNode.directiveStart) { ngDevMode && assertDirectiveDef(tData[index]); registerPreOrderHooks(index, tData[index], tView); } } finally { previousInjectImplementation !== null && setInjectImplementation(previousInjectImplementation); setIncludeViewProviders(previousIncludeViewProviders); factory.resolving = false; leaveDI(); } } return value; } /** * Returns the bit in an injector's bloom filter that should be used to determine whether or not * the directive might be provided by the injector. * * When a directive is public, it is added to the bloom filter and given a unique ID that can be * retrieved on the Type. When the directive isn't public or the token is not a directive `null` * is returned as the node injector can not possibly provide that token. * * @param token the injection token * @returns the matching bit to check in the bloom filter or `null` if the token is not known. * When the returned value is negative then it represents special values such as `Injector`. */ function bloomHashBitOrFactory(token) { ngDevMode && assertDefined(token, 'token must be defined'); if (typeof token === 'string') { return token.charCodeAt(0) || 0; } const tokenId = // First check with `hasOwnProperty` so we don't get an inherited ID. token.hasOwnProperty(NG_ELEMENT_ID) ? token[NG_ELEMENT_ID] : undefined; // Negative token IDs are used for special objects such as `Injector` if (typeof tokenId === 'number') { if (tokenId >= 0) { return tokenId & BLOOM_MASK; } else { ngDevMode && assertEqual(tokenId, -1 /* InjectorMarkers.Injector */, 'Expecting to get Special Injector Id'); return createNodeInjector; } } else { return tokenId; } } function bloomHasToken(bloomHash, injectorIndex, injectorView) { // Create a mask that targets the specific bit associated with the directive we're looking for. // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding // to bit positions 0 - 31 in a 32 bit integer. const mask = 1 << bloomHash; // Each bloom bucket in `injectorView` represents `BLOOM_BUCKET_BITS` number of bits of // `bloomHash`. Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset // that should be used. const value = injectorView[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)]; // If the bloom filter value has the bit corresponding to the directive's bloomBit flipped on, // this injector is a potential match. return !!(value & mask); } /** Returns true if flags prevent parent injector from being searched for tokens */ function shouldSearchParent(flags, isFirstHostTNode) { return !(flags & InjectFlags.Self) && !(flags & InjectFlags.Host && isFirstHostTNode); } class NodeInjector { constructor(_tNode, _lView) { this._tNode = _tNode; this._lView = _lView; } get(token, notFoundValue, flags) { return getOrCreateInjectable(this._tNode, this._lView, token, convertToBitFlags(flags), notFoundValue); } } /** Creates a `NodeInjector` for the current node. */ function createNodeInjector() { return new NodeInjector(getCurrentTNode(), getLView()); } /** * @codeGenApi */ function ɵɵgetInheritedFactory(type) { return noSideEffects(() => { const ownConstructor = type.prototype.constructor; const ownFactory = ownConstructor[NG_FACTORY_DEF] || getFactoryOf(ownConstructor); const objectPrototype = Object.prototype; let parent = Object.getPrototypeOf(type.prototype).constructor; // Go up the prototype until we hit `Object`. while (parent && parent !== objectPrototype) { const factory = parent[NG_FACTORY_DEF] || getFactoryOf(parent); // If we hit something that has a factory and the factory isn't the same as the type, // we've found the inherited factory. Note the check that the factory isn't the type's // own factory is redundant in most cases, but if the user has custom decorators on the // class, this lookup will start one level down in the prototype chain, causing us to // find the own factory first and potentially triggering an infinite loop downstream. if (factory && factory !== ownFactory) { return factory; } parent = Object.getPrototypeOf(parent); } // There is no factory defined. Either this was improper usage of inheritance // (no Angular decorator on the superclass) or there is no constructor at all // in the inheritance chain. Since the two cases cannot be distinguished, the // latter has to be assumed. return t => new t(); }); } function getFactoryOf(type) { if (isForwardRef(type)) { return () => { const factory = getFactoryOf(resolveForwardRef(type)); return factory && factory(); }; } return getFactoryDef(type); } /** * Returns a value from the closest embedded or node injector. * * @param tNode The Node where the search for the injector should start * @param lView The `LView` that contains the `tNode` * @param token The token to look for * @param flags Injection flags * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional` * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided */ function lookupTokenUsingEmbeddedInjector(tNode, lView, token, flags, notFoundValue) { let currentTNode = tNode; let currentLView = lView; // When an LView with an embedded view injector is inserted, it'll likely be interlaced with // nodes who may have injectors (e.g. node injector -> embedded view injector -> node injector). // Since the bloom filters for the node injectors have already been constructed and we don't // have a way of extracting the records from an injector, the only way to maintain the correct // hierarchy when resolving the value is to walk it node-by-node while attempting to resolve // the token at each level. while (currentTNode !== null && currentLView !== null && currentLView[FLAGS] & 1024 /* LViewFlags.HasEmbeddedViewInjector */ && !(currentLView[FLAGS] & 256 /* LViewFlags.IsRoot */)) { ngDevMode && assertTNodeForLView(currentTNode, currentLView); // Note that this lookup on the node injector is using the `Self` flag, because // we don't want the node injector to look at any parent injectors since we // may hit the embedded view injector first. const nodeInjectorValue = lookupTokenUsingNodeInjector(currentTNode, currentLView, token, flags | InjectFlags.Self, NOT_FOUND); if (nodeInjectorValue !== NOT_FOUND) { return nodeInjectorValue; } // Has an explicit type due to a TS bug: https://github.com/microsoft/TypeScript/issues/33191 let parentTNode = currentTNode.parent; // `TNode.parent` includes the parent within the current view only. If it doesn't exist, // it means that we've hit the view boundary and we need to go up to the next view. if (!parentTNode) { // Before we go to the next LView, check if the token exists on the current embedded injector. const embeddedViewInjector = currentLView[EMBEDDED_VIEW_INJECTOR]; if (embeddedViewInjector) { const embeddedViewInjectorValue = embeddedViewInjector.get(token, NOT_FOUND, flags); if (embeddedViewInjectorValue !== NOT_FOUND) { return embeddedViewInjectorValue; } } // Otherwise keep going up the tree. parentTNode = getTNodeFromLView(currentLView); currentLView = currentLView[DECLARATION_VIEW]; } currentTNode = parentTNode; } return notFoundValue; } /** Gets the TNode associated with an LView inside of the declaration view. */ function getTNodeFromLView(lView) { const tView = lView[TVIEW]; const tViewType = tView.type; // The parent pointer differs based on `TView.type`. if (tViewType === 2 /* TViewType.Embedded */) { ngDevMode && assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.'); return tView.declTNode; } else if (tViewType === 1 /* TViewType.Component */) { // Components don't have `TView.declTNode` because each instance of component could be // inserted in different location, hence `TView.declTNode` is meaningless. return lView[T_HOST]; } return null; } /** * Facade for the attribute injection from DI. * * @codeGenApi */ function ɵɵinjectAttribute(attrNameToInject) { return injectAttributeImpl(getCurrentTNode(), attrNameToInject); } const ANNOTATIONS = '__annotations__'; const PARAMETERS = '__parameters__'; const PROP_METADATA = '__prop__metadata__'; /** * @suppress {globalThis} */ function makeDecorator(name, props, parentClass, additionalProcessing, typeFn) { return noSideEffects(() => { const metaCtor = makeMetadataCtor(props); function DecoratorFactory(...args) { if (this instanceof DecoratorFactory) { metaCtor.call(this, ...args); return this; } const annotationInstance = new DecoratorFactory(...args); return function TypeDecorator(cls) { if (typeFn) typeFn(cls, ...args); // Use of Object.defineProperty is important since it creates non-enumerable property which // prevents the property is copied during subclassing. const annotations = cls.hasOwnProperty(ANNOTATIONS) ? cls[ANNOTATIONS] : Object.defineProperty(cls, ANNOTATIONS, { value: [] })[ANNOTATIONS]; annotations.push(annotationInstance); if (additionalProcessing) additionalProcessing(cls); return cls; }; } if (parentClass) { DecoratorFactory.prototype = Object.create(parentClass.prototype); } DecoratorFactory.prototype.ngMetadataName = name; DecoratorFactory.annotationCls = DecoratorFactory; return DecoratorFactory; }); } function makeMetadataCtor(props) { return function ctor(...args) { if (props) { const values = props(...args); for (const propName in values) { this[propName] = values[propName]; } } }; } function makeParamDecorator(name, props, parentClass) { return noSideEffects(() => { const metaCtor = makeMetadataCtor(props); function ParamDecoratorFactory(...args) { if (this instanceof ParamDecoratorFactory) { metaCtor.apply(this, args); return this; } const annotationInstance = new ParamDecoratorFactory(...args); ParamDecorator.annotation = annotationInstance; return ParamDecorator; function ParamDecorator(cls, unusedKey, index) { // Use of Object.defineProperty is important since it creates non-enumerable property which // prevents the property is copied during subclassing. const parameters = cls.hasOwnProperty(PARAMETERS) ? cls[PARAMETERS] : Object.defineProperty(cls, PARAMETERS, { value: [] })[PARAMETERS]; // there might be gaps if some in between parameters do not have annotations. // we pad with nulls. while (parameters.length <= index) { parameters.push(null); } (parameters[index] = parameters[index] || []).push(annotationInstance); return cls; } } if (parentClass) { ParamDecoratorFactory.prototype = Object.create(parentClass.prototype); } ParamDecoratorFactory.prototype.ngMetadataName = name; ParamDecoratorFactory.annotationCls = ParamDecoratorFactory; return ParamDecoratorFactory; }); } function makePropDecorator(name, props, parentClass, additionalProcessing) { return noSideEffects(() => { const metaCtor = makeMetadataCtor(props); function PropDecoratorFactory(...args) { if (this instanceof PropDecoratorFactory) { metaCtor.apply(this, args); return this; } const decoratorInstance = new PropDecoratorFactory(...args); function PropDecorator(target, name) { const constructor = target.constructor; // Use of Object.defineProperty is important because it creates a non-enumerable property // which prevents the property from being copied during subclassing. const meta = constructor.hasOwnProperty(PROP_METADATA) ? constructor[PROP_METADATA] : Object.defineProperty(constructor, PROP_METADATA, { value: {} })[PROP_METADATA]; meta[name] = meta.hasOwnProperty(name) && meta[name] || []; meta[name].unshift(decoratorInstance); if (additionalProcessing) additionalProcessing(target, name, ...args); } return PropDecorator; } if (parentClass) { PropDecoratorFactory.prototype = Object.create(parentClass.prototype); } PropDecoratorFactory.prototype.ngMetadataName = name; PropDecoratorFactory.annotationCls = PropDecoratorFactory; return PropDecoratorFactory; }); } /** * Attribute decorator and metadata. * * @Annotation * @publicApi */ const Attribute = makeParamDecorator('Attribute', attributeName => ({ attributeName, __NG_ELEMENT_ID__: () => ɵɵinjectAttribute(attributeName) })); /** * Creates a token that can be used in a DI Provider. * * Use an `InjectionToken` whenever the type you are injecting is not reified (does not have a * runtime representation) such as when injecting an interface, callable type, array or * parameterized type. * * `InjectionToken` is parameterized on `T` which is the type of object which will be returned by * the `Injector`. This provides an additional level of type safety. * * ``` * interface MyInterface {...} * const myInterface = injector.get(new InjectionToken<MyInterface>('SomeToken')); * // myInterface is inferred to be MyInterface. * ``` * * When creating an `InjectionToken`, you can optionally specify a factory function which returns * (possibly by creating) a default value of the parameterized type `T`. This sets up the * `InjectionToken` using this factory as a provider as if it was defined explicitly in the * application's root injector. If the factory function, which takes zero arguments, needs to inject * dependencies, it can do so using the `inject` function. * As you can see in the Tree-shakable InjectionToken example below. * * Additionally, if a `factory` is specified you can also specify the `providedIn` option, which * overrides the above behavior and marks the token as belonging to a particular `@NgModule` (note: * this option is now deprecated). As mentioned above, `'root'` is the default value for * `providedIn`. * * The `providedIn: NgModule` and `providedIn: 'any'` options are deprecated. * * @usageNotes * ### Basic Examples * * ### Plain InjectionToken * * {@example core/di/ts/injector_spec.ts region='InjectionToken'} * * ### Tree-shakable InjectionToken * * {@example core/di/ts/injector_spec.ts region='ShakableInjectionToken'} * * * @publicApi */ class InjectionToken { /** * @param _desc Description for the token, * used only for debugging purposes, * it should but does not need to be unique * @param options Options for the token's usage, as described above */ constructor(_desc, options) { this._desc = _desc; /** @internal */ this.ngMetadataName = 'InjectionToken'; this.ɵprov = undefined; if (typeof options == 'number') { (typeof ngDevMode === 'undefined' || ngDevMode) && assertLessThan(options, 0, 'Only negative numbers are supported here'); // This is a special hack to assign __NG_ELEMENT_ID__ to this instance. // See `InjectorMarkers` this.__NG_ELEMENT_ID__ = options; } else if (options !== undefined) { this.ɵprov = ɵɵdefineInjectable({ token: this, providedIn: options.providedIn || 'root', factory: options.factory }); } } /** * @internal */ get multi() { return this; } toString() { return `InjectionToken ${this._desc}`; } } /** * A DI token that you can use to create a virtual [provider](guide/glossary#provider) * that will populate the `entryComponents` field of components and NgModules * based on its `useValue` property value. * All components that are referenced in the `useValue` value (either directly * or in a nested array or map) are added to the `entryComponents` property. * * @usageNotes * * The following example shows how the router can populate the `entryComponents` * field of an NgModule based on a router configuration that refers * to components. * * ```typescript * // helper function inside the router * function provideRoutes(routes) { * return [ * {provide: ROUTES, useValue: routes}, * {provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: routes, multi: true} * ]; * } * * // user code * let routes = [ * {path: '/root', component: RootComp}, * {path: '/teams', component: TeamsComp} * ]; * * @NgModule({ * providers: [provideRoutes(routes)] * }) * class ModuleWithRoutes {} * ``` * * @publicApi * @deprecated Since 9.0.0. With Ivy, this property is no longer necessary. */ const ANALYZE_FOR_ENTRY_COMPONENTS = new InjectionToken('AnalyzeForEntryComponents'); // Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not // explicitly set. const emitDistinctChangesOnlyDefaultValue = true; /** * Base class for query metadata. * * @see `ContentChildren`. * @see `ContentChild`. * @see `ViewChildren`. * @see `ViewChild`. * * @publicApi */ class Query {} /** * ContentChildren decorator and metadata. * * * @Annotation * @publicApi */ const ContentChildren = makePropDecorator('ContentChildren', (selector, data = {}) => ({ selector, first: false, isViewQuery: false, descendants: false, emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue, ...data }), Query); /** * ContentChild decorator and metadata. * * * @Annotation * * @publicApi */ const ContentChild = makePropDecorator('ContentChild', (selector, data = {}) => ({ selector, first: true, isViewQuery: false, descendants: true, ...data }), Query); /** * ViewChildren decorator and metadata. * * @Annotation * @publicApi */ const ViewChildren = makePropDecorator('ViewChildren', (selector, data = {}) => ({ selector, first: false, isViewQuery: true, descendants: true, emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue, ...data }), Query); /** * ViewChild decorator and metadata. * * @Annotation * @publicApi */ const ViewChild = makePropDecorator('ViewChild', (selector, data) => ({ selector, first: true, isViewQuery: true, descendants: true, ...data }), Query); var FactoryTarget; (function (FactoryTarget) { FactoryTarget[FactoryTarget["Directive"] = 0] = "Directive"; FactoryTarget[FactoryTarget["Component"] = 1] = "Component"; FactoryTarget[FactoryTarget["Injectable"] = 2] = "Injectable"; FactoryTarget[FactoryTarget["Pipe"] = 3] = "Pipe"; FactoryTarget[FactoryTarget["NgModule"] = 4] = "NgModule"; })(FactoryTarget || (FactoryTarget = {})); var R3TemplateDependencyKind; (function (R3TemplateDependencyKind) { R3TemplateDependencyKind[R3TemplateDependencyKind["Directive"] = 0] = "Directive"; R3TemplateDependencyKind[R3TemplateDependencyKind["Pipe"] = 1] = "Pipe"; R3TemplateDependencyKind[R3TemplateDependencyKind["NgModule"] = 2] = "NgModule"; })(R3TemplateDependencyKind || (R3TemplateDependencyKind = {})); var ViewEncapsulation; (function (ViewEncapsulation) { ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated"; // Historically the 1 value was for `Native` encapsulation which has been removed as of v11. ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None"; ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom"; })(ViewEncapsulation || (ViewEncapsulation = {})); function getCompilerFacade(request) { const globalNg = _global['ng']; if (globalNg && globalNg.ɵcompilerFacade) { return globalNg.ɵcompilerFacade; } if (typeof ngDevMode === 'undefined' || ngDevMode) { // Log the type as an error so that a developer can easily navigate to the type from the // console. console.error(`JIT compilation failed for ${request.kind}`, request.type); let message = `The ${request.kind} '${request.type.name}' needs to be compiled using the JIT compiler, but '@angular/compiler' is not available.\n\n`; if (request.usage === 1 /* JitCompilerUsage.PartialDeclaration */) { message += `The ${request.kind} is part of a library that has been partially compiled.\n`; message += `However, the Angular Linker has not processed the library such that JIT compilation is used as fallback.\n`; message += '\n'; message += `Ideally, the library is processed using the Angular Linker to become fully AOT compiled.\n`; } else { message += `JIT compilation is discouraged for production use-cases! Consider using AOT mode instead.\n`; } message += `Alternatively, the JIT compiler should be loaded by bootstrapping using '@angular/platform-browser-dynamic' or '@angular/platform-server',\n`; message += `or manually provide the compiler with 'import "@angular/compiler";' before bootstrapping.`; throw new Error(message); } else { throw new Error('JIT compiler unavailable'); } } /** * @description * * Represents a type that a Component or other object is instances of. * * An example of a `Type` is `MyCustomComponent` class, which in JavaScript is represented by * the `MyCustomComponent` constructor function. * * @publicApi */ const Type = Function; function isType(v) { return typeof v === 'function'; } /** * Determines if the contents of two arrays is identical * * @param a first array * @param b second array * @param identityAccessor Optional function for extracting stable object identity from a value in * the array. */ function arrayEquals(a, b, identityAccessor) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { let valueA = a[i]; let valueB = b[i]; if (identityAccessor) { valueA = identityAccessor(valueA); valueB = identityAccessor(valueB); } if (valueB !== valueA) { return false; } } return true; } /** * Flattens an array. */ function flatten(list) { return list.flat(Number.POSITIVE_INFINITY); } function deepForEach(input, fn) { input.forEach(value => Array.isArray(value) ? deepForEach(value, fn) : fn(value)); } function addToArray(arr, index, value) { // perf: array.push is faster than array.splice! if (index >= arr.length) { arr.push(value); } else { arr.splice(index, 0, value); } } function removeFromArray(arr, index) { // perf: array.pop is faster than array.splice! if (index >= arr.length - 1) { return arr.pop(); } else { return arr.splice(index, 1)[0]; } } function newArray(size, value) { const list = []; for (let i = 0; i < size; i++) { list.push(value); } return list; } /** * Remove item from array (Same as `Array.splice()` but faster.) * * `Array.splice()` is not as fast because it has to allocate an array for the elements which were * removed. This causes memory pressure and slows down code when most of the time we don't * care about the deleted items array. * * https://jsperf.com/fast-array-splice (About 20x faster) * * @param array Array to splice * @param index Index of element in array to remove. * @param count Number of items to remove. */ function arraySplice(array, index, count) { const length = array.length - count; while (index < length) { array[index] = array[index + count]; index++; } while (count--) { array.pop(); // shrink the array } } /** * Same as `Array.splice(index, 0, value)` but faster. * * `Array.splice()` is not fast because it has to allocate an array for the elements which were * removed. This causes memory pressure and slows down code when most of the time we don't * care about the deleted items array. * * @param array Array to splice. * @param index Index in array where the `value` should be added. * @param value Value to add to array. */ function arrayInsert(array, index, value) { ngDevMode && assertLessThanOrEqual(index, array.length, 'Can\'t insert past array end.'); let end = array.length; while (end > index) { const previousEnd = end - 1; array[end] = array[previousEnd]; end = previousEnd; } array[index] = value; } /** * Same as `Array.splice2(index, 0, value1, value2)` but faster. * * `Array.splice()` is not fast because it has to allocate an array for the elements which were * removed. This causes memory pressure and slows down code when most of the time we don't * care about the deleted items array. * * @param array Array to splice. * @param index Index in array where the `value` should be added. * @param value1 Value to add to array. * @param value2 Value to add to array. */ function arrayInsert2(array, index, value1, value2) { ngDevMode && assertLessThanOrEqual(index, array.length, 'Can\'t insert past array end.'); let end = array.length; if (end == index) { // inserting at the end. array.push(value1, value2); } else if (end === 1) { // corner case when we have less items in array than we have items to insert. array.push(value2, array[0]); array[0] = value1; } else { end--; array.push(array[end - 1], array[end]); while (end > index) { const previousEnd = end - 2; array[end] = array[previousEnd]; end--; } array[index] = value1; array[index + 1] = value2; } } /** * Get an index of an `value` in a sorted `array`. * * NOTE: * - This uses binary search algorithm for fast removals. * * @param array A sorted array to binary search. * @param value The value to look for. * @returns index of the value. * - positive index if value found. * - negative index if value not found. (`~index` to get the value where it should have been * located) */ function arrayIndexOfSorted(array, value) { return _arrayIndexOfSorted(array, value, 0); } /** * Set a `value` for a `key`. * * @param keyValueArray to modify. * @param key The key to locate or create. * @param value The value to set for a `key`. * @returns index (always even) of where the value vas set. */ function keyValueArraySet(keyValueArray, key, value) { let index = keyValueArrayIndexOf(keyValueArray, key); if (index >= 0) { // if we found it set it. keyValueArray[index | 1] = value; } else { index = ~index; arrayInsert2(keyValueArray, index, key, value); } return index; } /** * Retrieve a `value` for a `key` (on `undefined` if not found.) * * @param keyValueArray to search. * @param key The key to locate. * @return The `value` stored at the `key` location or `undefined if not found. */ function keyValueArrayGet(keyValueArray, key) { const index = keyValueArrayIndexOf(keyValueArray, key); if (index >= 0) { // if we found it retrieve it. return keyValueArray[index | 1]; } return undefined; } /** * Retrieve a `key` index value in the array or `-1` if not found. * * @param keyValueArray to search. * @param key The key to locate. * @returns index of where the key is (or should have been.) * - positive (even) index if key found. * - negative index if key not found. (`~index` (even) to get the index where it should have * been inserted.) */ function keyValueArrayIndexOf(keyValueArray, key) { return _arrayIndexOfSorted(keyValueArray, key, 1); } /** * Delete a `key` (and `value`) from the `KeyValueArray`. * * @param keyValueArray to modify. * @param key The key to locate or delete (if exist). * @returns index of where the key was (or should have been.) * - positive (even) index if key found and deleted. * - negative index if key not found. (`~index` (even) to get the index where it should have * been.) */ function keyValueArrayDelete(keyValueArray, key) { const index = keyValueArrayIndexOf(keyValueArray, key); if (index >= 0) { // if we found it remove it. arraySplice(keyValueArray, index, 2); } return index; } /** * INTERNAL: Get an index of an `value` in a sorted `array` by grouping search by `shift`. * * NOTE: * - This uses binary search algorithm for fast removals. * * @param array A sorted array to binary search. * @param value The value to look for. * @param shift grouping shift. * - `0` means look at every location * - `1` means only look at every other (even) location (the odd locations are to be ignored as * they are values.) * @returns index of the value. * - positive index if value found. * - negative index if value not found. (`~index` to get the value where it should have been * inserted) */ function _arrayIndexOfSorted(array, value, shift) { ngDevMode && assertEqual(Array.isArray(array), true, 'Expecting an array'); let start = 0; let end = array.length >> shift; while (end !== start) { const middle = start + (end - start >> 1); // find the middle. const current = array[middle << shift]; if (value === current) { return middle << shift; } else if (current > value) { end = middle; } else { start = middle + 1; // We already searched middle so make it non-inclusive by adding 1 } } return ~(end << shift); } /* * ######################### * Attention: These Regular expressions have to hold even if the code is minified! * ########################## */ /** * Regular expression that detects pass-through constructors for ES5 output. This Regex * intends to capture the common delegation pattern emitted by TypeScript and Babel. Also * it intends to capture the pattern where existing constructors have been downleveled from * ES2015 to ES5 using TypeScript w/ downlevel iteration. e.g. * * ``` * function MyClass() { * var _this = _super.apply(this, arguments) || this; * ``` * * downleveled to ES5 with `downlevelIteration` for TypeScript < 4.2: * ``` * function MyClass() { * var _this = _super.apply(this, __spread(arguments)) || this; * ``` * * or downleveled to ES5 with `downlevelIteration` for TypeScript >= 4.2: * ``` * function MyClass() { * var _this = _super.apply(this, __spreadArray([], __read(arguments), false)) || this; * ``` * * More details can be found in: https://github.com/angular/angular/issues/38453. */ const ES5_DELEGATE_CTOR = /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*(arguments|(?:[^()]+\(\[\],)?[^()]+\(arguments\).*)\)/; /** Regular expression that detects ES2015 classes which extend from other classes. */ const ES2015_INHERITED_CLASS = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{/; /** * Regular expression that detects ES2015 classes which extend from other classes and * have an explicit constructor defined. */ const ES2015_INHERITED_CLASS_WITH_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(/; /** * Regular expression that detects ES2015 classes which extend from other classes * and inherit a constructor. */ const ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(\)\s*{[^}]*super\(\.\.\.arguments\)/; /** * Determine whether a stringified type is a class which delegates its constructor * to its parent. * * This is not trivial since compiled code can actually contain a constructor function * even if the original source code did not. For instance, when the child class contains * an initialized instance property. */ function isDelegateCtor(typeStr) { return ES5_DELEGATE_CTOR.test(typeStr) || ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR.test(typeStr) || ES2015_INHERITED_CLASS.test(typeStr) && !ES2015_INHERITED_CLASS_WITH_CTOR.test(typeStr); } class ReflectionCapabilities { constructor(reflect) { this._reflect = reflect || _global['Reflect']; } factory(t) { return (...args) => new t(...args); } /** @internal */ _zipTypesAndAnnotations(paramTypes, paramAnnotations) { let result; if (typeof paramTypes === 'undefined') { result = newArray(paramAnnotations.length); } else { result = newArray(paramTypes.length); } for (let i = 0; i < result.length; i++) { // TS outputs Object for parameters without types, while Traceur omits // the annotations. For now we preserve the Traceur behavior to aid // migration, but this can be revisited. if (typeof paramTypes === 'undefined') { result[i] = []; } else if (paramTypes[i] && paramTypes[i] != Object) { result[i] = [paramTypes[i]]; } else { result[i] = []; } if (paramAnnotations && paramAnnotations[i] != null) { result[i] = result[i].concat(paramAnnotations[i]); } } return result; } _ownParameters(type, parentCtor) { const typeStr = type.toString(); // If we have no decorators, we only have function.length as metadata. // In that case, to detect whether a child class declared an own constructor or not, // we need to look inside of that constructor to check whether it is // just calling the parent. // This also helps to work around for https://github.com/Microsoft/TypeScript/issues/12439 // that sets 'design:paramtypes' to [] // if a class inherits from another class but has no ctor declared itself. if (isDelegateCtor(typeStr)) { return null; } // Prefer the direct API. if (type.parameters && type.parameters !== parentCtor.parameters) { return type.parameters; } // API of tsickle for lowering decorators to properties on the class. const tsickleCtorParams = type.ctorParameters; if (tsickleCtorParams && tsickleCtorParams !== parentCtor.ctorParameters) { // Newer tsickle uses a function closure // Retain the non-function case for compatibility with older tsickle const ctorParameters = typeof tsickleCtorParams === 'function' ? tsickleCtorParams() : tsickleCtorParams; const paramTypes = ctorParameters.map(ctorParam => ctorParam && ctorParam.type); const paramAnnotations = ctorParameters.map(ctorParam => ctorParam && convertTsickleDecoratorIntoMetadata(ctorParam.decorators)); return this._zipTypesAndAnnotations(paramTypes, paramAnnotations); } // API for metadata created by invoking the decorators. const paramAnnotations = type.hasOwnProperty(PARAMETERS) && type[PARAMETERS]; const paramTypes = this._reflect && this._reflect.getOwnMetadata && this._reflect.getOwnMetadata('design:paramtypes', type); if (paramTypes || paramAnnotations) { return this._zipTypesAndAnnotations(paramTypes, paramAnnotations); } // If a class has no decorators, at least create metadata // based on function.length. // Note: We know that this is a real constructor as we checked // the content of the constructor above. return newArray(type.length); } parameters(type) { // Note: only report metadata if we have at least one class decorator // to stay in sync with the static reflector. if (!isType(type)) { return []; } const parentCtor = getParentCtor(type); let parameters = this._ownParameters(type, parentCtor); if (!parameters && parentCtor !== Object) { parameters = this.parameters(parentCtor); } return parameters || []; } _ownAnnotations(typeOrFunc, parentCtor) { // Prefer the direct API. if (typeOrFunc.annotations && typeOrFunc.annotations !== parentCtor.annotations) { let annotations = typeOrFunc.annotations; if (typeof annotations === 'function' && annotations.annotations) { annotations = annotations.annotations; } return annotations; } // API of tsickle for lowering decorators to properties on the class. if (typeOrFunc.decorators && typeOrFunc.decorators !== parentCtor.decorators) { return convertTsickleDecoratorIntoMetadata(typeOrFunc.decorators); } // API for metadata created by invoking the decorators. if (typeOrFunc.hasOwnProperty(ANNOTATIONS)) { return typeOrFunc[ANNOTATIONS]; } return null; } annotations(typeOrFunc) { if (!isType(typeOrFunc)) { return []; } const parentCtor = getParentCtor(typeOrFunc); const ownAnnotations = this._ownAnnotations(typeOrFunc, parentCtor) || []; const parentAnnotations = parentCtor !== Object ? this.annotations(parentCtor) : []; return parentAnnotations.concat(ownAnnotations); } _ownPropMetadata(typeOrFunc, parentCtor) { // Prefer the direct API. if (typeOrFunc.propMetadata && typeOrFunc.propMetadata !== parentCtor.propMetadata) { let propMetadata = typeOrFunc.propMetadata; if (typeof propMetadata === 'function' && propMetadata.propMetadata) { propMetadata = propMetadata.propMetadata; } return propMetadata; } // API of tsickle for lowering decorators to properties on the class. if (typeOrFunc.propDecorators && typeOrFunc.propDecorators !== parentCtor.propDecorators) { const propDecorators = typeOrFunc.propDecorators; const propMetadata = {}; Object.keys(propDecorators).forEach(prop => { propMetadata[prop] = convertTsickleDecoratorIntoMetadata(propDecorators[prop]); }); return propMetadata; } // API for metadata created by invoking the decorators. if (typeOrFunc.hasOwnProperty(PROP_METADATA)) { return typeOrFunc[PROP_METADATA]; } return null; } propMetadata(typeOrFunc) { if (!isType(typeOrFunc)) { return {}; } const parentCtor = getParentCtor(typeOrFunc); const propMetadata = {}; if (parentCtor !== Object) { const parentPropMetadata = this.propMetadata(parentCtor); Object.keys(parentPropMetadata).forEach(propName => { propMetadata[propName] = parentPropMetadata[propName]; }); } const ownPropMetadata = this._ownPropMetadata(typeOrFunc, parentCtor); if (ownPropMetadata) { Object.keys(ownPropMetadata).forEach(propName => { const decorators = []; if (propMetadata.hasOwnProperty(propName)) { decorators.push(...propMetadata[propName]); } decorators.push(...ownPropMetadata[propName]); propMetadata[propName] = decorators; }); } return propMetadata; } ownPropMetadata(typeOrFunc) { if (!isType(typeOrFunc)) { return {}; } return this._ownPropMetadata(typeOrFunc, getParentCtor(typeOrFunc)) || {}; } hasLifecycleHook(type, lcProperty) { return type instanceof Type && lcProperty in type.prototype; } } function convertTsickleDecoratorIntoMetadata(decoratorInvocations) { if (!decoratorInvocations) { return []; } return decoratorInvocations.map(decoratorInvocation => { const decoratorType = decoratorInvocation.type; const annotationCls = decoratorType.annotationCls; const annotationArgs = decoratorInvocation.args ? decoratorInvocation.args : []; return new annotationCls(...annotationArgs); }); } function getParentCtor(ctor) { const parentProto = ctor.prototype ? Object.getPrototypeOf(ctor.prototype) : null; const parentCtor = parentProto ? parentProto.constructor : null; // Note: We always use `Object` as the null value // to simplify checking later on. return parentCtor || Object; } /** * Inject decorator and metadata. * * @Annotation * @publicApi */ const Inject = attachInjectFlag( // Disable tslint because `DecoratorFlags` is a const enum which gets inlined. // tslint:disable-next-line: no-toplevel-property-access makeParamDecorator('Inject', token => ({ token })), -1 /* DecoratorFlags.Inject */); /** * Optional decorator and metadata. * * @Annotation * @publicApi */ const Optional = // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. // tslint:disable-next-line: no-toplevel-property-access attachInjectFlag(makeParamDecorator('Optional'), 8 /* InternalInjectFlags.Optional */); /** * Self decorator and metadata. * * @Annotation * @publicApi */ const Self = // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. // tslint:disable-next-line: no-toplevel-property-access attachInjectFlag(makeParamDecorator('Self'), 2 /* InternalInjectFlags.Self */); /** * `SkipSelf` decorator and metadata. * * @Annotation * @publicApi */ const SkipSelf = // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. // tslint:disable-next-line: no-toplevel-property-access attachInjectFlag(makeParamDecorator('SkipSelf'), 4 /* InternalInjectFlags.SkipSelf */); /** * Host decorator and metadata. * * @Annotation * @publicApi */ const Host = // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined. // tslint:disable-next-line: no-toplevel-property-access attachInjectFlag(makeParamDecorator('Host'), 1 /* InternalInjectFlags.Host */); let _reflect = null; function getReflect() { return _reflect = _reflect || new ReflectionCapabilities(); } function reflectDependencies(type) { return convertDependencies(getReflect().parameters(type)); } function convertDependencies(deps) { return deps.map(dep => reflectDependency(dep)); } function reflectDependency(dep) { const meta = { token: null, attribute: null, host: false, optional: false, self: false, skipSelf: false }; if (Array.isArray(dep) && dep.length > 0) { for (let j = 0; j < dep.length; j++) { const param = dep[j]; if (param === undefined) { // param may be undefined if type of dep is not set by ngtsc continue; } const proto = Object.getPrototypeOf(param); if (param instanceof Optional || proto.ngMetadataName === 'Optional') { meta.optional = true; } else if (param instanceof SkipSelf || proto.ngMetadataName === 'SkipSelf') { meta.skipSelf = true; } else if (param instanceof Self || proto.ngMetadataName === 'Self') { meta.self = true; } else if (param instanceof Host || proto.ngMetadataName === 'Host') { meta.host = true; } else if (param instanceof Inject) { meta.token = param.token; } else if (param instanceof Attribute) { if (param.attributeName === undefined) { throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Attribute name must be defined.`); } meta.attribute = param.attributeName; } else { meta.token = param; } } } else if (dep === undefined || Array.isArray(dep) && dep.length === 0) { meta.token = null; } else { meta.token = dep; } return meta; } /** * Used to resolve resource URLs on `@Component` when used with JIT compilation. * * Example: * ``` * @Component({ * selector: 'my-comp', * templateUrl: 'my-comp.html', // This requires asynchronous resolution * }) * class MyComponent{ * } * * // Calling `renderComponent` will fail because `renderComponent` is a synchronous process * // and `MyComponent`'s `@Component.templateUrl` needs to be resolved asynchronously. * * // Calling `resolveComponentResources()` will resolve `@Component.templateUrl` into * // `@Component.template`, which allows `renderComponent` to proceed in a synchronous manner. * * // Use browser's `fetch()` function as the default resource resolution strategy. * resolveComponentResources(fetch).then(() => { * // After resolution all URLs have been converted into `template` strings. * renderComponent(MyComponent); * }); * * ``` * * NOTE: In AOT the resolution happens during compilation, and so there should be no need * to call this method outside JIT mode. * * @param resourceResolver a function which is responsible for returning a `Promise` to the * contents of the resolved URL. Browser's `fetch()` method is a good default implementation. */ function resolveComponentResources(resourceResolver) { // Store all promises which are fetching the resources. const componentResolved = []; // Cache so that we don't fetch the same resource more than once. const urlMap = new Map(); function cachedResourceResolve(url) { let promise = urlMap.get(url); if (!promise) { const resp = resourceResolver(url); urlMap.set(url, promise = resp.then(unwrapResponse)); } return promise; } componentResourceResolutionQueue.forEach((component, type) => { const promises = []; if (component.templateUrl) { promises.push(cachedResourceResolve(component.templateUrl).then(template => { component.template = template; })); } const styleUrls = component.styleUrls; const styles = component.styles || (component.styles = []); const styleOffset = component.styles.length; styleUrls && styleUrls.forEach((styleUrl, index) => { styles.push(''); // pre-allocate array. promises.push(cachedResourceResolve(styleUrl).then(style => { styles[styleOffset + index] = style; styleUrls.splice(styleUrls.indexOf(styleUrl), 1); if (styleUrls.length == 0) { component.styleUrls = undefined; } })); }); const fullyResolved = Promise.all(promises).then(() => componentDefResolved(type)); componentResolved.push(fullyResolved); }); clearResolutionOfComponentResourcesQueue(); return Promise.all(componentResolved).then(() => undefined); } let componentResourceResolutionQueue = new Map(); // Track when existing ɵcmp for a Type is waiting on resources. const componentDefPendingResolution = new Set(); function maybeQueueResolutionOfComponentResources(type, metadata) { if (componentNeedsResolution(metadata)) { componentResourceResolutionQueue.set(type, metadata); componentDefPendingResolution.add(type); } } function isComponentDefPendingResolution(type) { return componentDefPendingResolution.has(type); } function componentNeedsResolution(component) { return !!(component.templateUrl && !component.hasOwnProperty('template') || component.styleUrls && component.styleUrls.length); } function clearResolutionOfComponentResourcesQueue() { const old = componentResourceResolutionQueue; componentResourceResolutionQueue = new Map(); return old; } function restoreComponentResolutionQueue(queue) { componentDefPendingResolution.clear(); queue.forEach((_, type) => componentDefPendingResolution.add(type)); componentResourceResolutionQueue = queue; } function isComponentResourceResolutionQueueEmpty() { return componentResourceResolutionQueue.size === 0; } function unwrapResponse(response) { return typeof response == 'string' ? response : response.text(); } function componentDefResolved(type) { componentDefPendingResolution.delete(type); } /** * Map of module-id to the corresponding NgModule. */ const modules = new Map(); /** * Whether to check for duplicate NgModule registrations. * * This can be disabled for testing. */ let checkForDuplicateNgModules = true; function assertSameOrNotExisting(id, type, incoming) { if (type && type !== incoming && checkForDuplicateNgModules) { throw new Error(`Duplicate module registered for ${id} - ${stringify(type)} vs ${stringify(type.name)}`); } } /** * Adds the given NgModule type to Angular's NgModule registry. * * This is generated as a side-effect of NgModule compilation. Note that the `id` is passed in * explicitly and not read from the NgModule definition. This is for two reasons: it avoids a * megamorphic read, and in JIT there's a chicken-and-egg problem where the NgModule may not be * fully resolved when it's registered. * * @codeGenApi */ function registerNgModuleType(ngModuleType, id) { const existing = modules.get(id) || null; assertSameOrNotExisting(id, existing, ngModuleType); modules.set(id, ngModuleType); } function clearModulesForTest() { modules.clear(); } function getRegisteredNgModuleType(id) { return modules.get(id); } /** * Control whether the NgModule registration system enforces that each NgModule type registered has * a unique id. * * This is useful for testing as the NgModule registry cannot be properly reset between tests with * Angular's current API. */ function setAllowDuplicateNgModuleIdsForTest(allowDuplicates) { checkForDuplicateNgModules = !allowDuplicates; } /** * Defines a schema that allows an NgModule to contain the following: * - Non-Angular elements named with dash case (`-`). * - Element properties named with dash case (`-`). * Dash case is the naming convention for custom elements. * * @publicApi */ const CUSTOM_ELEMENTS_SCHEMA = { name: 'custom-elements' }; /** * Defines a schema that allows any property on any element. * * This schema allows you to ignore the errors related to any unknown elements or properties in a * template. The usage of this schema is generally discouraged because it prevents useful validation * and may hide real errors in your template. Consider using the `CUSTOM_ELEMENTS_SCHEMA` instead. * * @publicApi */ const NO_ERRORS_SCHEMA = { name: 'no-errors-schema' }; let shouldThrowErrorOnUnknownElement = false; /** * Sets a strict mode for JIT-compiled components to throw an error on unknown elements, * instead of just logging the error. * (for AOT-compiled ones this check happens at build time). */ function ɵsetUnknownElementStrictMode(shouldThrow) { shouldThrowErrorOnUnknownElement = shouldThrow; } /** * Gets the current value of the strict mode. */ function ɵgetUnknownElementStrictMode() { return shouldThrowErrorOnUnknownElement; } let shouldThrowErrorOnUnknownProperty = false; /** * Sets a strict mode for JIT-compiled components to throw an error on unknown properties, * instead of just logging the error. * (for AOT-compiled ones this check happens at build time). */ function ɵsetUnknownPropertyStrictMode(shouldThrow) { shouldThrowErrorOnUnknownProperty = shouldThrow; } /** * Gets the current value of the strict mode. */ function ɵgetUnknownPropertyStrictMode() { return shouldThrowErrorOnUnknownProperty; } /** * Validates that the element is known at runtime and produces * an error if it's not the case. * This check is relevant for JIT-compiled components (for AOT-compiled * ones this check happens at build time). * * The element is considered known if either: * - it's a known HTML element * - it's a known custom element * - the element matches any directive * - the element is allowed by one of the schemas * * @param element Element to validate * @param lView An `LView` that represents a current component that is being rendered * @param tagName Name of the tag to check * @param schemas Array of schemas * @param hasDirectives Boolean indicating that the element matches any directive */ function validateElementIsKnown(element, lView, tagName, schemas, hasDirectives) { // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT // mode where this check happens at compile time. In JIT mode, `schemas` is always present and // defined as an array (as an empty array in case `schemas` field is not defined) and we should // execute the check below. if (schemas === null) return; // If the element matches any directive, it's considered as valid. if (!hasDirectives && tagName !== null) { // The element is unknown if it's an instance of HTMLUnknownElement, or it isn't registered // as a custom element. Note that unknown elements with a dash in their name won't be instances // of HTMLUnknownElement in browsers that support web components. const isUnknown = // Note that we can't check for `typeof HTMLUnknownElement === 'function'` because // Domino doesn't expose HTMLUnknownElement globally. typeof HTMLUnknownElement !== 'undefined' && HTMLUnknownElement && element instanceof HTMLUnknownElement || typeof customElements !== 'undefined' && tagName.indexOf('-') > -1 && !customElements.get(tagName); if (isUnknown && !matchingSchemas(schemas, tagName)) { const isHostStandalone = isHostComponentStandalone(lView); const templateLocation = getTemplateLocationDetails(lView); const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`; let message = `'${tagName}' is not a known element${templateLocation}:\n`; message += `1. If '${tagName}' is an Angular component, then verify that it is ${isHostStandalone ? 'included in the \'@Component.imports\' of this component' : 'a part of an @NgModule where this component is declared'}.\n`; if (tagName && tagName.indexOf('-') > -1) { message += `2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the ${schemas} of this component to suppress this message.`; } else { message += `2. To allow any element add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`; } if (shouldThrowErrorOnUnknownElement) { throw new RuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message); } else { console.error(formatRuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message)); } } } } /** * Validates that the property of the element is known at runtime and returns * false if it's not the case. * This check is relevant for JIT-compiled components (for AOT-compiled * ones this check happens at build time). * * The property is considered known if either: * - it's a known property of the element * - the element is allowed by one of the schemas * - the property is used for animations * * @param element Element to validate * @param propName Name of the property to check * @param tagName Name of the tag hosting the property * @param schemas Array of schemas */ function isPropertyValid(element, propName, tagName, schemas) { // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT // mode where this check happens at compile time. In JIT mode, `schemas` is always present and // defined as an array (as an empty array in case `schemas` field is not defined) and we should // execute the check below. if (schemas === null) return true; // The property is considered valid if the element matches the schema, it exists on the element, // or it is synthetic, and we are in a browser context (web worker nodes should be skipped). if (matchingSchemas(schemas, tagName) || propName in element || isAnimationProp(propName)) { return true; } // Note: `typeof Node` returns 'function' in most browsers, but is undefined with domino. return typeof Node === 'undefined' || Node === null || !(element instanceof Node); } /** * Logs or throws an error that a property is not supported on an element. * * @param propName Name of the invalid property * @param tagName Name of the tag hosting the property * @param nodeType Type of the node hosting the property * @param lView An `LView` that represents a current component */ function handleUnknownPropertyError(propName, tagName, nodeType, lView) { // Special-case a situation when a structural directive is applied to // an `<ng-template>` element, for example: `<ng-template *ngIf="true">`. // In this case the compiler generates the `ɵɵtemplate` instruction with // the `null` as the tagName. The directive matching logic at runtime relies // on this effect (see `isInlineTemplate`), thus using the 'ng-template' as // a default value of the `tNode.value` is not feasible at this moment. if (!tagName && nodeType === 4 /* TNodeType.Container */) { tagName = 'ng-template'; } const isHostStandalone = isHostComponentStandalone(lView); const templateLocation = getTemplateLocationDetails(lView); let message = `Can't bind to '${propName}' since it isn't a known property of '${tagName}'${templateLocation}.`; const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`; const importLocation = isHostStandalone ? 'included in the \'@Component.imports\' of this component' : 'a part of an @NgModule where this component is declared'; if (KNOWN_CONTROL_FLOW_DIRECTIVES.has(propName)) { // Most likely this is a control flow directive (such as `*ngIf`) used in // a template, but the directive or the `CommonModule` is not imported. const correspondingImport = KNOWN_CONTROL_FLOW_DIRECTIVES.get(propName); message += `\nIf the '${propName}' is an Angular control flow directive, ` + `please make sure that either the '${correspondingImport}' directive or the 'CommonModule' is ${importLocation}.`; } else { // May be an Angular component, which is not imported/declared? message += `\n1. If '${tagName}' is an Angular component and it has the ` + `'${propName}' input, then verify that it is ${importLocation}.`; // May be a Web Component? if (tagName && tagName.indexOf('-') > -1) { message += `\n2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' ` + `to the ${schemas} of this component to suppress this message.`; message += `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to ` + `the ${schemas} of this component.`; } else { // If it's expected, the error can be suppressed by the `NO_ERRORS_SCHEMA` schema. message += `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to ` + `the ${schemas} of this component.`; } } reportUnknownPropertyError(message); } function reportUnknownPropertyError(message) { if (shouldThrowErrorOnUnknownProperty) { throw new RuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message); } else { console.error(formatRuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message)); } } /** * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`) * and must **not** be used in production bundles. The function makes megamorphic reads, which might * be too slow for production mode and also it relies on the constructor function being available. * * Gets a reference to the host component def (where a current component is declared). * * @param lView An `LView` that represents a current component that is being rendered. */ function getDeclarationComponentDef(lView) { !ngDevMode && throwError('Must never be called in production mode'); const declarationLView = lView[DECLARATION_COMPONENT_VIEW]; const context = declarationLView[CONTEXT]; // Unable to obtain a context. if (!context) return null; return context.constructor ? getComponentDef(context.constructor) : null; } /** * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`) * and must **not** be used in production bundles. The function makes megamorphic reads, which might * be too slow for production mode. * * Checks if the current component is declared inside of a standalone component template. * * @param lView An `LView` that represents a current component that is being rendered. */ function isHostComponentStandalone(lView) { !ngDevMode && throwError('Must never be called in production mode'); const componentDef = getDeclarationComponentDef(lView); // Treat host component as non-standalone if we can't obtain the def. return !!componentDef?.standalone; } /** * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`) * and must **not** be used in production bundles. The function makes megamorphic reads, which might * be too slow for production mode. * * Constructs a string describing the location of the host component template. The function is used * in dev mode to produce error messages. * * @param lView An `LView` that represents a current component that is being rendered. */ function getTemplateLocationDetails(lView) { !ngDevMode && throwError('Must never be called in production mode'); const hostComponentDef = getDeclarationComponentDef(lView); const componentClassName = hostComponentDef?.type?.name; return componentClassName ? ` (used in the '${componentClassName}' component template)` : ''; } /** * The set of known control flow directives and their corresponding imports. * We use this set to produce a more precises error message with a note * that the `CommonModule` should also be included. */ const KNOWN_CONTROL_FLOW_DIRECTIVES = new Map([['ngIf', 'NgIf'], ['ngFor', 'NgFor'], ['ngSwitchCase', 'NgSwitchCase'], ['ngSwitchDefault', 'NgSwitchDefault']]); /** * Returns true if the tag name is allowed by specified schemas. * @param schemas Array of schemas * @param tagName Name of the tag */ function matchingSchemas(schemas, tagName) { if (schemas !== null) { for (let i = 0; i < schemas.length; i++) { const schema = schemas[i]; if (schema === NO_ERRORS_SCHEMA || schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) { return true; } } } return false; } /** * Flags for renderer-specific style modifiers. * @publicApi */ var RendererStyleFlags2; (function (RendererStyleFlags2) { // TODO(misko): This needs to be refactored into a separate file so that it can be imported from // `node_manipulation.ts` Currently doing the import cause resolution order to change and fails // the tests. The work around is to have hard coded value in `node_manipulation.ts` for now. /** * Marks a style as important. */ RendererStyleFlags2[RendererStyleFlags2["Important"] = 1] = "Important"; /** * Marks a style as using dash case naming (this-is-dash-case). */ RendererStyleFlags2[RendererStyleFlags2["DashCase"] = 2] = "DashCase"; })(RendererStyleFlags2 || (RendererStyleFlags2 = {})); /** * Disallowed strings in the comment. * * see: https://html.spec.whatwg.org/multipage/syntax.html#comments */ const COMMENT_DISALLOWED = /^>|^->|<!--|-->|--!>|<!-$/g; /** * Delimiter in the disallowed strings which needs to be wrapped with zero with character. */ const COMMENT_DELIMITER = /(<|>)/; const COMMENT_DELIMITER_ESCAPED = '\u200B$1\u200B'; /** * Escape the content of comment strings so that it can be safely inserted into a comment node. * * The issue is that HTML does not specify any way to escape comment end text inside the comment. * Consider: `<!-- The way you close a comment is with ">", and "->" at the beginning or by "-->" or * "--!>" at the end. -->`. Above the `"-->"` is meant to be text not an end to the comment. This * can be created programmatically through DOM APIs. (`<!--` are also disallowed.) * * see: https://html.spec.whatwg.org/multipage/syntax.html#comments * * ``` * div.innerHTML = div.innerHTML * ``` * * One would expect that the above code would be safe to do, but it turns out that because comment * text is not escaped, the comment may contain text which will prematurely close the comment * opening up the application for XSS attack. (In SSR we programmatically create comment nodes which * may contain such text and expect them to be safe.) * * This function escapes the comment text by looking for comment delimiters (`<` and `>`) and * surrounding them with `_>_` where the `_` is a zero width space `\u200B`. The result is that if a * comment contains any of the comment start/end delimiters (such as `<!--`, `-->` or `--!>`) the * text it will render normally but it will not cause the HTML parser to close/open the comment. * * @param value text to make safe for comment node by escaping the comment open/close character * sequence. */ function escapeCommentText(value) { return value.replace(COMMENT_DISALLOWED, text => text.replace(COMMENT_DELIMITER, COMMENT_DELIMITER_ESCAPED)); } // Keeps track of the currently-active LViews. const TRACKED_LVIEWS = new Map(); // Used for generating unique IDs for LViews. let uniqueIdCounter = 0; /** Gets a unique ID that can be assigned to an LView. */ function getUniqueLViewId() { return uniqueIdCounter++; } /** Starts tracking an LView. */ function registerLView(lView) { ngDevMode && assertNumber(lView[ID], 'LView must have an ID in order to be registered'); TRACKED_LVIEWS.set(lView[ID], lView); } /** Gets an LView by its unique ID. */ function getLViewById(id) { ngDevMode && assertNumber(id, 'ID used for LView lookup must be a number'); return TRACKED_LVIEWS.get(id) || null; } /** Stops tracking an LView. */ function unregisterLView(lView) { ngDevMode && assertNumber(lView[ID], 'Cannot stop tracking an LView that does not have an ID'); TRACKED_LVIEWS.delete(lView[ID]); } /** * The internal view context which is specific to a given DOM element, directive or * component instance. Each value in here (besides the LView and element node details) * can be present, null or undefined. If undefined then it implies the value has not been * looked up yet, otherwise, if null, then a lookup was executed and nothing was found. * * Each value will get filled when the respective value is examined within the getContext * function. The component, element and each directive instance will share the same instance * of the context. */ class LContext { /** Component's parent view data. */ get lView() { return getLViewById(this.lViewId); } constructor( /** * ID of the component's parent view data. */ lViewId, /** * The index instance of the node. */ nodeIndex, /** * The instance of the DOM node that is attached to the lNode. */ native) { this.lViewId = lViewId; this.nodeIndex = nodeIndex; this.native = native; } } /** * Returns the matching `LContext` data for a given DOM node, directive or component instance. * * This function will examine the provided DOM element, component, or directive instance\'s * monkey-patched property to derive the `LContext` data. Once called then the monkey-patched * value will be that of the newly created `LContext`. * * If the monkey-patched value is the `LView` instance then the context value for that * target will be created and the monkey-patch reference will be updated. Therefore when this * function is called it may mutate the provided element\'s, component\'s or any of the associated * directive\'s monkey-patch values. * * If the monkey-patch value is not detected then the code will walk up the DOM until an element * is found which contains a monkey-patch reference. When that occurs then the provided element * will be updated with a new context (which is then returned). If the monkey-patch value is not * detected for a component/directive instance then it will throw an error (all components and * directives should be automatically monkey-patched by ivy). * * @param target Component, Directive or DOM Node. */ function getLContext(target) { let mpValue = readPatchedData(target); if (mpValue) { // only when it's an array is it considered an LView instance // ... otherwise it's an already constructed LContext instance if (isLView(mpValue)) { const lView = mpValue; let nodeIndex; let component = undefined; let directives = undefined; if (isComponentInstance(target)) { nodeIndex = findViaComponent(lView, target); if (nodeIndex == -1) { throw new Error('The provided component was not found in the application'); } component = target; } else if (isDirectiveInstance(target)) { nodeIndex = findViaDirective(lView, target); if (nodeIndex == -1) { throw new Error('The provided directive was not found in the application'); } directives = getDirectivesAtNodeIndex(nodeIndex, lView); } else { nodeIndex = findViaNativeElement(lView, target); if (nodeIndex == -1) { return null; } } // the goal is not to fill the entire context full of data because the lookups // are expensive. Instead, only the target data (the element, component, container, ICU // expression or directive details) are filled into the context. If called multiple times // with different target values then the missing target data will be filled in. const native = unwrapRNode(lView[nodeIndex]); const existingCtx = readPatchedData(native); const context = existingCtx && !Array.isArray(existingCtx) ? existingCtx : createLContext(lView, nodeIndex, native); // only when the component has been discovered then update the monkey-patch if (component && context.component === undefined) { context.component = component; attachPatchData(context.component, context); } // only when the directives have been discovered then update the monkey-patch if (directives && context.directives === undefined) { context.directives = directives; for (let i = 0; i < directives.length; i++) { attachPatchData(directives[i], context); } } attachPatchData(context.native, context); mpValue = context; } } else { const rElement = target; ngDevMode && assertDomNode(rElement); // if the context is not found then we need to traverse upwards up the DOM // to find the nearest element that has already been monkey patched with data let parent = rElement; while (parent = parent.parentNode) { const parentContext = readPatchedData(parent); if (parentContext) { const lView = Array.isArray(parentContext) ? parentContext : parentContext.lView; // the edge of the app was also reached here through another means // (maybe because the DOM was changed manually). if (!lView) { return null; } const index = findViaNativeElement(lView, rElement); if (index >= 0) { const native = unwrapRNode(lView[index]); const context = createLContext(lView, index, native); attachPatchData(native, context); mpValue = context; break; } } } } return mpValue || null; } /** * Creates an empty instance of a `LContext` context */ function createLContext(lView, nodeIndex, native) { return new LContext(lView[ID], nodeIndex, native); } /** * Takes a component instance and returns the view for that component. * * @param componentInstance * @returns The component's view */ function getComponentViewByInstance(componentInstance) { let patchedData = readPatchedData(componentInstance); let lView; if (isLView(patchedData)) { const contextLView = patchedData; const nodeIndex = findViaComponent(contextLView, componentInstance); lView = getComponentLViewByIndex(nodeIndex, contextLView); const context = createLContext(contextLView, nodeIndex, lView[HOST]); context.component = componentInstance; attachPatchData(componentInstance, context); attachPatchData(context.native, context); } else { const context = patchedData; const contextLView = context.lView; ngDevMode && assertLView(contextLView); lView = getComponentLViewByIndex(context.nodeIndex, contextLView); } return lView; } /** * This property will be monkey-patched on elements, components and directives. */ const MONKEY_PATCH_KEY_NAME = '__ngContext__'; /** * Assigns the given data to the given target (which could be a component, * directive or DOM node instance) using monkey-patching. */ function attachPatchData(target, data) { ngDevMode && assertDefined(target, 'Target expected'); // Only attach the ID of the view in order to avoid memory leaks (see #41047). We only do this // for `LView`, because we have control over when an `LView` is created and destroyed, whereas // we can't know when to remove an `LContext`. if (isLView(data)) { target[MONKEY_PATCH_KEY_NAME] = data[ID]; registerLView(data); } else { target[MONKEY_PATCH_KEY_NAME] = data; } } /** * Returns the monkey-patch value data present on the target (which could be * a component, directive or a DOM node). */ function readPatchedData(target) { ngDevMode && assertDefined(target, 'Target expected'); const data = target[MONKEY_PATCH_KEY_NAME]; return typeof data === 'number' ? getLViewById(data) : data || null; } function readPatchedLView(target) { const value = readPatchedData(target); if (value) { return isLView(value) ? value : value.lView; } return null; } function isComponentInstance(instance) { return instance && instance.constructor && instance.constructor.ɵcmp; } function isDirectiveInstance(instance) { return instance && instance.constructor && instance.constructor.ɵdir; } /** * Locates the element within the given LView and returns the matching index */ function findViaNativeElement(lView, target) { const tView = lView[TVIEW]; for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) { if (unwrapRNode(lView[i]) === target) { return i; } } return -1; } /** * Locates the next tNode (child, sibling or parent). */ function traverseNextElement(tNode) { if (tNode.child) { return tNode.child; } else if (tNode.next) { return tNode.next; } else { // Let's take the following template: <div><span>text</span></div><component/> // After checking the text node, we need to find the next parent that has a "next" TNode, // in this case the parent `div`, so that we can find the component. while (tNode.parent && !tNode.parent.next) { tNode = tNode.parent; } return tNode.parent && tNode.parent.next; } } /** * Locates the component within the given LView and returns the matching index */ function findViaComponent(lView, componentInstance) { const componentIndices = lView[TVIEW].components; if (componentIndices) { for (let i = 0; i < componentIndices.length; i++) { const elementComponentIndex = componentIndices[i]; const componentView = getComponentLViewByIndex(elementComponentIndex, lView); if (componentView[CONTEXT] === componentInstance) { return elementComponentIndex; } } } else { const rootComponentView = getComponentLViewByIndex(HEADER_OFFSET, lView); const rootComponent = rootComponentView[CONTEXT]; if (rootComponent === componentInstance) { // we are dealing with the root element here therefore we know that the // element is the very first element after the HEADER data in the lView return HEADER_OFFSET; } } return -1; } /** * Locates the directive within the given LView and returns the matching index */ function findViaDirective(lView, directiveInstance) { // if a directive is monkey patched then it will (by default) // have a reference to the LView of the current view. The // element bound to the directive being search lives somewhere // in the view data. We loop through the nodes and check their // list of directives for the instance. let tNode = lView[TVIEW].firstChild; while (tNode) { const directiveIndexStart = tNode.directiveStart; const directiveIndexEnd = tNode.directiveEnd; for (let i = directiveIndexStart; i < directiveIndexEnd; i++) { if (lView[i] === directiveInstance) { return tNode.index; } } tNode = traverseNextElement(tNode); } return -1; } /** * Returns a list of directives applied to a node at a specific index. The list includes * directives matched by selector and any host directives, but it excludes components. * Use `getComponentAtNodeIndex` to find the component applied to a node. * * @param nodeIndex The node index * @param lView The target view data */ function getDirectivesAtNodeIndex(nodeIndex, lView) { const tNode = lView[TVIEW].data[nodeIndex]; if (tNode.directiveStart === 0) return EMPTY_ARRAY; const results = []; for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) { const directiveInstance = lView[i]; if (!isComponentInstance(directiveInstance)) { results.push(directiveInstance); } } return results; } function getComponentAtNodeIndex(nodeIndex, lView) { const tNode = lView[TVIEW].data[nodeIndex]; const { directiveStart, componentOffset } = tNode; return componentOffset > -1 ? lView[directiveStart + componentOffset] : null; } /** * Returns a map of local references (local reference name => element or directive instance) that * exist on a given element. */ function discoverLocalRefs(lView, nodeIndex) { const tNode = lView[TVIEW].data[nodeIndex]; if (tNode && tNode.localNames) { const result = {}; let localIndex = tNode.index + 1; for (let i = 0; i < tNode.localNames.length; i += 2) { result[tNode.localNames[i]] = lView[localIndex]; localIndex++; } return result; } return null; } let _icuContainerIterate; /** * Iterator which provides ability to visit all of the `TIcuContainerNode` root `RNode`s. */ function icuContainerIterate(tIcuContainerNode, lView) { return _icuContainerIterate(tIcuContainerNode, lView); } /** * Ensures that `IcuContainerVisitor`'s implementation is present. * * This function is invoked when i18n instruction comes across an ICU. The purpose is to allow the * bundler to tree shake ICU logic and only load it if ICU instruction is executed. */ function ensureIcuContainerVisitorLoaded(loader) { if (_icuContainerIterate === undefined) { // Do not inline this function. We want to keep `ensureIcuContainerVisitorLoaded` light, so it // can be inlined into call-site. _icuContainerIterate = loader(); } } /** * Gets the parent LView of the passed LView, if the PARENT is an LContainer, will get the parent of * that LContainer, which is an LView * @param lView the lView whose parent to get */ function getLViewParent(lView) { ngDevMode && assertLView(lView); const parent = lView[PARENT]; return isLContainer(parent) ? parent[PARENT] : parent; } /** * Retrieve the root view from any component or `LView` by walking the parent `LView` until * reaching the root `LView`. * * @param componentOrLView any component or `LView` */ function getRootView(componentOrLView) { ngDevMode && assertDefined(componentOrLView, 'component'); let lView = isLView(componentOrLView) ? componentOrLView : readPatchedLView(componentOrLView); while (lView && !(lView[FLAGS] & 256 /* LViewFlags.IsRoot */)) { lView = getLViewParent(lView); } ngDevMode && assertLView(lView); return lView; } /** * Returns the context information associated with the application where the target is situated. It * does this by walking the parent views until it gets to the root view, then getting the context * off of that. * * @param viewOrComponent the `LView` or component to get the root context for. */ function getRootContext(viewOrComponent) { const rootView = getRootView(viewOrComponent); ngDevMode && assertDefined(rootView[CONTEXT], 'Root view has no context. Perhaps it is disconnected?'); return rootView[CONTEXT]; } /** * Gets the first `LContainer` in the LView or `null` if none exists. */ function getFirstLContainer(lView) { return getNearestLContainer(lView[CHILD_HEAD]); } /** * Gets the next `LContainer` that is a sibling of the given container. */ function getNextLContainer(container) { return getNearestLContainer(container[NEXT]); } function getNearestLContainer(viewOrContainer) { while (viewOrContainer !== null && !isLContainer(viewOrContainer)) { viewOrContainer = viewOrContainer[NEXT]; } return viewOrContainer; } /** * NOTE: for performance reasons, the possible actions are inlined within the function instead of * being passed as an argument. */ function applyToElementOrContainer(action, renderer, parent, lNodeToHandle, beforeNode) { // If this slot was allocated for a text node dynamically created by i18n, the text node itself // won't be created until i18nApply() in the update block, so this node should be skipped. // For more info, see "ICU expressions should work inside an ngTemplateOutlet inside an ngFor" // in `i18n_spec.ts`. if (lNodeToHandle != null) { let lContainer; let isComponent = false; // We are expecting an RNode, but in the case of a component or LContainer the `RNode` is // wrapped in an array which needs to be unwrapped. We need to know if it is a component and if // it has LContainer so that we can process all of those cases appropriately. if (isLContainer(lNodeToHandle)) { lContainer = lNodeToHandle; } else if (isLView(lNodeToHandle)) { isComponent = true; ngDevMode && assertDefined(lNodeToHandle[HOST], 'HOST must be defined for a component LView'); lNodeToHandle = lNodeToHandle[HOST]; } const rNode = unwrapRNode(lNodeToHandle); if (action === 0 /* WalkTNodeTreeAction.Create */ && parent !== null) { if (beforeNode == null) { nativeAppendChild(renderer, parent, rNode); } else { nativeInsertBefore(renderer, parent, rNode, beforeNode || null, true); } } else if (action === 1 /* WalkTNodeTreeAction.Insert */ && parent !== null) { nativeInsertBefore(renderer, parent, rNode, beforeNode || null, true); } else if (action === 2 /* WalkTNodeTreeAction.Detach */) { nativeRemoveNode(renderer, rNode, isComponent); } else if (action === 3 /* WalkTNodeTreeAction.Destroy */) { ngDevMode && ngDevMode.rendererDestroyNode++; renderer.destroyNode(rNode); } if (lContainer != null) { applyContainer(renderer, action, lContainer, parent, beforeNode); } } } function createTextNode(renderer, value) { ngDevMode && ngDevMode.rendererCreateTextNode++; ngDevMode && ngDevMode.rendererSetText++; return renderer.createText(value); } function updateTextNode(renderer, rNode, value) { ngDevMode && ngDevMode.rendererSetText++; renderer.setValue(rNode, value); } function createCommentNode(renderer, value) { ngDevMode && ngDevMode.rendererCreateComment++; return renderer.createComment(escapeCommentText(value)); } /** * Creates a native element from a tag name, using a renderer. * @param renderer A renderer to use * @param name the tag name * @param namespace Optional namespace for element. * @returns the element created */ function createElementNode(renderer, name, namespace) { ngDevMode && ngDevMode.rendererCreateElement++; return renderer.createElement(name, namespace); } /** * Removes all DOM elements associated with a view. * * Because some root nodes of the view may be containers, we sometimes need * to propagate deeply into the nested containers to remove all elements in the * views beneath it. * * @param tView The `TView' of the `LView` from which elements should be added or removed * @param lView The view from which elements should be added or removed */ function removeViewFromContainer(tView, lView) { const renderer = lView[RENDERER]; applyView(tView, lView, renderer, 2 /* WalkTNodeTreeAction.Detach */, null, null); lView[HOST] = null; lView[T_HOST] = null; } /** * Adds all DOM elements associated with a view. * * Because some root nodes of the view may be containers, we sometimes need * to propagate deeply into the nested containers to add all elements in the * views beneath it. * * @param tView The `TView' of the `LView` from which elements should be added or removed * @param parentTNode The `TNode` where the `LView` should be attached to. * @param renderer Current renderer to use for DOM manipulations. * @param lView The view from which elements should be added or removed * @param parentNativeNode The parent `RElement` where it should be inserted into. * @param beforeNode The node before which elements should be added, if insert mode */ function addViewToContainer(tView, parentTNode, renderer, lView, parentNativeNode, beforeNode) { lView[HOST] = parentNativeNode; lView[T_HOST] = parentTNode; applyView(tView, lView, renderer, 1 /* WalkTNodeTreeAction.Insert */, parentNativeNode, beforeNode); } /** * Detach a `LView` from the DOM by detaching its nodes. * * @param tView The `TView' of the `LView` to be detached * @param lView the `LView` to be detached. */ function renderDetachView(tView, lView) { applyView(tView, lView, lView[RENDERER], 2 /* WalkTNodeTreeAction.Detach */, null, null); } /** * Traverses down and up the tree of views and containers to remove listeners and * call onDestroy callbacks. * * Notes: * - Because it's used for onDestroy calls, it needs to be bottom-up. * - Must process containers instead of their views to avoid splicing * when views are destroyed and re-added. * - Using a while loop because it's faster than recursion * - Destroy only called on movement to sibling or movement to parent (laterally or up) * * @param rootView The view to destroy */ function destroyViewTree(rootView) { // If the view has no children, we can clean it up and return early. let lViewOrLContainer = rootView[CHILD_HEAD]; if (!lViewOrLContainer) { return cleanUpView(rootView[TVIEW], rootView); } while (lViewOrLContainer) { let next = null; if (isLView(lViewOrLContainer)) { // If LView, traverse down to child. next = lViewOrLContainer[CHILD_HEAD]; } else { ngDevMode && assertLContainer(lViewOrLContainer); // If container, traverse down to its first LView. const firstView = lViewOrLContainer[CONTAINER_HEADER_OFFSET]; if (firstView) next = firstView; } if (!next) { // Only clean up view when moving to the side or up, as destroy hooks // should be called in order from the bottom up. while (lViewOrLContainer && !lViewOrLContainer[NEXT] && lViewOrLContainer !== rootView) { if (isLView(lViewOrLContainer)) { cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer); } lViewOrLContainer = lViewOrLContainer[PARENT]; } if (lViewOrLContainer === null) lViewOrLContainer = rootView; if (isLView(lViewOrLContainer)) { cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer); } next = lViewOrLContainer && lViewOrLContainer[NEXT]; } lViewOrLContainer = next; } } /** * Inserts a view into a container. * * This adds the view to the container's array of active views in the correct * position. It also adds the view's elements to the DOM if the container isn't a * root node of another view (in that case, the view's elements will be added when * the container's parent view is added later). * * @param tView The `TView' of the `LView` to insert * @param lView The view to insert * @param lContainer The container into which the view should be inserted * @param index Which index in the container to insert the child view into */ function insertView(tView, lView, lContainer, index) { ngDevMode && assertLView(lView); ngDevMode && assertLContainer(lContainer); const indexInContainer = CONTAINER_HEADER_OFFSET + index; const containerLength = lContainer.length; if (index > 0) { // This is a new view, we need to add it to the children. lContainer[indexInContainer - 1][NEXT] = lView; } if (index < containerLength - CONTAINER_HEADER_OFFSET) { lView[NEXT] = lContainer[indexInContainer]; addToArray(lContainer, CONTAINER_HEADER_OFFSET + index, lView); } else { lContainer.push(lView); lView[NEXT] = null; } lView[PARENT] = lContainer; // track views where declaration and insertion points are different const declarationLContainer = lView[DECLARATION_LCONTAINER]; if (declarationLContainer !== null && lContainer !== declarationLContainer) { trackMovedView(declarationLContainer, lView); } // notify query that a new view has been added const lQueries = lView[QUERIES]; if (lQueries !== null) { lQueries.insertView(tView); } // Sets the attached flag lView[FLAGS] |= 64 /* LViewFlags.Attached */; } /** * Track views created from the declaration container (TemplateRef) and inserted into a * different LContainer. */ function trackMovedView(declarationContainer, lView) { ngDevMode && assertDefined(lView, 'LView required'); ngDevMode && assertLContainer(declarationContainer); const movedViews = declarationContainer[MOVED_VIEWS]; const insertedLContainer = lView[PARENT]; ngDevMode && assertLContainer(insertedLContainer); const insertedComponentLView = insertedLContainer[PARENT][DECLARATION_COMPONENT_VIEW]; ngDevMode && assertDefined(insertedComponentLView, 'Missing insertedComponentLView'); const declaredComponentLView = lView[DECLARATION_COMPONENT_VIEW]; ngDevMode && assertDefined(declaredComponentLView, 'Missing declaredComponentLView'); if (declaredComponentLView !== insertedComponentLView) { // At this point the declaration-component is not same as insertion-component; this means that // this is a transplanted view. Mark the declared lView as having transplanted views so that // those views can participate in CD. declarationContainer[HAS_TRANSPLANTED_VIEWS] = true; } if (movedViews === null) { declarationContainer[MOVED_VIEWS] = [lView]; } else { movedViews.push(lView); } } function detachMovedView(declarationContainer, lView) { ngDevMode && assertLContainer(declarationContainer); ngDevMode && assertDefined(declarationContainer[MOVED_VIEWS], 'A projected view should belong to a non-empty projected views collection'); const movedViews = declarationContainer[MOVED_VIEWS]; const declarationViewIndex = movedViews.indexOf(lView); const insertionLContainer = lView[PARENT]; ngDevMode && assertLContainer(insertionLContainer); // If the view was marked for refresh but then detached before it was checked (where the flag // would be cleared and the counter decremented), we need to decrement the view counter here // instead. if (lView[FLAGS] & 512 /* LViewFlags.RefreshTransplantedView */) { lView[FLAGS] &= ~512 /* LViewFlags.RefreshTransplantedView */; updateTransplantedViewCount(insertionLContainer, -1); } movedViews.splice(declarationViewIndex, 1); } /** * Detaches a view from a container. * * This method removes the view from the container's array of active views. It also * removes the view's elements from the DOM. * * @param lContainer The container from which to detach a view * @param removeIndex The index of the view to detach * @returns Detached LView instance. */ function detachView(lContainer, removeIndex) { if (lContainer.length <= CONTAINER_HEADER_OFFSET) return; const indexInContainer = CONTAINER_HEADER_OFFSET + removeIndex; const viewToDetach = lContainer[indexInContainer]; if (viewToDetach) { const declarationLContainer = viewToDetach[DECLARATION_LCONTAINER]; if (declarationLContainer !== null && declarationLContainer !== lContainer) { detachMovedView(declarationLContainer, viewToDetach); } if (removeIndex > 0) { lContainer[indexInContainer - 1][NEXT] = viewToDetach[NEXT]; } const removedLView = removeFromArray(lContainer, CONTAINER_HEADER_OFFSET + removeIndex); removeViewFromContainer(viewToDetach[TVIEW], viewToDetach); // notify query that a view has been removed const lQueries = removedLView[QUERIES]; if (lQueries !== null) { lQueries.detachView(removedLView[TVIEW]); } viewToDetach[PARENT] = null; viewToDetach[NEXT] = null; // Unsets the attached flag viewToDetach[FLAGS] &= ~64 /* LViewFlags.Attached */; } return viewToDetach; } /** * A standalone function which destroys an LView, * conducting clean up (e.g. removing listeners, calling onDestroys). * * @param tView The `TView' of the `LView` to be destroyed * @param lView The view to be destroyed. */ function destroyLView(tView, lView) { if (!(lView[FLAGS] & 128 /* LViewFlags.Destroyed */)) { const renderer = lView[RENDERER]; if (renderer.destroyNode) { applyView(tView, lView, renderer, 3 /* WalkTNodeTreeAction.Destroy */, null, null); } destroyViewTree(lView); } } /** * Calls onDestroys hooks for all directives and pipes in a given view and then removes all * listeners. Listeners are removed as the last step so events delivered in the onDestroys hooks * can be propagated to @Output listeners. * * @param tView `TView` for the `LView` to clean up. * @param lView The LView to clean up */ function cleanUpView(tView, lView) { if (!(lView[FLAGS] & 128 /* LViewFlags.Destroyed */)) { // Usually the Attached flag is removed when the view is detached from its parent, however // if it's a root view, the flag won't be unset hence why we're also removing on destroy. lView[FLAGS] &= ~64 /* LViewFlags.Attached */; // Mark the LView as destroyed *before* executing the onDestroy hooks. An onDestroy hook // runs arbitrary user code, which could include its own `viewRef.destroy()` (or similar). If // We don't flag the view as destroyed before the hooks, this could lead to an infinite loop. // This also aligns with the ViewEngine behavior. It also means that the onDestroy hook is // really more of an "afterDestroy" hook if you think about it. lView[FLAGS] |= 128 /* LViewFlags.Destroyed */; executeOnDestroys(tView, lView); processCleanups(tView, lView); // For component views only, the local renderer is destroyed at clean up time. if (lView[TVIEW].type === 1 /* TViewType.Component */) { ngDevMode && ngDevMode.rendererDestroy++; lView[RENDERER].destroy(); } const declarationContainer = lView[DECLARATION_LCONTAINER]; // we are dealing with an embedded view that is still inserted into a container if (declarationContainer !== null && isLContainer(lView[PARENT])) { // and this is a projected view if (declarationContainer !== lView[PARENT]) { detachMovedView(declarationContainer, lView); } // For embedded views still attached to a container: remove query result from this view. const lQueries = lView[QUERIES]; if (lQueries !== null) { lQueries.detachView(tView); } } // Unregister the view once everything else has been cleaned up. unregisterLView(lView); } } /** Removes listeners and unsubscribes from output subscriptions */ function processCleanups(tView, lView) { const tCleanup = tView.cleanup; const lCleanup = lView[CLEANUP]; // `LCleanup` contains both share information with `TCleanup` as well as instance specific // information appended at the end. We need to know where the end of the `TCleanup` information // is, and we track this with `lastLCleanupIndex`. let lastLCleanupIndex = -1; if (tCleanup !== null) { for (let i = 0; i < tCleanup.length - 1; i += 2) { if (typeof tCleanup[i] === 'string') { // This is a native DOM listener. It will occupy 4 entries in the TCleanup array (hence i += // 2 at the end of this block). const targetIdx = tCleanup[i + 3]; ngDevMode && assertNumber(targetIdx, 'cleanup target must be a number'); if (targetIdx >= 0) { // unregister lCleanup[lastLCleanupIndex = targetIdx](); } else { // Subscription lCleanup[lastLCleanupIndex = -targetIdx].unsubscribe(); } i += 2; } else { // This is a cleanup function that is grouped with the index of its context const context = lCleanup[lastLCleanupIndex = tCleanup[i + 1]]; tCleanup[i].call(context); } } } if (lCleanup !== null) { for (let i = lastLCleanupIndex + 1; i < lCleanup.length; i++) { const instanceCleanupFn = lCleanup[i]; ngDevMode && assertFunction(instanceCleanupFn, 'Expecting instance cleanup function.'); instanceCleanupFn(); } lView[CLEANUP] = null; } } /** Calls onDestroy hooks for this view */ function executeOnDestroys(tView, lView) { let destroyHooks; if (tView != null && (destroyHooks = tView.destroyHooks) != null) { for (let i = 0; i < destroyHooks.length; i += 2) { const context = lView[destroyHooks[i]]; // Only call the destroy hook if the context has been requested. if (!(context instanceof NodeInjectorFactory)) { const toCall = destroyHooks[i + 1]; if (Array.isArray(toCall)) { for (let j = 0; j < toCall.length; j += 2) { const callContext = context[toCall[j]]; const hook = toCall[j + 1]; profiler(4 /* ProfilerEvent.LifecycleHookStart */, callContext, hook); try { hook.call(callContext); } finally { profiler(5 /* ProfilerEvent.LifecycleHookEnd */, callContext, hook); } } } else { profiler(4 /* ProfilerEvent.LifecycleHookStart */, context, toCall); try { toCall.call(context); } finally { profiler(5 /* ProfilerEvent.LifecycleHookEnd */, context, toCall); } } } } } } /** * Returns a native element if a node can be inserted into the given parent. * * There are two reasons why we may not be able to insert a element immediately. * - Projection: When creating a child content element of a component, we have to skip the * insertion because the content of a component will be projected. * `<component><content>delayed due to projection</content></component>` * - Parent container is disconnected: This can happen when we are inserting a view into * parent container, which itself is disconnected. For example the parent container is part * of a View which has not be inserted or is made for projection but has not been inserted * into destination. * * @param tView: Current `TView`. * @param tNode: `TNode` for which we wish to retrieve render parent. * @param lView: Current `LView`. */ function getParentRElement(tView, tNode, lView) { return getClosestRElement(tView, tNode.parent, lView); } /** * Get closest `RElement` or `null` if it can't be found. * * If `TNode` is `TNodeType.Element` => return `RElement` at `LView[tNode.index]` location. * If `TNode` is `TNodeType.ElementContainer|IcuContain` => return the parent (recursively). * If `TNode` is `null` then return host `RElement`: * - return `null` if projection * - return `null` if parent container is disconnected (we have no parent.) * * @param tView: Current `TView`. * @param tNode: `TNode` for which we wish to retrieve `RElement` (or `null` if host element is * needed). * @param lView: Current `LView`. * @returns `null` if the `RElement` can't be determined at this time (no parent / projection) */ function getClosestRElement(tView, tNode, lView) { let parentTNode = tNode; // Skip over element and ICU containers as those are represented by a comment node and // can't be used as a render parent. while (parentTNode !== null && parentTNode.type & (8 /* TNodeType.ElementContainer */ | 32 /* TNodeType.Icu */)) { tNode = parentTNode; parentTNode = tNode.parent; } // If the parent tNode is null, then we are inserting across views: either into an embedded view // or a component view. if (parentTNode === null) { // We are inserting a root element of the component view into the component host element and // it should always be eager. return lView[HOST]; } else { ngDevMode && assertTNodeType(parentTNode, 3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */); const { componentOffset } = parentTNode; if (componentOffset > -1) { ngDevMode && assertTNodeForLView(parentTNode, lView); const { encapsulation } = tView.data[parentTNode.directiveStart + componentOffset]; // We've got a parent which is an element in the current view. We just need to verify if the // parent element is not a component. Component's content nodes are not inserted immediately // because they will be projected, and so doing insert at this point would be wasteful. // Since the projection would then move it to its final destination. Note that we can't // make this assumption when using the Shadow DOM, because the native projection placeholders // (<content> or <slot>) have to be in place as elements are being inserted. if (encapsulation === ViewEncapsulation$1.None || encapsulation === ViewEncapsulation$1.Emulated) { return null; } } return getNativeByTNode(parentTNode, lView); } } /** * Inserts a native node before another native node for a given parent. * This is a utility function that can be used when native nodes were determined. */ function nativeInsertBefore(renderer, parent, child, beforeNode, isMove) { ngDevMode && ngDevMode.rendererInsertBefore++; renderer.insertBefore(parent, child, beforeNode, isMove); } function nativeAppendChild(renderer, parent, child) { ngDevMode && ngDevMode.rendererAppendChild++; ngDevMode && assertDefined(parent, 'parent node must be defined'); renderer.appendChild(parent, child); } function nativeAppendOrInsertBefore(renderer, parent, child, beforeNode, isMove) { if (beforeNode !== null) { nativeInsertBefore(renderer, parent, child, beforeNode, isMove); } else { nativeAppendChild(renderer, parent, child); } } /** Removes a node from the DOM given its native parent. */ function nativeRemoveChild(renderer, parent, child, isHostElement) { renderer.removeChild(parent, child, isHostElement); } /** Checks if an element is a `<template>` node. */ function isTemplateNode(node) { return node.tagName === 'TEMPLATE' && node.content !== undefined; } /** * Returns a native parent of a given native node. */ function nativeParentNode(renderer, node) { return renderer.parentNode(node); } /** * Returns a native sibling of a given native node. */ function nativeNextSibling(renderer, node) { return renderer.nextSibling(node); } /** * Find a node in front of which `currentTNode` should be inserted. * * This method determines the `RNode` in front of which we should insert the `currentRNode`. This * takes `TNode.insertBeforeIndex` into account if i18n code has been invoked. * * @param parentTNode parent `TNode` * @param currentTNode current `TNode` (The node which we would like to insert into the DOM) * @param lView current `LView` */ function getInsertInFrontOfRNode(parentTNode, currentTNode, lView) { return _getInsertInFrontOfRNodeWithI18n(parentTNode, currentTNode, lView); } /** * Find a node in front of which `currentTNode` should be inserted. (Does not take i18n into * account) * * This method determines the `RNode` in front of which we should insert the `currentRNode`. This * does not take `TNode.insertBeforeIndex` into account. * * @param parentTNode parent `TNode` * @param currentTNode current `TNode` (The node which we would like to insert into the DOM) * @param lView current `LView` */ function getInsertInFrontOfRNodeWithNoI18n(parentTNode, currentTNode, lView) { if (parentTNode.type & (8 /* TNodeType.ElementContainer */ | 32 /* TNodeType.Icu */)) { return getNativeByTNode(parentTNode, lView); } return null; } /** * Tree shakable boundary for `getInsertInFrontOfRNodeWithI18n` function. * * This function will only be set if i18n code runs. */ let _getInsertInFrontOfRNodeWithI18n = getInsertInFrontOfRNodeWithNoI18n; /** * Tree shakable boundary for `processI18nInsertBefore` function. * * This function will only be set if i18n code runs. */ let _processI18nInsertBefore; function setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore) { _getInsertInFrontOfRNodeWithI18n = getInsertInFrontOfRNodeWithI18n; _processI18nInsertBefore = processI18nInsertBefore; } /** * Appends the `child` native node (or a collection of nodes) to the `parent`. * * @param tView The `TView' to be appended * @param lView The current LView * @param childRNode The native child (or children) that should be appended * @param childTNode The TNode of the child element */ function appendChild(tView, lView, childRNode, childTNode) { const parentRNode = getParentRElement(tView, childTNode, lView); const renderer = lView[RENDERER]; const parentTNode = childTNode.parent || lView[T_HOST]; const anchorNode = getInsertInFrontOfRNode(parentTNode, childTNode, lView); if (parentRNode != null) { if (Array.isArray(childRNode)) { for (let i = 0; i < childRNode.length; i++) { nativeAppendOrInsertBefore(renderer, parentRNode, childRNode[i], anchorNode, false); } } else { nativeAppendOrInsertBefore(renderer, parentRNode, childRNode, anchorNode, false); } } _processI18nInsertBefore !== undefined && _processI18nInsertBefore(renderer, childTNode, lView, childRNode, parentRNode); } /** * Returns the first native node for a given LView, starting from the provided TNode. * * Native nodes are returned in the order in which those appear in the native tree (DOM). */ function getFirstNativeNode(lView, tNode) { if (tNode !== null) { ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 32 /* TNodeType.Icu */ | 16 /* TNodeType.Projection */); const tNodeType = tNode.type; if (tNodeType & 3 /* TNodeType.AnyRNode */) { return getNativeByTNode(tNode, lView); } else if (tNodeType & 4 /* TNodeType.Container */) { return getBeforeNodeForView(-1, lView[tNode.index]); } else if (tNodeType & 8 /* TNodeType.ElementContainer */) { const elIcuContainerChild = tNode.child; if (elIcuContainerChild !== null) { return getFirstNativeNode(lView, elIcuContainerChild); } else { const rNodeOrLContainer = lView[tNode.index]; if (isLContainer(rNodeOrLContainer)) { return getBeforeNodeForView(-1, rNodeOrLContainer); } else { return unwrapRNode(rNodeOrLContainer); } } } else if (tNodeType & 32 /* TNodeType.Icu */) { let nextRNode = icuContainerIterate(tNode, lView); let rNode = nextRNode(); // If the ICU container has no nodes, than we use the ICU anchor as the node. return rNode || unwrapRNode(lView[tNode.index]); } else { const projectionNodes = getProjectionNodes(lView, tNode); if (projectionNodes !== null) { if (Array.isArray(projectionNodes)) { return projectionNodes[0]; } const parentView = getLViewParent(lView[DECLARATION_COMPONENT_VIEW]); ngDevMode && assertParentView(parentView); return getFirstNativeNode(parentView, projectionNodes); } else { return getFirstNativeNode(lView, tNode.next); } } } return null; } function getProjectionNodes(lView, tNode) { if (tNode !== null) { const componentView = lView[DECLARATION_COMPONENT_VIEW]; const componentHost = componentView[T_HOST]; const slotIdx = tNode.projection; ngDevMode && assertProjectionSlots(lView); return componentHost.projection[slotIdx]; } return null; } function getBeforeNodeForView(viewIndexInContainer, lContainer) { const nextViewIndex = CONTAINER_HEADER_OFFSET + viewIndexInContainer + 1; if (nextViewIndex < lContainer.length) { const lView = lContainer[nextViewIndex]; const firstTNodeOfView = lView[TVIEW].firstChild; if (firstTNodeOfView !== null) { return getFirstNativeNode(lView, firstTNodeOfView); } } return lContainer[NATIVE]; } /** * Removes a native node itself using a given renderer. To remove the node we are looking up its * parent from the native tree as not all platforms / browsers support the equivalent of * node.remove(). * * @param renderer A renderer to be used * @param rNode The native node that should be removed * @param isHostElement A flag indicating if a node to be removed is a host of a component. */ function nativeRemoveNode(renderer, rNode, isHostElement) { ngDevMode && ngDevMode.rendererRemoveNode++; const nativeParent = nativeParentNode(renderer, rNode); if (nativeParent) { nativeRemoveChild(renderer, nativeParent, rNode, isHostElement); } } /** * Performs the operation of `action` on the node. Typically this involves inserting or removing * nodes on the LView or projection boundary. */ function applyNodes(renderer, action, tNode, lView, parentRElement, beforeNode, isProjection) { while (tNode != null) { ngDevMode && assertTNodeForLView(tNode, lView); ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 16 /* TNodeType.Projection */ | 32 /* TNodeType.Icu */); const rawSlotValue = lView[tNode.index]; const tNodeType = tNode.type; if (isProjection) { if (action === 0 /* WalkTNodeTreeAction.Create */) { rawSlotValue && attachPatchData(unwrapRNode(rawSlotValue), lView); tNode.flags |= 2 /* TNodeFlags.isProjected */; } } if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) { if (tNodeType & 8 /* TNodeType.ElementContainer */) { applyNodes(renderer, action, tNode.child, lView, parentRElement, beforeNode, false); applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode); } else if (tNodeType & 32 /* TNodeType.Icu */) { const nextRNode = icuContainerIterate(tNode, lView); let rNode; while (rNode = nextRNode()) { applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode); } applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode); } else if (tNodeType & 16 /* TNodeType.Projection */) { applyProjectionRecursive(renderer, action, lView, tNode, parentRElement, beforeNode); } else { ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */); applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode); } } tNode = isProjection ? tNode.projectionNext : tNode.next; } } function applyView(tView, lView, renderer, action, parentRElement, beforeNode) { applyNodes(renderer, action, tView.firstChild, lView, parentRElement, beforeNode, false); } /** * `applyProjection` performs operation on the projection. * * Inserting a projection requires us to locate the projected nodes from the parent component. The * complication is that those nodes themselves could be re-projected from their parent component. * * @param tView The `TView` of `LView` which needs to be inserted, detached, destroyed * @param lView The `LView` which needs to be inserted, detached, destroyed. * @param tProjectionNode node to project */ function applyProjection(tView, lView, tProjectionNode) { const renderer = lView[RENDERER]; const parentRNode = getParentRElement(tView, tProjectionNode, lView); const parentTNode = tProjectionNode.parent || lView[T_HOST]; let beforeNode = getInsertInFrontOfRNode(parentTNode, tProjectionNode, lView); applyProjectionRecursive(renderer, 0 /* WalkTNodeTreeAction.Create */, lView, tProjectionNode, parentRNode, beforeNode); } /** * `applyProjectionRecursive` performs operation on the projection specified by `action` (insert, * detach, destroy) * * Inserting a projection requires us to locate the projected nodes from the parent component. The * complication is that those nodes themselves could be re-projected from their parent component. * * @param renderer Render to use * @param action action to perform (insert, detach, destroy) * @param lView The LView which needs to be inserted, detached, destroyed. * @param tProjectionNode node to project * @param parentRElement parent DOM element for insertion/removal. * @param beforeNode Before which node the insertions should happen. */ function applyProjectionRecursive(renderer, action, lView, tProjectionNode, parentRElement, beforeNode) { const componentLView = lView[DECLARATION_COMPONENT_VIEW]; const componentNode = componentLView[T_HOST]; ngDevMode && assertEqual(typeof tProjectionNode.projection, 'number', 'expecting projection index'); const nodeToProjectOrRNodes = componentNode.projection[tProjectionNode.projection]; if (Array.isArray(nodeToProjectOrRNodes)) { // This should not exist, it is a bit of a hack. When we bootstrap a top level node and we // need to support passing projectable nodes, so we cheat and put them in the TNode // of the Host TView. (Yes we put instance info at the T Level). We can get away with it // because we know that that TView is not shared and therefore it will not be a problem. // This should be refactored and cleaned up. for (let i = 0; i < nodeToProjectOrRNodes.length; i++) { const rNode = nodeToProjectOrRNodes[i]; applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode); } } else { let nodeToProject = nodeToProjectOrRNodes; const projectedComponentLView = componentLView[PARENT]; applyNodes(renderer, action, nodeToProject, projectedComponentLView, parentRElement, beforeNode, true); } } /** * `applyContainer` performs an operation on the container and its views as specified by * `action` (insert, detach, destroy) * * Inserting a Container is complicated by the fact that the container may have Views which * themselves have containers or projections. * * @param renderer Renderer to use * @param action action to perform (insert, detach, destroy) * @param lContainer The LContainer which needs to be inserted, detached, destroyed. * @param parentRElement parent DOM element for insertion/removal. * @param beforeNode Before which node the insertions should happen. */ function applyContainer(renderer, action, lContainer, parentRElement, beforeNode) { ngDevMode && assertLContainer(lContainer); const anchor = lContainer[NATIVE]; // LContainer has its own before node. const native = unwrapRNode(lContainer); // An LContainer can be created dynamically on any node by injecting ViewContainerRef. // Asking for a ViewContainerRef on an element will result in a creation of a separate anchor // node (comment in the DOM) that will be different from the LContainer's host node. In this // particular case we need to execute action on 2 nodes: // - container's host node (this is done in the executeActionOnElementOrContainer) // - container's host node (this is done here) if (anchor !== native) { // This is very strange to me (Misko). I would expect that the native is same as anchor. I // don't see a reason why they should be different, but they are. // // If they are we need to process the second anchor as well. applyToElementOrContainer(action, renderer, parentRElement, anchor, beforeNode); } for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) { const lView = lContainer[i]; applyView(lView[TVIEW], lView, renderer, action, parentRElement, anchor); } } /** * Writes class/style to element. * * @param renderer Renderer to use. * @param isClassBased `true` if it should be written to `class` (`false` to write to `style`) * @param rNode The Node to write to. * @param prop Property to write to. This would be the class/style name. * @param value Value to write. If `null`/`undefined`/`false` this is considered a remove (set/add * otherwise). */ function applyStyling(renderer, isClassBased, rNode, prop, value) { if (isClassBased) { // We actually want JS true/false here because any truthy value should add the class if (!value) { ngDevMode && ngDevMode.rendererRemoveClass++; renderer.removeClass(rNode, prop); } else { ngDevMode && ngDevMode.rendererAddClass++; renderer.addClass(rNode, prop); } } else { let flags = prop.indexOf('-') === -1 ? undefined : RendererStyleFlags2.DashCase; if (value == null /** || value === undefined */) { ngDevMode && ngDevMode.rendererRemoveStyle++; renderer.removeStyle(rNode, prop, flags); } else { // A value is important if it ends with `!important`. The style // parser strips any semicolons at the end of the value. const isImportant = typeof value === 'string' ? value.endsWith('!important') : false; if (isImportant) { // !important has to be stripped from the value for it to be valid. value = value.slice(0, -10); flags |= RendererStyleFlags2.Important; } ngDevMode && ngDevMode.rendererSetStyle++; renderer.setStyle(rNode, prop, value, flags); } } } /** * Write `cssText` to `RElement`. * * This function does direct write without any reconciliation. Used for writing initial values, so * that static styling values do not pull in the style parser. * * @param renderer Renderer to use * @param element The element which needs to be updated. * @param newValue The new class list to write. */ function writeDirectStyle(renderer, element, newValue) { ngDevMode && assertString(newValue, '\'newValue\' should be a string'); renderer.setAttribute(element, 'style', newValue); ngDevMode && ngDevMode.rendererSetStyle++; } /** * Write `className` to `RElement`. * * This function does direct write without any reconciliation. Used for writing initial values, so * that static styling values do not pull in the style parser. * * @param renderer Renderer to use * @param element The element which needs to be updated. * @param newValue The new class list to write. */ function writeDirectClass(renderer, element, newValue) { ngDevMode && assertString(newValue, '\'newValue\' should be a string'); if (newValue === '') { // There are tests in `google3` which expect `element.getAttribute('class')` to be `null`. renderer.removeAttribute(element, 'class'); } else { renderer.setAttribute(element, 'class', newValue); } ngDevMode && ngDevMode.rendererSetClassName++; } /** Sets up the static DOM attributes on an `RNode`. */ function setupStaticAttributes(renderer, element, tNode) { const { mergedAttrs, classes, styles } = tNode; if (mergedAttrs !== null) { setUpAttributes(renderer, element, mergedAttrs); } if (classes !== null) { writeDirectClass(renderer, element, classes); } if (styles !== null) { writeDirectStyle(renderer, element, styles); } } /** * @fileoverview * A module to facilitate use of a Trusted Types policy internally within * Angular. It lazily constructs the Trusted Types policy, providing helper * utilities for promoting strings to Trusted Types. When Trusted Types are not * available, strings are used as a fallback. * @security All use of this module is security-sensitive and should go through * security review. */ /** * The Trusted Types policy, or null if Trusted Types are not * enabled/supported, or undefined if the policy has not been created yet. */ let policy$1; /** * Returns the Trusted Types policy, or null if Trusted Types are not * enabled/supported. The first call to this function will create the policy. */ function getPolicy$1() { if (policy$1 === undefined) { policy$1 = null; if (_global.trustedTypes) { try { policy$1 = _global.trustedTypes.createPolicy('angular', { createHTML: s => s, createScript: s => s, createScriptURL: s => s }); } catch { // trustedTypes.createPolicy throws if called with a name that is // already registered, even in report-only mode. Until the API changes, // catch the error not to break the applications functionally. In such // cases, the code will fall back to using strings. } } } return policy$1; } /** * Unsafely promote a string to a TrustedHTML, falling back to strings when * Trusted Types are not available. * @security This is a security-sensitive function; any use of this function * must go through security review. In particular, it must be assured that the * provided string will never cause an XSS vulnerability if used in a context * that will be interpreted as HTML by a browser, e.g. when assigning to * element.innerHTML. */ function trustedHTMLFromString(html) { return getPolicy$1()?.createHTML(html) || html; } /** * Unsafely promote a string to a TrustedScript, falling back to strings when * Trusted Types are not available. * @security In particular, it must be assured that the provided string will * never cause an XSS vulnerability if used in a context that will be * interpreted and executed as a script by a browser, e.g. when calling eval. */ function trustedScriptFromString(script) { return getPolicy$1()?.createScript(script) || script; } /** * Unsafely promote a string to a TrustedScriptURL, falling back to strings * when Trusted Types are not available. * @security This is a security-sensitive function; any use of this function * must go through security review. In particular, it must be assured that the * provided string will never cause an XSS vulnerability if used in a context * that will cause a browser to load and execute a resource, e.g. when * assigning to script.src. */ function trustedScriptURLFromString(url) { return getPolicy$1()?.createScriptURL(url) || url; } /** * Unsafely call the Function constructor with the given string arguments. It * is only available in development mode, and should be stripped out of * production code. * @security This is a security-sensitive function; any use of this function * must go through security review. In particular, it must be assured that it * is only called from development code, as use in production code can lead to * XSS vulnerabilities. */ function newTrustedFunctionForDev(...args) { if (typeof ngDevMode === 'undefined') { throw new Error('newTrustedFunctionForDev should never be called in production'); } if (!_global.trustedTypes) { // In environments that don't support Trusted Types, fall back to the most // straightforward implementation: return new Function(...args); } // Chrome currently does not support passing TrustedScript to the Function // constructor. The following implements the workaround proposed on the page // below, where the Chromium bug is also referenced: // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor const fnArgs = args.slice(0, -1).join(','); const fnBody = args[args.length - 1]; const body = `(function anonymous(${fnArgs} ) { ${fnBody} })`; // Using eval directly confuses the compiler and prevents this module from // being stripped out of JS binaries even if not used. The global['eval'] // indirection fixes that. const fn = _global['eval'](trustedScriptFromString(body)); if (fn.bind === undefined) { // Workaround for a browser bug that only exists in Chrome 83, where passing // a TrustedScript to eval just returns the TrustedScript back without // evaluating it. In that case, fall back to the most straightforward // implementation: return new Function(...args); } // To completely mimic the behavior of calling "new Function", two more // things need to happen: // 1. Stringifying the resulting function should return its source code fn.toString = () => body; // 2. When calling the resulting function, `this` should refer to `global` return fn.bind(_global); // When Trusted Types support in Function constructors is widely available, // the implementation of this function can be simplified to: // return new Function(...args.map(a => trustedScriptFromString(a))); } /** * Validation function invoked at runtime for each binding that might potentially * represent a security-sensitive attribute of an <iframe>. * See `IFRAME_SECURITY_SENSITIVE_ATTRS` in the * `packages/compiler/src/schema/dom_security_schema.ts` script for the full list * of such attributes. * * @codeGenApi */ function ɵɵvalidateIframeAttribute(attrValue, tagName, attrName) { const lView = getLView(); const tNode = getSelectedTNode(); const element = getNativeByTNode(tNode, lView); // Restrict any dynamic bindings of security-sensitive attributes/properties // on an <iframe> for security reasons. if (tNode.type === 2 /* TNodeType.Element */ && tagName.toLowerCase() === 'iframe') { const iframe = element; // Unset previously applied `src` and `srcdoc` if we come across a situation when // a security-sensitive attribute is set later via an attribute/property binding. iframe.src = ''; iframe.srcdoc = trustedHTMLFromString(''); // Also remove the <iframe> from the document. nativeRemoveNode(lView[RENDERER], iframe); const errorMessage = ngDevMode && `Angular has detected that the \`${attrName}\` was applied ` + `as a binding to an <iframe>${getTemplateLocationDetails(lView)}. ` + `For security reasons, the \`${attrName}\` can be set on an <iframe> ` + `as a static attribute only. \n` + `To fix this, switch the \`${attrName}\` binding to a static attribute ` + `in a template or in host bindings section.`; throw new RuntimeError(-910 /* RuntimeErrorCode.UNSAFE_IFRAME_ATTRS */, errorMessage); } return attrValue; } /** * Most of the use of `document` in Angular is from within the DI system so it is possible to simply * inject the `DOCUMENT` token and are done. * * Ivy is special because it does not rely upon the DI and must get hold of the document some other * way. * * The solution is to define `getDocument()` and `setDocument()` top-level functions for ivy. * Wherever ivy needs the global document, it calls `getDocument()` instead. * * When running ivy outside of a browser environment, it is necessary to call `setDocument()` to * tell ivy what the global `document` is. * * Angular does this for us in each of the standard platforms (`Browser`, `Server`, and `WebWorker`) * by calling `setDocument()` when providing the `DOCUMENT` token. */ let DOCUMENT = undefined; /** * Tell ivy what the `document` is for this platform. * * It is only necessary to call this if the current platform is not a browser. * * @param document The object representing the global `document` in this environment. */ function setDocument(document) { DOCUMENT = document; } /** * Access the object that represents the `document` for this platform. * * Ivy calls this whenever it needs to access the `document` object. * For example to create the renderer or to do sanitization. */ function getDocument() { if (DOCUMENT !== undefined) { return DOCUMENT; } else if (typeof document !== 'undefined') { return document; } // No "document" can be found. This should only happen if we are running ivy outside Angular and // the current platform is not a browser. Since this is not a supported scenario at the moment // this should not happen in Angular apps. // Once we support running ivy outside of Angular we will need to publish `setDocument()` as a // public API. Meanwhile we just return `undefined` and let the application fail. return undefined; } /** * @fileoverview * A module to facilitate use of a Trusted Types policy internally within * Angular specifically for bypassSecurityTrust* and custom sanitizers. It * lazily constructs the Trusted Types policy, providing helper utilities for * promoting strings to Trusted Types. When Trusted Types are not available, * strings are used as a fallback. * @security All use of this module is security-sensitive and should go through * security review. */ /** * The Trusted Types policy, or null if Trusted Types are not * enabled/supported, or undefined if the policy has not been created yet. */ let policy; /** * Returns the Trusted Types policy, or null if Trusted Types are not * enabled/supported. The first call to this function will create the policy. */ function getPolicy() { if (policy === undefined) { policy = null; if (_global.trustedTypes) { try { policy = _global.trustedTypes.createPolicy('angular#unsafe-bypass', { createHTML: s => s, createScript: s => s, createScriptURL: s => s }); } catch { // trustedTypes.createPolicy throws if called with a name that is // already registered, even in report-only mode. Until the API changes, // catch the error not to break the applications functionally. In such // cases, the code will fall back to using strings. } } } return policy; } /** * Unsafely promote a string to a TrustedHTML, falling back to strings when * Trusted Types are not available. * @security This is a security-sensitive function; any use of this function * must go through security review. In particular, it must be assured that it * is only passed strings that come directly from custom sanitizers or the * bypassSecurityTrust* functions. */ function trustedHTMLFromStringBypass(html) { return getPolicy()?.createHTML(html) || html; } /** * Unsafely promote a string to a TrustedScript, falling back to strings when * Trusted Types are not available. * @security This is a security-sensitive function; any use of this function * must go through security review. In particular, it must be assured that it * is only passed strings that come directly from custom sanitizers or the * bypassSecurityTrust* functions. */ function trustedScriptFromStringBypass(script) { return getPolicy()?.createScript(script) || script; } /** * Unsafely promote a string to a TrustedScriptURL, falling back to strings * when Trusted Types are not available. * @security This is a security-sensitive function; any use of this function * must go through security review. In particular, it must be assured that it * is only passed strings that come directly from custom sanitizers or the * bypassSecurityTrust* functions. */ function trustedScriptURLFromStringBypass(url) { return getPolicy()?.createScriptURL(url) || url; } class SafeValueImpl { constructor(changingThisBreaksApplicationSecurity) { this.changingThisBreaksApplicationSecurity = changingThisBreaksApplicationSecurity; } toString() { return `SafeValue must use [property]=binding: ${this.changingThisBreaksApplicationSecurity}` + ` (see ${XSS_SECURITY_URL})`; } } class SafeHtmlImpl extends SafeValueImpl { getTypeName() { return "HTML" /* BypassType.Html */; } } class SafeStyleImpl extends SafeValueImpl { getTypeName() { return "Style" /* BypassType.Style */; } } class SafeScriptImpl extends SafeValueImpl { getTypeName() { return "Script" /* BypassType.Script */; } } class SafeUrlImpl extends SafeValueImpl { getTypeName() { return "URL" /* BypassType.Url */; } } class SafeResourceUrlImpl extends SafeValueImpl { getTypeName() { return "ResourceURL" /* BypassType.ResourceUrl */; } } function unwrapSafeValue(value) { return value instanceof SafeValueImpl ? value.changingThisBreaksApplicationSecurity : value; } function allowSanitizationBypassAndThrow(value, type) { const actualType = getSanitizationBypassType(value); if (actualType != null && actualType !== type) { // Allow ResourceURLs in URL contexts, they are strictly more trusted. if (actualType === "ResourceURL" /* BypassType.ResourceUrl */ && type === "URL" /* BypassType.Url */) return true; throw new Error(`Required a safe ${type}, got a ${actualType} (see ${XSS_SECURITY_URL})`); } return actualType === type; } function getSanitizationBypassType(value) { return value instanceof SafeValueImpl && value.getTypeName() || null; } /** * Mark `html` string as trusted. * * This function wraps the trusted string in `String` and brands it in a way which makes it * recognizable to {@link htmlSanitizer} to be trusted implicitly. * * @param trustedHtml `html` string which needs to be implicitly trusted. * @returns a `html` which has been branded to be implicitly trusted. */ function bypassSanitizationTrustHtml(trustedHtml) { return new SafeHtmlImpl(trustedHtml); } /** * Mark `style` string as trusted. * * This function wraps the trusted string in `String` and brands it in a way which makes it * recognizable to {@link styleSanitizer} to be trusted implicitly. * * @param trustedStyle `style` string which needs to be implicitly trusted. * @returns a `style` hich has been branded to be implicitly trusted. */ function bypassSanitizationTrustStyle(trustedStyle) { return new SafeStyleImpl(trustedStyle); } /** * Mark `script` string as trusted. * * This function wraps the trusted string in `String` and brands it in a way which makes it * recognizable to {@link scriptSanitizer} to be trusted implicitly. * * @param trustedScript `script` string which needs to be implicitly trusted. * @returns a `script` which has been branded to be implicitly trusted. */ function bypassSanitizationTrustScript(trustedScript) { return new SafeScriptImpl(trustedScript); } /** * Mark `url` string as trusted. * * This function wraps the trusted string in `String` and brands it in a way which makes it * recognizable to {@link urlSanitizer} to be trusted implicitly. * * @param trustedUrl `url` string which needs to be implicitly trusted. * @returns a `url` which has been branded to be implicitly trusted. */ function bypassSanitizationTrustUrl(trustedUrl) { return new SafeUrlImpl(trustedUrl); } /** * Mark `url` string as trusted. * * This function wraps the trusted string in `String` and brands it in a way which makes it * recognizable to {@link resourceUrlSanitizer} to be trusted implicitly. * * @param trustedResourceUrl `url` string which needs to be implicitly trusted. * @returns a `url` which has been branded to be implicitly trusted. */ function bypassSanitizationTrustResourceUrl(trustedResourceUrl) { return new SafeResourceUrlImpl(trustedResourceUrl); } /** * This helper is used to get hold of an inert tree of DOM elements containing dirty HTML * that needs sanitizing. * Depending upon browser support we use one of two strategies for doing this. * Default: DOMParser strategy * Fallback: InertDocument strategy */ function getInertBodyHelper(defaultDoc) { const inertDocumentHelper = new InertDocumentHelper(defaultDoc); return isDOMParserAvailable() ? new DOMParserHelper(inertDocumentHelper) : inertDocumentHelper; } /** * Uses DOMParser to create and fill an inert body element. * This is the default strategy used in browsers that support it. */ class DOMParserHelper { constructor(inertDocumentHelper) { this.inertDocumentHelper = inertDocumentHelper; } getInertBodyElement(html) { // We add these extra elements to ensure that the rest of the content is parsed as expected // e.g. leading whitespace is maintained and tags like `<meta>` do not get hoisted to the // `<head>` tag. Note that the `<body>` tag is closed implicitly to prevent unclosed tags // in `html` from consuming the otherwise explicit `</body>` tag. html = '<body><remove></remove>' + html; try { const body = new window.DOMParser().parseFromString(trustedHTMLFromString(html), 'text/html').body; if (body === null) { // In some browsers (e.g. Mozilla/5.0 iPad AppleWebKit Mobile) the `body` property only // becomes available in the following tick of the JS engine. In that case we fall back to // the `inertDocumentHelper` instead. return this.inertDocumentHelper.getInertBodyElement(html); } body.removeChild(body.firstChild); return body; } catch { return null; } } } /** * Use an HTML5 `template` element to create and fill an inert DOM element. * This is the fallback strategy if the browser does not support DOMParser. */ class InertDocumentHelper { constructor(defaultDoc) { this.defaultDoc = defaultDoc; this.inertDocument = this.defaultDoc.implementation.createHTMLDocument('sanitization-inert'); } getInertBodyElement(html) { const templateEl = this.inertDocument.createElement('template'); templateEl.innerHTML = trustedHTMLFromString(html); return templateEl; } } /** * We need to determine whether the DOMParser exists in the global context and * supports parsing HTML; HTML parsing support is not as wide as other formats, see * https://developer.mozilla.org/en-US/docs/Web/API/DOMParser#Browser_compatibility. * * @suppress {uselessCode} */ function isDOMParserAvailable() { try { return !!new window.DOMParser().parseFromString(trustedHTMLFromString(''), 'text/html'); } catch { return false; } } /** * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation * contexts. * * This regular expression matches a subset of URLs that will not cause script * execution if used in URL context within a HTML document. Specifically, this * regular expression matches if: * (1) Either a protocol that is not javascript:, and that has valid characters * (alphanumeric or [+-.]). * (2) or no protocol. A protocol must be followed by a colon. The below * allows that by allowing colons only after one of the characters [/?#]. * A colon after a hash (#) must be in the fragment. * Otherwise, a colon after a (?) must be in a query. * Otherwise, a colon after a single solidus (/) must be in a path. * Otherwise, a colon after a double solidus (//) must be in the authority * (before port). * * The pattern disallows &, used in HTML entity declarations before * one of the characters in [/?#]. This disallows HTML entities used in the * protocol name, which should never happen, e.g. "http" for "http". * It also disallows HTML entities in the first path part of a relative path, * e.g. "foo<bar/baz". Our existing escaping functions should not produce * that. More importantly, it disallows masking of a colon, * e.g. "javascript:...". * * This regular expression was taken from the Closure sanitization library. */ const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:\/?#]*(?:[\/?#]|$))/i; function _sanitizeUrl(url) { url = String(url); if (url.match(SAFE_URL_PATTERN)) return url; if (typeof ngDevMode === 'undefined' || ngDevMode) { console.warn(`WARNING: sanitizing unsafe URL value ${url} (see ${XSS_SECURITY_URL})`); } return 'unsafe:' + url; } function tagSet(tags) { const res = {}; for (const t of tags.split(',')) res[t] = true; return res; } function merge(...sets) { const res = {}; for (const s of sets) { for (const v in s) { if (s.hasOwnProperty(v)) res[v] = true; } } return res; } // Good source of info about elements and attributes // https://html.spec.whatwg.org/#semantics // https://simon.html5.org/html-elements // Safe Void Elements - HTML5 // https://html.spec.whatwg.org/#void-elements const VOID_ELEMENTS = tagSet('area,br,col,hr,img,wbr'); // Elements that you can, intentionally, leave open (and which close themselves) // https://html.spec.whatwg.org/#optional-tags const OPTIONAL_END_TAG_BLOCK_ELEMENTS = tagSet('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr'); const OPTIONAL_END_TAG_INLINE_ELEMENTS = tagSet('rp,rt'); const OPTIONAL_END_TAG_ELEMENTS = merge(OPTIONAL_END_TAG_INLINE_ELEMENTS, OPTIONAL_END_TAG_BLOCK_ELEMENTS); // Safe Block Elements - HTML5 const BLOCK_ELEMENTS = merge(OPTIONAL_END_TAG_BLOCK_ELEMENTS, tagSet('address,article,' + 'aside,blockquote,caption,center,del,details,dialog,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,' + 'h6,header,hgroup,hr,ins,main,map,menu,nav,ol,pre,section,summary,table,ul')); // Inline Elements - HTML5 const INLINE_ELEMENTS = merge(OPTIONAL_END_TAG_INLINE_ELEMENTS, tagSet('a,abbr,acronym,audio,b,' + 'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,picture,q,ruby,rp,rt,s,' + 'samp,small,source,span,strike,strong,sub,sup,time,track,tt,u,var,video')); const VALID_ELEMENTS = merge(VOID_ELEMENTS, BLOCK_ELEMENTS, INLINE_ELEMENTS, OPTIONAL_END_TAG_ELEMENTS); // Attributes that have href and hence need to be sanitized const URI_ATTRS = tagSet('background,cite,href,itemtype,longdesc,poster,src,xlink:href'); const HTML_ATTRS = tagSet('abbr,accesskey,align,alt,autoplay,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,' + 'compact,controls,coords,datetime,default,dir,download,face,headers,height,hidden,hreflang,hspace,' + 'ismap,itemscope,itemprop,kind,label,lang,language,loop,media,muted,nohref,nowrap,open,preload,rel,rev,role,rows,rowspan,rules,' + 'scope,scrolling,shape,size,sizes,span,srclang,srcset,start,summary,tabindex,target,title,translate,type,usemap,' + 'valign,value,vspace,width'); // Accessibility attributes as per WAI-ARIA 1.1 (W3C Working Draft 14 December 2018) const ARIA_ATTRS = tagSet('aria-activedescendant,aria-atomic,aria-autocomplete,aria-busy,aria-checked,aria-colcount,aria-colindex,' + 'aria-colspan,aria-controls,aria-current,aria-describedby,aria-details,aria-disabled,aria-dropeffect,' + 'aria-errormessage,aria-expanded,aria-flowto,aria-grabbed,aria-haspopup,aria-hidden,aria-invalid,' + 'aria-keyshortcuts,aria-label,aria-labelledby,aria-level,aria-live,aria-modal,aria-multiline,' + 'aria-multiselectable,aria-orientation,aria-owns,aria-placeholder,aria-posinset,aria-pressed,aria-readonly,' + 'aria-relevant,aria-required,aria-roledescription,aria-rowcount,aria-rowindex,aria-rowspan,aria-selected,' + 'aria-setsize,aria-sort,aria-valuemax,aria-valuemin,aria-valuenow,aria-valuetext'); // NB: This currently consciously doesn't support SVG. SVG sanitization has had several security // issues in the past, so it seems safer to leave it out if possible. If support for binding SVG via // innerHTML is required, SVG attributes should be added here. // NB: Sanitization does not allow <form> elements or other active elements (<button> etc). Those // can be sanitized, but they increase security surface area without a legitimate use case, so they // are left out here. const VALID_ATTRS = merge(URI_ATTRS, HTML_ATTRS, ARIA_ATTRS); // Elements whose content should not be traversed/preserved, if the elements themselves are invalid. // // Typically, `<invalid>Some content</invalid>` would traverse (and in this case preserve) // `Some content`, but strip `invalid-element` opening/closing tags. For some elements, though, we // don't want to preserve the content, if the elements themselves are going to be removed. const SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS = tagSet('script,style,template'); /** * SanitizingHtmlSerializer serializes a DOM fragment, stripping out any unsafe elements and unsafe * attributes. */ class SanitizingHtmlSerializer { constructor() { // Explicitly track if something was stripped, to avoid accidentally warning of sanitization just // because characters were re-encoded. this.sanitizedSomething = false; this.buf = []; } sanitizeChildren(el) { // This cannot use a TreeWalker, as it has to run on Angular's various DOM adapters. // However this code never accesses properties off of `document` before deleting its contents // again, so it shouldn't be vulnerable to DOM clobbering. let current = el.firstChild; let traverseContent = true; while (current) { if (current.nodeType === Node.ELEMENT_NODE) { traverseContent = this.startElement(current); } else if (current.nodeType === Node.TEXT_NODE) { this.chars(current.nodeValue); } else { // Strip non-element, non-text nodes. this.sanitizedSomething = true; } if (traverseContent && current.firstChild) { current = current.firstChild; continue; } while (current) { // Leaving the element. Walk up and to the right, closing tags as we go. if (current.nodeType === Node.ELEMENT_NODE) { this.endElement(current); } let next = this.checkClobberedElement(current, current.nextSibling); if (next) { current = next; break; } current = this.checkClobberedElement(current, current.parentNode); } } return this.buf.join(''); } /** * Sanitizes an opening element tag (if valid) and returns whether the element's contents should * be traversed. Element content must always be traversed (even if the element itself is not * valid/safe), unless the element is one of `SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS`. * * @param element The element to sanitize. * @return True if the element's contents should be traversed. */ startElement(element) { const tagName = element.nodeName.toLowerCase(); if (!VALID_ELEMENTS.hasOwnProperty(tagName)) { this.sanitizedSomething = true; return !SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS.hasOwnProperty(tagName); } this.buf.push('<'); this.buf.push(tagName); const elAttrs = element.attributes; for (let i = 0; i < elAttrs.length; i++) { const elAttr = elAttrs.item(i); const attrName = elAttr.name; const lower = attrName.toLowerCase(); if (!VALID_ATTRS.hasOwnProperty(lower)) { this.sanitizedSomething = true; continue; } let value = elAttr.value; // TODO(martinprobst): Special case image URIs for data:image/... if (URI_ATTRS[lower]) value = _sanitizeUrl(value); this.buf.push(' ', attrName, '="', encodeEntities(value), '"'); } this.buf.push('>'); return true; } endElement(current) { const tagName = current.nodeName.toLowerCase(); if (VALID_ELEMENTS.hasOwnProperty(tagName) && !VOID_ELEMENTS.hasOwnProperty(tagName)) { this.buf.push('</'); this.buf.push(tagName); this.buf.push('>'); } } chars(chars) { this.buf.push(encodeEntities(chars)); } checkClobberedElement(node, nextNode) { if (nextNode && (node.compareDocumentPosition(nextNode) & Node.DOCUMENT_POSITION_CONTAINED_BY) === Node.DOCUMENT_POSITION_CONTAINED_BY) { throw new Error(`Failed to sanitize html because the element is clobbered: ${node.outerHTML}`); } return nextNode; } } // Regular Expressions for parsing tags and attributes const SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g; // ! to ~ is the ASCII range. const NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g; /** * Escapes all potentially dangerous characters, so that the * resulting string can be safely inserted into attribute or * element text. * @param value */ function encodeEntities(value) { return value.replace(/&/g, '&').replace(SURROGATE_PAIR_REGEXP, function (match) { const hi = match.charCodeAt(0); const low = match.charCodeAt(1); return '&#' + ((hi - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000) + ';'; }).replace(NON_ALPHANUMERIC_REGEXP, function (match) { return '&#' + match.charCodeAt(0) + ';'; }).replace(/</g, '<').replace(/>/g, '>'); } let inertBodyHelper; /** * Sanitizes the given unsafe, untrusted HTML fragment, and returns HTML text that is safe to add to * the DOM in a browser environment. */ function _sanitizeHtml(defaultDoc, unsafeHtmlInput) { let inertBodyElement = null; try { inertBodyHelper = inertBodyHelper || getInertBodyHelper(defaultDoc); // Make sure unsafeHtml is actually a string (TypeScript types are not enforced at runtime). let unsafeHtml = unsafeHtmlInput ? String(unsafeHtmlInput) : ''; inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeHtml); // mXSS protection. Repeatedly parse the document to make sure it stabilizes, so that a browser // trying to auto-correct incorrect HTML cannot cause formerly inert HTML to become dangerous. let mXSSAttempts = 5; let parsedHtml = unsafeHtml; do { if (mXSSAttempts === 0) { throw new Error('Failed to sanitize html because the input is unstable'); } mXSSAttempts--; unsafeHtml = parsedHtml; parsedHtml = inertBodyElement.innerHTML; inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeHtml); } while (unsafeHtml !== parsedHtml); const sanitizer = new SanitizingHtmlSerializer(); const safeHtml = sanitizer.sanitizeChildren(getTemplateContent(inertBodyElement) || inertBodyElement); if ((typeof ngDevMode === 'undefined' || ngDevMode) && sanitizer.sanitizedSomething) { console.warn(`WARNING: sanitizing HTML stripped some content, see ${XSS_SECURITY_URL}`); } return trustedHTMLFromString(safeHtml); } finally { // In case anything goes wrong, clear out inertElement to reset the entire DOM structure. if (inertBodyElement) { const parent = getTemplateContent(inertBodyElement) || inertBodyElement; while (parent.firstChild) { parent.removeChild(parent.firstChild); } } } } function getTemplateContent(el) { return 'content' in el /** Microsoft/TypeScript#21517 */ && isTemplateElement(el) ? el.content : null; } function isTemplateElement(el) { return el.nodeType === Node.ELEMENT_NODE && el.nodeName === 'TEMPLATE'; } /** * A SecurityContext marks a location that has dangerous security implications, e.g. a DOM property * like `innerHTML` that could cause Cross Site Scripting (XSS) security bugs when improperly * handled. * * See DomSanitizer for more details on security in Angular applications. * * @publicApi */ var SecurityContext; (function (SecurityContext) { SecurityContext[SecurityContext["NONE"] = 0] = "NONE"; SecurityContext[SecurityContext["HTML"] = 1] = "HTML"; SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE"; SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT"; SecurityContext[SecurityContext["URL"] = 4] = "URL"; SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL"; })(SecurityContext || (SecurityContext = {})); /** * An `html` sanitizer which converts untrusted `html` **string** into trusted string by removing * dangerous content. * * This method parses the `html` and locates potentially dangerous content (such as urls and * javascript) and removes it. * * It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustHtml}. * * @param unsafeHtml untrusted `html`, typically from the user. * @returns `html` string which is safe to display to user, because all of the dangerous javascript * and urls have been removed. * * @codeGenApi */ function ɵɵsanitizeHtml(unsafeHtml) { const sanitizer = getSanitizer(); if (sanitizer) { return trustedHTMLFromStringBypass(sanitizer.sanitize(SecurityContext.HTML, unsafeHtml) || ''); } if (allowSanitizationBypassAndThrow(unsafeHtml, "HTML" /* BypassType.Html */)) { return trustedHTMLFromStringBypass(unwrapSafeValue(unsafeHtml)); } return _sanitizeHtml(getDocument(), renderStringify(unsafeHtml)); } /** * A `style` sanitizer which converts untrusted `style` **string** into trusted string by removing * dangerous content. * * It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustStyle}. * * @param unsafeStyle untrusted `style`, typically from the user. * @returns `style` string which is safe to bind to the `style` properties. * * @codeGenApi */ function ɵɵsanitizeStyle(unsafeStyle) { const sanitizer = getSanitizer(); if (sanitizer) { return sanitizer.sanitize(SecurityContext.STYLE, unsafeStyle) || ''; } if (allowSanitizationBypassAndThrow(unsafeStyle, "Style" /* BypassType.Style */)) { return unwrapSafeValue(unsafeStyle); } return renderStringify(unsafeStyle); } /** * A `url` sanitizer which converts untrusted `url` **string** into trusted string by removing * dangerous * content. * * This method parses the `url` and locates potentially dangerous content (such as javascript) and * removes it. * * It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustUrl}. * * @param unsafeUrl untrusted `url`, typically from the user. * @returns `url` string which is safe to bind to the `src` properties such as `<img src>`, because * all of the dangerous javascript has been removed. * * @codeGenApi */ function ɵɵsanitizeUrl(unsafeUrl) { const sanitizer = getSanitizer(); if (sanitizer) { return sanitizer.sanitize(SecurityContext.URL, unsafeUrl) || ''; } if (allowSanitizationBypassAndThrow(unsafeUrl, "URL" /* BypassType.Url */)) { return unwrapSafeValue(unsafeUrl); } return _sanitizeUrl(renderStringify(unsafeUrl)); } /** * A `url` sanitizer which only lets trusted `url`s through. * * This passes only `url`s marked trusted by calling {@link bypassSanitizationTrustResourceUrl}. * * @param unsafeResourceUrl untrusted `url`, typically from the user. * @returns `url` string which is safe to bind to the `src` properties such as `<img src>`, because * only trusted `url`s have been allowed to pass. * * @codeGenApi */ function ɵɵsanitizeResourceUrl(unsafeResourceUrl) { const sanitizer = getSanitizer(); if (sanitizer) { return trustedScriptURLFromStringBypass(sanitizer.sanitize(SecurityContext.RESOURCE_URL, unsafeResourceUrl) || ''); } if (allowSanitizationBypassAndThrow(unsafeResourceUrl, "ResourceURL" /* BypassType.ResourceUrl */)) { return trustedScriptURLFromStringBypass(unwrapSafeValue(unsafeResourceUrl)); } throw new RuntimeError(904 /* RuntimeErrorCode.UNSAFE_VALUE_IN_RESOURCE_URL */, ngDevMode && `unsafe value used in a resource URL context (see ${XSS_SECURITY_URL})`); } /** * A `script` sanitizer which only lets trusted javascript through. * * This passes only `script`s marked trusted by calling {@link * bypassSanitizationTrustScript}. * * @param unsafeScript untrusted `script`, typically from the user. * @returns `url` string which is safe to bind to the `<script>` element such as `<img src>`, * because only trusted `scripts` have been allowed to pass. * * @codeGenApi */ function ɵɵsanitizeScript(unsafeScript) { const sanitizer = getSanitizer(); if (sanitizer) { return trustedScriptFromStringBypass(sanitizer.sanitize(SecurityContext.SCRIPT, unsafeScript) || ''); } if (allowSanitizationBypassAndThrow(unsafeScript, "Script" /* BypassType.Script */)) { return trustedScriptFromStringBypass(unwrapSafeValue(unsafeScript)); } throw new RuntimeError(905 /* RuntimeErrorCode.UNSAFE_VALUE_IN_SCRIPT */, ngDevMode && 'unsafe value used in a script context'); } /** * A template tag function for promoting the associated constant literal to a * TrustedHTML. Interpolation is explicitly not allowed. * * @param html constant template literal containing trusted HTML. * @returns TrustedHTML wrapping `html`. * * @security This is a security-sensitive function and should only be used to * convert constant values of attributes and properties found in * application-provided Angular templates to TrustedHTML. * * @codeGenApi */ function ɵɵtrustConstantHtml(html) { // The following runtime check ensures that the function was called as a // template tag (e.g. ɵɵtrustConstantHtml`content`), without any interpolation // (e.g. not ɵɵtrustConstantHtml`content ${variable}`). A TemplateStringsArray // is an array with a `raw` property that is also an array. The associated // template literal has no interpolation if and only if the length of the // TemplateStringsArray is 1. if (ngDevMode && (!Array.isArray(html) || !Array.isArray(html.raw) || html.length !== 1)) { throw new Error(`Unexpected interpolation in trusted HTML constant: ${html.join('?')}`); } return trustedHTMLFromString(html[0]); } /** * A template tag function for promoting the associated constant literal to a * TrustedScriptURL. Interpolation is explicitly not allowed. * * @param url constant template literal containing a trusted script URL. * @returns TrustedScriptURL wrapping `url`. * * @security This is a security-sensitive function and should only be used to * convert constant values of attributes and properties found in * application-provided Angular templates to TrustedScriptURL. * * @codeGenApi */ function ɵɵtrustConstantResourceUrl(url) { // The following runtime check ensures that the function was called as a // template tag (e.g. ɵɵtrustConstantResourceUrl`content`), without any // interpolation (e.g. not ɵɵtrustConstantResourceUrl`content ${variable}`). A // TemplateStringsArray is an array with a `raw` property that is also an // array. The associated template literal has no interpolation if and only if // the length of the TemplateStringsArray is 1. if (ngDevMode && (!Array.isArray(url) || !Array.isArray(url.raw) || url.length !== 1)) { throw new Error(`Unexpected interpolation in trusted URL constant: ${url.join('?')}`); } return trustedScriptURLFromString(url[0]); } /** * Detects which sanitizer to use for URL property, based on tag name and prop name. * * The rules are based on the RESOURCE_URL context config from * `packages/compiler/src/schema/dom_security_schema.ts`. * If tag and prop names don't match Resource URL schema, use URL sanitizer. */ function getUrlSanitizer(tag, prop) { if (prop === 'src' && (tag === 'embed' || tag === 'frame' || tag === 'iframe' || tag === 'media' || tag === 'script') || prop === 'href' && (tag === 'base' || tag === 'link')) { return ɵɵsanitizeResourceUrl; } return ɵɵsanitizeUrl; } /** * Sanitizes URL, selecting sanitizer function based on tag and property names. * * This function is used in case we can't define security context at compile time, when only prop * name is available. This happens when we generate host bindings for Directives/Components. The * host element is unknown at compile time, so we defer calculation of specific sanitizer to * runtime. * * @param unsafeUrl untrusted `url`, typically from the user. * @param tag target element tag name. * @param prop name of the property that contains the value. * @returns `url` string which is safe to bind. * * @codeGenApi */ function ɵɵsanitizeUrlOrResourceUrl(unsafeUrl, tag, prop) { return getUrlSanitizer(tag, prop)(unsafeUrl); } function validateAgainstEventProperties(name) { if (name.toLowerCase().startsWith('on')) { const errorMessage = `Binding to event property '${name}' is disallowed for security reasons, ` + `please use (${name.slice(2)})=...` + `\nIf '${name}' is a directive input, make sure the directive is imported by the` + ` current module.`; throw new RuntimeError(306 /* RuntimeErrorCode.INVALID_EVENT_BINDING */, errorMessage); } } function validateAgainstEventAttributes(name) { if (name.toLowerCase().startsWith('on')) { const errorMessage = `Binding to event attribute '${name}' is disallowed for security reasons, ` + `please use (${name.slice(2)})=...`; throw new RuntimeError(306 /* RuntimeErrorCode.INVALID_EVENT_BINDING */, errorMessage); } } function getSanitizer() { const lView = getLView(); return lView && lView[SANITIZER]; } /** * A multi-provider token for initialization functions that will run upon construction of an * environment injector. * * @publicApi */ const ENVIRONMENT_INITIALIZER = new InjectionToken('ENVIRONMENT_INITIALIZER'); /** * An InjectionToken that gets the current `Injector` for `createInjector()`-style injectors. * * Requesting this token instead of `Injector` allows `StaticInjector` to be tree-shaken from a * project. * * @publicApi */ const INJECTOR = new InjectionToken('INJECTOR', // Disable tslint because this is const enum which gets inlined not top level prop access. // tslint:disable-next-line: no-toplevel-property-access -1 /* InjectorMarkers.Injector */); const INJECTOR_DEF_TYPES = new InjectionToken('INJECTOR_DEF_TYPES'); class NullInjector { get(token, notFoundValue = THROW_IF_NOT_FOUND) { if (notFoundValue === THROW_IF_NOT_FOUND) { const error = new Error(`NullInjectorError: No provider for ${stringify(token)}!`); error.name = 'NullInjectorError'; throw error; } return notFoundValue; } } /** * Wrap an array of `Provider`s into `EnvironmentProviders`, preventing them from being accidentally * referenced in `@Component in a component injector. */ function makeEnvironmentProviders(providers) { return { ɵproviders: providers }; } /** * Collects providers from all NgModules and standalone components, including transitively imported * ones. * * Providers extracted via `importProvidersFrom` are only usable in an application injector or * another environment injector (such as a route injector). They should not be used in component * providers. * * More information about standalone components can be found in [this * guide](guide/standalone-components). * * @usageNotes * The results of the `importProvidersFrom` call can be used in the `bootstrapApplication` call: * * ```typescript * await bootstrapApplication(RootComponent, { * providers: [ * importProvidersFrom(NgModuleOne, NgModuleTwo) * ] * }); * ``` * * You can also use the `importProvidersFrom` results in the `providers` field of a route, when a * standalone component is used: * * ```typescript * export const ROUTES: Route[] = [ * { * path: 'foo', * providers: [ * importProvidersFrom(NgModuleOne, NgModuleTwo) * ], * component: YourStandaloneComponent * } * ]; * ``` * * @returns Collected providers from the specified list of types. * @publicApi */ function importProvidersFrom(...sources) { return { ɵproviders: internalImportProvidersFrom(true, sources), ɵfromNgModule: true }; } function internalImportProvidersFrom(checkForStandaloneCmp, ...sources) { const providersOut = []; const dedup = new Set(); // already seen types let injectorTypesWithProviders; deepForEach(sources, source => { if ((typeof ngDevMode === 'undefined' || ngDevMode) && checkForStandaloneCmp) { const cmpDef = getComponentDef(source); if (cmpDef?.standalone) { throw new RuntimeError(800 /* RuntimeErrorCode.IMPORT_PROVIDERS_FROM_STANDALONE */, `Importing providers supports NgModule or ModuleWithProviders but got a standalone component "${stringifyForError(source)}"`); } } // Narrow `source` to access the internal type analogue for `ModuleWithProviders`. const internalSource = source; if (walkProviderTree(internalSource, providersOut, [], dedup)) { injectorTypesWithProviders || (injectorTypesWithProviders = []); injectorTypesWithProviders.push(internalSource); } }); // Collect all providers from `ModuleWithProviders` types. if (injectorTypesWithProviders !== undefined) { processInjectorTypesWithProviders(injectorTypesWithProviders, providersOut); } return providersOut; } /** * Collects all providers from the list of `ModuleWithProviders` and appends them to the provided * array. */ function processInjectorTypesWithProviders(typesWithProviders, providersOut) { for (let i = 0; i < typesWithProviders.length; i++) { const { ngModule, providers } = typesWithProviders[i]; deepForEachProvider(providers, provider => { ngDevMode && validateProvider(provider, providers || EMPTY_ARRAY, ngModule); providersOut.push(provider); }); } } /** * The logic visits an `InjectorType`, an `InjectorTypeWithProviders`, or a standalone * `ComponentType`, and all of its transitive providers and collects providers. * * If an `InjectorTypeWithProviders` that declares providers besides the type is specified, * the function will return "true" to indicate that the providers of the type definition need * to be processed. This allows us to process providers of injector types after all imports of * an injector definition are processed. (following View Engine semantics: see FW-1349) */ function walkProviderTree(container, providersOut, parents, dedup) { container = resolveForwardRef(container); if (!container) return false; // The actual type which had the definition. Usually `container`, but may be an unwrapped type // from `InjectorTypeWithProviders`. let defType = null; let injDef = getInjectorDef(container); const cmpDef = !injDef && getComponentDef(container); if (!injDef && !cmpDef) { // `container` is not an injector type or a component type. It might be: // * An `InjectorTypeWithProviders` that wraps an injector type. // * A standalone directive or pipe that got pulled in from a standalone component's // dependencies. // Try to unwrap it as an `InjectorTypeWithProviders` first. const ngModule = container.ngModule; injDef = getInjectorDef(ngModule); if (injDef) { defType = ngModule; } else { // Not a component or injector type, so ignore it. return false; } } else if (cmpDef && !cmpDef.standalone) { return false; } else { defType = container; } // Check for circular dependencies. if (ngDevMode && parents.indexOf(defType) !== -1) { const defName = stringify(defType); const path = parents.map(stringify); throwCyclicDependencyError(defName, path); } // Check for multiple imports of the same module const isDuplicate = dedup.has(defType); if (cmpDef) { if (isDuplicate) { // This component definition has already been processed. return false; } dedup.add(defType); if (cmpDef.dependencies) { const deps = typeof cmpDef.dependencies === 'function' ? cmpDef.dependencies() : cmpDef.dependencies; for (const dep of deps) { walkProviderTree(dep, providersOut, parents, dedup); } } } else if (injDef) { // First, include providers from any imports. if (injDef.imports != null && !isDuplicate) { // Before processing defType's imports, add it to the set of parents. This way, if it ends // up deeply importing itself, this can be detected. ngDevMode && parents.push(defType); // Add it to the set of dedups. This way we can detect multiple imports of the same module dedup.add(defType); let importTypesWithProviders; try { deepForEach(injDef.imports, imported => { if (walkProviderTree(imported, providersOut, parents, dedup)) { importTypesWithProviders || (importTypesWithProviders = []); // If the processed import is an injector type with providers, we store it in the // list of import types with providers, so that we can process those afterwards. importTypesWithProviders.push(imported); } }); } finally { // Remove it from the parents set when finished. ngDevMode && parents.pop(); } // Imports which are declared with providers (TypeWithProviders) need to be processed // after all imported modules are processed. This is similar to how View Engine // processes/merges module imports in the metadata resolver. See: FW-1349. if (importTypesWithProviders !== undefined) { processInjectorTypesWithProviders(importTypesWithProviders, providersOut); } } if (!isDuplicate) { // Track the InjectorType and add a provider for it. // It's important that this is done after the def's imports. const factory = getFactoryDef(defType) || (() => new defType()); // Append extra providers to make more info available for consumers (to retrieve an injector // type), as well as internally (to calculate an injection scope correctly and eagerly // instantiate a `defType` when an injector is created). providersOut.push( // Provider to create `defType` using its factory. { provide: defType, useFactory: factory, deps: EMPTY_ARRAY }, // Make this `defType` available to an internal logic that calculates injector scope. { provide: INJECTOR_DEF_TYPES, useValue: defType, multi: true }, // Provider to eagerly instantiate `defType` via `ENVIRONMENT_INITIALIZER`. { provide: ENVIRONMENT_INITIALIZER, useValue: () => ɵɵinject(defType), multi: true } // ); } // Next, include providers listed on the definition itself. const defProviders = injDef.providers; if (defProviders != null && !isDuplicate) { const injectorType = container; deepForEachProvider(defProviders, provider => { ngDevMode && validateProvider(provider, defProviders, injectorType); providersOut.push(provider); }); } } else { // Should not happen, but just in case. return false; } return defType !== container && container.providers !== undefined; } function validateProvider(provider, providers, containerType) { if (isTypeProvider(provider) || isValueProvider(provider) || isFactoryProvider(provider) || isExistingProvider(provider)) { return; } // Here we expect the provider to be a `useClass` provider (by elimination). const classRef = resolveForwardRef(provider && (provider.useClass || provider.provide)); if (!classRef) { throwInvalidProviderError(containerType, providers, provider); } } function deepForEachProvider(providers, fn) { for (let provider of providers) { if (isEnvironmentProviders(provider)) { provider = provider.ɵproviders; } if (Array.isArray(provider)) { deepForEachProvider(provider, fn); } else { fn(provider); } } } const USE_VALUE$1 = getClosureSafeProperty({ provide: String, useValue: getClosureSafeProperty }); function isValueProvider(value) { return value !== null && typeof value == 'object' && USE_VALUE$1 in value; } function isExistingProvider(value) { return !!(value && value.useExisting); } function isFactoryProvider(value) { return !!(value && value.useFactory); } function isTypeProvider(value) { return typeof value === 'function'; } function isClassProvider(value) { return !!value.useClass; } /** * An internal token whose presence in an injector indicates that the injector should treat itself * as a root scoped injector when processing requests for unknown tokens which may indicate * they are provided in the root scope. */ const INJECTOR_SCOPE = new InjectionToken('Set Injector scope.'); /** * Marker which indicates that a value has not yet been created from the factory function. */ const NOT_YET = {}; /** * Marker which indicates that the factory function for a token is in the process of being called. * * If the injector is asked to inject a token with its value set to CIRCULAR, that indicates * injection of a dependency has recursively attempted to inject the original token, and there is * a circular dependency among the providers. */ const CIRCULAR = {}; /** * A lazily initialized NullInjector. */ let NULL_INJECTOR$1 = undefined; function getNullInjector() { if (NULL_INJECTOR$1 === undefined) { NULL_INJECTOR$1 = new NullInjector(); } return NULL_INJECTOR$1; } /** * An `Injector` that's part of the environment injector hierarchy, which exists outside of the * component tree. */ class EnvironmentInjector {} class R3Injector extends EnvironmentInjector { /** * Flag indicating that this injector was previously destroyed. */ get destroyed() { return this._destroyed; } constructor(providers, parent, source, scopes) { super(); this.parent = parent; this.source = source; this.scopes = scopes; /** * Map of tokens to records which contain the instances of those tokens. * - `null` value implies that we don't have the record. Used by tree-shakable injectors * to prevent further searches. */ this.records = new Map(); /** * Set of values instantiated by this injector which contain `ngOnDestroy` lifecycle hooks. */ this._ngOnDestroyHooks = new Set(); this._onDestroyHooks = []; this._destroyed = false; // Start off by creating Records for every provider. forEachSingleProvider(providers, provider => this.processProvider(provider)); // Make sure the INJECTOR token provides this injector. this.records.set(INJECTOR, makeRecord(undefined, this)); // And `EnvironmentInjector` if the current injector is supposed to be env-scoped. if (scopes.has('environment')) { this.records.set(EnvironmentInjector, makeRecord(undefined, this)); } // Detect whether this injector has the APP_ROOT_SCOPE token and thus should provide // any injectable scoped to APP_ROOT_SCOPE. const record = this.records.get(INJECTOR_SCOPE); if (record != null && typeof record.value === 'string') { this.scopes.add(record.value); } this.injectorDefTypes = new Set(this.get(INJECTOR_DEF_TYPES.multi, EMPTY_ARRAY, InjectFlags.Self)); } /** * Destroy the injector and release references to every instance or provider associated with it. * * Also calls the `OnDestroy` lifecycle hooks of every instance that was created for which a * hook was found. */ destroy() { this.assertNotDestroyed(); // Set destroyed = true first, in case lifecycle hooks re-enter destroy(). this._destroyed = true; try { // Call all the lifecycle hooks. for (const service of this._ngOnDestroyHooks) { service.ngOnDestroy(); } for (const hook of this._onDestroyHooks) { hook(); } } finally { // Release all references. this.records.clear(); this._ngOnDestroyHooks.clear(); this.injectorDefTypes.clear(); this._onDestroyHooks.length = 0; } } onDestroy(callback) { this._onDestroyHooks.push(callback); } runInContext(fn) { this.assertNotDestroyed(); const previousInjector = setCurrentInjector(this); const previousInjectImplementation = setInjectImplementation(undefined); try { return fn(); } finally { setCurrentInjector(previousInjector); setInjectImplementation(previousInjectImplementation); } } get(token, notFoundValue = THROW_IF_NOT_FOUND, flags = InjectFlags.Default) { this.assertNotDestroyed(); flags = convertToBitFlags(flags); // Set the injection context. const previousInjector = setCurrentInjector(this); const previousInjectImplementation = setInjectImplementation(undefined); try { // Check for the SkipSelf flag. if (!(flags & InjectFlags.SkipSelf)) { // SkipSelf isn't set, check if the record belongs to this injector. let record = this.records.get(token); if (record === undefined) { // No record, but maybe the token is scoped to this injector. Look for an injectable // def with a scope matching this injector. const def = couldBeInjectableType(token) && getInjectableDef(token); if (def && this.injectableDefInScope(def)) { // Found an injectable def and it's scoped to this injector. Pretend as if it was here // all along. record = makeRecord(injectableDefOrInjectorDefFactory(token), NOT_YET); } else { record = null; } this.records.set(token, record); } // If a record was found, get the instance for it and return it. if (record != null /* NOT null || undefined */) { return this.hydrate(token, record); } } // Select the next injector based on the Self flag - if self is set, the next injector is // the NullInjector, otherwise it's the parent. const nextInjector = !(flags & InjectFlags.Self) ? this.parent : getNullInjector(); // Set the notFoundValue based on the Optional flag - if optional is set and notFoundValue // is undefined, the value is null, otherwise it's the notFoundValue. notFoundValue = flags & InjectFlags.Optional && notFoundValue === THROW_IF_NOT_FOUND ? null : notFoundValue; return nextInjector.get(token, notFoundValue); } catch (e) { if (e.name === 'NullInjectorError') { const path = e[NG_TEMP_TOKEN_PATH] = e[NG_TEMP_TOKEN_PATH] || []; path.unshift(stringify(token)); if (previousInjector) { // We still have a parent injector, keep throwing throw e; } else { // Format & throw the final error message when we don't have any previous injector return catchInjectorError(e, token, 'R3InjectorError', this.source); } } else { throw e; } } finally { // Lastly, restore the previous injection context. setInjectImplementation(previousInjectImplementation); setCurrentInjector(previousInjector); } } /** @internal */ resolveInjectorInitializers() { const previousInjector = setCurrentInjector(this); const previousInjectImplementation = setInjectImplementation(undefined); try { const initializers = this.get(ENVIRONMENT_INITIALIZER.multi, EMPTY_ARRAY, InjectFlags.Self); if (ngDevMode && !Array.isArray(initializers)) { throw new RuntimeError(-209 /* RuntimeErrorCode.INVALID_MULTI_PROVIDER */, 'Unexpected type of the `ENVIRONMENT_INITIALIZER` token value ' + `(expected an array, but got ${typeof initializers}). ` + 'Please check that the `ENVIRONMENT_INITIALIZER` token is configured as a ' + '`multi: true` provider.'); } for (const initializer of initializers) { initializer(); } } finally { setCurrentInjector(previousInjector); setInjectImplementation(previousInjectImplementation); } } toString() { const tokens = []; const records = this.records; for (const token of records.keys()) { tokens.push(stringify(token)); } return `R3Injector[${tokens.join(', ')}]`; } assertNotDestroyed() { if (this._destroyed) { throw new RuntimeError(205 /* RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED */, ngDevMode && 'Injector has already been destroyed.'); } } /** * Process a `SingleProvider` and add it. */ processProvider(provider) { // Determine the token from the provider. Either it's its own token, or has a {provide: ...} // property. provider = resolveForwardRef(provider); let token = isTypeProvider(provider) ? provider : resolveForwardRef(provider && provider.provide); // Construct a `Record` for the provider. const record = providerToRecord(provider); if (!isTypeProvider(provider) && provider.multi === true) { // If the provider indicates that it's a multi-provider, process it specially. // First check whether it's been defined already. let multiRecord = this.records.get(token); if (multiRecord) { // It has. Throw a nice error if if (ngDevMode && multiRecord.multi === undefined) { throwMixedMultiProviderError(); } } else { multiRecord = makeRecord(undefined, NOT_YET, true); multiRecord.factory = () => injectArgs(multiRecord.multi); this.records.set(token, multiRecord); } token = provider; multiRecord.multi.push(provider); } else { const existing = this.records.get(token); if (ngDevMode && existing && existing.multi !== undefined) { throwMixedMultiProviderError(); } } this.records.set(token, record); } hydrate(token, record) { if (ngDevMode && record.value === CIRCULAR) { throwCyclicDependencyError(stringify(token)); } else if (record.value === NOT_YET) { record.value = CIRCULAR; record.value = record.factory(); } if (typeof record.value === 'object' && record.value && hasOnDestroy(record.value)) { this._ngOnDestroyHooks.add(record.value); } return record.value; } injectableDefInScope(def) { if (!def.providedIn) { return false; } const providedIn = resolveForwardRef(def.providedIn); if (typeof providedIn === 'string') { return providedIn === 'any' || this.scopes.has(providedIn); } else { return this.injectorDefTypes.has(providedIn); } } } function injectableDefOrInjectorDefFactory(token) { // Most tokens will have an injectable def directly on them, which specifies a factory directly. const injectableDef = getInjectableDef(token); const factory = injectableDef !== null ? injectableDef.factory : getFactoryDef(token); if (factory !== null) { return factory; } // InjectionTokens should have an injectable def (ɵprov) and thus should be handled above. // If it's missing that, it's an error. if (token instanceof InjectionToken) { throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Token ${stringify(token)} is missing a ɵprov definition.`); } // Undecorated types can sometimes be created if they have no constructor arguments. if (token instanceof Function) { return getUndecoratedInjectableFactory(token); } // There was no way to resolve a factory for this token. throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && 'unreachable'); } function getUndecoratedInjectableFactory(token) { // If the token has parameters then it has dependencies that we cannot resolve implicitly. const paramLength = token.length; if (paramLength > 0) { const args = newArray(paramLength, '?'); throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Can't resolve all parameters for ${stringify(token)}: (${args.join(', ')}).`); } // The constructor function appears to have no parameters. // This might be because it inherits from a super-class. In which case, use an injectable // def from an ancestor if there is one. // Otherwise this really is a simple class with no dependencies, so return a factory that // just instantiates the zero-arg constructor. const inheritedInjectableDef = getInheritedInjectableDef(token); if (inheritedInjectableDef !== null) { return () => inheritedInjectableDef.factory(token); } else { return () => new token(); } } function providerToRecord(provider) { if (isValueProvider(provider)) { return makeRecord(undefined, provider.useValue); } else { const factory = providerToFactory(provider); return makeRecord(factory, NOT_YET); } } /** * Converts a `SingleProvider` into a factory function. * * @param provider provider to convert to factory */ function providerToFactory(provider, ngModuleType, providers) { let factory = undefined; if (ngDevMode && isEnvironmentProviders(provider)) { throwInvalidProviderError(undefined, providers, provider); } if (isTypeProvider(provider)) { const unwrappedProvider = resolveForwardRef(provider); return getFactoryDef(unwrappedProvider) || injectableDefOrInjectorDefFactory(unwrappedProvider); } else { if (isValueProvider(provider)) { factory = () => resolveForwardRef(provider.useValue); } else if (isFactoryProvider(provider)) { factory = () => provider.useFactory(...injectArgs(provider.deps || [])); } else if (isExistingProvider(provider)) { factory = () => ɵɵinject(resolveForwardRef(provider.useExisting)); } else { const classRef = resolveForwardRef(provider && (provider.useClass || provider.provide)); if (ngDevMode && !classRef) { throwInvalidProviderError(ngModuleType, providers, provider); } if (hasDeps(provider)) { factory = () => new classRef(...injectArgs(provider.deps)); } else { return getFactoryDef(classRef) || injectableDefOrInjectorDefFactory(classRef); } } } return factory; } function makeRecord(factory, value, multi = false) { return { factory: factory, value: value, multi: multi ? [] : undefined }; } function hasDeps(value) { return !!value.deps; } function hasOnDestroy(value) { return value !== null && typeof value === 'object' && typeof value.ngOnDestroy === 'function'; } function couldBeInjectableType(value) { return typeof value === 'function' || typeof value === 'object' && value instanceof InjectionToken; } function forEachSingleProvider(providers, fn) { for (const provider of providers) { if (Array.isArray(provider)) { forEachSingleProvider(provider, fn); } else if (provider && isEnvironmentProviders(provider)) { forEachSingleProvider(provider.ɵproviders, fn); } else { fn(provider); } } } /** * Represents a component created by a `ComponentFactory`. * Provides access to the component instance and related objects, * and provides the means of destroying the instance. * * @publicApi */ class ComponentRef$1 {} /** * Base class for a factory that can create a component dynamically. * Instantiate a factory for a given type of component with `resolveComponentFactory()`. * Use the resulting `ComponentFactory.create()` method to create a component of that type. * * @see [Dynamic Components](guide/dynamic-component-loader) * * @publicApi * * @deprecated Angular no longer requires Component factories. Please use other APIs where * Component class can be used directly. */ class ComponentFactory$1 {} function noComponentFactoryError(component) { const error = Error(`No component factory found for ${stringify(component)}. Did you add it to @NgModule.entryComponents?`); error[ERROR_COMPONENT] = component; return error; } const ERROR_COMPONENT = 'ngComponent'; function getComponent$1(error) { return error[ERROR_COMPONENT]; } class _NullComponentFactoryResolver { resolveComponentFactory(component) { throw noComponentFactoryError(component); } } /** * A simple registry that maps `Components` to generated `ComponentFactory` classes * that can be used to create instances of components. * Use to obtain the factory for a given component type, * then use the factory's `create()` method to create a component of that type. * * Note: since v13, dynamic component creation via * [`ViewContainerRef.createComponent`](api/core/ViewContainerRef#createComponent) * does **not** require resolving component factory: component class can be used directly. * * @publicApi * * @deprecated Angular no longer requires Component factories. Please use other APIs where * Component class can be used directly. */ class ComponentFactoryResolver$1 {} ComponentFactoryResolver$1.NULL = /* @__PURE__ */new _NullComponentFactoryResolver(); /** * Creates an ElementRef from the most recent node. * * @returns The ElementRef instance to use */ function injectElementRef() { return createElementRef(getCurrentTNode(), getLView()); } /** * Creates an ElementRef given a node. * * @param tNode The node for which you'd like an ElementRef * @param lView The view to which the node belongs * @returns The ElementRef instance to use */ function createElementRef(tNode, lView) { return new ElementRef(getNativeByTNode(tNode, lView)); } /** * A wrapper around a native element inside of a View. * * An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM * element. * * @security Permitting direct access to the DOM can make your application more vulnerable to * XSS attacks. Carefully review any use of `ElementRef` in your code. For more detail, see the * [Security Guide](https://g.co/ng/security). * * @publicApi */ // Note: We don't expose things like `Injector`, `ViewContainer`, ... here, // i.e. users have to ask for what they need. With that, we can build better analysis tools // and could do better codegen in the future. class ElementRef { constructor(nativeElement) { this.nativeElement = nativeElement; } } /** * @internal * @nocollapse */ ElementRef.__NG_ELEMENT_ID__ = injectElementRef; /** * Unwraps `ElementRef` and return the `nativeElement`. * * @param value value to unwrap * @returns `nativeElement` if `ElementRef` otherwise returns value as is. */ function unwrapElementRef(value) { return value instanceof ElementRef ? value.nativeElement : value; } /** * Creates and initializes a custom renderer that implements the `Renderer2` base class. * * @publicApi */ class RendererFactory2 {} /** * Extend this base class to implement custom rendering. By default, Angular * renders a template into DOM. You can use custom rendering to intercept * rendering calls, or to render to something other than DOM. * * Create your custom renderer using `RendererFactory2`. * * Use a custom renderer to bypass Angular's templating and * make custom UI changes that can't be expressed declaratively. * For example if you need to set a property or an attribute whose name is * not statically known, use the `setProperty()` or * `setAttribute()` method. * * @publicApi */ class Renderer2 {} /** * @internal * @nocollapse */ Renderer2.__NG_ELEMENT_ID__ = () => injectRenderer2(); /** Injects a Renderer2 for the current component. */ function injectRenderer2() { // We need the Renderer to be based on the component that it's being injected into, however since // DI happens before we've entered its view, `getLView` will return the parent view instead. const lView = getLView(); const tNode = getCurrentTNode(); const nodeAtIndex = getComponentLViewByIndex(tNode.index, lView); return (isLView(nodeAtIndex) ? nodeAtIndex : lView)[RENDERER]; } /** * Sanitizer is used by the views to sanitize potentially dangerous values. * * @publicApi */ class Sanitizer {} /** @nocollapse */ Sanitizer.ɵprov = ɵɵdefineInjectable({ token: Sanitizer, providedIn: 'root', factory: () => null }); /** * @description Represents the version of Angular * * @publicApi */ class Version { constructor(full) { this.full = full; this.major = full.split('.')[0]; this.minor = full.split('.')[1]; this.patch = full.split('.').slice(2).join('.'); } } /** * @publicApi */ const VERSION = new Version('15.2.9'); // This default value is when checking the hierarchy for a token. // // It means both: // - the token is not provided by the current injector, // - only the element injectors should be checked (ie do not check module injectors // // mod1 // / // el1 mod2 // \ / // el2 // // When requesting el2.injector.get(token), we should check in the following order and return the // first found value: // - el2.injector.get(token, default) // - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module // - mod2.injector.get(token, default) const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {}; const ERROR_ORIGINAL_ERROR = 'ngOriginalError'; function wrappedError(message, originalError) { const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`; const error = Error(msg); error[ERROR_ORIGINAL_ERROR] = originalError; return error; } function getOriginalError(error) { return error[ERROR_ORIGINAL_ERROR]; } /** * Provides a hook for centralized exception handling. * * The default implementation of `ErrorHandler` prints error messages to the `console`. To * intercept error handling, write a custom exception handler that replaces this default as * appropriate for your app. * * @usageNotes * ### Example * * ``` * class MyErrorHandler implements ErrorHandler { * handleError(error) { * // do something with the exception * } * } * * @NgModule({ * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}] * }) * class MyModule {} * ``` * * @publicApi */ class ErrorHandler { constructor() { /** * @internal */ this._console = console; } handleError(error) { const originalError = this._findOriginalError(error); this._console.error('ERROR', error); if (originalError) { this._console.error('ORIGINAL ERROR', originalError); } } /** @internal */ _findOriginalError(error) { let e = error && getOriginalError(error); while (e && getOriginalError(e)) { e = getOriginalError(e); } return e || null; } } function normalizeDebugBindingName(name) { // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers name = camelCaseToDashCase(name.replace(/[$@]/g, '_')); return `ng-reflect-${name}`; } const CAMEL_CASE_REGEXP = /([A-Z])/g; function camelCaseToDashCase(input) { return input.replace(CAMEL_CASE_REGEXP, (...m) => '-' + m[1].toLowerCase()); } function normalizeDebugBindingValue(value) { try { // Limit the size of the value as otherwise the DOM just gets polluted. return value != null ? value.toString().slice(0, 30) : value; } catch (e) { return '[ERROR] Exception while trying to serialize the value'; } } /** * * @codeGenApi */ function ɵɵresolveWindow(element) { return element.ownerDocument.defaultView; } /** * * @codeGenApi */ function ɵɵresolveDocument(element) { return element.ownerDocument; } /** * * @codeGenApi */ function ɵɵresolveBody(element) { return element.ownerDocument.body; } /** * The special delimiter we use to separate property names, prefixes, and suffixes * in property binding metadata. See storeBindingMetadata(). * * We intentionally use the Unicode "REPLACEMENT CHARACTER" (U+FFFD) as a delimiter * because it is a very uncommon character that is unlikely to be part of a user's * property names or interpolation strings. If it is in fact used in a property * binding, DebugElement.properties will not return the correct value for that * binding. However, there should be no runtime effect for real applications. * * This character is typically rendered as a question mark inside of a diamond. * See https://en.wikipedia.org/wiki/Specials_(Unicode_block) * */ const INTERPOLATION_DELIMITER = `�`; /** * Unwrap a value which might be behind a closure (for forward declaration reasons). */ function maybeUnwrapFn(value) { if (value instanceof Function) { return value(); } else { return value; } } /** Verifies that a given type is a Standalone Component. */ function assertStandaloneComponentType(type) { assertComponentDef(type); const componentDef = getComponentDef(type); if (!componentDef.standalone) { throw new RuntimeError(907 /* RuntimeErrorCode.TYPE_IS_NOT_STANDALONE */, `The ${stringifyForError(type)} component is not marked as standalone, ` + `but Angular expects to have a standalone component here. ` + `Please make sure the ${stringifyForError(type)} component has ` + `the \`standalone: true\` flag in the decorator.`); } } /** Verifies whether a given type is a component */ function assertComponentDef(type) { if (!getComponentDef(type)) { throw new RuntimeError(906 /* RuntimeErrorCode.MISSING_GENERATED_DEF */, `The ${stringifyForError(type)} is not an Angular component, ` + `make sure it has the \`@Component\` decorator.`); } } /** Called when there are multiple component selectors that match a given node */ function throwMultipleComponentError(tNode, first, second) { throw new RuntimeError(-300 /* RuntimeErrorCode.MULTIPLE_COMPONENTS_MATCH */, `Multiple components match node with tagname ${tNode.value}: ` + `${stringifyForError(first)} and ` + `${stringifyForError(second)}`); } /** Throws an ExpressionChangedAfterChecked error if checkNoChanges mode is on. */ function throwErrorIfNoChangesMode(creationMode, oldValue, currValue, propName) { const field = propName ? ` for '${propName}'` : ''; let msg = `ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value${field}: '${oldValue}'. Current value: '${currValue}'.`; if (creationMode) { msg += ` It seems like the view has been created after its parent and its children have been dirty checked.` + ` Has it been created in a change detection hook?`; } throw new RuntimeError(-100 /* RuntimeErrorCode.EXPRESSION_CHANGED_AFTER_CHECKED */, msg); } function constructDetailsForInterpolation(lView, rootIndex, expressionIndex, meta, changedValue) { const [propName, prefix, ...chunks] = meta.split(INTERPOLATION_DELIMITER); let oldValue = prefix, newValue = prefix; for (let i = 0; i < chunks.length; i++) { const slotIdx = rootIndex + i; oldValue += `${lView[slotIdx]}${chunks[i]}`; newValue += `${slotIdx === expressionIndex ? changedValue : lView[slotIdx]}${chunks[i]}`; } return { propName, oldValue, newValue }; } /** * Constructs an object that contains details for the ExpressionChangedAfterItHasBeenCheckedError: * - property name (for property bindings or interpolations) * - old and new values, enriched using information from metadata * * More information on the metadata storage format can be found in `storePropertyBindingMetadata` * function description. */ function getExpressionChangedErrorDetails(lView, bindingIndex, oldValue, newValue) { const tData = lView[TVIEW].data; const metadata = tData[bindingIndex]; if (typeof metadata === 'string') { // metadata for property interpolation if (metadata.indexOf(INTERPOLATION_DELIMITER) > -1) { return constructDetailsForInterpolation(lView, bindingIndex, bindingIndex, metadata, newValue); } // metadata for property binding return { propName: metadata, oldValue, newValue }; } // metadata is not available for this expression, check if this expression is a part of the // property interpolation by going from the current binding index left and look for a string that // contains INTERPOLATION_DELIMITER, the layout in tView.data for this case will look like this: // [..., 'id�Prefix � and � suffix', null, null, null, ...] if (metadata === null) { let idx = bindingIndex - 1; while (typeof tData[idx] !== 'string' && tData[idx + 1] === null) { idx--; } const meta = tData[idx]; if (typeof meta === 'string') { const matches = meta.match(new RegExp(INTERPOLATION_DELIMITER, 'g')); // first interpolation delimiter separates property name from interpolation parts (in case of // property interpolations), so we subtract one from total number of found delimiters if (matches && matches.length - 1 > bindingIndex - idx) { return constructDetailsForInterpolation(lView, idx, bindingIndex, meta, newValue); } } } return { propName: undefined, oldValue, newValue }; } /** * Returns an index of `classToSearch` in `className` taking token boundaries into account. * * `classIndexOf('AB A', 'A', 0)` will be 3 (not 0 since `AB!==A`) * * @param className A string containing classes (whitespace separated) * @param classToSearch A class name to locate * @param startingIndex Starting location of search * @returns an index of the located class (or -1 if not found) */ function classIndexOf(className, classToSearch, startingIndex) { ngDevMode && assertNotEqual(classToSearch, '', 'can not look for "" string.'); let end = className.length; while (true) { const foundIndex = className.indexOf(classToSearch, startingIndex); if (foundIndex === -1) return foundIndex; if (foundIndex === 0 || className.charCodeAt(foundIndex - 1) <= 32 /* CharCode.SPACE */) { // Ensure that it has leading whitespace const length = classToSearch.length; if (foundIndex + length === end || className.charCodeAt(foundIndex + length) <= 32 /* CharCode.SPACE */) { // Ensure that it has trailing whitespace return foundIndex; } } // False positive, keep searching from where we left off. startingIndex = foundIndex + 1; } } const NG_TEMPLATE_SELECTOR = 'ng-template'; /** * Search the `TAttributes` to see if it contains `cssClassToMatch` (case insensitive) * * @param attrs `TAttributes` to search through. * @param cssClassToMatch class to match (lowercase) * @param isProjectionMode Whether or not class matching should look into the attribute `class` in * addition to the `AttributeMarker.Classes`. */ function isCssClassMatching(attrs, cssClassToMatch, isProjectionMode) { // TODO(misko): The fact that this function needs to know about `isProjectionMode` seems suspect. // It is strange to me that sometimes the class information comes in form of `class` attribute // and sometimes in form of `AttributeMarker.Classes`. Some investigation is needed to determine // if that is the right behavior. ngDevMode && assertEqual(cssClassToMatch, cssClassToMatch.toLowerCase(), 'Class name expected to be lowercase.'); let i = 0; // Indicates whether we are processing value from the implicit // attribute section (i.e. before the first marker in the array). let isImplicitAttrsSection = true; while (i < attrs.length) { let item = attrs[i++]; if (typeof item === 'string' && isImplicitAttrsSection) { const value = attrs[i++]; if (isProjectionMode && item === 'class') { // We found a `class` attribute in the implicit attribute section, // check if it matches the value of the `cssClassToMatch` argument. if (classIndexOf(value.toLowerCase(), cssClassToMatch, 0) !== -1) { return true; } } } else if (item === 1 /* AttributeMarker.Classes */) { // We found the classes section. Start searching for the class. while (i < attrs.length && typeof (item = attrs[i++]) == 'string') { // while we have strings if (item.toLowerCase() === cssClassToMatch) return true; } return false; } else if (typeof item === 'number') { // We've came across a first marker, which indicates // that the implicit attribute section is over. isImplicitAttrsSection = false; } } return false; } /** * Checks whether the `tNode` represents an inline template (e.g. `*ngFor`). * * @param tNode current TNode */ function isInlineTemplate(tNode) { return tNode.type === 4 /* TNodeType.Container */ && tNode.value !== NG_TEMPLATE_SELECTOR; } /** * Function that checks whether a given tNode matches tag-based selector and has a valid type. * * Matching can be performed in 2 modes: projection mode (when we project nodes) and regular * directive matching mode: * - in the "directive matching" mode we do _not_ take TContainer's tagName into account if it is * different from NG_TEMPLATE_SELECTOR (value different from NG_TEMPLATE_SELECTOR indicates that a * tag name was extracted from * syntax so we would match the same directive twice); * - in the "projection" mode, we use a tag name potentially extracted from the * syntax processing * (applicable to TNodeType.Container only). */ function hasTagAndTypeMatch(tNode, currentSelector, isProjectionMode) { const tagNameToCompare = tNode.type === 4 /* TNodeType.Container */ && !isProjectionMode ? NG_TEMPLATE_SELECTOR : tNode.value; return currentSelector === tagNameToCompare; } /** * A utility function to match an Ivy node static data against a simple CSS selector * * @param node static data of the node to match * @param selector The selector to try matching against the node. * @param isProjectionMode if `true` we are matching for content projection, otherwise we are doing * directive matching. * @returns true if node matches the selector. */ function isNodeMatchingSelector(tNode, selector, isProjectionMode) { ngDevMode && assertDefined(selector[0], 'Selector should have a tag name'); let mode = 4 /* SelectorFlags.ELEMENT */; const nodeAttrs = tNode.attrs || []; // Find the index of first attribute that has no value, only a name. const nameOnlyMarkerIdx = getNameOnlyMarkerIndex(nodeAttrs); // When processing ":not" selectors, we skip to the next ":not" if the // current one doesn't match let skipToNextSelector = false; for (let i = 0; i < selector.length; i++) { const current = selector[i]; if (typeof current === 'number') { // If we finish processing a :not selector and it hasn't failed, return false if (!skipToNextSelector && !isPositive(mode) && !isPositive(current)) { return false; } // If we are skipping to the next :not() and this mode flag is positive, // it's a part of the current :not() selector, and we should keep skipping if (skipToNextSelector && isPositive(current)) continue; skipToNextSelector = false; mode = current | mode & 1 /* SelectorFlags.NOT */; continue; } if (skipToNextSelector) continue; if (mode & 4 /* SelectorFlags.ELEMENT */) { mode = 2 /* SelectorFlags.ATTRIBUTE */ | mode & 1 /* SelectorFlags.NOT */; if (current !== '' && !hasTagAndTypeMatch(tNode, current, isProjectionMode) || current === '' && selector.length === 1) { if (isPositive(mode)) return false; skipToNextSelector = true; } } else { const selectorAttrValue = mode & 8 /* SelectorFlags.CLASS */ ? current : selector[++i]; // special case for matching against classes when a tNode has been instantiated with // class and style values as separate attribute values (e.g. ['title', CLASS, 'foo']) if (mode & 8 /* SelectorFlags.CLASS */ && tNode.attrs !== null) { if (!isCssClassMatching(tNode.attrs, selectorAttrValue, isProjectionMode)) { if (isPositive(mode)) return false; skipToNextSelector = true; } continue; } const attrName = mode & 8 /* SelectorFlags.CLASS */ ? 'class' : current; const attrIndexInNode = findAttrIndexInNode(attrName, nodeAttrs, isInlineTemplate(tNode), isProjectionMode); if (attrIndexInNode === -1) { if (isPositive(mode)) return false; skipToNextSelector = true; continue; } if (selectorAttrValue !== '') { let nodeAttrValue; if (attrIndexInNode > nameOnlyMarkerIdx) { nodeAttrValue = ''; } else { ngDevMode && assertNotEqual(nodeAttrs[attrIndexInNode], 0 /* AttributeMarker.NamespaceURI */, 'We do not match directives on namespaced attributes'); // we lowercase the attribute value to be able to match // selectors without case-sensitivity // (selectors are already in lowercase when generated) nodeAttrValue = nodeAttrs[attrIndexInNode + 1].toLowerCase(); } const compareAgainstClassName = mode & 8 /* SelectorFlags.CLASS */ ? nodeAttrValue : null; if (compareAgainstClassName && classIndexOf(compareAgainstClassName, selectorAttrValue, 0) !== -1 || mode & 2 /* SelectorFlags.ATTRIBUTE */ && selectorAttrValue !== nodeAttrValue) { if (isPositive(mode)) return false; skipToNextSelector = true; } } } } return isPositive(mode) || skipToNextSelector; } function isPositive(mode) { return (mode & 1 /* SelectorFlags.NOT */) === 0; } /** * Examines the attribute's definition array for a node to find the index of the * attribute that matches the given `name`. * * NOTE: This will not match namespaced attributes. * * Attribute matching depends upon `isInlineTemplate` and `isProjectionMode`. * The following table summarizes which types of attributes we attempt to match: * * =========================================================================================================== * Modes | Normal Attributes | Bindings Attributes | Template Attributes | I18n * Attributes * =========================================================================================================== * Inline + Projection | YES | YES | NO | YES * ----------------------------------------------------------------------------------------------------------- * Inline + Directive | NO | NO | YES | NO * ----------------------------------------------------------------------------------------------------------- * Non-inline + Projection | YES | YES | NO | YES * ----------------------------------------------------------------------------------------------------------- * Non-inline + Directive | YES | YES | NO | YES * =========================================================================================================== * * @param name the name of the attribute to find * @param attrs the attribute array to examine * @param isInlineTemplate true if the node being matched is an inline template (e.g. `*ngFor`) * rather than a manually expanded template node (e.g `<ng-template>`). * @param isProjectionMode true if we are matching against content projection otherwise we are * matching against directives. */ function findAttrIndexInNode(name, attrs, isInlineTemplate, isProjectionMode) { if (attrs === null) return -1; let i = 0; if (isProjectionMode || !isInlineTemplate) { let bindingsMode = false; while (i < attrs.length) { const maybeAttrName = attrs[i]; if (maybeAttrName === name) { return i; } else if (maybeAttrName === 3 /* AttributeMarker.Bindings */ || maybeAttrName === 6 /* AttributeMarker.I18n */) { bindingsMode = true; } else if (maybeAttrName === 1 /* AttributeMarker.Classes */ || maybeAttrName === 2 /* AttributeMarker.Styles */) { let value = attrs[++i]; // We should skip classes here because we have a separate mechanism for // matching classes in projection mode. while (typeof value === 'string') { value = attrs[++i]; } continue; } else if (maybeAttrName === 4 /* AttributeMarker.Template */) { // We do not care about Template attributes in this scenario. break; } else if (maybeAttrName === 0 /* AttributeMarker.NamespaceURI */) { // Skip the whole namespaced attribute and value. This is by design. i += 4; continue; } // In binding mode there are only names, rather than name-value pairs. i += bindingsMode ? 1 : 2; } // We did not match the attribute return -1; } else { return matchTemplateAttribute(attrs, name); } } function isNodeMatchingSelectorList(tNode, selector, isProjectionMode = false) { for (let i = 0; i < selector.length; i++) { if (isNodeMatchingSelector(tNode, selector[i], isProjectionMode)) { return true; } } return false; } function getProjectAsAttrValue(tNode) { const nodeAttrs = tNode.attrs; if (nodeAttrs != null) { const ngProjectAsAttrIdx = nodeAttrs.indexOf(5 /* AttributeMarker.ProjectAs */); // only check for ngProjectAs in attribute names, don't accidentally match attribute's value // (attribute names are stored at even indexes) if ((ngProjectAsAttrIdx & 1) === 0) { return nodeAttrs[ngProjectAsAttrIdx + 1]; } } return null; } function getNameOnlyMarkerIndex(nodeAttrs) { for (let i = 0; i < nodeAttrs.length; i++) { const nodeAttr = nodeAttrs[i]; if (isNameOnlyAttributeMarker(nodeAttr)) { return i; } } return nodeAttrs.length; } function matchTemplateAttribute(attrs, name) { let i = attrs.indexOf(4 /* AttributeMarker.Template */); if (i > -1) { i++; while (i < attrs.length) { const attr = attrs[i]; // Return in case we checked all template attrs and are switching to the next section in the // attrs array (that starts with a number that represents an attribute marker). if (typeof attr === 'number') return -1; if (attr === name) return i; i++; } } return -1; } /** * Checks whether a selector is inside a CssSelectorList * @param selector Selector to be checked. * @param list List in which to look for the selector. */ function isSelectorInSelectorList(selector, list) { selectorListLoop: for (let i = 0; i < list.length; i++) { const currentSelectorInList = list[i]; if (selector.length !== currentSelectorInList.length) { continue; } for (let j = 0; j < selector.length; j++) { if (selector[j] !== currentSelectorInList[j]) { continue selectorListLoop; } } return true; } return false; } function maybeWrapInNotSelector(isNegativeMode, chunk) { return isNegativeMode ? ':not(' + chunk.trim() + ')' : chunk; } function stringifyCSSSelector(selector) { let result = selector[0]; let i = 1; let mode = 2 /* SelectorFlags.ATTRIBUTE */; let currentChunk = ''; let isNegativeMode = false; while (i < selector.length) { let valueOrMarker = selector[i]; if (typeof valueOrMarker === 'string') { if (mode & 2 /* SelectorFlags.ATTRIBUTE */) { const attrValue = selector[++i]; currentChunk += '[' + valueOrMarker + (attrValue.length > 0 ? '="' + attrValue + '"' : '') + ']'; } else if (mode & 8 /* SelectorFlags.CLASS */) { currentChunk += '.' + valueOrMarker; } else if (mode & 4 /* SelectorFlags.ELEMENT */) { currentChunk += ' ' + valueOrMarker; } } else { // // Append current chunk to the final result in case we come across SelectorFlag, which // indicates that the previous section of a selector is over. We need to accumulate content // between flags to make sure we wrap the chunk later in :not() selector if needed, e.g. // ``` // ['', Flags.CLASS, '.classA', Flags.CLASS | Flags.NOT, '.classB', '.classC'] // ``` // should be transformed to `.classA :not(.classB .classC)`. // // Note: for negative selector part, we accumulate content between flags until we find the // next negative flag. This is needed to support a case where `:not()` rule contains more than // one chunk, e.g. the following selector: // ``` // ['', Flags.ELEMENT | Flags.NOT, 'p', Flags.CLASS, 'foo', Flags.CLASS | Flags.NOT, 'bar'] // ``` // should be stringified to `:not(p.foo) :not(.bar)` // if (currentChunk !== '' && !isPositive(valueOrMarker)) { result += maybeWrapInNotSelector(isNegativeMode, currentChunk); currentChunk = ''; } mode = valueOrMarker; // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative // mode is maintained for remaining chunks of a selector. isNegativeMode = isNegativeMode || !isPositive(mode); } i++; } if (currentChunk !== '') { result += maybeWrapInNotSelector(isNegativeMode, currentChunk); } return result; } /** * Generates string representation of CSS selector in parsed form. * * ComponentDef and DirectiveDef are generated with the selector in parsed form to avoid doing * additional parsing at runtime (for example, for directive matching). However in some cases (for * example, while bootstrapping a component), a string version of the selector is required to query * for the host element on the page. This function takes the parsed form of a selector and returns * its string representation. * * @param selectorList selector in parsed form * @returns string representation of a given selector */ function stringifyCSSSelectorList(selectorList) { return selectorList.map(stringifyCSSSelector).join(','); } /** * Extracts attributes and classes information from a given CSS selector. * * This function is used while creating a component dynamically. In this case, the host element * (that is created dynamically) should contain attributes and classes specified in component's CSS * selector. * * @param selector CSS selector in parsed form (in a form of array) * @returns object with `attrs` and `classes` fields that contain extracted information */ function extractAttrsAndClassesFromSelector(selector) { const attrs = []; const classes = []; let i = 1; let mode = 2 /* SelectorFlags.ATTRIBUTE */; while (i < selector.length) { let valueOrMarker = selector[i]; if (typeof valueOrMarker === 'string') { if (mode === 2 /* SelectorFlags.ATTRIBUTE */) { if (valueOrMarker !== '') { attrs.push(valueOrMarker, selector[++i]); } } else if (mode === 8 /* SelectorFlags.CLASS */) { classes.push(valueOrMarker); } } else { // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative // mode is maintained for remaining chunks of a selector. Since attributes and classes are // extracted only for "positive" part of the selector, we can stop here. if (!isPositive(mode)) break; mode = valueOrMarker; } i++; } return { attrs, classes }; } /** A special value which designates that a value has not changed. */ const NO_CHANGE = typeof ngDevMode === 'undefined' || ngDevMode ? { __brand__: 'NO_CHANGE' } : {}; /** * Advances to an element for later binding instructions. * * Used in conjunction with instructions like {@link property} to act on elements with specified * indices, for example those created with {@link element} or {@link elementStart}. * * ```ts * (rf: RenderFlags, ctx: any) => { * if (rf & 1) { * text(0, 'Hello'); * text(1, 'Goodbye') * element(2, 'div'); * } * if (rf & 2) { * advance(2); // Advance twice to the <div>. * property('title', 'test'); * } * } * ``` * @param delta Number of elements to advance forwards by. * * @codeGenApi */ function ɵɵadvance(delta) { ngDevMode && assertGreaterThan(delta, 0, 'Can only advance forward'); selectIndexInternal(getTView(), getLView(), getSelectedIndex() + delta, !!ngDevMode && isInCheckNoChangesMode()); } function selectIndexInternal(tView, lView, index, checkNoChangesMode) { ngDevMode && assertIndexInDeclRange(lView, index); // Flush the initial hooks for elements in the view that have been added up to this point. // PERF WARNING: do NOT extract this to a separate function without running benchmarks if (!checkNoChangesMode) { const hooksInitPhaseCompleted = (lView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === 3 /* InitPhaseState.InitPhaseCompleted */; if (hooksInitPhaseCompleted) { const preOrderCheckHooks = tView.preOrderCheckHooks; if (preOrderCheckHooks !== null) { executeCheckHooks(lView, preOrderCheckHooks, index); } } else { const preOrderHooks = tView.preOrderHooks; if (preOrderHooks !== null) { executeInitAndCheckHooks(lView, preOrderHooks, 0 /* InitPhaseState.OnInitHooksToBeRun */, index); } } } // We must set the selected index *after* running the hooks, because hooks may have side-effects // that cause other template functions to run, thus updating the selected index, which is global // state. If we run `setSelectedIndex` *before* we run the hooks, in some cases the selected index // will be altered by the time we leave the `ɵɵadvance` instruction. setSelectedIndex(index); } /** * A mapping of the @angular/core API surface used in generated expressions to the actual symbols. * * This should be kept up to date with the public exports of @angular/core. */ const angularCoreDiEnv = { 'ɵɵdefineInjectable': ɵɵdefineInjectable, 'ɵɵdefineInjector': ɵɵdefineInjector, 'ɵɵinject': ɵɵinject, 'ɵɵinvalidFactoryDep': ɵɵinvalidFactoryDep, 'resolveForwardRef': resolveForwardRef }; /** * Compile an Angular injectable according to its `Injectable` metadata, and patch the resulting * injectable def (`ɵprov`) onto the injectable type. */ function compileInjectable(type, meta) { let ngInjectableDef = null; let ngFactoryDef = null; // if NG_PROV_DEF is already defined on this class then don't overwrite it if (!type.hasOwnProperty(NG_PROV_DEF)) { Object.defineProperty(type, NG_PROV_DEF, { get: () => { if (ngInjectableDef === null) { const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'injectable', type }); ngInjectableDef = compiler.compileInjectable(angularCoreDiEnv, `ng:///${type.name}/ɵprov.js`, getInjectableMetadata(type, meta)); } return ngInjectableDef; } }); } // if NG_FACTORY_DEF is already defined on this class then don't overwrite it if (!type.hasOwnProperty(NG_FACTORY_DEF)) { Object.defineProperty(type, NG_FACTORY_DEF, { get: () => { if (ngFactoryDef === null) { const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'injectable', type }); ngFactoryDef = compiler.compileFactory(angularCoreDiEnv, `ng:///${type.name}/ɵfac.js`, { name: type.name, type, typeArgumentCount: 0, deps: reflectDependencies(type), target: compiler.FactoryTarget.Injectable }); } return ngFactoryDef; }, // Leave this configurable so that the factories from directives or pipes can take precedence. configurable: true }); } } const USE_VALUE = getClosureSafeProperty({ provide: String, useValue: getClosureSafeProperty }); function isUseClassProvider(meta) { return meta.useClass !== undefined; } function isUseValueProvider(meta) { return USE_VALUE in meta; } function isUseFactoryProvider(meta) { return meta.useFactory !== undefined; } function isUseExistingProvider(meta) { return meta.useExisting !== undefined; } function getInjectableMetadata(type, srcMeta) { // Allow the compilation of a class with a `@Injectable()` decorator without parameters const meta = srcMeta || { providedIn: null }; const compilerMeta = { name: type.name, type: type, typeArgumentCount: 0, providedIn: meta.providedIn }; if ((isUseClassProvider(meta) || isUseFactoryProvider(meta)) && meta.deps !== undefined) { compilerMeta.deps = convertDependencies(meta.deps); } // Check to see if the user explicitly provided a `useXxxx` property. if (isUseClassProvider(meta)) { compilerMeta.useClass = meta.useClass; } else if (isUseValueProvider(meta)) { compilerMeta.useValue = meta.useValue; } else if (isUseFactoryProvider(meta)) { compilerMeta.useFactory = meta.useFactory; } else if (isUseExistingProvider(meta)) { compilerMeta.useExisting = meta.useExisting; } return compilerMeta; } /** * Injectable decorator and metadata. * * @Annotation * @publicApi */ const Injectable = makeDecorator('Injectable', undefined, undefined, undefined, (type, meta) => compileInjectable(type, meta)); /** * Create a new `Injector` which is configured using a `defType` of `InjectorType<any>`s. * * @publicApi */ function createInjector(defType, parent = null, additionalProviders = null, name) { const injector = createInjectorWithoutInjectorInstances(defType, parent, additionalProviders, name); injector.resolveInjectorInitializers(); return injector; } /** * Creates a new injector without eagerly resolving its injector types. Can be used in places * where resolving the injector types immediately can lead to an infinite loop. The injector types * should be resolved at a later point by calling `_resolveInjectorDefTypes`. */ function createInjectorWithoutInjectorInstances(defType, parent = null, additionalProviders = null, name, scopes = new Set()) { const providers = [additionalProviders || EMPTY_ARRAY, importProvidersFrom(defType)]; name = name || (typeof defType === 'object' ? undefined : stringify(defType)); return new R3Injector(providers, parent || getNullInjector(), name || null, scopes); } /** * Concrete injectors implement this interface. Injectors are configured * with [providers](guide/glossary#provider) that associate * dependencies of various types with [injection tokens](guide/glossary#di-token). * * @see ["DI Providers"](guide/dependency-injection-providers). * @see `StaticProvider` * * @usageNotes * * The following example creates a service injector instance. * * {@example core/di/ts/provider_spec.ts region='ConstructorProvider'} * * ### Usage example * * {@example core/di/ts/injector_spec.ts region='Injector'} * * `Injector` returns itself when given `Injector` as a token: * * {@example core/di/ts/injector_spec.ts region='injectInjector'} * * @publicApi */ class Injector { static create(options, parent) { if (Array.isArray(options)) { return createInjector({ name: '' }, parent, options, ''); } else { const name = options.name ?? ''; return createInjector({ name }, options.parent, options.providers, name); } } } Injector.THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUND; Injector.NULL = /* @__PURE__ */new NullInjector(); /** @nocollapse */ Injector.ɵprov = ɵɵdefineInjectable({ token: Injector, providedIn: 'any', factory: () => ɵɵinject(INJECTOR) }); /** * @internal * @nocollapse */ Injector.__NG_ELEMENT_ID__ = -1 /* InjectorMarkers.Injector */; function findFirstClosedCycle(keys) { const res = []; for (let i = 0; i < keys.length; ++i) { if (res.indexOf(keys[i]) > -1) { res.push(keys[i]); return res; } res.push(keys[i]); } return res; } function constructResolvingPath(keys) { if (keys.length > 1) { const reversed = findFirstClosedCycle(keys.slice().reverse()); const tokenStrs = reversed.map(k => stringify(k.token)); return ' (' + tokenStrs.join(' -> ') + ')'; } return ''; } function injectionError(injector, key, constructResolvingMessage, originalError) { const keys = [key]; const errMsg = constructResolvingMessage(keys); const error = originalError ? wrappedError(errMsg, originalError) : Error(errMsg); error.addKey = addKey; error.keys = keys; error.injectors = [injector]; error.constructResolvingMessage = constructResolvingMessage; error[ERROR_ORIGINAL_ERROR] = originalError; return error; } function addKey(injector, key) { this.injectors.push(injector); this.keys.push(key); // Note: This updated message won't be reflected in the `.stack` property this.message = this.constructResolvingMessage(this.keys); } /** * Thrown when trying to retrieve a dependency by key from {@link Injector}, but the * {@link Injector} does not have a {@link Provider} for the given key. * * @usageNotes * ### Example * * ```typescript * class A { * constructor(b:B) {} * } * * expect(() => Injector.resolveAndCreate([A])).toThrowError(); * ``` */ function noProviderError(injector, key) { return injectionError(injector, key, function (keys) { const first = stringify(keys[0].token); return `No provider for ${first}!${constructResolvingPath(keys)}`; }); } /** * Thrown when dependencies form a cycle. * * @usageNotes * ### Example * * ```typescript * var injector = Injector.resolveAndCreate([ * {provide: "one", useFactory: (two) => "two", deps: [[new Inject("two")]]}, * {provide: "two", useFactory: (one) => "one", deps: [[new Inject("one")]]} * ]); * * expect(() => injector.get("one")).toThrowError(); * ``` * * Retrieving `A` or `B` throws a `CyclicDependencyError` as the graph above cannot be constructed. */ function cyclicDependencyError(injector, key) { return injectionError(injector, key, function (keys) { return `Cannot instantiate cyclic dependency!${constructResolvingPath(keys)}`; }); } /** * Thrown when a constructing type returns with an Error. * * The `InstantiationError` class contains the original error plus the dependency graph which caused * this object to be instantiated. * * @usageNotes * ### Example * * ```typescript * class A { * constructor() { * throw new Error('message'); * } * } * * var injector = Injector.resolveAndCreate([A]); * try { * injector.get(A); * } catch (e) { * expect(e instanceof InstantiationError).toBe(true); * expect(e.originalException.message).toEqual("message"); * expect(e.originalStack).toBeDefined(); * } * ``` */ function instantiationError(injector, originalException, originalStack, key) { return injectionError(injector, key, function (keys) { const first = stringify(keys[0].token); return `${originalException.message}: Error during instantiation of ${first}!${constructResolvingPath(keys)}.`; }, originalException); } /** * Thrown when an object other then {@link Provider} (or `Type`) is passed to {@link Injector} * creation. * * @usageNotes * ### Example * * ```typescript * expect(() => Injector.resolveAndCreate(["not a type"])).toThrowError(); * ``` */ function invalidProviderError(provider) { return Error(`Invalid provider - only instances of Provider and Type are allowed, got: ${provider}`); } /** * Thrown when the class has no annotation information. * * Lack of annotation information prevents the {@link Injector} from determining which dependencies * need to be injected into the constructor. * * @usageNotes * ### Example * * ```typescript * class A { * constructor(b) {} * } * * expect(() => Injector.resolveAndCreate([A])).toThrowError(); * ``` * * This error is also thrown when the class not marked with {@link Injectable} has parameter types. * * ```typescript * class B {} * * class A { * constructor(b:B) {} // no information about the parameter types of A is available at runtime. * } * * expect(() => Injector.resolveAndCreate([A,B])).toThrowError(); * ``` * */ function noAnnotationError(typeOrFunc, params) { const signature = []; for (let i = 0, ii = params.length; i < ii; i++) { const parameter = params[i]; if (!parameter || parameter.length == 0) { signature.push('?'); } else { signature.push(parameter.map(stringify).join(' ')); } } return Error('Cannot resolve all parameters for \'' + stringify(typeOrFunc) + '\'(' + signature.join(', ') + '). ' + 'Make sure that all the parameters are decorated with Inject or have valid type annotations and that \'' + stringify(typeOrFunc) + '\' is decorated with Injectable.'); } /** * Thrown when getting an object by index. * * @usageNotes * ### Example * * ```typescript * class A {} * * var injector = Injector.resolveAndCreate([A]); * * expect(() => injector.getAt(100)).toThrowError(); * ``` * */ function outOfBoundsError(index) { return Error(`Index ${index} is out-of-bounds.`); } // TODO: add a working example after alpha38 is released /** * Thrown when a multi provider and a regular provider are bound to the same token. * * @usageNotes * ### Example * * ```typescript * expect(() => Injector.resolveAndCreate([ * { provide: "Strings", useValue: "string1", multi: true}, * { provide: "Strings", useValue: "string2", multi: false} * ])).toThrowError(); * ``` */ function mixingMultiProvidersWithRegularProvidersError(provider1, provider2) { return Error(`Cannot mix multi providers and regular providers, got: ${provider1} ${provider2}`); } /** * A unique object used for retrieving items from the {@link ReflectiveInjector}. * * Keys have: * - a system-wide unique `id`. * - a `token`. * * `Key` is used internally by {@link ReflectiveInjector} because its system-wide unique `id` allows * the * injector to store created objects in a more efficient way. * * `Key` should not be created directly. {@link ReflectiveInjector} creates keys automatically when * resolving * providers. * * @deprecated No replacement * @publicApi */ class ReflectiveKey { /** * Private */ constructor(token, id) { this.token = token; this.id = id; if (!token) { throw new RuntimeError(208 /* RuntimeErrorCode.MISSING_INJECTION_TOKEN */, ngDevMode && 'Token must be defined!'); } this.displayName = stringify(this.token); } /** * Retrieves a `Key` for a token. */ static get(token) { return _globalKeyRegistry.get(resolveForwardRef(token)); } /** * @returns the number of keys registered in the system. */ static get numberOfKeys() { return _globalKeyRegistry.numberOfKeys; } } class KeyRegistry { constructor() { this._allKeys = new Map(); } get(token) { if (token instanceof ReflectiveKey) return token; if (this._allKeys.has(token)) { return this._allKeys.get(token); } const newKey = new ReflectiveKey(token, ReflectiveKey.numberOfKeys); this._allKeys.set(token, newKey); return newKey; } get numberOfKeys() { return this._allKeys.size; } } const _globalKeyRegistry = new KeyRegistry(); /** * `Dependency` is used by the framework to extend DI. * This is internal to Angular and should not be used directly. */ class ReflectiveDependency { constructor(key, optional, visibility) { this.key = key; this.optional = optional; this.visibility = visibility; } static fromKey(key) { return new ReflectiveDependency(key, false, null); } } const _EMPTY_LIST = []; class ResolvedReflectiveProvider_ { constructor(key, resolvedFactories, multiProvider) { this.key = key; this.resolvedFactories = resolvedFactories; this.multiProvider = multiProvider; this.resolvedFactory = this.resolvedFactories[0]; } } /** * An internal resolved representation of a factory function created by resolving `Provider`. * @publicApi */ class ResolvedReflectiveFactory { constructor( /** * Factory function which can return an instance of an object represented by a key. */ factory, /** * Arguments (dependencies) to the `factory` function. */ dependencies) { this.factory = factory; this.dependencies = dependencies; } } /** * Resolve a single provider. */ function resolveReflectiveFactory(provider) { let factoryFn; let resolvedDeps; if (provider.useClass) { const useClass = resolveForwardRef(provider.useClass); factoryFn = getReflect().factory(useClass); resolvedDeps = _dependenciesFor(useClass); } else if (provider.useExisting) { factoryFn = aliasInstance => aliasInstance; resolvedDeps = [ReflectiveDependency.fromKey(ReflectiveKey.get(provider.useExisting))]; } else if (provider.useFactory) { factoryFn = provider.useFactory; resolvedDeps = constructDependencies(provider.useFactory, provider.deps); } else { factoryFn = () => provider.useValue; resolvedDeps = _EMPTY_LIST; } return new ResolvedReflectiveFactory(factoryFn, resolvedDeps); } /** * Converts the `Provider` into `ResolvedProvider`. * * `Injector` internally only uses `ResolvedProvider`, `Provider` contains convenience provider * syntax. */ function resolveReflectiveProvider(provider) { return new ResolvedReflectiveProvider_(ReflectiveKey.get(provider.provide), [resolveReflectiveFactory(provider)], provider.multi || false); } /** * Resolve a list of Providers. */ function resolveReflectiveProviders(providers) { const normalized = _normalizeProviders(providers, []); const resolved = normalized.map(resolveReflectiveProvider); const resolvedProviderMap = mergeResolvedReflectiveProviders(resolved, new Map()); return Array.from(resolvedProviderMap.values()); } /** * Merges a list of ResolvedProviders into a list where each key is contained exactly once and * multi providers have been merged. */ function mergeResolvedReflectiveProviders(providers, normalizedProvidersMap) { for (let i = 0; i < providers.length; i++) { const provider = providers[i]; const existing = normalizedProvidersMap.get(provider.key.id); if (existing) { if (provider.multiProvider !== existing.multiProvider) { throw mixingMultiProvidersWithRegularProvidersError(existing, provider); } if (provider.multiProvider) { for (let j = 0; j < provider.resolvedFactories.length; j++) { existing.resolvedFactories.push(provider.resolvedFactories[j]); } } else { normalizedProvidersMap.set(provider.key.id, provider); } } else { let resolvedProvider; if (provider.multiProvider) { resolvedProvider = new ResolvedReflectiveProvider_(provider.key, provider.resolvedFactories.slice(), provider.multiProvider); } else { resolvedProvider = provider; } normalizedProvidersMap.set(provider.key.id, resolvedProvider); } } return normalizedProvidersMap; } function _normalizeProviders(providers, res) { providers.forEach(b => { if (b instanceof Type) { res.push({ provide: b, useClass: b }); } else if (b && typeof b == 'object' && b.provide !== undefined) { res.push(b); } else if (Array.isArray(b)) { _normalizeProviders(b, res); } else { throw invalidProviderError(b); } }); return res; } function constructDependencies(typeOrFunc, dependencies) { if (!dependencies) { return _dependenciesFor(typeOrFunc); } else { const params = dependencies.map(t => [t]); return dependencies.map(t => _extractToken(typeOrFunc, t, params)); } } function _dependenciesFor(typeOrFunc) { const params = getReflect().parameters(typeOrFunc); if (!params) return []; if (params.some(p => p == null)) { throw noAnnotationError(typeOrFunc, params); } return params.map(p => _extractToken(typeOrFunc, p, params)); } function _extractToken(typeOrFunc, metadata, params) { let token = null; let optional = false; if (!Array.isArray(metadata)) { if (metadata instanceof Inject) { return _createDependency(metadata.token, optional, null); } else { return _createDependency(metadata, optional, null); } } let visibility = null; for (let i = 0; i < metadata.length; ++i) { const paramMetadata = metadata[i]; if (paramMetadata instanceof Type) { token = paramMetadata; } else if (paramMetadata instanceof Inject) { token = paramMetadata.token; } else if (paramMetadata instanceof Optional) { optional = true; } else if (paramMetadata instanceof Self || paramMetadata instanceof SkipSelf) { visibility = paramMetadata; } else if (paramMetadata instanceof InjectionToken) { token = paramMetadata; } } token = resolveForwardRef(token); if (token != null) { return _createDependency(token, optional, visibility); } else { throw noAnnotationError(typeOrFunc, params); } } function _createDependency(token, optional, visibility) { return new ReflectiveDependency(ReflectiveKey.get(token), optional, visibility); } // Threshold for the dynamic version const UNDEFINED = {}; /** * A ReflectiveDependency injection container used for instantiating objects and resolving * dependencies. * * An `Injector` is a replacement for a `new` operator, which can automatically resolve the * constructor dependencies. * * In typical use, application code asks for the dependencies in the constructor and they are * resolved by the `Injector`. * * @usageNotes * ### Example * * The following example creates an `Injector` configured to create `Engine` and `Car`. * * ```typescript * @Injectable() * class Engine { * } * * @Injectable() * class Car { * constructor(public engine:Engine) {} * } * * var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]); * var car = injector.get(Car); * expect(car instanceof Car).toBe(true); * expect(car.engine instanceof Engine).toBe(true); * ``` * * Notice, we don't use the `new` operator because we explicitly want to have the `Injector` * resolve all of the object's dependencies automatically. * * TODO: delete in v14. * * @deprecated from v5 - slow and brings in a lot of code, Use `Injector.create` instead. * @publicApi */ class ReflectiveInjector { /** * Turns an array of provider definitions into an array of resolved providers. * * A resolution is a process of flattening multiple nested arrays and converting individual * providers into an array of `ResolvedReflectiveProvider`s. * * @usageNotes * ### Example * * ```typescript * @Injectable() * class Engine { * } * * @Injectable() * class Car { * constructor(public engine:Engine) {} * } * * var providers = ReflectiveInjector.resolve([Car, [[Engine]]]); * * expect(providers.length).toEqual(2); * * expect(providers[0] instanceof ResolvedReflectiveProvider).toBe(true); * expect(providers[0].key.displayName).toBe("Car"); * expect(providers[0].dependencies.length).toEqual(1); * expect(providers[0].factory).toBeDefined(); * * expect(providers[1].key.displayName).toBe("Engine"); * }); * ``` * */ static resolve(providers) { return resolveReflectiveProviders(providers); } /** * Resolves an array of providers and creates an injector from those providers. * * The passed-in providers can be an array of `Type`, `Provider`, * or a recursive array of more providers. * * @usageNotes * ### Example * * ```typescript * @Injectable() * class Engine { * } * * @Injectable() * class Car { * constructor(public engine:Engine) {} * } * * var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]); * expect(injector.get(Car) instanceof Car).toBe(true); * ``` */ static resolveAndCreate(providers, parent) { const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers); return ReflectiveInjector.fromResolvedProviders(ResolvedReflectiveProviders, parent); } /** * Creates an injector from previously resolved providers. * * This API is the recommended way to construct injectors in performance-sensitive parts. * * @usageNotes * ### Example * * ```typescript * @Injectable() * class Engine { * } * * @Injectable() * class Car { * constructor(public engine:Engine) {} * } * * var providers = ReflectiveInjector.resolve([Car, Engine]); * var injector = ReflectiveInjector.fromResolvedProviders(providers); * expect(injector.get(Car) instanceof Car).toBe(true); * ``` */ static fromResolvedProviders(providers, parent) { return new ReflectiveInjector_(providers, parent); } } class ReflectiveInjector_ { /** * Private */ constructor(_providers, _parent) { /** @internal */ this._constructionCounter = 0; this._providers = _providers; this.parent = _parent || null; const len = _providers.length; this.keyIds = []; this.objs = []; for (let i = 0; i < len; i++) { this.keyIds[i] = _providers[i].key.id; this.objs[i] = UNDEFINED; } } get(token, notFoundValue = THROW_IF_NOT_FOUND) { return this._getByKey(ReflectiveKey.get(token), null, notFoundValue); } resolveAndCreateChild(providers) { const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers); return this.createChildFromResolved(ResolvedReflectiveProviders); } createChildFromResolved(providers) { const inj = new ReflectiveInjector_(providers); inj.parent = this; return inj; } resolveAndInstantiate(provider) { return this.instantiateResolved(ReflectiveInjector.resolve([provider])[0]); } instantiateResolved(provider) { return this._instantiateProvider(provider); } getProviderAtIndex(index) { if (index < 0 || index >= this._providers.length) { throw outOfBoundsError(index); } return this._providers[index]; } /** @internal */ _new(provider) { if (this._constructionCounter++ > this._getMaxNumberOfObjects()) { throw cyclicDependencyError(this, provider.key); } return this._instantiateProvider(provider); } _getMaxNumberOfObjects() { return this.objs.length; } _instantiateProvider(provider) { if (provider.multiProvider) { const res = []; for (let i = 0; i < provider.resolvedFactories.length; ++i) { res[i] = this._instantiate(provider, provider.resolvedFactories[i]); } return res; } else { return this._instantiate(provider, provider.resolvedFactories[0]); } } _instantiate(provider, ResolvedReflectiveFactory) { const factory = ResolvedReflectiveFactory.factory; let deps; try { deps = ResolvedReflectiveFactory.dependencies.map(dep => this._getByReflectiveDependency(dep)); } catch (e) { if (e.addKey) { e.addKey(this, provider.key); } throw e; } let obj; try { obj = factory(...deps); } catch (e) { throw instantiationError(this, e, e.stack, provider.key); } return obj; } _getByReflectiveDependency(dep) { return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND); } _getByKey(key, visibility, notFoundValue) { if (key === ReflectiveInjector_.INJECTOR_KEY) { return this; } if (visibility instanceof Self) { return this._getByKeySelf(key, notFoundValue); } else { return this._getByKeyDefault(key, notFoundValue, visibility); } } _getObjByKeyId(keyId) { for (let i = 0; i < this.keyIds.length; i++) { if (this.keyIds[i] === keyId) { if (this.objs[i] === UNDEFINED) { this.objs[i] = this._new(this._providers[i]); } return this.objs[i]; } } return UNDEFINED; } /** @internal */ _throwOrNull(key, notFoundValue) { if (notFoundValue !== THROW_IF_NOT_FOUND) { return notFoundValue; } else { throw noProviderError(this, key); } } /** @internal */ _getByKeySelf(key, notFoundValue) { const obj = this._getObjByKeyId(key.id); return obj !== UNDEFINED ? obj : this._throwOrNull(key, notFoundValue); } /** @internal */ _getByKeyDefault(key, notFoundValue, visibility) { let inj; if (visibility instanceof SkipSelf) { inj = this.parent; } else { inj = this; } while (inj instanceof ReflectiveInjector_) { const inj_ = inj; const obj = inj_._getObjByKeyId(key.id); if (obj !== UNDEFINED) return obj; inj = inj_.parent; } if (inj !== null) { return inj.get(key.token, notFoundValue); } else { return this._throwOrNull(key, notFoundValue); } } get displayName() { const providers = _mapProviders(this, b => ' "' + b.key.displayName + '" ').join(', '); return `ReflectiveInjector(providers: [${providers}])`; } toString() { return this.displayName; } } ReflectiveInjector_.INJECTOR_KEY = /* @__PURE__ */ReflectiveKey.get(Injector); function _mapProviders(injector, fn) { const res = []; for (let i = 0; i < injector._providers.length; ++i) { res[i] = fn(injector.getProviderAtIndex(i)); } return res; } /** * @module * @description * The `di` module provides dependency injection container services. */ /** * This file should not be necessary because node resolution should just default to `./di/index`! * * However it does not seem to work and it breaks: * - //packages/animations/browser/test:test_web_chromium-local * - //packages/compiler-cli/test:extract_i18n * - //packages/compiler-cli/test:ngc * - //packages/compiler-cli/test:perform_watch * - //packages/compiler-cli/test/diagnostics:check_types * - //packages/compiler-cli/test/transformers:test * - //packages/compiler/test:test * - //tools/public_api_guard:core_api * * Remove this file once the above is solved or wait until `ngc` is deleted and then it should be * safe to delete this file. */ function ɵɵdirectiveInject(token, flags = InjectFlags.Default) { const lView = getLView(); // Fall back to inject() if view hasn't been created. This situation can happen in tests // if inject utilities are used before bootstrapping. if (lView === null) { // Verify that we will not get into infinite loop. ngDevMode && assertInjectImplementationNotEqual(ɵɵdirectiveInject); return ɵɵinject(token, flags); } const tNode = getCurrentTNode(); return getOrCreateInjectable(tNode, lView, resolveForwardRef(token), flags); } /** * Throws an error indicating that a factory function could not be generated by the compiler for a * particular class. * * This instruction allows the actual error message to be optimized away when ngDevMode is turned * off, saving bytes of generated code while still providing a good experience in dev mode. * * The name of the class is not mentioned here, but will be in the generated factory function name * and thus in the stack trace. * * @codeGenApi */ function ɵɵinvalidFactory() { const msg = ngDevMode ? `This constructor was not compatible with Dependency Injection.` : 'invalid'; throw new Error(msg); } /** * Invoke `HostBindingsFunction`s for view. * * This methods executes `TView.hostBindingOpCodes`. It is used to execute the * `HostBindingsFunction`s associated with the current `LView`. * * @param tView Current `TView`. * @param lView Current `LView`. */ function processHostBindingOpCodes(tView, lView) { const hostBindingOpCodes = tView.hostBindingOpCodes; if (hostBindingOpCodes === null) return; try { for (let i = 0; i < hostBindingOpCodes.length; i++) { const opCode = hostBindingOpCodes[i]; if (opCode < 0) { // Negative numbers are element indexes. setSelectedIndex(~opCode); } else { // Positive numbers are NumberTuple which store bindingRootIndex and directiveIndex. const directiveIdx = opCode; const bindingRootIndx = hostBindingOpCodes[++i]; const hostBindingFn = hostBindingOpCodes[++i]; setBindingRootForHostBindings(bindingRootIndx, directiveIdx); const context = lView[directiveIdx]; hostBindingFn(2 /* RenderFlags.Update */, context); } } } finally { setSelectedIndex(-1); } } /** Refreshes all content queries declared by directives in a given view */ function refreshContentQueries(tView, lView) { const contentQueries = tView.contentQueries; if (contentQueries !== null) { for (let i = 0; i < contentQueries.length; i += 2) { const queryStartIdx = contentQueries[i]; const directiveDefIdx = contentQueries[i + 1]; if (directiveDefIdx !== -1) { const directiveDef = tView.data[directiveDefIdx]; ngDevMode && assertDefined(directiveDef, 'DirectiveDef not found.'); ngDevMode && assertDefined(directiveDef.contentQueries, 'contentQueries function should be defined'); setCurrentQueryIndex(queryStartIdx); directiveDef.contentQueries(2 /* RenderFlags.Update */, lView[directiveDefIdx], directiveDefIdx); } } } } /** Refreshes child components in the current view (update mode). */ function refreshChildComponents(hostLView, components) { for (let i = 0; i < components.length; i++) { refreshComponent(hostLView, components[i]); } } /** Renders child components in the current view (creation mode). */ function renderChildComponents(hostLView, components) { for (let i = 0; i < components.length; i++) { renderComponent(hostLView, components[i]); } } function createLView(parentLView, tView, context, flags, host, tHostNode, rendererFactory, renderer, sanitizer, injector, embeddedViewInjector) { const lView = tView.blueprint.slice(); lView[HOST] = host; lView[FLAGS] = flags | 4 /* LViewFlags.CreationMode */ | 64 /* LViewFlags.Attached */ | 8 /* LViewFlags.FirstLViewPass */; if (embeddedViewInjector !== null || parentLView && parentLView[FLAGS] & 1024 /* LViewFlags.HasEmbeddedViewInjector */) { lView[FLAGS] |= 1024 /* LViewFlags.HasEmbeddedViewInjector */; } resetPreOrderHookFlags(lView); ngDevMode && tView.declTNode && parentLView && assertTNodeForLView(tView.declTNode, parentLView); lView[PARENT] = lView[DECLARATION_VIEW] = parentLView; lView[CONTEXT] = context; lView[RENDERER_FACTORY] = rendererFactory || parentLView && parentLView[RENDERER_FACTORY]; ngDevMode && assertDefined(lView[RENDERER_FACTORY], 'RendererFactory is required'); lView[RENDERER] = renderer || parentLView && parentLView[RENDERER]; ngDevMode && assertDefined(lView[RENDERER], 'Renderer is required'); lView[SANITIZER] = sanitizer || parentLView && parentLView[SANITIZER] || null; lView[INJECTOR$1] = injector || parentLView && parentLView[INJECTOR$1] || null; lView[T_HOST] = tHostNode; lView[ID] = getUniqueLViewId(); lView[EMBEDDED_VIEW_INJECTOR] = embeddedViewInjector; ngDevMode && assertEqual(tView.type == 2 /* TViewType.Embedded */ ? parentLView !== null : true, true, 'Embedded views must have parentLView'); lView[DECLARATION_COMPONENT_VIEW] = tView.type == 2 /* TViewType.Embedded */ ? parentLView[DECLARATION_COMPONENT_VIEW] : lView; return lView; } function getOrCreateTNode(tView, index, type, name, attrs) { ngDevMode && index !== 0 && // 0 are bogus nodes and they are OK. See `createContainerRef` in // `view_engine_compatibility` for additional context. assertGreaterThanOrEqual(index, HEADER_OFFSET, 'TNodes can\'t be in the LView header.'); // Keep this function short, so that the VM will inline it. ngDevMode && assertPureTNodeType(type); let tNode = tView.data[index]; if (tNode === null) { tNode = createTNodeAtIndex(tView, index, type, name, attrs); if (isInI18nBlock()) { // If we are in i18n block then all elements should be pre declared through `Placeholder` // See `TNodeType.Placeholder` and `LFrame.inI18n` for more context. // If the `TNode` was not pre-declared than it means it was not mentioned which means it was // removed, so we mark it as detached. tNode.flags |= 32 /* TNodeFlags.isDetached */; } } else if (tNode.type & 64 /* TNodeType.Placeholder */) { tNode.type = type; tNode.value = name; tNode.attrs = attrs; const parent = getCurrentParentTNode(); tNode.injectorIndex = parent === null ? -1 : parent.injectorIndex; ngDevMode && assertTNodeForTView(tNode, tView); ngDevMode && assertEqual(index, tNode.index, 'Expecting same index'); } setCurrentTNode(tNode, true); return tNode; } function createTNodeAtIndex(tView, index, type, name, attrs) { const currentTNode = getCurrentTNodePlaceholderOk(); const isParent = isCurrentTNodeParent(); const parent = isParent ? currentTNode : currentTNode && currentTNode.parent; // Parents cannot cross component boundaries because components will be used in multiple places. const tNode = tView.data[index] = createTNode(tView, parent, type, index, name, attrs); // Assign a pointer to the first child node of a given view. The first node is not always the one // at index 0, in case of i18n, index 0 can be the instruction `i18nStart` and the first node has // the index 1 or more, so we can't just check node index. if (tView.firstChild === null) { tView.firstChild = tNode; } if (currentTNode !== null) { if (isParent) { // FIXME(misko): This logic looks unnecessarily complicated. Could we simplify? if (currentTNode.child == null && tNode.parent !== null) { // We are in the same view, which means we are adding content node to the parent view. currentTNode.child = tNode; } } else { if (currentTNode.next === null) { // In the case of i18n the `currentTNode` may already be linked, in which case we don't want // to break the links which i18n created. currentTNode.next = tNode; tNode.prev = currentTNode; } } } return tNode; } /** * When elements are created dynamically after a view blueprint is created (e.g. through * i18nApply()), we need to adjust the blueprint for future * template passes. * * @param tView `TView` associated with `LView` * @param lView The `LView` containing the blueprint to adjust * @param numSlotsToAlloc The number of slots to alloc in the LView, should be >0 * @param initialValue Initial value to store in blueprint */ function allocExpando(tView, lView, numSlotsToAlloc, initialValue) { if (numSlotsToAlloc === 0) return -1; if (ngDevMode) { assertFirstCreatePass(tView); assertSame(tView, lView[TVIEW], '`LView` must be associated with `TView`!'); assertEqual(tView.data.length, lView.length, 'Expecting LView to be same size as TView'); assertEqual(tView.data.length, tView.blueprint.length, 'Expecting Blueprint to be same size as TView'); assertFirstUpdatePass(tView); } const allocIdx = lView.length; for (let i = 0; i < numSlotsToAlloc; i++) { lView.push(initialValue); tView.blueprint.push(initialValue); tView.data.push(null); } return allocIdx; } ////////////////////////// //// Render ////////////////////////// /** * Processes a view in the creation mode. This includes a number of steps in a specific order: * - creating view query functions (if any); * - executing a template function in the creation mode; * - updating static queries (if any); * - creating child components defined in a given view. */ function renderView(tView, lView, context) { ngDevMode && assertEqual(isCreationMode(lView), true, 'Should be run in creation mode'); enterView(lView); try { const viewQuery = tView.viewQuery; if (viewQuery !== null) { executeViewQueryFn(1 /* RenderFlags.Create */, viewQuery, context); } // Execute a template associated with this view, if it exists. A template function might not be // defined for the root component views. const templateFn = tView.template; if (templateFn !== null) { executeTemplate(tView, lView, templateFn, 1 /* RenderFlags.Create */, context); } // This needs to be set before children are processed to support recursive components. // This must be set to false immediately after the first creation run because in an // ngFor loop, all the views will be created together before update mode runs and turns // off firstCreatePass. If we don't set it here, instances will perform directive // matching, etc again and again. if (tView.firstCreatePass) { tView.firstCreatePass = false; } // We resolve content queries specifically marked as `static` in creation mode. Dynamic // content queries are resolved during change detection (i.e. update mode), after embedded // views are refreshed (see block above). if (tView.staticContentQueries) { refreshContentQueries(tView, lView); } // We must materialize query results before child components are processed // in case a child component has projected a container. The LContainer needs // to exist so the embedded views are properly attached by the container. if (tView.staticViewQueries) { executeViewQueryFn(2 /* RenderFlags.Update */, tView.viewQuery, context); } // Render child component views. const components = tView.components; if (components !== null) { renderChildComponents(lView, components); } } catch (error) { // If we didn't manage to get past the first template pass due to // an error, mark the view as corrupted so we can try to recover. if (tView.firstCreatePass) { tView.incompleteFirstPass = true; tView.firstCreatePass = false; } throw error; } finally { lView[FLAGS] &= ~4 /* LViewFlags.CreationMode */; leaveView(); } } /** * Processes a view in update mode. This includes a number of steps in a specific order: * - executing a template function in update mode; * - executing hooks; * - refreshing queries; * - setting host bindings; * - refreshing child (embedded and component) views. */ function refreshView(tView, lView, templateFn, context) { ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode'); const flags = lView[FLAGS]; if ((flags & 128 /* LViewFlags.Destroyed */) === 128 /* LViewFlags.Destroyed */) return; enterView(lView); // Check no changes mode is a dev only mode used to verify that bindings have not changed // since they were assigned. We do not want to execute lifecycle hooks in that mode. const isInCheckNoChangesPass = ngDevMode && isInCheckNoChangesMode(); try { resetPreOrderHookFlags(lView); setBindingIndex(tView.bindingStartIndex); if (templateFn !== null) { executeTemplate(tView, lView, templateFn, 2 /* RenderFlags.Update */, context); } const hooksInitPhaseCompleted = (flags & 3 /* LViewFlags.InitPhaseStateMask */) === 3 /* InitPhaseState.InitPhaseCompleted */; // execute pre-order hooks (OnInit, OnChanges, DoCheck) // PERF WARNING: do NOT extract this to a separate function without running benchmarks if (!isInCheckNoChangesPass) { if (hooksInitPhaseCompleted) { const preOrderCheckHooks = tView.preOrderCheckHooks; if (preOrderCheckHooks !== null) { executeCheckHooks(lView, preOrderCheckHooks, null); } } else { const preOrderHooks = tView.preOrderHooks; if (preOrderHooks !== null) { executeInitAndCheckHooks(lView, preOrderHooks, 0 /* InitPhaseState.OnInitHooksToBeRun */, null); } incrementInitPhaseFlags(lView, 0 /* InitPhaseState.OnInitHooksToBeRun */); } } // First mark transplanted views that are declared in this lView as needing a refresh at their // insertion points. This is needed to avoid the situation where the template is defined in this // `LView` but its declaration appears after the insertion component. markTransplantedViewsForRefresh(lView); refreshEmbeddedViews(lView); // Content query results must be refreshed before content hooks are called. if (tView.contentQueries !== null) { refreshContentQueries(tView, lView); } // execute content hooks (AfterContentInit, AfterContentChecked) // PERF WARNING: do NOT extract this to a separate function without running benchmarks if (!isInCheckNoChangesPass) { if (hooksInitPhaseCompleted) { const contentCheckHooks = tView.contentCheckHooks; if (contentCheckHooks !== null) { executeCheckHooks(lView, contentCheckHooks); } } else { const contentHooks = tView.contentHooks; if (contentHooks !== null) { executeInitAndCheckHooks(lView, contentHooks, 1 /* InitPhaseState.AfterContentInitHooksToBeRun */); } incrementInitPhaseFlags(lView, 1 /* InitPhaseState.AfterContentInitHooksToBeRun */); } } processHostBindingOpCodes(tView, lView); // Refresh child component views. const components = tView.components; if (components !== null) { refreshChildComponents(lView, components); } // View queries must execute after refreshing child components because a template in this view // could be inserted in a child component. If the view query executes before child component // refresh, the template might not yet be inserted. const viewQuery = tView.viewQuery; if (viewQuery !== null) { executeViewQueryFn(2 /* RenderFlags.Update */, viewQuery, context); } // execute view hooks (AfterViewInit, AfterViewChecked) // PERF WARNING: do NOT extract this to a separate function without running benchmarks if (!isInCheckNoChangesPass) { if (hooksInitPhaseCompleted) { const viewCheckHooks = tView.viewCheckHooks; if (viewCheckHooks !== null) { executeCheckHooks(lView, viewCheckHooks); } } else { const viewHooks = tView.viewHooks; if (viewHooks !== null) { executeInitAndCheckHooks(lView, viewHooks, 2 /* InitPhaseState.AfterViewInitHooksToBeRun */); } incrementInitPhaseFlags(lView, 2 /* InitPhaseState.AfterViewInitHooksToBeRun */); } } if (tView.firstUpdatePass === true) { // We need to make sure that we only flip the flag on successful `refreshView` only // Don't do this in `finally` block. // If we did this in `finally` block then an exception could block the execution of styling // instructions which in turn would be unable to insert themselves into the styling linked // list. The result of this would be that if the exception would not be throw on subsequent CD // the styling would be unable to process it data and reflect to the DOM. tView.firstUpdatePass = false; } // Do not reset the dirty state when running in check no changes mode. We don't want components // to behave differently depending on whether check no changes is enabled or not. For example: // Marking an OnPush component as dirty from within the `ngAfterViewInit` hook in order to // refresh a `NgClass` binding should work. If we would reset the dirty state in the check // no changes cycle, the component would be not be dirty for the next update pass. This would // be different in production mode where the component dirty state is not reset. if (!isInCheckNoChangesPass) { lView[FLAGS] &= ~(32 /* LViewFlags.Dirty */ | 8 /* LViewFlags.FirstLViewPass */); } if (lView[FLAGS] & 512 /* LViewFlags.RefreshTransplantedView */) { lView[FLAGS] &= ~512 /* LViewFlags.RefreshTransplantedView */; updateTransplantedViewCount(lView[PARENT], -1); } } finally { leaveView(); } } function executeTemplate(tView, lView, templateFn, rf, context) { const prevSelectedIndex = getSelectedIndex(); const isUpdatePhase = rf & 2 /* RenderFlags.Update */; try { setSelectedIndex(-1); if (isUpdatePhase && lView.length > HEADER_OFFSET) { // When we're updating, inherently select 0 so we don't // have to generate that instruction for most update blocks. selectIndexInternal(tView, lView, HEADER_OFFSET, !!ngDevMode && isInCheckNoChangesMode()); } const preHookType = isUpdatePhase ? 2 /* ProfilerEvent.TemplateUpdateStart */ : 0 /* ProfilerEvent.TemplateCreateStart */; profiler(preHookType, context); templateFn(rf, context); } finally { setSelectedIndex(prevSelectedIndex); const postHookType = isUpdatePhase ? 3 /* ProfilerEvent.TemplateUpdateEnd */ : 1 /* ProfilerEvent.TemplateCreateEnd */; profiler(postHookType, context); } } ////////////////////////// //// Element ////////////////////////// function executeContentQueries(tView, tNode, lView) { if (isContentQueryHost(tNode)) { const start = tNode.directiveStart; const end = tNode.directiveEnd; for (let directiveIndex = start; directiveIndex < end; directiveIndex++) { const def = tView.data[directiveIndex]; if (def.contentQueries) { def.contentQueries(1 /* RenderFlags.Create */, lView[directiveIndex], directiveIndex); } } } } /** * Creates directive instances. */ function createDirectivesInstances(tView, lView, tNode) { if (!getBindingsEnabled()) return; instantiateAllDirectives(tView, lView, tNode, getNativeByTNode(tNode, lView)); if ((tNode.flags & 64 /* TNodeFlags.hasHostBindings */) === 64 /* TNodeFlags.hasHostBindings */) { invokeDirectivesHostBindings(tView, lView, tNode); } } /** * Takes a list of local names and indices and pushes the resolved local variable values * to LView in the same order as they are loaded in the template with load(). */ function saveResolvedLocalsInData(viewData, tNode, localRefExtractor = getNativeByTNode) { const localNames = tNode.localNames; if (localNames !== null) { let localIndex = tNode.index + 1; for (let i = 0; i < localNames.length; i += 2) { const index = localNames[i + 1]; const value = index === -1 ? localRefExtractor(tNode, viewData) : viewData[index]; viewData[localIndex++] = value; } } } /** * Gets TView from a template function or creates a new TView * if it doesn't already exist. * * @param def ComponentDef * @returns TView */ function getOrCreateComponentTView(def) { const tView = def.tView; // Create a TView if there isn't one, or recreate it if the first create pass didn't // complete successfully since we can't know for sure whether it's in a usable shape. if (tView === null || tView.incompleteFirstPass) { // Declaration node here is null since this function is called when we dynamically create a // component and hence there is no declaration. const declTNode = null; return def.tView = createTView(1 /* TViewType.Component */, declTNode, def.template, def.decls, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery, def.schemas, def.consts); } return tView; } /** * Creates a TView instance * * @param type Type of `TView`. * @param declTNode Declaration location of this `TView`. * @param templateFn Template function * @param decls The number of nodes, local refs, and pipes in this template * @param directives Registry of directives for this view * @param pipes Registry of pipes for this view * @param viewQuery View queries for this view * @param schemas Schemas for this view * @param consts Constants for this view */ function createTView(type, declTNode, templateFn, decls, vars, directives, pipes, viewQuery, schemas, constsOrFactory) { ngDevMode && ngDevMode.tView++; const bindingStartIndex = HEADER_OFFSET + decls; // This length does not yet contain host bindings from child directives because at this point, // we don't know which directives are active on this template. As soon as a directive is matched // that has a host binding, we will update the blueprint with that def's hostVars count. const initialViewLength = bindingStartIndex + vars; const blueprint = createViewBlueprint(bindingStartIndex, initialViewLength); const consts = typeof constsOrFactory === 'function' ? constsOrFactory() : constsOrFactory; const tView = blueprint[TVIEW] = { type: type, blueprint: blueprint, template: templateFn, queries: null, viewQuery: viewQuery, declTNode: declTNode, data: blueprint.slice().fill(null, bindingStartIndex), bindingStartIndex: bindingStartIndex, expandoStartIndex: initialViewLength, hostBindingOpCodes: null, firstCreatePass: true, firstUpdatePass: true, staticViewQueries: false, staticContentQueries: false, preOrderHooks: null, preOrderCheckHooks: null, contentHooks: null, contentCheckHooks: null, viewHooks: null, viewCheckHooks: null, destroyHooks: null, cleanup: null, contentQueries: null, components: null, directiveRegistry: typeof directives === 'function' ? directives() : directives, pipeRegistry: typeof pipes === 'function' ? pipes() : pipes, firstChild: null, schemas: schemas, consts: consts, incompleteFirstPass: false }; if (ngDevMode) { // For performance reasons it is important that the tView retains the same shape during runtime. // (To make sure that all of the code is monomorphic.) For this reason we seal the object to // prevent class transitions. Object.seal(tView); } return tView; } function createViewBlueprint(bindingStartIndex, initialViewLength) { const blueprint = []; for (let i = 0; i < initialViewLength; i++) { blueprint.push(i < bindingStartIndex ? null : NO_CHANGE); } return blueprint; } /** * Locates the host native element, used for bootstrapping existing nodes into rendering pipeline. * * @param rendererFactory Factory function to create renderer instance. * @param elementOrSelector Render element or CSS selector to locate the element. * @param encapsulation View Encapsulation defined for component that requests host element. */ function locateHostElement(renderer, elementOrSelector, encapsulation) { // When using native Shadow DOM, do not clear host element to allow native slot projection const preserveContent = encapsulation === ViewEncapsulation$1.ShadowDom; return renderer.selectRootElement(elementOrSelector, preserveContent); } /** * Saves context for this cleanup function in LView.cleanupInstances. * * On the first template pass, saves in TView: * - Cleanup function * - Index of context we just saved in LView.cleanupInstances * * This function can also be used to store instance specific cleanup fns. In that case the `context` * is `null` and the function is store in `LView` (rather than it `TView`). */ function storeCleanupWithContext(tView, lView, context, cleanupFn) { const lCleanup = getOrCreateLViewCleanup(lView); if (context === null) { // If context is null that this is instance specific callback. These callbacks can only be // inserted after template shared instances. For this reason in ngDevMode we freeze the TView. if (ngDevMode) { Object.freeze(getOrCreateTViewCleanup(tView)); } lCleanup.push(cleanupFn); } else { lCleanup.push(context); if (tView.firstCreatePass) { getOrCreateTViewCleanup(tView).push(cleanupFn, lCleanup.length - 1); } } } function createTNode(tView, tParent, type, index, value, attrs) { ngDevMode && index !== 0 && // 0 are bogus nodes and they are OK. See `createContainerRef` in // `view_engine_compatibility` for additional context. assertGreaterThanOrEqual(index, HEADER_OFFSET, 'TNodes can\'t be in the LView header.'); ngDevMode && assertNotSame(attrs, undefined, '\'undefined\' is not valid value for \'attrs\''); ngDevMode && ngDevMode.tNode++; ngDevMode && tParent && assertTNodeForTView(tParent, tView); let injectorIndex = tParent ? tParent.injectorIndex : -1; const tNode = { type, index, insertBeforeIndex: null, injectorIndex, directiveStart: -1, directiveEnd: -1, directiveStylingLast: -1, componentOffset: -1, propertyBindings: null, flags: 0, providerIndexes: 0, value: value, attrs: attrs, mergedAttrs: null, localNames: null, initialInputs: undefined, inputs: null, outputs: null, tView: null, next: null, prev: null, projectionNext: null, child: null, parent: tParent, projection: null, styles: null, stylesWithoutHost: null, residualStyles: undefined, classes: null, classesWithoutHost: null, residualClasses: undefined, classBindings: 0, styleBindings: 0 }; if (ngDevMode) { // For performance reasons it is important that the tNode retains the same shape during runtime. // (To make sure that all of the code is monomorphic.) For this reason we seal the object to // prevent class transitions. Object.seal(tNode); } return tNode; } /** * Generates the `PropertyAliases` data structure from the provided input/output mapping. * @param aliasMap Input/output mapping from the directive definition. * @param directiveIndex Index of the directive. * @param propertyAliases Object in which to store the results. * @param hostDirectiveAliasMap Object used to alias or filter out properties for host directives. * If the mapping is provided, it'll act as an allowlist, as well as a mapping of what public * name inputs/outputs should be exposed under. */ function generatePropertyAliases(aliasMap, directiveIndex, propertyAliases, hostDirectiveAliasMap) { for (let publicName in aliasMap) { if (aliasMap.hasOwnProperty(publicName)) { propertyAliases = propertyAliases === null ? {} : propertyAliases; const internalName = aliasMap[publicName]; // If there are no host directive mappings, we want to remap using the alias map from the // definition itself. If there is an alias map, it has two functions: // 1. It serves as an allowlist of bindings that are exposed by the host directives. Only the // ones inside the host directive map will be exposed on the host. // 2. The public name of the property is aliased using the host directive alias map, rather // than the alias map from the definition. if (hostDirectiveAliasMap === null) { addPropertyAlias(propertyAliases, directiveIndex, publicName, internalName); } else if (hostDirectiveAliasMap.hasOwnProperty(publicName)) { addPropertyAlias(propertyAliases, directiveIndex, hostDirectiveAliasMap[publicName], internalName); } } } return propertyAliases; } function addPropertyAlias(propertyAliases, directiveIndex, publicName, internalName) { if (propertyAliases.hasOwnProperty(publicName)) { propertyAliases[publicName].push(directiveIndex, internalName); } else { propertyAliases[publicName] = [directiveIndex, internalName]; } } /** * Initializes data structures required to work with directive inputs and outputs. * Initialization is done for all directives matched on a given TNode. */ function initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefinitionMap) { ngDevMode && assertFirstCreatePass(tView); const start = tNode.directiveStart; const end = tNode.directiveEnd; const tViewData = tView.data; const tNodeAttrs = tNode.attrs; const inputsFromAttrs = []; let inputsStore = null; let outputsStore = null; for (let directiveIndex = start; directiveIndex < end; directiveIndex++) { const directiveDef = tViewData[directiveIndex]; const aliasData = hostDirectiveDefinitionMap ? hostDirectiveDefinitionMap.get(directiveDef) : null; const aliasedInputs = aliasData ? aliasData.inputs : null; const aliasedOutputs = aliasData ? aliasData.outputs : null; inputsStore = generatePropertyAliases(directiveDef.inputs, directiveIndex, inputsStore, aliasedInputs); outputsStore = generatePropertyAliases(directiveDef.outputs, directiveIndex, outputsStore, aliasedOutputs); // Do not use unbound attributes as inputs to structural directives, since structural // directive inputs can only be set using microsyntax (e.g. `<div *dir="exp">`). // TODO(FW-1930): microsyntax expressions may also contain unbound/static attributes, which // should be set for inline templates. const initialInputs = inputsStore !== null && tNodeAttrs !== null && !isInlineTemplate(tNode) ? generateInitialInputs(inputsStore, directiveIndex, tNodeAttrs) : null; inputsFromAttrs.push(initialInputs); } if (inputsStore !== null) { if (inputsStore.hasOwnProperty('class')) { tNode.flags |= 8 /* TNodeFlags.hasClassInput */; } if (inputsStore.hasOwnProperty('style')) { tNode.flags |= 16 /* TNodeFlags.hasStyleInput */; } } tNode.initialInputs = inputsFromAttrs; tNode.inputs = inputsStore; tNode.outputs = outputsStore; } /** * Mapping between attributes names that don't correspond to their element property names. * * Performance note: this function is written as a series of if checks (instead of, say, a property * object lookup) for performance reasons - the series of `if` checks seems to be the fastest way of * mapping property names. Do NOT change without benchmarking. * * Note: this mapping has to be kept in sync with the equally named mapping in the template * type-checking machinery of ngtsc. */ function mapPropName(name) { if (name === 'class') return 'className'; if (name === 'for') return 'htmlFor'; if (name === 'formaction') return 'formAction'; if (name === 'innerHtml') return 'innerHTML'; if (name === 'readonly') return 'readOnly'; if (name === 'tabindex') return 'tabIndex'; return name; } function elementPropertyInternal(tView, tNode, lView, propName, value, renderer, sanitizer, nativeOnly) { ngDevMode && assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.'); const element = getNativeByTNode(tNode, lView); let inputData = tNode.inputs; let dataValue; if (!nativeOnly && inputData != null && (dataValue = inputData[propName])) { setInputsForProperty(tView, lView, dataValue, propName, value); if (isComponentHost(tNode)) markDirtyIfOnPush(lView, tNode.index); if (ngDevMode) { setNgReflectProperties(lView, element, tNode.type, dataValue, value); } } else if (tNode.type & 3 /* TNodeType.AnyRNode */) { propName = mapPropName(propName); if (ngDevMode) { validateAgainstEventProperties(propName); if (!isPropertyValid(element, propName, tNode.value, tView.schemas)) { handleUnknownPropertyError(propName, tNode.value, tNode.type, lView); } ngDevMode.rendererSetProperty++; } // It is assumed that the sanitizer is only added when the compiler determines that the // property is risky, so sanitization can be done without further checks. value = sanitizer != null ? sanitizer(value, tNode.value || '', propName) : value; renderer.setProperty(element, propName, value); } else if (tNode.type & 12 /* TNodeType.AnyContainer */) { // If the node is a container and the property didn't // match any of the inputs or schemas we should throw. if (ngDevMode && !matchingSchemas(tView.schemas, tNode.value)) { handleUnknownPropertyError(propName, tNode.value, tNode.type, lView); } } } /** If node is an OnPush component, marks its LView dirty. */ function markDirtyIfOnPush(lView, viewIndex) { ngDevMode && assertLView(lView); const childComponentLView = getComponentLViewByIndex(viewIndex, lView); if (!(childComponentLView[FLAGS] & 16 /* LViewFlags.CheckAlways */)) { childComponentLView[FLAGS] |= 32 /* LViewFlags.Dirty */; } } function setNgReflectProperty(lView, element, type, attrName, value) { const renderer = lView[RENDERER]; attrName = normalizeDebugBindingName(attrName); const debugValue = normalizeDebugBindingValue(value); if (type & 3 /* TNodeType.AnyRNode */) { if (value == null) { renderer.removeAttribute(element, attrName); } else { renderer.setAttribute(element, attrName, debugValue); } } else { const textContent = escapeCommentText(`bindings=${JSON.stringify({ [attrName]: debugValue }, null, 2)}`); renderer.setValue(element, textContent); } } function setNgReflectProperties(lView, element, type, dataValue, value) { if (type & (3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */)) { /** * dataValue is an array containing runtime input or output names for the directives: * i+0: directive instance index * i+1: privateName * * e.g. [0, 'change', 'change-minified'] * we want to set the reflected property with the privateName: dataValue[i+1] */ for (let i = 0; i < dataValue.length; i += 2) { setNgReflectProperty(lView, element, type, dataValue[i + 1], value); } } } /** * Resolve the matched directives on a node. */ function resolveDirectives(tView, lView, tNode, localRefs) { // Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in // tsickle. ngDevMode && assertFirstCreatePass(tView); if (getBindingsEnabled()) { const exportsMap = localRefs === null ? null : { '': -1 }; const matchResult = findDirectiveDefMatches(tView, tNode); let directiveDefs; let hostDirectiveDefs; if (matchResult === null) { directiveDefs = hostDirectiveDefs = null; } else { [directiveDefs, hostDirectiveDefs] = matchResult; } if (directiveDefs !== null) { initializeDirectives(tView, lView, tNode, directiveDefs, exportsMap, hostDirectiveDefs); } if (exportsMap) cacheMatchingLocalNames(tNode, localRefs, exportsMap); } // Merge the template attrs last so that they have the highest priority. tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs); } /** Initializes the data structures necessary for a list of directives to be instantiated. */ function initializeDirectives(tView, lView, tNode, directives, exportsMap, hostDirectiveDefs) { ngDevMode && assertFirstCreatePass(tView); // Publishes the directive types to DI so they can be injected. Needs to // happen in a separate pass before the TNode flags have been initialized. for (let i = 0; i < directives.length; i++) { diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, directives[i].type); } initTNodeFlags(tNode, tView.data.length, directives.length); // When the same token is provided by several directives on the same node, some rules apply in // the viewEngine: // - viewProviders have priority over providers // - the last directive in NgModule.declarations has priority over the previous one // So to match these rules, the order in which providers are added in the arrays is very // important. for (let i = 0; i < directives.length; i++) { const def = directives[i]; if (def.providersResolver) def.providersResolver(def); } let preOrderHooksFound = false; let preOrderCheckHooksFound = false; let directiveIdx = allocExpando(tView, lView, directives.length, null); ngDevMode && assertSame(directiveIdx, tNode.directiveStart, 'TNode.directiveStart should point to just allocated space'); for (let i = 0; i < directives.length; i++) { const def = directives[i]; // Merge the attrs in the order of matches. This assumes that the first directive is the // component itself, so that the component has the least priority. tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs); configureViewWithDirective(tView, tNode, lView, directiveIdx, def); saveNameToExportMap(directiveIdx, def, exportsMap); if (def.contentQueries !== null) tNode.flags |= 4 /* TNodeFlags.hasContentQuery */; if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0) tNode.flags |= 64 /* TNodeFlags.hasHostBindings */; const lifeCycleHooks = def.type.prototype; // Only push a node index into the preOrderHooks array if this is the first // pre-order hook found on this node. if (!preOrderHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngOnInit || lifeCycleHooks.ngDoCheck)) { // We will push the actual hook function into this array later during dir instantiation. // We cannot do it now because we must ensure hooks are registered in the same // order that directives are created (i.e. injection order). (tView.preOrderHooks ?? (tView.preOrderHooks = [])).push(tNode.index); preOrderHooksFound = true; } if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) { (tView.preOrderCheckHooks ?? (tView.preOrderCheckHooks = [])).push(tNode.index); preOrderCheckHooksFound = true; } directiveIdx++; } initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefs); } /** * Add `hostBindings` to the `TView.hostBindingOpCodes`. * * @param tView `TView` to which the `hostBindings` should be added. * @param tNode `TNode` the element which contains the directive * @param directiveIdx Directive index in view. * @param directiveVarsIdx Where will the directive's vars be stored * @param def `ComponentDef`/`DirectiveDef`, which contains the `hostVars`/`hostBindings` to add. */ function registerHostBindingOpCodes(tView, tNode, directiveIdx, directiveVarsIdx, def) { ngDevMode && assertFirstCreatePass(tView); const hostBindings = def.hostBindings; if (hostBindings) { let hostBindingOpCodes = tView.hostBindingOpCodes; if (hostBindingOpCodes === null) { hostBindingOpCodes = tView.hostBindingOpCodes = []; } const elementIndx = ~tNode.index; if (lastSelectedElementIdx(hostBindingOpCodes) != elementIndx) { // Conditionally add select element so that we are more efficient in execution. // NOTE: this is strictly not necessary and it trades code size for runtime perf. // (We could just always add it.) hostBindingOpCodes.push(elementIndx); } hostBindingOpCodes.push(directiveIdx, directiveVarsIdx, hostBindings); } } /** * Returns the last selected element index in the `HostBindingOpCodes` * * For perf reasons we don't need to update the selected element index in `HostBindingOpCodes` only * if it changes. This method returns the last index (or '0' if not found.) * * Selected element index are only the ones which are negative. */ function lastSelectedElementIdx(hostBindingOpCodes) { let i = hostBindingOpCodes.length; while (i > 0) { const value = hostBindingOpCodes[--i]; if (typeof value === 'number' && value < 0) { return value; } } return 0; } /** * Instantiate all the directives that were previously resolved on the current node. */ function instantiateAllDirectives(tView, lView, tNode, native) { const start = tNode.directiveStart; const end = tNode.directiveEnd; // The component view needs to be created before creating the node injector // since it is used to inject some special symbols like `ChangeDetectorRef`. if (isComponentHost(tNode)) { ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */); addComponentLogic(lView, tNode, tView.data[start + tNode.componentOffset]); } if (!tView.firstCreatePass) { getOrCreateNodeInjectorForNode(tNode, lView); } attachPatchData(native, lView); const initialInputs = tNode.initialInputs; for (let i = start; i < end; i++) { const def = tView.data[i]; const directive = getNodeInjectable(lView, tView, i, tNode); attachPatchData(directive, lView); if (initialInputs !== null) { setInputsFromAttrs(lView, i - start, directive, def, tNode, initialInputs); } if (isComponentDef(def)) { const componentView = getComponentLViewByIndex(tNode.index, lView); componentView[CONTEXT] = getNodeInjectable(lView, tView, i, tNode); } } } function invokeDirectivesHostBindings(tView, lView, tNode) { const start = tNode.directiveStart; const end = tNode.directiveEnd; const elementIndex = tNode.index; const currentDirectiveIndex = getCurrentDirectiveIndex(); try { setSelectedIndex(elementIndex); for (let dirIndex = start; dirIndex < end; dirIndex++) { const def = tView.data[dirIndex]; const directive = lView[dirIndex]; setCurrentDirectiveIndex(dirIndex); if (def.hostBindings !== null || def.hostVars !== 0 || def.hostAttrs !== null) { invokeHostBindingsInCreationMode(def, directive); } } } finally { setSelectedIndex(-1); setCurrentDirectiveIndex(currentDirectiveIndex); } } /** * Invoke the host bindings in creation mode. * * @param def `DirectiveDef` which may contain the `hostBindings` function. * @param directive Instance of directive. */ function invokeHostBindingsInCreationMode(def, directive) { if (def.hostBindings !== null) { def.hostBindings(1 /* RenderFlags.Create */, directive); } } /** * Matches the current node against all available selectors. * If a component is matched (at most one), it is returned in first position in the array. */ function findDirectiveDefMatches(tView, tNode) { ngDevMode && assertFirstCreatePass(tView); ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */); const registry = tView.directiveRegistry; let matches = null; let hostDirectiveDefs = null; if (registry) { for (let i = 0; i < registry.length; i++) { const def = registry[i]; if (isNodeMatchingSelectorList(tNode, def.selectors, /* isProjectionMode */false)) { matches || (matches = []); if (isComponentDef(def)) { if (ngDevMode) { assertTNodeType(tNode, 2 /* TNodeType.Element */, `"${tNode.value}" tags cannot be used as component hosts. ` + `Please use a different tag to activate the ${stringify(def.type)} component.`); if (isComponentHost(tNode)) { throwMultipleComponentError(tNode, matches.find(isComponentDef).type, def.type); } } // Components are inserted at the front of the matches array so that their lifecycle // hooks run before any directive lifecycle hooks. This appears to be for ViewEngine // compatibility. This logic doesn't make sense with host directives, because it // would allow the host directives to undo any overrides the host may have made. // To handle this case, the host directives of components are inserted at the beginning // of the array, followed by the component. As such, the insertion order is as follows: // 1. Host directives belonging to the selector-matched component. // 2. Selector-matched component. // 3. Host directives belonging to selector-matched directives. // 4. Selector-matched directives. if (def.findHostDirectiveDefs !== null) { const hostDirectiveMatches = []; hostDirectiveDefs = hostDirectiveDefs || new Map(); def.findHostDirectiveDefs(def, hostDirectiveMatches, hostDirectiveDefs); // Add all host directives declared on this component, followed by the component itself. // Host directives should execute first so the host has a chance to override changes // to the DOM made by them. matches.unshift(...hostDirectiveMatches, def); // Component is offset starting from the beginning of the host directives array. const componentOffset = hostDirectiveMatches.length; markAsComponentHost(tView, tNode, componentOffset); } else { // No host directives on this component, just add the // component def to the beginning of the matches. matches.unshift(def); markAsComponentHost(tView, tNode, 0); } } else { // Append any host directives to the matches first. hostDirectiveDefs = hostDirectiveDefs || new Map(); def.findHostDirectiveDefs?.(def, matches, hostDirectiveDefs); matches.push(def); } } } } return matches === null ? null : [matches, hostDirectiveDefs]; } /** * Marks a given TNode as a component's host. This consists of: * - setting the component offset on the TNode. * - storing index of component's host element so it will be queued for view refresh during CD. */ function markAsComponentHost(tView, hostTNode, componentOffset) { ngDevMode && assertFirstCreatePass(tView); ngDevMode && assertGreaterThan(componentOffset, -1, 'componentOffset must be great than -1'); hostTNode.componentOffset = componentOffset; (tView.components ?? (tView.components = [])).push(hostTNode.index); } /** Caches local names and their matching directive indices for query and template lookups. */ function cacheMatchingLocalNames(tNode, localRefs, exportsMap) { if (localRefs) { const localNames = tNode.localNames = []; // Local names must be stored in tNode in the same order that localRefs are defined // in the template to ensure the data is loaded in the same slots as their refs // in the template (for template queries). for (let i = 0; i < localRefs.length; i += 2) { const index = exportsMap[localRefs[i + 1]]; if (index == null) throw new RuntimeError(-301 /* RuntimeErrorCode.EXPORT_NOT_FOUND */, ngDevMode && `Export of name '${localRefs[i + 1]}' not found!`); localNames.push(localRefs[i], index); } } } /** * Builds up an export map as directives are created, so local refs can be quickly mapped * to their directive instances. */ function saveNameToExportMap(directiveIdx, def, exportsMap) { if (exportsMap) { if (def.exportAs) { for (let i = 0; i < def.exportAs.length; i++) { exportsMap[def.exportAs[i]] = directiveIdx; } } if (isComponentDef(def)) exportsMap[''] = directiveIdx; } } /** * Initializes the flags on the current node, setting all indices to the initial index, * the directive count to 0, and adding the isComponent flag. * @param index the initial index */ function initTNodeFlags(tNode, index, numberOfDirectives) { ngDevMode && assertNotEqual(numberOfDirectives, tNode.directiveEnd - tNode.directiveStart, 'Reached the max number of directives'); tNode.flags |= 1 /* TNodeFlags.isDirectiveHost */; // When the first directive is created on a node, save the index tNode.directiveStart = index; tNode.directiveEnd = index + numberOfDirectives; tNode.providerIndexes = index; } /** * Setup directive for instantiation. * * We need to create a `NodeInjectorFactory` which is then inserted in both the `Blueprint` as well * as `LView`. `TView` gets the `DirectiveDef`. * * @param tView `TView` * @param tNode `TNode` * @param lView `LView` * @param directiveIndex Index where the directive will be stored in the Expando. * @param def `DirectiveDef` */ function configureViewWithDirective(tView, tNode, lView, directiveIndex, def) { ngDevMode && assertGreaterThanOrEqual(directiveIndex, HEADER_OFFSET, 'Must be in Expando section'); tView.data[directiveIndex] = def; const directiveFactory = def.factory || (def.factory = getFactoryDef(def.type, true)); // Even though `directiveFactory` will already be using `ɵɵdirectiveInject` in its generated code, // we also want to support `inject()` directly from the directive constructor context so we set // `ɵɵdirectiveInject` as the inject implementation here too. const nodeInjectorFactory = new NodeInjectorFactory(directiveFactory, isComponentDef(def), ɵɵdirectiveInject); tView.blueprint[directiveIndex] = nodeInjectorFactory; lView[directiveIndex] = nodeInjectorFactory; registerHostBindingOpCodes(tView, tNode, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def); } function addComponentLogic(lView, hostTNode, def) { const native = getNativeByTNode(hostTNode, lView); const tView = getOrCreateComponentTView(def); // Only component views should be added to the view tree directly. Embedded views are // accessed through their containers because they may be removed / re-added later. const rendererFactory = lView[RENDERER_FACTORY]; const componentView = addToViewTree(lView, createLView(lView, tView, null, def.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, native, hostTNode, rendererFactory, rendererFactory.createRenderer(native, def), null, null, null)); // Component view will always be created before any injected LContainers, // so this is a regular element, wrap it with the component view lView[hostTNode.index] = componentView; } function elementAttributeInternal(tNode, lView, name, value, sanitizer, namespace) { if (ngDevMode) { assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.'); validateAgainstEventAttributes(name); assertTNodeType(tNode, 2 /* TNodeType.Element */, `Attempted to set attribute \`${name}\` on a container node. ` + `Host bindings are not valid on ng-container or ng-template.`); } const element = getNativeByTNode(tNode, lView); setElementAttribute(lView[RENDERER], element, namespace, tNode.value, name, value, sanitizer); } function setElementAttribute(renderer, element, namespace, tagName, name, value, sanitizer) { if (value == null) { ngDevMode && ngDevMode.rendererRemoveAttribute++; renderer.removeAttribute(element, name, namespace); } else { ngDevMode && ngDevMode.rendererSetAttribute++; const strValue = sanitizer == null ? renderStringify(value) : sanitizer(value, tagName || '', name); renderer.setAttribute(element, name, strValue, namespace); } } /** * Sets initial input properties on directive instances from attribute data * * @param lView Current LView that is being processed. * @param directiveIndex Index of the directive in directives array * @param instance Instance of the directive on which to set the initial inputs * @param def The directive def that contains the list of inputs * @param tNode The static data for this node */ function setInputsFromAttrs(lView, directiveIndex, instance, def, tNode, initialInputData) { const initialInputs = initialInputData[directiveIndex]; if (initialInputs !== null) { const setInput = def.setInput; for (let i = 0; i < initialInputs.length;) { const publicName = initialInputs[i++]; const privateName = initialInputs[i++]; const value = initialInputs[i++]; if (setInput !== null) { def.setInput(instance, value, publicName, privateName); } else { instance[privateName] = value; } if (ngDevMode) { const nativeElement = getNativeByTNode(tNode, lView); setNgReflectProperty(lView, nativeElement, tNode.type, privateName, value); } } } } /** * Generates initialInputData for a node and stores it in the template's static storage * so subsequent template invocations don't have to recalculate it. * * initialInputData is an array containing values that need to be set as input properties * for directives on this node, but only once on creation. We need this array to support * the case where you set an @Input property of a directive using attribute-like syntax. * e.g. if you have a `name` @Input, you can set it once like this: * * <my-component name="Bess"></my-component> * * @param inputs Input alias map that was generated from the directive def inputs. * @param directiveIndex Index of the directive that is currently being processed. * @param attrs Static attrs on this node. */ function generateInitialInputs(inputs, directiveIndex, attrs) { let inputsToStore = null; let i = 0; while (i < attrs.length) { const attrName = attrs[i]; if (attrName === 0 /* AttributeMarker.NamespaceURI */) { // We do not allow inputs on namespaced attributes. i += 4; continue; } else if (attrName === 5 /* AttributeMarker.ProjectAs */) { // Skip over the `ngProjectAs` value. i += 2; continue; } // If we hit any other attribute markers, we're done anyway. None of those are valid inputs. if (typeof attrName === 'number') break; if (inputs.hasOwnProperty(attrName)) { if (inputsToStore === null) inputsToStore = []; // Find the input's public name from the input store. Note that we can be found easier // through the directive def, but we want to do it using the inputs store so that it can // account for host directive aliases. const inputConfig = inputs[attrName]; for (let j = 0; j < inputConfig.length; j += 2) { if (inputConfig[j] === directiveIndex) { inputsToStore.push(attrName, inputConfig[j + 1], attrs[i + 1]); // A directive can't have multiple inputs with the same name so we can break here. break; } } } i += 2; } return inputsToStore; } ////////////////////////// //// ViewContainer & View ////////////////////////// /** * Creates a LContainer, either from a container instruction, or for a ViewContainerRef. * * @param hostNative The host element for the LContainer * @param hostTNode The host TNode for the LContainer * @param currentView The parent view of the LContainer * @param native The native comment element * @param isForViewContainerRef Optional a flag indicating the ViewContainerRef case * @returns LContainer */ function createLContainer(hostNative, currentView, native, tNode) { ngDevMode && assertLView(currentView); const lContainer = [hostNative, true, false, currentView, null, 0, tNode, native, null, null // moved views ]; ngDevMode && assertEqual(lContainer.length, CONTAINER_HEADER_OFFSET, 'Should allocate correct number of slots for LContainer header.'); return lContainer; } /** * Goes over embedded views (ones created through ViewContainerRef APIs) and refreshes * them by executing an associated template function. */ function refreshEmbeddedViews(lView) { for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) { for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) { const embeddedLView = lContainer[i]; const embeddedTView = embeddedLView[TVIEW]; ngDevMode && assertDefined(embeddedTView, 'TView must be allocated'); if (viewAttachedToChangeDetector(embeddedLView)) { refreshView(embeddedTView, embeddedLView, embeddedTView.template, embeddedLView[CONTEXT]); } } } } /** * Mark transplanted views as needing to be refreshed at their insertion points. * * @param lView The `LView` that may have transplanted views. */ function markTransplantedViewsForRefresh(lView) { for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) { if (!lContainer[HAS_TRANSPLANTED_VIEWS]) continue; const movedViews = lContainer[MOVED_VIEWS]; ngDevMode && assertDefined(movedViews, 'Transplanted View flags set but missing MOVED_VIEWS'); for (let i = 0; i < movedViews.length; i++) { const movedLView = movedViews[i]; const insertionLContainer = movedLView[PARENT]; ngDevMode && assertLContainer(insertionLContainer); // We don't want to increment the counter if the moved LView was already marked for // refresh. if ((movedLView[FLAGS] & 512 /* LViewFlags.RefreshTransplantedView */) === 0) { updateTransplantedViewCount(insertionLContainer, 1); } // Note, it is possible that the `movedViews` is tracking views that are transplanted *and* // those that aren't (declaration component === insertion component). In the latter case, // it's fine to add the flag, as we will clear it immediately in // `refreshEmbeddedViews` for the view currently being refreshed. movedLView[FLAGS] |= 512 /* LViewFlags.RefreshTransplantedView */; } } } ///////////// /** * Refreshes components by entering the component view and processing its bindings, queries, etc. * * @param componentHostIdx Element index in LView[] (adjusted for HEADER_OFFSET) */ function refreshComponent(hostLView, componentHostIdx) { ngDevMode && assertEqual(isCreationMode(hostLView), false, 'Should be run in update mode'); const componentView = getComponentLViewByIndex(componentHostIdx, hostLView); // Only attached components that are CheckAlways or OnPush and dirty should be refreshed if (viewAttachedToChangeDetector(componentView)) { const tView = componentView[TVIEW]; if (componentView[FLAGS] & (16 /* LViewFlags.CheckAlways */ | 32 /* LViewFlags.Dirty */)) { refreshView(tView, componentView, tView.template, componentView[CONTEXT]); } else if (componentView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) { // Only attached components that are CheckAlways or OnPush and dirty should be refreshed refreshContainsDirtyView(componentView); } } } /** * Refreshes all transplanted views marked with `LViewFlags.RefreshTransplantedView` that are * children or descendants of the given lView. * * @param lView The lView which contains descendant transplanted views that need to be refreshed. */ function refreshContainsDirtyView(lView) { for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) { for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) { const embeddedLView = lContainer[i]; if (viewAttachedToChangeDetector(embeddedLView)) { if (embeddedLView[FLAGS] & 512 /* LViewFlags.RefreshTransplantedView */) { const embeddedTView = embeddedLView[TVIEW]; ngDevMode && assertDefined(embeddedTView, 'TView must be allocated'); refreshView(embeddedTView, embeddedLView, embeddedTView.template, embeddedLView[CONTEXT]); } else if (embeddedLView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) { refreshContainsDirtyView(embeddedLView); } } } } const tView = lView[TVIEW]; // Refresh child component views. const components = tView.components; if (components !== null) { for (let i = 0; i < components.length; i++) { const componentView = getComponentLViewByIndex(components[i], lView); // Only attached components that are CheckAlways or OnPush and dirty should be refreshed if (viewAttachedToChangeDetector(componentView) && componentView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) { refreshContainsDirtyView(componentView); } } } } function renderComponent(hostLView, componentHostIdx) { ngDevMode && assertEqual(isCreationMode(hostLView), true, 'Should be run in creation mode'); const componentView = getComponentLViewByIndex(componentHostIdx, hostLView); const componentTView = componentView[TVIEW]; syncViewWithBlueprint(componentTView, componentView); renderView(componentTView, componentView, componentView[CONTEXT]); } /** * Syncs an LView instance with its blueprint if they have gotten out of sync. * * Typically, blueprints and their view instances should always be in sync, so the loop here * will be skipped. However, consider this case of two components side-by-side: * * App template: * ``` * <comp></comp> * <comp></comp> * ``` * * The following will happen: * 1. App template begins processing. * 2. First <comp> is matched as a component and its LView is created. * 3. Second <comp> is matched as a component and its LView is created. * 4. App template completes processing, so it's time to check child templates. * 5. First <comp> template is checked. It has a directive, so its def is pushed to blueprint. * 6. Second <comp> template is checked. Its blueprint has been updated by the first * <comp> template, but its LView was created before this update, so it is out of sync. * * Note that embedded views inside ngFor loops will never be out of sync because these views * are processed as soon as they are created. * * @param tView The `TView` that contains the blueprint for syncing * @param lView The view to sync */ function syncViewWithBlueprint(tView, lView) { for (let i = lView.length; i < tView.blueprint.length; i++) { lView.push(tView.blueprint[i]); } } /** * Adds LView or LContainer to the end of the current view tree. * * This structure will be used to traverse through nested views to remove listeners * and call onDestroy callbacks. * * @param lView The view where LView or LContainer should be added * @param adjustedHostIndex Index of the view's host node in LView[], adjusted for header * @param lViewOrLContainer The LView or LContainer to add to the view tree * @returns The state passed in */ function addToViewTree(lView, lViewOrLContainer) { // TODO(benlesh/misko): This implementation is incorrect, because it always adds the LContainer // to the end of the queue, which means if the developer retrieves the LContainers from RNodes out // of order, the change detection will run out of order, as the act of retrieving the the // LContainer from the RNode is what adds it to the queue. if (lView[CHILD_HEAD]) { lView[CHILD_TAIL][NEXT] = lViewOrLContainer; } else { lView[CHILD_HEAD] = lViewOrLContainer; } lView[CHILD_TAIL] = lViewOrLContainer; return lViewOrLContainer; } /////////////////////////////// //// Change detection /////////////////////////////// /** * Marks current view and all ancestors dirty. * * Returns the root view because it is found as a byproduct of marking the view tree * dirty, and can be used by methods that consume markViewDirty() to easily schedule * change detection. Otherwise, such methods would need to traverse up the view tree * an additional time to get the root view and schedule a tick on it. * * @param lView The starting LView to mark dirty * @returns the root LView */ function markViewDirty(lView) { while (lView) { lView[FLAGS] |= 32 /* LViewFlags.Dirty */; const parent = getLViewParent(lView); // Stop traversing up as soon as you find a root view that wasn't attached to any container if (isRootView(lView) && !parent) { return lView; } // continue otherwise lView = parent; } return null; } function detectChangesInternal(tView, lView, context, notifyErrorHandler = true) { const rendererFactory = lView[RENDERER_FACTORY]; // Check no changes mode is a dev only mode used to verify that bindings have not changed // since they were assigned. We do not want to invoke renderer factory functions in that mode // to avoid any possible side-effects. const checkNoChangesMode = !!ngDevMode && isInCheckNoChangesMode(); if (!checkNoChangesMode && rendererFactory.begin) rendererFactory.begin(); try { refreshView(tView, lView, tView.template, context); } catch (error) { if (notifyErrorHandler) { handleError(lView, error); } throw error; } finally { if (!checkNoChangesMode && rendererFactory.end) rendererFactory.end(); } } function checkNoChangesInternal(tView, lView, context, notifyErrorHandler = true) { setIsInCheckNoChangesMode(true); try { detectChangesInternal(tView, lView, context, notifyErrorHandler); } finally { setIsInCheckNoChangesMode(false); } } function executeViewQueryFn(flags, viewQueryFn, component) { ngDevMode && assertDefined(viewQueryFn, 'View queries function to execute must be defined.'); setCurrentQueryIndex(0); viewQueryFn(flags, component); } /////////////////////////////// //// Bindings & interpolations /////////////////////////////// /** * Stores meta-data for a property binding to be used by TestBed's `DebugElement.properties`. * * In order to support TestBed's `DebugElement.properties` we need to save, for each binding: * - a bound property name; * - a static parts of interpolated strings; * * A given property metadata is saved at the binding's index in the `TView.data` (in other words, a * property binding metadata will be stored in `TView.data` at the same index as a bound value in * `LView`). Metadata are represented as `INTERPOLATION_DELIMITER`-delimited string with the * following format: * - `propertyName` for bound properties; * - `propertyName�prefix�interpolation_static_part1�..interpolation_static_partN�suffix` for * interpolated properties. * * @param tData `TData` where meta-data will be saved; * @param tNode `TNode` that is a target of the binding; * @param propertyName bound property name; * @param bindingIndex binding index in `LView` * @param interpolationParts static interpolation parts (for property interpolations) */ function storePropertyBindingMetadata(tData, tNode, propertyName, bindingIndex, ...interpolationParts) { // Binding meta-data are stored only the first time a given property instruction is processed. // Since we don't have a concept of the "first update pass" we need to check for presence of the // binding meta-data to decide if one should be stored (or if was stored already). if (tData[bindingIndex] === null) { if (tNode.inputs == null || !tNode.inputs[propertyName]) { const propBindingIdxs = tNode.propertyBindings || (tNode.propertyBindings = []); propBindingIdxs.push(bindingIndex); let bindingMetadata = propertyName; if (interpolationParts.length > 0) { bindingMetadata += INTERPOLATION_DELIMITER + interpolationParts.join(INTERPOLATION_DELIMITER); } tData[bindingIndex] = bindingMetadata; } } } function getOrCreateLViewCleanup(view) { // top level variables should not be exported for performance reasons (PERF_NOTES.md) return view[CLEANUP] || (view[CLEANUP] = []); } function getOrCreateTViewCleanup(tView) { return tView.cleanup || (tView.cleanup = []); } /** * There are cases where the sub component's renderer needs to be included * instead of the current renderer (see the componentSyntheticHost* instructions). */ function loadComponentRenderer(currentDef, tNode, lView) { // TODO(FW-2043): the `currentDef` is null when host bindings are invoked while creating root // component (see packages/core/src/render3/component.ts). This is not consistent with the process // of creating inner components, when current directive index is available in the state. In order // to avoid relying on current def being `null` (thus special-casing root component creation), the // process of creating root component should be unified with the process of creating inner // components. if (currentDef === null || isComponentDef(currentDef)) { lView = unwrapLView(lView[tNode.index]); } return lView[RENDERER]; } /** Handles an error thrown in an LView. */ function handleError(lView, error) { const injector = lView[INJECTOR$1]; const errorHandler = injector ? injector.get(ErrorHandler, null) : null; errorHandler && errorHandler.handleError(error); } /** * Set the inputs of directives at the current node to corresponding value. * * @param tView The current TView * @param lView the `LView` which contains the directives. * @param inputs mapping between the public "input" name and privately-known, * possibly minified, property names to write to. * @param value Value to set. */ function setInputsForProperty(tView, lView, inputs, publicName, value) { for (let i = 0; i < inputs.length;) { const index = inputs[i++]; const privateName = inputs[i++]; const instance = lView[index]; ngDevMode && assertIndexInRange(lView, index); const def = tView.data[index]; if (def.setInput !== null) { def.setInput(instance, value, publicName, privateName); } else { instance[privateName] = value; } } } /** * Updates a text binding at a given index in a given LView. */ function textBindingInternal(lView, index, value) { ngDevMode && assertString(value, 'Value should be a string'); ngDevMode && assertNotSame(value, NO_CHANGE, 'value should not be NO_CHANGE'); ngDevMode && assertIndexInRange(lView, index); const element = getNativeByIndex(index, lView); ngDevMode && assertDefined(element, 'native element should exist'); updateTextNode(lView[RENDERER], element, value); } /** * Compute the static styling (class/style) from `TAttributes`. * * This function should be called during `firstCreatePass` only. * * @param tNode The `TNode` into which the styling information should be loaded. * @param attrs `TAttributes` containing the styling information. * @param writeToHost Where should the resulting static styles be written? * - `false` Write to `TNode.stylesWithoutHost` / `TNode.classesWithoutHost` * - `true` Write to `TNode.styles` / `TNode.classes` */ function computeStaticStyling(tNode, attrs, writeToHost) { ngDevMode && assertFirstCreatePass(getTView(), 'Expecting to be called in first template pass only'); let styles = writeToHost ? tNode.styles : null; let classes = writeToHost ? tNode.classes : null; let mode = 0; if (attrs !== null) { for (let i = 0; i < attrs.length; i++) { const value = attrs[i]; if (typeof value === 'number') { mode = value; } else if (mode == 1 /* AttributeMarker.Classes */) { classes = concatStringsWithSpace(classes, value); } else if (mode == 2 /* AttributeMarker.Styles */) { const style = value; const styleValue = attrs[++i]; styles = concatStringsWithSpace(styles, style + ': ' + styleValue + ';'); } } } writeToHost ? tNode.styles = styles : tNode.stylesWithoutHost = styles; writeToHost ? tNode.classes = classes : tNode.classesWithoutHost = classes; } function collectNativeNodes(tView, lView, tNode, result, isProjection = false) { while (tNode !== null) { ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 16 /* TNodeType.Projection */ | 32 /* TNodeType.Icu */); const lNode = lView[tNode.index]; if (lNode !== null) { result.push(unwrapRNode(lNode)); } // A given lNode can represent either a native node or a LContainer (when it is a host of a // ViewContainerRef). When we find a LContainer we need to descend into it to collect root nodes // from the views in this container. if (isLContainer(lNode)) { for (let i = CONTAINER_HEADER_OFFSET; i < lNode.length; i++) { const lViewInAContainer = lNode[i]; const lViewFirstChildTNode = lViewInAContainer[TVIEW].firstChild; if (lViewFirstChildTNode !== null) { collectNativeNodes(lViewInAContainer[TVIEW], lViewInAContainer, lViewFirstChildTNode, result); } } } const tNodeType = tNode.type; if (tNodeType & 8 /* TNodeType.ElementContainer */) { collectNativeNodes(tView, lView, tNode.child, result); } else if (tNodeType & 32 /* TNodeType.Icu */) { const nextRNode = icuContainerIterate(tNode, lView); let rNode; while (rNode = nextRNode()) { result.push(rNode); } } else if (tNodeType & 16 /* TNodeType.Projection */) { const nodesInSlot = getProjectionNodes(lView, tNode); if (Array.isArray(nodesInSlot)) { result.push(...nodesInSlot); } else { const parentView = getLViewParent(lView[DECLARATION_COMPONENT_VIEW]); ngDevMode && assertParentView(parentView); collectNativeNodes(parentView[TVIEW], parentView, nodesInSlot, result, true); } } tNode = isProjection ? tNode.projectionNext : tNode.next; } return result; } class ViewRef$1 { get rootNodes() { const lView = this._lView; const tView = lView[TVIEW]; return collectNativeNodes(tView, lView, tView.firstChild, []); } constructor( /** * This represents `LView` associated with the component when ViewRef is a ChangeDetectorRef. * * When ViewRef is created for a dynamic component, this also represents the `LView` for the * component. * * For a "regular" ViewRef created for an embedded view, this is the `LView` for the embedded * view. * * @internal */ _lView, /** * This represents the `LView` associated with the point where `ChangeDetectorRef` was * requested. * * This may be different from `_lView` if the `_cdRefInjectingView` is an embedded view. */ _cdRefInjectingView) { this._lView = _lView; this._cdRefInjectingView = _cdRefInjectingView; this._appRef = null; this._attachedToViewContainer = false; } get context() { return this._lView[CONTEXT]; } set context(value) { this._lView[CONTEXT] = value; } get destroyed() { return (this._lView[FLAGS] & 128 /* LViewFlags.Destroyed */) === 128 /* LViewFlags.Destroyed */; } destroy() { if (this._appRef) { this._appRef.detachView(this); } else if (this._attachedToViewContainer) { const parent = this._lView[PARENT]; if (isLContainer(parent)) { const viewRefs = parent[VIEW_REFS]; const index = viewRefs ? viewRefs.indexOf(this) : -1; if (index > -1) { ngDevMode && assertEqual(index, parent.indexOf(this._lView) - CONTAINER_HEADER_OFFSET, 'An attached view should be in the same position within its container as its ViewRef in the VIEW_REFS array.'); detachView(parent, index); removeFromArray(viewRefs, index); } } this._attachedToViewContainer = false; } destroyLView(this._lView[TVIEW], this._lView); } onDestroy(callback) { storeCleanupWithContext(this._lView[TVIEW], this._lView, null, callback); } /** * Marks a view and all of its ancestors dirty. * * This can be used to ensure an {@link ChangeDetectionStrategy#OnPush OnPush} component is * checked when it needs to be re-rendered but the two normal triggers haven't marked it * dirty (i.e. inputs haven't changed and events haven't fired in the view). * * <!-- TODO: Add a link to a chapter on OnPush components --> * * @usageNotes * ### Example * * ```typescript * @Component({ * selector: 'app-root', * template: `Number of ticks: {{numberOfTicks}}` * changeDetection: ChangeDetectionStrategy.OnPush, * }) * class AppComponent { * numberOfTicks = 0; * * constructor(private ref: ChangeDetectorRef) { * setInterval(() => { * this.numberOfTicks++; * // the following is required, otherwise the view will not be updated * this.ref.markForCheck(); * }, 1000); * } * } * ``` */ markForCheck() { markViewDirty(this._cdRefInjectingView || this._lView); } /** * Detaches the view from the change detection tree. * * Detached views will not be checked during change detection runs until they are * re-attached, even if they are dirty. `detach` can be used in combination with * {@link ChangeDetectorRef#detectChanges detectChanges} to implement local change * detection checks. * * <!-- TODO: Add a link to a chapter on detach/reattach/local digest --> * <!-- TODO: Add a live demo once ref.detectChanges is merged into master --> * * @usageNotes * ### Example * * The following example defines a component with a large list of readonly data. * Imagine the data changes constantly, many times per second. For performance reasons, * we want to check and update the list every five seconds. We can do that by detaching * the component's change detector and doing a local check every five seconds. * * ```typescript * class DataProvider { * // in a real application the returned data will be different every time * get data() { * return [1,2,3,4,5]; * } * } * * @Component({ * selector: 'giant-list', * template: ` * <li *ngFor="let d of dataProvider.data">Data {{d}}</li> * `, * }) * class GiantList { * constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) { * ref.detach(); * setInterval(() => { * this.ref.detectChanges(); * }, 5000); * } * } * * @Component({ * selector: 'app', * providers: [DataProvider], * template: ` * <giant-list><giant-list> * `, * }) * class App { * } * ``` */ detach() { this._lView[FLAGS] &= ~64 /* LViewFlags.Attached */; } /** * Re-attaches a view to the change detection tree. * * This can be used to re-attach views that were previously detached from the tree * using {@link ChangeDetectorRef#detach detach}. Views are attached to the tree by default. * * <!-- TODO: Add a link to a chapter on detach/reattach/local digest --> * * @usageNotes * ### Example * * The following example creates a component displaying `live` data. The component will detach * its change detector from the main change detector tree when the component's live property * is set to false. * * ```typescript * class DataProvider { * data = 1; * * constructor() { * setInterval(() => { * this.data = this.data * 2; * }, 500); * } * } * * @Component({ * selector: 'live-data', * inputs: ['live'], * template: 'Data: {{dataProvider.data}}' * }) * class LiveData { * constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {} * * set live(value) { * if (value) { * this.ref.reattach(); * } else { * this.ref.detach(); * } * } * } * * @Component({ * selector: 'app-root', * providers: [DataProvider], * template: ` * Live Update: <input type="checkbox" [(ngModel)]="live"> * <live-data [live]="live"><live-data> * `, * }) * class AppComponent { * live = true; * } * ``` */ reattach() { this._lView[FLAGS] |= 64 /* LViewFlags.Attached */; } /** * Checks the view and its children. * * This can also be used in combination with {@link ChangeDetectorRef#detach detach} to implement * local change detection checks. * * <!-- TODO: Add a link to a chapter on detach/reattach/local digest --> * <!-- TODO: Add a live demo once ref.detectChanges is merged into master --> * * @usageNotes * ### Example * * The following example defines a component with a large list of readonly data. * Imagine, the data changes constantly, many times per second. For performance reasons, * we want to check and update the list every five seconds. * * We can do that by detaching the component's change detector and doing a local change detection * check every five seconds. * * See {@link ChangeDetectorRef#detach detach} for more information. */ detectChanges() { detectChangesInternal(this._lView[TVIEW], this._lView, this.context); } /** * Checks the change detector and its children, and throws if any changes are detected. * * This is used in development mode to verify that running change detection doesn't * introduce other changes. */ checkNoChanges() { if (ngDevMode) { checkNoChangesInternal(this._lView[TVIEW], this._lView, this.context); } } attachToViewContainerRef() { if (this._appRef) { throw new RuntimeError(902 /* RuntimeErrorCode.VIEW_ALREADY_ATTACHED */, ngDevMode && 'This view is already attached directly to the ApplicationRef!'); } this._attachedToViewContainer = true; } detachFromAppRef() { this._appRef = null; renderDetachView(this._lView[TVIEW], this._lView); } attachToAppRef(appRef) { if (this._attachedToViewContainer) { throw new RuntimeError(902 /* RuntimeErrorCode.VIEW_ALREADY_ATTACHED */, ngDevMode && 'This view is already attached to a ViewContainer!'); } this._appRef = appRef; } } /** @internal */ class RootViewRef extends ViewRef$1 { constructor(_view) { super(_view); this._view = _view; } detectChanges() { const lView = this._view; const tView = lView[TVIEW]; const context = lView[CONTEXT]; detectChangesInternal(tView, lView, context, false); } checkNoChanges() { if (ngDevMode) { const lView = this._view; const tView = lView[TVIEW]; const context = lView[CONTEXT]; checkNoChangesInternal(tView, lView, context, false); } } get context() { return null; } } class ComponentFactoryResolver extends ComponentFactoryResolver$1 { /** * @param ngModule The NgModuleRef to which all resolved factories are bound. */ constructor(ngModule) { super(); this.ngModule = ngModule; } resolveComponentFactory(component) { ngDevMode && assertComponentType(component); const componentDef = getComponentDef(component); return new ComponentFactory(componentDef, this.ngModule); } } function toRefArray(map) { const array = []; for (let nonMinified in map) { if (map.hasOwnProperty(nonMinified)) { const minified = map[nonMinified]; array.push({ propName: minified, templateName: nonMinified }); } } return array; } function getNamespace(elementName) { const name = elementName.toLowerCase(); return name === 'svg' ? SVG_NAMESPACE : name === 'math' ? MATH_ML_NAMESPACE : null; } /** * Injector that looks up a value using a specific injector, before falling back to the module * injector. Used primarily when creating components or embedded views dynamically. */ class ChainedInjector { constructor(injector, parentInjector) { this.injector = injector; this.parentInjector = parentInjector; } get(token, notFoundValue, flags) { flags = convertToBitFlags(flags); const value = this.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, flags); if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR || notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) { // Return the value from the root element injector when // - it provides it // (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) // - the module injector should not be checked // (notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) return value; } return this.parentInjector.get(token, notFoundValue, flags); } } /** * ComponentFactory interface implementation. */ class ComponentFactory extends ComponentFactory$1 { get inputs() { return toRefArray(this.componentDef.inputs); } get outputs() { return toRefArray(this.componentDef.outputs); } /** * @param componentDef The component definition. * @param ngModule The NgModuleRef to which the factory is bound. */ constructor(componentDef, ngModule) { super(); this.componentDef = componentDef; this.ngModule = ngModule; this.componentType = componentDef.type; this.selector = stringifyCSSSelectorList(componentDef.selectors); this.ngContentSelectors = componentDef.ngContentSelectors ? componentDef.ngContentSelectors : []; this.isBoundToModule = !!ngModule; } create(injector, projectableNodes, rootSelectorOrNode, environmentInjector) { environmentInjector = environmentInjector || this.ngModule; let realEnvironmentInjector = environmentInjector instanceof EnvironmentInjector ? environmentInjector : environmentInjector?.injector; if (realEnvironmentInjector && this.componentDef.getStandaloneInjector !== null) { realEnvironmentInjector = this.componentDef.getStandaloneInjector(realEnvironmentInjector) || realEnvironmentInjector; } const rootViewInjector = realEnvironmentInjector ? new ChainedInjector(injector, realEnvironmentInjector) : injector; const rendererFactory = rootViewInjector.get(RendererFactory2, null); if (rendererFactory === null) { throw new RuntimeError(407 /* RuntimeErrorCode.RENDERER_NOT_FOUND */, ngDevMode && 'Angular was not able to inject a renderer (RendererFactory2). ' + 'Likely this is due to a broken DI hierarchy. ' + 'Make sure that any injector used to create this component has a correct parent.'); } const sanitizer = rootViewInjector.get(Sanitizer, null); const hostRenderer = rendererFactory.createRenderer(null, this.componentDef); // Determine a tag name used for creating host elements when this component is created // dynamically. Default to 'div' if this component did not specify any tag name in its selector. const elementName = this.componentDef.selectors[0][0] || 'div'; const hostRNode = rootSelectorOrNode ? locateHostElement(hostRenderer, rootSelectorOrNode, this.componentDef.encapsulation) : createElementNode(hostRenderer, elementName, getNamespace(elementName)); const rootFlags = this.componentDef.onPush ? 32 /* LViewFlags.Dirty */ | 256 /* LViewFlags.IsRoot */ : 16 /* LViewFlags.CheckAlways */ | 256 /* LViewFlags.IsRoot */; // Create the root view. Uses empty TView and ContentTemplate. const rootTView = createTView(0 /* TViewType.Root */, null, null, 1, 0, null, null, null, null, null); const rootLView = createLView(null, rootTView, null, rootFlags, null, null, rendererFactory, hostRenderer, sanitizer, rootViewInjector, null); // rootView is the parent when bootstrapping // TODO(misko): it looks like we are entering view here but we don't really need to as // `renderView` does that. However as the code is written it is needed because // `createRootComponentView` and `createRootComponent` both read global state. Fixing those // issues would allow us to drop this. enterView(rootLView); let component; let tElementNode; try { const rootComponentDef = this.componentDef; let rootDirectives; let hostDirectiveDefs = null; if (rootComponentDef.findHostDirectiveDefs) { rootDirectives = []; hostDirectiveDefs = new Map(); rootComponentDef.findHostDirectiveDefs(rootComponentDef, rootDirectives, hostDirectiveDefs); rootDirectives.push(rootComponentDef); } else { rootDirectives = [rootComponentDef]; } const hostTNode = createRootComponentTNode(rootLView, hostRNode); const componentView = createRootComponentView(hostTNode, hostRNode, rootComponentDef, rootDirectives, rootLView, rendererFactory, hostRenderer); tElementNode = getTNode(rootTView, HEADER_OFFSET); // TODO(crisbeto): in practice `hostRNode` should always be defined, but there are some tests // where the renderer is mocked out and `undefined` is returned. We should update the tests so // that this check can be removed. if (hostRNode) { setRootNodeAttributes(hostRenderer, rootComponentDef, hostRNode, rootSelectorOrNode); } if (projectableNodes !== undefined) { projectNodes(tElementNode, this.ngContentSelectors, projectableNodes); } // TODO: should LifecycleHooksFeature and other host features be generated by the compiler and // executed here? // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref component = createRootComponent(componentView, rootComponentDef, rootDirectives, hostDirectiveDefs, rootLView, [LifecycleHooksFeature]); renderView(rootTView, rootLView, null); } finally { leaveView(); } return new ComponentRef(this.componentType, component, createElementRef(tElementNode, rootLView), rootLView, tElementNode); } } /** * Represents an instance of a Component created via a {@link ComponentFactory}. * * `ComponentRef` provides access to the Component Instance as well other objects related to this * Component Instance and allows you to destroy the Component Instance via the {@link #destroy} * method. * */ class ComponentRef extends ComponentRef$1 { constructor(componentType, instance, location, _rootLView, _tNode) { super(); this.location = location; this._rootLView = _rootLView; this._tNode = _tNode; this.instance = instance; this.hostView = this.changeDetectorRef = new RootViewRef(_rootLView); this.componentType = componentType; } setInput(name, value) { const inputData = this._tNode.inputs; let dataValue; if (inputData !== null && (dataValue = inputData[name])) { const lView = this._rootLView; setInputsForProperty(lView[TVIEW], lView, dataValue, name, value); const childComponentLView = getComponentLViewByIndex(this._tNode.index, lView); markViewDirty(childComponentLView); } else { if (ngDevMode) { const cmpNameForError = stringifyForError(this.componentType); let message = `Can't set value of the '${name}' input on the '${cmpNameForError}' component. `; message += `Make sure that the '${name}' property is annotated with @Input() or a mapped @Input('${name}') exists.`; reportUnknownPropertyError(message); } } } get injector() { return new NodeInjector(this._tNode, this._rootLView); } destroy() { this.hostView.destroy(); } onDestroy(callback) { this.hostView.onDestroy(callback); } } // TODO: A hack to not pull in the NullInjector from @angular/core. const NULL_INJECTOR = { get: (token, notFoundValue) => { throwProviderNotFoundError(token, 'NullInjector'); } }; /** Creates a TNode that can be used to instantiate a root component. */ function createRootComponentTNode(lView, rNode) { const tView = lView[TVIEW]; const index = HEADER_OFFSET; ngDevMode && assertIndexInRange(lView, index); lView[index] = rNode; // '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at // the same time we want to communicate the debug `TNode` that this is a special `TNode` // representing a host element. return getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, '#host', null); } /** * Creates the root component view and the root component node. * * @param rNode Render host element. * @param rootComponentDef ComponentDef * @param rootView The parent view where the host node is stored * @param rendererFactory Factory to be used for creating child renderers. * @param hostRenderer The current renderer * @param sanitizer The sanitizer, if provided * * @returns Component view created */ function createRootComponentView(tNode, rNode, rootComponentDef, rootDirectives, rootView, rendererFactory, hostRenderer, sanitizer) { const tView = rootView[TVIEW]; applyRootComponentStyling(rootDirectives, tNode, rNode, hostRenderer); const viewRenderer = rendererFactory.createRenderer(rNode, rootComponentDef); const componentView = createLView(rootView, getOrCreateComponentTView(rootComponentDef), null, rootComponentDef.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, rootView[tNode.index], tNode, rendererFactory, viewRenderer, sanitizer || null, null, null); if (tView.firstCreatePass) { markAsComponentHost(tView, tNode, rootDirectives.length - 1); } addToViewTree(rootView, componentView); // Store component view at node index, with node as the HOST return rootView[tNode.index] = componentView; } /** Sets up the styling information on a root component. */ function applyRootComponentStyling(rootDirectives, tNode, rNode, hostRenderer) { for (const def of rootDirectives) { tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs); } if (tNode.mergedAttrs !== null) { computeStaticStyling(tNode, tNode.mergedAttrs, true); if (rNode !== null) { setupStaticAttributes(hostRenderer, rNode, tNode); } } } /** * Creates a root component and sets it up with features and host bindings.Shared by * renderComponent() and ViewContainerRef.createComponent(). */ function createRootComponent(componentView, rootComponentDef, rootDirectives, hostDirectiveDefs, rootLView, hostFeatures) { const rootTNode = getCurrentTNode(); ngDevMode && assertDefined(rootTNode, 'tNode should have been already created'); const tView = rootLView[TVIEW]; const native = getNativeByTNode(rootTNode, rootLView); initializeDirectives(tView, rootLView, rootTNode, rootDirectives, null, hostDirectiveDefs); for (let i = 0; i < rootDirectives.length; i++) { const directiveIndex = rootTNode.directiveStart + i; const directiveInstance = getNodeInjectable(rootLView, tView, directiveIndex, rootTNode); attachPatchData(directiveInstance, rootLView); } invokeDirectivesHostBindings(tView, rootLView, rootTNode); if (native) { attachPatchData(native, rootLView); } // We're guaranteed for the `componentOffset` to be positive here // since a root component always matches a component def. ngDevMode && assertGreaterThan(rootTNode.componentOffset, -1, 'componentOffset must be great than -1'); const component = getNodeInjectable(rootLView, tView, rootTNode.directiveStart + rootTNode.componentOffset, rootTNode); componentView[CONTEXT] = rootLView[CONTEXT] = component; if (hostFeatures !== null) { for (const feature of hostFeatures) { feature(component, rootComponentDef); } } // We want to generate an empty QueryList for root content queries for backwards // compatibility with ViewEngine. executeContentQueries(tView, rootTNode, componentView); return component; } /** Sets the static attributes on a root component. */ function setRootNodeAttributes(hostRenderer, componentDef, hostRNode, rootSelectorOrNode) { if (rootSelectorOrNode) { setUpAttributes(hostRenderer, hostRNode, ['ng-version', VERSION.full]); } else { // If host element is created as a part of this function call (i.e. `rootSelectorOrNode` // is not defined), also apply attributes and classes extracted from component selector. // Extract attributes and classes from the first selector only to match VE behavior. const { attrs, classes } = extractAttrsAndClassesFromSelector(componentDef.selectors[0]); if (attrs) { setUpAttributes(hostRenderer, hostRNode, attrs); } if (classes && classes.length > 0) { writeDirectClass(hostRenderer, hostRNode, classes.join(' ')); } } } /** Projects the `projectableNodes` that were specified when creating a root component. */ function projectNodes(tNode, ngContentSelectors, projectableNodes) { const projection = tNode.projection = []; for (let i = 0; i < ngContentSelectors.length; i++) { const nodesforSlot = projectableNodes[i]; // Projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade // case). Here we do normalize passed data structure to be an array of arrays to avoid // complex checks down the line. // We also normalize the length of the passed in projectable nodes (to match the number of // <ng-container> slots defined by a component). projection.push(nodesforSlot != null ? Array.from(nodesforSlot) : null); } } /** * Used to enable lifecycle hooks on the root component. * * Include this feature when calling `renderComponent` if the root component * you are rendering has lifecycle hooks defined. Otherwise, the hooks won't * be called properly. * * Example: * * ``` * renderComponent(AppComponent, {hostFeatures: [LifecycleHooksFeature]}); * ``` */ function LifecycleHooksFeature() { const tNode = getCurrentTNode(); ngDevMode && assertDefined(tNode, 'TNode is required'); registerPostOrderHooks(getLView()[TVIEW], tNode); } function getSuperType(type) { return Object.getPrototypeOf(type.prototype).constructor; } /** * Merges the definition from a super class to a sub class. * @param definition The definition that is a SubClass of another directive of component * * @codeGenApi */ function ɵɵInheritDefinitionFeature(definition) { let superType = getSuperType(definition.type); let shouldInheritFields = true; const inheritanceChain = [definition]; while (superType) { let superDef = undefined; if (isComponentDef(definition)) { // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance. superDef = superType.ɵcmp || superType.ɵdir; } else { if (superType.ɵcmp) { throw new RuntimeError(903 /* RuntimeErrorCode.INVALID_INHERITANCE */, ngDevMode && `Directives cannot inherit Components. Directive ${stringifyForError(definition.type)} is attempting to extend component ${stringifyForError(superType)}`); } // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance. superDef = superType.ɵdir; } if (superDef) { if (shouldInheritFields) { inheritanceChain.push(superDef); // Some fields in the definition may be empty, if there were no values to put in them that // would've justified object creation. Unwrap them if necessary. const writeableDef = definition; writeableDef.inputs = maybeUnwrapEmpty(definition.inputs); writeableDef.declaredInputs = maybeUnwrapEmpty(definition.declaredInputs); writeableDef.outputs = maybeUnwrapEmpty(definition.outputs); // Merge hostBindings const superHostBindings = superDef.hostBindings; superHostBindings && inheritHostBindings(definition, superHostBindings); // Merge queries const superViewQuery = superDef.viewQuery; const superContentQueries = superDef.contentQueries; superViewQuery && inheritViewQuery(definition, superViewQuery); superContentQueries && inheritContentQueries(definition, superContentQueries); // Merge inputs and outputs fillProperties(definition.inputs, superDef.inputs); fillProperties(definition.declaredInputs, superDef.declaredInputs); fillProperties(definition.outputs, superDef.outputs); // Merge animations metadata. // If `superDef` is a Component, the `data` field is present (defaults to an empty object). if (isComponentDef(superDef) && superDef.data.animation) { // If super def is a Component, the `definition` is also a Component, since Directives can // not inherit Components (we throw an error above and cannot reach this code). const defData = definition.data; defData.animation = (defData.animation || []).concat(superDef.data.animation); } } // Run parent features const features = superDef.features; if (features) { for (let i = 0; i < features.length; i++) { const feature = features[i]; if (feature && feature.ngInherit) { feature(definition); } // If `InheritDefinitionFeature` is a part of the current `superDef`, it means that this // def already has all the necessary information inherited from its super class(es), so we // can stop merging fields from super classes. However we need to iterate through the // prototype chain to look for classes that might contain other "features" (like // NgOnChanges), which we should invoke for the original `definition`. We set the // `shouldInheritFields` flag to indicate that, essentially skipping fields inheritance // logic and only invoking functions from the "features" list. if (feature === ɵɵInheritDefinitionFeature) { shouldInheritFields = false; } } } } superType = Object.getPrototypeOf(superType); } mergeHostAttrsAcrossInheritance(inheritanceChain); } /** * Merge the `hostAttrs` and `hostVars` from the inherited parent to the base class. * * @param inheritanceChain A list of `WritableDefs` starting at the top most type and listing * sub-types in order. For each type take the `hostAttrs` and `hostVars` and merge it with the child * type. */ function mergeHostAttrsAcrossInheritance(inheritanceChain) { let hostVars = 0; let hostAttrs = null; // We process the inheritance order from the base to the leaves here. for (let i = inheritanceChain.length - 1; i >= 0; i--) { const def = inheritanceChain[i]; // For each `hostVars`, we need to add the superclass amount. def.hostVars = hostVars += def.hostVars; // for each `hostAttrs` we need to merge it with superclass. def.hostAttrs = mergeHostAttrs(def.hostAttrs, hostAttrs = mergeHostAttrs(hostAttrs, def.hostAttrs)); } } function maybeUnwrapEmpty(value) { if (value === EMPTY_OBJ) { return {}; } else if (value === EMPTY_ARRAY) { return []; } else { return value; } } function inheritViewQuery(definition, superViewQuery) { const prevViewQuery = definition.viewQuery; if (prevViewQuery) { definition.viewQuery = (rf, ctx) => { superViewQuery(rf, ctx); prevViewQuery(rf, ctx); }; } else { definition.viewQuery = superViewQuery; } } function inheritContentQueries(definition, superContentQueries) { const prevContentQueries = definition.contentQueries; if (prevContentQueries) { definition.contentQueries = (rf, ctx, directiveIndex) => { superContentQueries(rf, ctx, directiveIndex); prevContentQueries(rf, ctx, directiveIndex); }; } else { definition.contentQueries = superContentQueries; } } function inheritHostBindings(definition, superHostBindings) { const prevHostBindings = definition.hostBindings; if (prevHostBindings) { definition.hostBindings = (rf, ctx) => { superHostBindings(rf, ctx); prevHostBindings(rf, ctx); }; } else { definition.hostBindings = superHostBindings; } } /** * Fields which exist on either directive or component definitions, and need to be copied from * parent to child classes by the `ɵɵCopyDefinitionFeature`. */ const COPY_DIRECTIVE_FIELDS = [ // The child class should use the providers of its parent. 'providersResolver' // Not listed here are any fields which are handled by the `ɵɵInheritDefinitionFeature`, such // as inputs, outputs, and host binding functions. ]; /** * Fields which exist only on component definitions, and need to be copied from parent to child * classes by the `ɵɵCopyDefinitionFeature`. * * The type here allows any field of `ComponentDef` which is not also a property of `DirectiveDef`, * since those should go in `COPY_DIRECTIVE_FIELDS` above. */ const COPY_COMPONENT_FIELDS = [ // The child class should use the template function of its parent, including all template // semantics. 'template', 'decls', 'consts', 'vars', 'onPush', 'ngContentSelectors', // The child class should use the CSS styles of its parent, including all styling semantics. 'styles', 'encapsulation', // The child class should be checked by the runtime in the same way as its parent. 'schemas']; /** * Copies the fields not handled by the `ɵɵInheritDefinitionFeature` from the supertype of a * definition. * * This exists primarily to support ngcc migration of an existing View Engine pattern, where an * entire decorator is inherited from a parent to a child class. When ngcc detects this case, it * generates a skeleton definition on the child class, and applies this feature. * * The `ɵɵCopyDefinitionFeature` then copies any needed fields from the parent class' definition, * including things like the component template function. * * @param definition The definition of a child class which inherits from a parent class with its * own definition. * * @codeGenApi */ function ɵɵCopyDefinitionFeature(definition) { let superType = getSuperType(definition.type); let superDef = undefined; if (isComponentDef(definition)) { // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance. superDef = superType.ɵcmp; } else { // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance. superDef = superType.ɵdir; } // Needed because `definition` fields are readonly. const defAny = definition; // Copy over any fields that apply to either directives or components. for (const field of COPY_DIRECTIVE_FIELDS) { defAny[field] = superDef[field]; } if (isComponentDef(superDef)) { // Copy over any component-specific fields. for (const field of COPY_COMPONENT_FIELDS) { defAny[field] = superDef[field]; } } } /** * This feature adds the host directives behavior to a directive definition by patching a * function onto it. The expectation is that the runtime will invoke the function during * directive matching. * * For example: * ```ts * class ComponentWithHostDirective { * static ɵcmp = defineComponent({ * type: ComponentWithHostDirective, * features: [ɵɵHostDirectivesFeature([ * SimpleHostDirective, * {directive: AdvancedHostDirective, inputs: ['foo: alias'], outputs: ['bar']}, * ])] * }); * } * ``` * * @codeGenApi */ function ɵɵHostDirectivesFeature(rawHostDirectives) { return definition => { definition.findHostDirectiveDefs = findHostDirectiveDefs; definition.hostDirectives = (Array.isArray(rawHostDirectives) ? rawHostDirectives : rawHostDirectives()).map(dir => { return typeof dir === 'function' ? { directive: resolveForwardRef(dir), inputs: EMPTY_OBJ, outputs: EMPTY_OBJ } : { directive: resolveForwardRef(dir.directive), inputs: bindingArrayToMap(dir.inputs), outputs: bindingArrayToMap(dir.outputs) }; }); }; } function findHostDirectiveDefs(currentDef, matchedDefs, hostDirectiveDefs) { if (currentDef.hostDirectives !== null) { for (const hostDirectiveConfig of currentDef.hostDirectives) { const hostDirectiveDef = getDirectiveDef(hostDirectiveConfig.directive); if (typeof ngDevMode === 'undefined' || ngDevMode) { validateHostDirective(hostDirectiveConfig, hostDirectiveDef, matchedDefs); } // We need to patch the `declaredInputs` so that // `ngOnChanges` can map the properties correctly. patchDeclaredInputs(hostDirectiveDef.declaredInputs, hostDirectiveConfig.inputs); // Host directives execute before the host so that its host bindings can be overwritten. findHostDirectiveDefs(hostDirectiveDef, matchedDefs, hostDirectiveDefs); hostDirectiveDefs.set(hostDirectiveDef, hostDirectiveConfig); matchedDefs.push(hostDirectiveDef); } } } /** * Converts an array in the form of `['publicName', 'alias', 'otherPublicName', 'otherAlias']` into * a map in the form of `{publicName: 'alias', otherPublicName: 'otherAlias'}`. */ function bindingArrayToMap(bindings) { if (bindings === undefined || bindings.length === 0) { return EMPTY_OBJ; } const result = {}; for (let i = 0; i < bindings.length; i += 2) { result[bindings[i]] = bindings[i + 1]; } return result; } /** * `ngOnChanges` has some leftover legacy ViewEngine behavior where the keys inside the * `SimpleChanges` event refer to the *declared* name of the input, not its public name or its * minified name. E.g. in `@Input('alias') foo: string`, the name in the `SimpleChanges` object * will always be `foo`, and not `alias` or the minified name of `foo` in apps using property * minification. * * This is achieved through the `DirectiveDef.declaredInputs` map that is constructed when the * definition is declared. When a property is written to the directive instance, the * `NgOnChangesFeature` will try to remap the property name being written to using the * `declaredInputs`. * * Since the host directive input remapping happens during directive matching, `declaredInputs` * won't contain the new alias that the input is available under. This function addresses the * issue by patching the host directive aliases to the `declaredInputs`. There is *not* a risk of * this patching accidentally introducing new inputs to the host directive, because `declaredInputs` * is used *only* by the `NgOnChangesFeature` when determining what name is used in the * `SimpleChanges` object which won't be reached if an input doesn't exist. */ function patchDeclaredInputs(declaredInputs, exposedInputs) { for (const publicName in exposedInputs) { if (exposedInputs.hasOwnProperty(publicName)) { const remappedPublicName = exposedInputs[publicName]; const privateName = declaredInputs[publicName]; // We *technically* shouldn't be able to hit this case because we can't have multiple // inputs on the same property and we have validations against conflicting aliases in // `validateMappings`. If we somehow did, it would lead to `ngOnChanges` being invoked // with the wrong name so we have a non-user-friendly assertion here just in case. if ((typeof ngDevMode === 'undefined' || ngDevMode) && declaredInputs.hasOwnProperty(remappedPublicName)) { assertEqual(declaredInputs[remappedPublicName], declaredInputs[publicName], `Conflicting host directive input alias ${publicName}.`); } declaredInputs[remappedPublicName] = privateName; } } } /** * Verifies that the host directive has been configured correctly. * @param hostDirectiveConfig Host directive configuration object. * @param directiveDef Directive definition of the host directive. * @param matchedDefs Directives that have been matched so far. */ function validateHostDirective(hostDirectiveConfig, directiveDef, matchedDefs) { const type = hostDirectiveConfig.directive; if (directiveDef === null) { if (getComponentDef(type) !== null) { throw new RuntimeError(310 /* RuntimeErrorCode.HOST_DIRECTIVE_COMPONENT */, `Host directive ${type.name} cannot be a component.`); } throw new RuntimeError(307 /* RuntimeErrorCode.HOST_DIRECTIVE_UNRESOLVABLE */, `Could not resolve metadata for host directive ${type.name}. ` + `Make sure that the ${type.name} class is annotated with an @Directive decorator.`); } if (!directiveDef.standalone) { throw new RuntimeError(308 /* RuntimeErrorCode.HOST_DIRECTIVE_NOT_STANDALONE */, `Host directive ${directiveDef.type.name} must be standalone.`); } if (matchedDefs.indexOf(directiveDef) > -1) { throw new RuntimeError(309 /* RuntimeErrorCode.DUPLICATE_DIRECTITVE */, `Directive ${directiveDef.type.name} matches multiple times on the same element. ` + `Directives can only match an element once.`); } validateMappings('input', directiveDef, hostDirectiveConfig.inputs); validateMappings('output', directiveDef, hostDirectiveConfig.outputs); } /** * Checks that the host directive inputs/outputs configuration is valid. * @param bindingType Kind of binding that is being validated. Used in the error message. * @param def Definition of the host directive that is being validated against. * @param hostDirectiveBindings Host directive mapping object that shold be validated. */ function validateMappings(bindingType, def, hostDirectiveBindings) { const className = def.type.name; const bindings = bindingType === 'input' ? def.inputs : def.outputs; for (const publicName in hostDirectiveBindings) { if (hostDirectiveBindings.hasOwnProperty(publicName)) { if (!bindings.hasOwnProperty(publicName)) { throw new RuntimeError(311 /* RuntimeErrorCode.HOST_DIRECTIVE_UNDEFINED_BINDING */, `Directive ${className} does not have an ${bindingType} with a public name of ${publicName}.`); } const remappedPublicName = hostDirectiveBindings[publicName]; if (bindings.hasOwnProperty(remappedPublicName) && bindings[remappedPublicName] !== publicName) { throw new RuntimeError(312 /* RuntimeErrorCode.HOST_DIRECTIVE_CONFLICTING_ALIAS */, `Cannot alias ${bindingType} ${publicName} of host directive ${className} to ${remappedPublicName}, because it already has a different ${bindingType} with the same public name.`); } } } } function isIterable(obj) { return obj !== null && typeof obj === 'object' && obj[Symbol.iterator] !== undefined; } function isListLikeIterable(obj) { if (!isJsObject(obj)) return false; return Array.isArray(obj) || !(obj instanceof Map) && // JS Map are iterables but return entries as [k, v] Symbol.iterator in obj; // JS Iterable have a Symbol.iterator prop } function areIterablesEqual(a, b, comparator) { const iterator1 = a[Symbol.iterator](); const iterator2 = b[Symbol.iterator](); while (true) { const item1 = iterator1.next(); const item2 = iterator2.next(); if (item1.done && item2.done) return true; if (item1.done || item2.done) return false; if (!comparator(item1.value, item2.value)) return false; } } function iterateListLike(obj, fn) { if (Array.isArray(obj)) { for (let i = 0; i < obj.length; i++) { fn(obj[i]); } } else { const iterator = obj[Symbol.iterator](); let item; while (!(item = iterator.next()).done) { fn(item.value); } } } function isJsObject(o) { return o !== null && (typeof o === 'function' || typeof o === 'object'); } function devModeEqual(a, b) { const isListLikeIterableA = isListLikeIterable(a); const isListLikeIterableB = isListLikeIterable(b); if (isListLikeIterableA && isListLikeIterableB) { return areIterablesEqual(a, b, devModeEqual); } else { const isAObject = a && (typeof a === 'object' || typeof a === 'function'); const isBObject = b && (typeof b === 'object' || typeof b === 'function'); if (!isListLikeIterableA && isAObject && !isListLikeIterableB && isBObject) { return true; } else { return Object.is(a, b); } } } // TODO(misko): consider inlining /** Updates binding and returns the value. */ function updateBinding(lView, bindingIndex, value) { return lView[bindingIndex] = value; } /** Gets the current binding value. */ function getBinding(lView, bindingIndex) { ngDevMode && assertIndexInRange(lView, bindingIndex); ngDevMode && assertNotSame(lView[bindingIndex], NO_CHANGE, 'Stored value should never be NO_CHANGE.'); return lView[bindingIndex]; } /** * Updates binding if changed, then returns whether it was updated. * * This function also checks the `CheckNoChangesMode` and throws if changes are made. * Some changes (Objects/iterables) during `CheckNoChangesMode` are exempt to comply with VE * behavior. * * @param lView current `LView` * @param bindingIndex The binding in the `LView` to check * @param value New value to check against `lView[bindingIndex]` * @returns `true` if the bindings has changed. (Throws if binding has changed during * `CheckNoChangesMode`) */ function bindingUpdated(lView, bindingIndex, value) { ngDevMode && assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.'); ngDevMode && assertLessThan(bindingIndex, lView.length, `Slot should have been initialized to NO_CHANGE`); const oldValue = lView[bindingIndex]; if (Object.is(oldValue, value)) { return false; } else { if (ngDevMode && isInCheckNoChangesMode()) { // View engine didn't report undefined values as changed on the first checkNoChanges pass // (before the change detection was run). const oldValueToCompare = oldValue !== NO_CHANGE ? oldValue : undefined; if (!devModeEqual(oldValueToCompare, value)) { const details = getExpressionChangedErrorDetails(lView, bindingIndex, oldValueToCompare, value); throwErrorIfNoChangesMode(oldValue === NO_CHANGE, details.oldValue, details.newValue, details.propName); } // There was a change, but the `devModeEqual` decided that the change is exempt from an error. // For this reason we exit as if no change. The early exit is needed to prevent the changed // value to be written into `LView` (If we would write the new value that we would not see it // as change on next CD.) return false; } lView[bindingIndex] = value; return true; } } /** Updates 2 bindings if changed, then returns whether either was updated. */ function bindingUpdated2(lView, bindingIndex, exp1, exp2) { const different = bindingUpdated(lView, bindingIndex, exp1); return bindingUpdated(lView, bindingIndex + 1, exp2) || different; } /** Updates 3 bindings if changed, then returns whether any was updated. */ function bindingUpdated3(lView, bindingIndex, exp1, exp2, exp3) { const different = bindingUpdated2(lView, bindingIndex, exp1, exp2); return bindingUpdated(lView, bindingIndex + 2, exp3) || different; } /** Updates 4 bindings if changed, then returns whether any was updated. */ function bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4) { const different = bindingUpdated2(lView, bindingIndex, exp1, exp2); return bindingUpdated2(lView, bindingIndex + 2, exp3, exp4) || different; } /** * Updates the value of or removes a bound attribute on an Element. * * Used in the case of `[attr.title]="value"` * * @param name name The name of the attribute. * @param value value The attribute is removed when value is `null` or `undefined`. * Otherwise the attribute value is set to the stringified value. * @param sanitizer An optional function used to sanitize the value. * @param namespace Optional namespace to use when setting the attribute. * * @codeGenApi */ function ɵɵattribute(name, value, sanitizer, namespace) { const lView = getLView(); const bindingIndex = nextBindingIndex(); if (bindingUpdated(lView, bindingIndex, value)) { const tView = getTView(); const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, name, value, sanitizer, namespace); ngDevMode && storePropertyBindingMetadata(tView.data, tNode, 'attr.' + name, bindingIndex); } return ɵɵattribute; } /** * Create interpolation bindings with a variable number of expressions. * * If there are 1 to 8 expressions `interpolation1()` to `interpolation8()` should be used instead. * Those are faster because there is no need to create an array of expressions and iterate over it. * * `values`: * - has static text at even indexes, * - has evaluated expressions at odd indexes. * * Returns the concatenated string when any of the arguments changes, `NO_CHANGE` otherwise. */ function interpolationV(lView, values) { ngDevMode && assertLessThan(2, values.length, 'should have at least 3 values'); ngDevMode && assertEqual(values.length % 2, 1, 'should have an odd number of values'); let isBindingUpdated = false; let bindingIndex = getBindingIndex(); for (let i = 1; i < values.length; i += 2) { // Check if bindings (odd indexes) have changed isBindingUpdated = bindingUpdated(lView, bindingIndex++, values[i]) || isBindingUpdated; } setBindingIndex(bindingIndex); if (!isBindingUpdated) { return NO_CHANGE; } // Build the updated content let content = values[0]; for (let i = 1; i < values.length; i += 2) { content += renderStringify(values[i]) + values[i + 1]; } return content; } /** * Creates an interpolation binding with 1 expression. * * @param prefix static value used for concatenation only. * @param v0 value checked for change. * @param suffix static value used for concatenation only. */ function interpolation1(lView, prefix, v0, suffix) { const different = bindingUpdated(lView, nextBindingIndex(), v0); return different ? prefix + renderStringify(v0) + suffix : NO_CHANGE; } /** * Creates an interpolation binding with 2 expressions. */ function interpolation2(lView, prefix, v0, i0, v1, suffix) { const bindingIndex = getBindingIndex(); const different = bindingUpdated2(lView, bindingIndex, v0, v1); incrementBindingIndex(2); return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + suffix : NO_CHANGE; } /** * Creates an interpolation binding with 3 expressions. */ function interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix) { const bindingIndex = getBindingIndex(); const different = bindingUpdated3(lView, bindingIndex, v0, v1, v2); incrementBindingIndex(3); return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + suffix : NO_CHANGE; } /** * Create an interpolation binding with 4 expressions. */ function interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix) { const bindingIndex = getBindingIndex(); const different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3); incrementBindingIndex(4); return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 + renderStringify(v3) + suffix : NO_CHANGE; } /** * Creates an interpolation binding with 5 expressions. */ function interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) { const bindingIndex = getBindingIndex(); let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3); different = bindingUpdated(lView, bindingIndex + 4, v4) || different; incrementBindingIndex(5); return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + suffix : NO_CHANGE; } /** * Creates an interpolation binding with 6 expressions. */ function interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) { const bindingIndex = getBindingIndex(); let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3); different = bindingUpdated2(lView, bindingIndex + 4, v4, v5) || different; incrementBindingIndex(6); return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + i4 + renderStringify(v5) + suffix : NO_CHANGE; } /** * Creates an interpolation binding with 7 expressions. */ function interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) { const bindingIndex = getBindingIndex(); let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3); different = bindingUpdated3(lView, bindingIndex + 4, v4, v5, v6) || different; incrementBindingIndex(7); return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + i4 + renderStringify(v5) + i5 + renderStringify(v6) + suffix : NO_CHANGE; } /** * Creates an interpolation binding with 8 expressions. */ function interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) { const bindingIndex = getBindingIndex(); let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3); different = bindingUpdated4(lView, bindingIndex + 4, v4, v5, v6, v7) || different; incrementBindingIndex(8); return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + i4 + renderStringify(v5) + i5 + renderStringify(v6) + i6 + renderStringify(v7) + suffix : NO_CHANGE; } /** * * Update an interpolated attribute on an element with single bound value surrounded by text. * * Used when the value passed to a property has 1 interpolated value in it: * * ```html * <div attr.title="prefix{{v0}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵattributeInterpolate1('title', 'prefix', v0, 'suffix'); * ``` * * @param attrName The name of the attribute to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵattributeInterpolate1(attrName, prefix, v0, suffix, sanitizer, namespace) { const lView = getLView(); const interpolatedValue = interpolation1(lView, prefix, v0, suffix); if (interpolatedValue !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace); ngDevMode && storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 1, prefix, suffix); } return ɵɵattributeInterpolate1; } /** * * Update an interpolated attribute on an element with 2 bound values surrounded by text. * * Used when the value passed to a property has 2 interpolated values in it: * * ```html * <div attr.title="prefix{{v0}}-{{v1}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵattributeInterpolate2('title', 'prefix', v0, '-', v1, 'suffix'); * ``` * * @param attrName The name of the attribute to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵattributeInterpolate2(attrName, prefix, v0, i0, v1, suffix, sanitizer, namespace) { const lView = getLView(); const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix); if (interpolatedValue !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace); ngDevMode && storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 2, prefix, i0, suffix); } return ɵɵattributeInterpolate2; } /** * * Update an interpolated attribute on an element with 3 bound values surrounded by text. * * Used when the value passed to a property has 3 interpolated values in it: * * ```html * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵattributeInterpolate3( * 'title', 'prefix', v0, '-', v1, '-', v2, 'suffix'); * ``` * * @param attrName The name of the attribute to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵattributeInterpolate3(attrName, prefix, v0, i0, v1, i1, v2, suffix, sanitizer, namespace) { const lView = getLView(); const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix); if (interpolatedValue !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace); ngDevMode && storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 3, prefix, i0, i1, suffix); } return ɵɵattributeInterpolate3; } /** * * Update an interpolated attribute on an element with 4 bound values surrounded by text. * * Used when the value passed to a property has 4 interpolated values in it: * * ```html * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵattributeInterpolate4( * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix'); * ``` * * @param attrName The name of the attribute to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵattributeInterpolate4(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, suffix, sanitizer, namespace) { const lView = getLView(); const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix); if (interpolatedValue !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace); ngDevMode && storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 4, prefix, i0, i1, i2, suffix); } return ɵɵattributeInterpolate4; } /** * * Update an interpolated attribute on an element with 5 bound values surrounded by text. * * Used when the value passed to a property has 5 interpolated values in it: * * ```html * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵattributeInterpolate5( * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix'); * ``` * * @param attrName The name of the attribute to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵattributeInterpolate5(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix, sanitizer, namespace) { const lView = getLView(); const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix); if (interpolatedValue !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace); ngDevMode && storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 5, prefix, i0, i1, i2, i3, suffix); } return ɵɵattributeInterpolate5; } /** * * Update an interpolated attribute on an element with 6 bound values surrounded by text. * * Used when the value passed to a property has 6 interpolated values in it: * * ```html * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵattributeInterpolate6( * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix'); * ``` * * @param attrName The name of the attribute to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵattributeInterpolate6(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix, sanitizer, namespace) { const lView = getLView(); const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix); if (interpolatedValue !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace); ngDevMode && storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 6, prefix, i0, i1, i2, i3, i4, suffix); } return ɵɵattributeInterpolate6; } /** * * Update an interpolated attribute on an element with 7 bound values surrounded by text. * * Used when the value passed to a property has 7 interpolated values in it: * * ```html * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵattributeInterpolate7( * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix'); * ``` * * @param attrName The name of the attribute to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param i5 Static value used for concatenation only. * @param v6 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵattributeInterpolate7(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix, sanitizer, namespace) { const lView = getLView(); const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix); if (interpolatedValue !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace); ngDevMode && storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 7, prefix, i0, i1, i2, i3, i4, i5, suffix); } return ɵɵattributeInterpolate7; } /** * * Update an interpolated attribute on an element with 8 bound values surrounded by text. * * Used when the value passed to a property has 8 interpolated values in it: * * ```html * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵattributeInterpolate8( * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix'); * ``` * * @param attrName The name of the attribute to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param i5 Static value used for concatenation only. * @param v6 Value checked for change. * @param i6 Static value used for concatenation only. * @param v7 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵattributeInterpolate8(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix, sanitizer, namespace) { const lView = getLView(); const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix); if (interpolatedValue !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace); ngDevMode && storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 8, prefix, i0, i1, i2, i3, i4, i5, i6, suffix); } return ɵɵattributeInterpolate8; } /** * Update an interpolated attribute on an element with 9 or more bound values surrounded by text. * * Used when the number of interpolated values exceeds 8. * * ```html * <div * title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵattributeInterpolateV( * 'title', ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9, * 'suffix']); * ``` * * @param attrName The name of the attribute to update. * @param values The collection of values and the strings in-between those values, beginning with * a string prefix and ending with a string suffix. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`) * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵattributeInterpolateV(attrName, values, sanitizer, namespace) { const lView = getLView(); const interpolated = interpolationV(lView, values); if (interpolated !== NO_CHANGE) { const tNode = getSelectedTNode(); elementAttributeInternal(tNode, lView, attrName, interpolated, sanitizer, namespace); if (ngDevMode) { const interpolationInBetween = [values[0]]; // prefix for (let i = 2; i < values.length; i += 2) { interpolationInBetween.push(values[i]); } storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - interpolationInBetween.length + 1, ...interpolationInBetween); } } return ɵɵattributeInterpolateV; } /** * Synchronously perform change detection on a component (and possibly its sub-components). * * This function triggers change detection in a synchronous way on a component. * * @param component The component which the change detection should be performed on. */ function detectChanges(component) { const view = getComponentViewByInstance(component); detectChangesInternal(view[TVIEW], view, component); } function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) { ngDevMode && assertFirstCreatePass(tView); ngDevMode && ngDevMode.firstCreatePass++; const tViewConsts = tView.consts; // TODO(pk): refactor getOrCreateTNode to have the "create" only version const tNode = getOrCreateTNode(tView, index, 4 /* TNodeType.Container */, tagName || null, getConstant(tViewConsts, attrsIndex)); resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex)); registerPostOrderHooks(tView, tNode); const embeddedTView = tNode.tView = createTView(2 /* TViewType.Embedded */, tNode, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts); if (tView.queries !== null) { tView.queries.template(tView, tNode); embeddedTView.queries = tView.queries.embeddedTView(tNode); } return tNode; } /** * Creates an LContainer for an ng-template (dynamically-inserted view), e.g. * * <ng-template #foo> * <div></div> * </ng-template> * * @param index The index of the container in the data array * @param templateFn Inline template * @param decls The number of nodes, local refs, and pipes for this template * @param vars The number of bindings for this template * @param tagName The name of the container element, if applicable * @param attrsIndex Index of template attributes in the `consts` array. * @param localRefs Index of the local references in the `consts` array. * @param localRefExtractor A function which extracts local-refs values from the template. * Defaults to the current element associated with the local-ref. * * @codeGenApi */ function ɵɵtemplate(index, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex, localRefExtractor) { const lView = getLView(); const tView = getTView(); const adjustedIndex = index + HEADER_OFFSET; const tNode = tView.firstCreatePass ? templateFirstCreatePass(adjustedIndex, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) : tView.data[adjustedIndex]; setCurrentTNode(tNode, false); const comment = lView[RENDERER].createComment(ngDevMode ? 'container' : ''); appendChild(tView, lView, comment, tNode); attachPatchData(comment, lView); addToViewTree(lView, lView[adjustedIndex] = createLContainer(comment, lView, comment, tNode)); if (isDirectiveHost(tNode)) { createDirectivesInstances(tView, lView, tNode); } if (localRefsIndex != null) { saveResolvedLocalsInData(lView, tNode, localRefExtractor); } } /** Store a value in the `data` at a given `index`. */ function store(tView, lView, index, value) { // We don't store any static data for local variables, so the first time // we see the template, we should store as null to avoid a sparse array if (index >= tView.data.length) { tView.data[index] = null; tView.blueprint[index] = null; } lView[index] = value; } /** * Retrieves a local reference from the current contextViewData. * * If the reference to retrieve is in a parent view, this instruction is used in conjunction * with a nextContext() call, which walks up the tree and updates the contextViewData instance. * * @param index The index of the local ref in contextViewData. * * @codeGenApi */ function ɵɵreference(index) { const contextLView = getContextLView(); return load(contextLView, HEADER_OFFSET + index); } /** * Update a property on a selected element. * * Operates on the element selected by index via the {@link select} instruction. * * If the property name also exists as an input property on one of the element's directives, * the component property will be set instead of the element property. This check must * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled * * @param propName Name of property. Because it is going to DOM, this is not subject to * renaming as part of minification. * @param value New value to write. * @param sanitizer An optional function used to sanitize the value. * @returns This function returns itself so that it may be chained * (e.g. `property('name', ctx.name)('title', ctx.title)`) * * @codeGenApi */ function ɵɵproperty(propName, value, sanitizer) { const lView = getLView(); const bindingIndex = nextBindingIndex(); if (bindingUpdated(lView, bindingIndex, value)) { const tView = getTView(); const tNode = getSelectedTNode(); elementPropertyInternal(tView, tNode, lView, propName, value, lView[RENDERER], sanitizer, false); ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, bindingIndex); } return ɵɵproperty; } /** * Given `<div style="..." my-dir>` and `MyDir` with `@Input('style')` we need to write to * directive input. */ function setDirectiveInputsWhichShadowsStyling(tView, tNode, lView, value, isClassBased) { const inputs = tNode.inputs; const property = isClassBased ? 'class' : 'style'; // We support both 'class' and `className` hence the fallback. setInputsForProperty(tView, lView, inputs[property], property, value); } function elementStartFirstCreatePass(index, tView, lView, name, attrsIndex, localRefsIndex) { ngDevMode && assertFirstCreatePass(tView); ngDevMode && ngDevMode.firstCreatePass++; const tViewConsts = tView.consts; const attrs = getConstant(tViewConsts, attrsIndex); const tNode = getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, name, attrs); resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex)); if (tNode.attrs !== null) { computeStaticStyling(tNode, tNode.attrs, false); } if (tNode.mergedAttrs !== null) { computeStaticStyling(tNode, tNode.mergedAttrs, true); } if (tView.queries !== null) { tView.queries.elementStart(tView, tNode); } return tNode; } /** * Create DOM element. The instruction must later be followed by `elementEnd()` call. * * @param index Index of the element in the LView array * @param name Name of the DOM Node * @param attrsIndex Index of the element's attributes in the `consts` array. * @param localRefsIndex Index of the element's local references in the `consts` array. * @returns This function returns itself so that it may be chained. * * Attributes and localRefs are passed as an array of strings where elements with an even index * hold an attribute name and elements with an odd index hold an attribute value, ex.: * ['id', 'warning5', 'class', 'alert'] * * @codeGenApi */ function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) { const lView = getLView(); const tView = getTView(); const adjustedIndex = HEADER_OFFSET + index; ngDevMode && assertEqual(getBindingIndex(), tView.bindingStartIndex, 'elements should be created before any bindings'); ngDevMode && assertIndexInRange(lView, adjustedIndex); const renderer = lView[RENDERER]; const tNode = tView.firstCreatePass ? elementStartFirstCreatePass(adjustedIndex, tView, lView, name, attrsIndex, localRefsIndex) : tView.data[adjustedIndex]; const native = lView[adjustedIndex] = createElementNode(renderer, name, getNamespace$1()); const hasDirectives = isDirectiveHost(tNode); if (ngDevMode && tView.firstCreatePass) { validateElementIsKnown(native, lView, tNode.value, tView.schemas, hasDirectives); } setCurrentTNode(tNode, true); setupStaticAttributes(renderer, native, tNode); if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) { // In the i18n case, the translation may have removed this element, so only add it if it is not // detached. See `TNodeType.Placeholder` and `LFrame.inI18n` for more context. appendChild(tView, lView, native, tNode); } // any immediate children of a component or template container must be pre-emptively // monkey-patched with the component view data so that the element can be inspected // later on using any element discovery utility methods (see `element_discovery.ts`) if (getElementDepthCount() === 0) { attachPatchData(native, lView); } increaseElementDepthCount(); if (hasDirectives) { createDirectivesInstances(tView, lView, tNode); executeContentQueries(tView, tNode, lView); } if (localRefsIndex !== null) { saveResolvedLocalsInData(lView, tNode); } return ɵɵelementStart; } /** * Mark the end of the element. * @returns This function returns itself so that it may be chained. * * @codeGenApi */ function ɵɵelementEnd() { let currentTNode = getCurrentTNode(); ngDevMode && assertDefined(currentTNode, 'No parent node to close.'); if (isCurrentTNodeParent()) { setCurrentTNodeAsNotParent(); } else { ngDevMode && assertHasParent(getCurrentTNode()); currentTNode = currentTNode.parent; setCurrentTNode(currentTNode, false); } const tNode = currentTNode; ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */); decreaseElementDepthCount(); const tView = getTView(); if (tView.firstCreatePass) { registerPostOrderHooks(tView, currentTNode); if (isContentQueryHost(currentTNode)) { tView.queries.elementEnd(currentTNode); } } if (tNode.classesWithoutHost != null && hasClassInput(tNode)) { setDirectiveInputsWhichShadowsStyling(tView, tNode, getLView(), tNode.classesWithoutHost, true); } if (tNode.stylesWithoutHost != null && hasStyleInput(tNode)) { setDirectiveInputsWhichShadowsStyling(tView, tNode, getLView(), tNode.stylesWithoutHost, false); } return ɵɵelementEnd; } /** * Creates an empty element using {@link elementStart} and {@link elementEnd} * * @param index Index of the element in the data array * @param name Name of the DOM Node * @param attrsIndex Index of the element's attributes in the `consts` array. * @param localRefsIndex Index of the element's local references in the `consts` array. * @returns This function returns itself so that it may be chained. * * @codeGenApi */ function ɵɵelement(index, name, attrsIndex, localRefsIndex) { ɵɵelementStart(index, name, attrsIndex, localRefsIndex); ɵɵelementEnd(); return ɵɵelement; } function elementContainerStartFirstCreatePass(index, tView, lView, attrsIndex, localRefsIndex) { ngDevMode && ngDevMode.firstCreatePass++; const tViewConsts = tView.consts; const attrs = getConstant(tViewConsts, attrsIndex); const tNode = getOrCreateTNode(tView, index, 8 /* TNodeType.ElementContainer */, 'ng-container', attrs); // While ng-container doesn't necessarily support styling, we use the style context to identify // and execute directives on the ng-container. if (attrs !== null) { computeStaticStyling(tNode, attrs, true); } const localRefs = getConstant(tViewConsts, localRefsIndex); resolveDirectives(tView, lView, tNode, localRefs); if (tView.queries !== null) { tView.queries.elementStart(tView, tNode); } return tNode; } /** * Creates a logical container for other nodes (<ng-container>) backed by a comment node in the DOM. * The instruction must later be followed by `elementContainerEnd()` call. * * @param index Index of the element in the LView array * @param attrsIndex Index of the container attributes in the `consts` array. * @param localRefsIndex Index of the container's local references in the `consts` array. * @returns This function returns itself so that it may be chained. * * Even if this instruction accepts a set of attributes no actual attribute values are propagated to * the DOM (as a comment node can't have attributes). Attributes are here only for directive * matching purposes and setting initial inputs of directives. * * @codeGenApi */ function ɵɵelementContainerStart(index, attrsIndex, localRefsIndex) { const lView = getLView(); const tView = getTView(); const adjustedIndex = index + HEADER_OFFSET; ngDevMode && assertIndexInRange(lView, adjustedIndex); ngDevMode && assertEqual(getBindingIndex(), tView.bindingStartIndex, 'element containers should be created before any bindings'); const tNode = tView.firstCreatePass ? elementContainerStartFirstCreatePass(adjustedIndex, tView, lView, attrsIndex, localRefsIndex) : tView.data[adjustedIndex]; setCurrentTNode(tNode, true); ngDevMode && ngDevMode.rendererCreateComment++; const native = lView[adjustedIndex] = lView[RENDERER].createComment(ngDevMode ? 'ng-container' : ''); appendChild(tView, lView, native, tNode); attachPatchData(native, lView); if (isDirectiveHost(tNode)) { createDirectivesInstances(tView, lView, tNode); executeContentQueries(tView, tNode, lView); } if (localRefsIndex != null) { saveResolvedLocalsInData(lView, tNode); } return ɵɵelementContainerStart; } /** * Mark the end of the <ng-container>. * @returns This function returns itself so that it may be chained. * * @codeGenApi */ function ɵɵelementContainerEnd() { let currentTNode = getCurrentTNode(); const tView = getTView(); if (isCurrentTNodeParent()) { setCurrentTNodeAsNotParent(); } else { ngDevMode && assertHasParent(currentTNode); currentTNode = currentTNode.parent; setCurrentTNode(currentTNode, false); } ngDevMode && assertTNodeType(currentTNode, 8 /* TNodeType.ElementContainer */); if (tView.firstCreatePass) { registerPostOrderHooks(tView, currentTNode); if (isContentQueryHost(currentTNode)) { tView.queries.elementEnd(currentTNode); } } return ɵɵelementContainerEnd; } /** * Creates an empty logical container using {@link elementContainerStart} * and {@link elementContainerEnd} * * @param index Index of the element in the LView array * @param attrsIndex Index of the container attributes in the `consts` array. * @param localRefsIndex Index of the container's local references in the `consts` array. * @returns This function returns itself so that it may be chained. * * @codeGenApi */ function ɵɵelementContainer(index, attrsIndex, localRefsIndex) { ɵɵelementContainerStart(index, attrsIndex, localRefsIndex); ɵɵelementContainerEnd(); return ɵɵelementContainer; } /** * Returns the current OpaqueViewState instance. * * Used in conjunction with the restoreView() instruction to save a snapshot * of the current view and restore it when listeners are invoked. This allows * walking the declaration view tree in listeners to get vars from parent views. * * @codeGenApi */ function ɵɵgetCurrentView() { return getLView(); } /** * Determine if the argument is shaped like a Promise */ function isPromise(obj) { // allow any Promise/A+ compliant thenable. // It's up to the caller to ensure that obj.then conforms to the spec return !!obj && typeof obj.then === 'function'; } /** * Determine if the argument is a Subscribable */ function isSubscribable(obj) { return !!obj && typeof obj.subscribe === 'function'; } /** * Determine if the argument is an Observable * * Strictly this tests that the `obj` is `Subscribable`, since `Observable` * types need additional methods, such as `lift()`. But it is adequate for our * needs since within the Angular framework code we only ever need to use the * `subscribe()` method, and RxJS has mechanisms to wrap `Subscribable` objects * into `Observable` as needed. */ const isObservable = isSubscribable; /** * Adds an event listener to the current node. * * If an output exists on one of the node's directives, it also subscribes to the output * and saves the subscription for later cleanup. * * @param eventName Name of the event * @param listenerFn The function to be called when event emits * @param useCapture Whether or not to use capture in event listener - this argument is a reminder * from the Renderer3 infrastructure and should be removed from the instruction arguments * @param eventTargetResolver Function that returns global target information in case this listener * should be attached to a global object like window, document or body * * @codeGenApi */ function ɵɵlistener(eventName, listenerFn, useCapture, eventTargetResolver) { const lView = getLView(); const tView = getTView(); const tNode = getCurrentTNode(); listenerInternal(tView, lView, lView[RENDERER], tNode, eventName, listenerFn, eventTargetResolver); return ɵɵlistener; } /** * Registers a synthetic host listener (e.g. `(@foo.start)`) on a component or directive. * * This instruction is for compatibility purposes and is designed to ensure that a * synthetic host listener (e.g. `@HostListener('@foo.start')`) properly gets rendered * in the component's renderer. Normally all host listeners are evaluated with the * parent component's renderer, but, in the case of animation @triggers, they need * to be evaluated with the sub component's renderer (because that's where the * animation triggers are defined). * * Do not use this instruction as a replacement for `listener`. This instruction * only exists to ensure compatibility with the ViewEngine's host binding behavior. * * @param eventName Name of the event * @param listenerFn The function to be called when event emits * @param useCapture Whether or not to use capture in event listener * @param eventTargetResolver Function that returns global target information in case this listener * should be attached to a global object like window, document or body * * @codeGenApi */ function ɵɵsyntheticHostListener(eventName, listenerFn) { const tNode = getCurrentTNode(); const lView = getLView(); const tView = getTView(); const currentDef = getCurrentDirectiveDef(tView.data); const renderer = loadComponentRenderer(currentDef, tNode, lView); listenerInternal(tView, lView, renderer, tNode, eventName, listenerFn); return ɵɵsyntheticHostListener; } /** * A utility function that checks if a given element has already an event handler registered for an * event with a specified name. The TView.cleanup data structure is used to find out which events * are registered for a given element. */ function findExistingListener(tView, lView, eventName, tNodeIdx) { const tCleanup = tView.cleanup; if (tCleanup != null) { for (let i = 0; i < tCleanup.length - 1; i += 2) { const cleanupEventName = tCleanup[i]; if (cleanupEventName === eventName && tCleanup[i + 1] === tNodeIdx) { // We have found a matching event name on the same node but it might not have been // registered yet, so we must explicitly verify entries in the LView cleanup data // structures. const lCleanup = lView[CLEANUP]; const listenerIdxInLCleanup = tCleanup[i + 2]; return lCleanup.length > listenerIdxInLCleanup ? lCleanup[listenerIdxInLCleanup] : null; } // TView.cleanup can have a mix of 4-elements entries (for event handler cleanups) or // 2-element entries (for directive and queries destroy hooks). As such we can encounter // blocks of 4 or 2 items in the tView.cleanup and this is why we iterate over 2 elements // first and jump another 2 elements if we detect listeners cleanup (4 elements). Also check // documentation of TView.cleanup for more details of this data structure layout. if (typeof cleanupEventName === 'string') { i += 2; } } } return null; } function listenerInternal(tView, lView, renderer, tNode, eventName, listenerFn, eventTargetResolver) { const isTNodeDirectiveHost = isDirectiveHost(tNode); const firstCreatePass = tView.firstCreatePass; const tCleanup = firstCreatePass && getOrCreateTViewCleanup(tView); const context = lView[CONTEXT]; // When the ɵɵlistener instruction was generated and is executed we know that there is either a // native listener or a directive output on this element. As such we we know that we will have to // register a listener and store its cleanup function on LView. const lCleanup = getOrCreateLViewCleanup(lView); ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */); let processOutputs = true; // Adding a native event listener is applicable when: // - The corresponding TNode represents a DOM element. // - The event target has a resolver (usually resulting in a global object, // such as `window` or `document`). if (tNode.type & 3 /* TNodeType.AnyRNode */ || eventTargetResolver) { const native = getNativeByTNode(tNode, lView); const target = eventTargetResolver ? eventTargetResolver(native) : native; const lCleanupIndex = lCleanup.length; const idxOrTargetGetter = eventTargetResolver ? _lView => eventTargetResolver(unwrapRNode(_lView[tNode.index])) : tNode.index; // In order to match current behavior, native DOM event listeners must be added for all // events (including outputs). // There might be cases where multiple directives on the same element try to register an event // handler function for the same event. In this situation we want to avoid registration of // several native listeners as each registration would be intercepted by NgZone and // trigger change detection. This would mean that a single user action would result in several // change detections being invoked. To avoid this situation we want to have only one call to // native handler registration (for the same element and same type of event). // // In order to have just one native event handler in presence of multiple handler functions, // we just register a first handler function as a native event listener and then chain // (coalesce) other handler functions on top of the first native handler function. let existingListener = null; // Please note that the coalescing described here doesn't happen for events specifying an // alternative target (ex. (document:click)) - this is to keep backward compatibility with the // view engine. // Also, we don't have to search for existing listeners is there are no directives // matching on a given node as we can't register multiple event handlers for the same event in // a template (this would mean having duplicate attributes). if (!eventTargetResolver && isTNodeDirectiveHost) { existingListener = findExistingListener(tView, lView, eventName, tNode.index); } if (existingListener !== null) { // Attach a new listener to coalesced listeners list, maintaining the order in which // listeners are registered. For performance reasons, we keep a reference to the last // listener in that list (in `__ngLastListenerFn__` field), so we can avoid going through // the entire set each time we need to add a new listener. const lastListenerFn = existingListener.__ngLastListenerFn__ || existingListener; lastListenerFn.__ngNextListenerFn__ = listenerFn; existingListener.__ngLastListenerFn__ = listenerFn; processOutputs = false; } else { listenerFn = wrapListener(tNode, lView, context, listenerFn, false /** preventDefault */); const cleanupFn = renderer.listen(target, eventName, listenerFn); ngDevMode && ngDevMode.rendererAddEventListener++; lCleanup.push(listenerFn, cleanupFn); tCleanup && tCleanup.push(eventName, idxOrTargetGetter, lCleanupIndex, lCleanupIndex + 1); } } else { // Even if there is no native listener to add, we still need to wrap the listener so that OnPush // ancestors are marked dirty when an event occurs. listenerFn = wrapListener(tNode, lView, context, listenerFn, false /** preventDefault */); } // subscribe to directive outputs const outputs = tNode.outputs; let props; if (processOutputs && outputs !== null && (props = outputs[eventName])) { const propsLength = props.length; if (propsLength) { for (let i = 0; i < propsLength; i += 2) { const index = props[i]; ngDevMode && assertIndexInRange(lView, index); const minifiedName = props[i + 1]; const directiveInstance = lView[index]; const output = directiveInstance[minifiedName]; if (ngDevMode && !isObservable(output)) { throw new Error(`@Output ${minifiedName} not initialized in '${directiveInstance.constructor.name}'.`); } const subscription = output.subscribe(listenerFn); const idx = lCleanup.length; lCleanup.push(listenerFn, subscription); tCleanup && tCleanup.push(eventName, tNode.index, idx, -(idx + 1)); } } } } function executeListenerWithErrorHandling(lView, context, listenerFn, e) { try { profiler(6 /* ProfilerEvent.OutputStart */, context, listenerFn); // Only explicitly returning false from a listener should preventDefault return listenerFn(e) !== false; } catch (error) { handleError(lView, error); return false; } finally { profiler(7 /* ProfilerEvent.OutputEnd */, context, listenerFn); } } /** * Wraps an event listener with a function that marks ancestors dirty and prevents default behavior, * if applicable. * * @param tNode The TNode associated with this listener * @param lView The LView that contains this listener * @param listenerFn The listener function to call * @param wrapWithPreventDefault Whether or not to prevent default behavior * (the procedural renderer does this already, so in those cases, we should skip) */ function wrapListener(tNode, lView, context, listenerFn, wrapWithPreventDefault) { // Note: we are performing most of the work in the listener function itself // to optimize listener registration. return function wrapListenerIn_markDirtyAndPreventDefault(e) { // Ivy uses `Function` as a special token that allows us to unwrap the function // so that it can be invoked programmatically by `DebugNode.triggerEventHandler`. if (e === Function) { return listenerFn; } // In order to be backwards compatible with View Engine, events on component host nodes // must also mark the component view itself dirty (i.e. the view that it owns). const startView = tNode.componentOffset > -1 ? getComponentLViewByIndex(tNode.index, lView) : lView; markViewDirty(startView); let result = executeListenerWithErrorHandling(lView, context, listenerFn, e); // A just-invoked listener function might have coalesced listeners so we need to check for // their presence and invoke as needed. let nextListenerFn = wrapListenerIn_markDirtyAndPreventDefault.__ngNextListenerFn__; while (nextListenerFn) { // We should prevent default if any of the listeners explicitly return false result = executeListenerWithErrorHandling(lView, context, nextListenerFn, e) && result; nextListenerFn = nextListenerFn.__ngNextListenerFn__; } if (wrapWithPreventDefault && result === false) { e.preventDefault(); // Necessary for legacy browsers that don't support preventDefault (e.g. IE) e.returnValue = false; } return result; }; } /** * Retrieves a context at the level specified and saves it as the global, contextViewData. * Will get the next level up if level is not specified. * * This is used to save contexts of parent views so they can be bound in embedded views, or * in conjunction with reference() to bind a ref from a parent view. * * @param level The relative level of the view from which to grab context compared to contextVewData * @returns context * * @codeGenApi */ function ɵɵnextContext(level = 1) { return nextContextImpl(level); } /** * Checks a given node against matching projection slots and returns the * determined slot index. Returns "null" if no slot matched the given node. * * This function takes into account the parsed ngProjectAs selector from the * node's attributes. If present, it will check whether the ngProjectAs selector * matches any of the projection slot selectors. */ function matchingProjectionSlotIndex(tNode, projectionSlots) { let wildcardNgContentIndex = null; const ngProjectAsAttrVal = getProjectAsAttrValue(tNode); for (let i = 0; i < projectionSlots.length; i++) { const slotValue = projectionSlots[i]; // The last wildcard projection slot should match all nodes which aren't matching // any selector. This is necessary to be backwards compatible with view engine. if (slotValue === '*') { wildcardNgContentIndex = i; continue; } // If we ran into an `ngProjectAs` attribute, we should match its parsed selector // to the list of selectors, otherwise we fall back to matching against the node. if (ngProjectAsAttrVal === null ? isNodeMatchingSelectorList(tNode, slotValue, /* isProjectionMode */true) : isSelectorInSelectorList(ngProjectAsAttrVal, slotValue)) { return i; // first matching selector "captures" a given node } } return wildcardNgContentIndex; } /** * Instruction to distribute projectable nodes among <ng-content> occurrences in a given template. * It takes all the selectors from the entire component's template and decides where * each projected node belongs (it re-distributes nodes among "buckets" where each "bucket" is * backed by a selector). * * This function requires CSS selectors to be provided in 2 forms: parsed (by a compiler) and text, * un-parsed form. * * The parsed form is needed for efficient matching of a node against a given CSS selector. * The un-parsed, textual form is needed for support of the ngProjectAs attribute. * * Having a CSS selector in 2 different formats is not ideal, but alternatives have even more * drawbacks: * - having only a textual form would require runtime parsing of CSS selectors; * - we can't have only a parsed as we can't re-construct textual form from it (as entered by a * template author). * * @param projectionSlots? A collection of projection slots. A projection slot can be based * on a parsed CSS selectors or set to the wildcard selector ("*") in order to match * all nodes which do not match any selector. If not specified, a single wildcard * selector projection slot will be defined. * * @codeGenApi */ function ɵɵprojectionDef(projectionSlots) { const componentNode = getLView()[DECLARATION_COMPONENT_VIEW][T_HOST]; if (!componentNode.projection) { // If no explicit projection slots are defined, fall back to a single // projection slot with the wildcard selector. const numProjectionSlots = projectionSlots ? projectionSlots.length : 1; const projectionHeads = componentNode.projection = newArray(numProjectionSlots, null); const tails = projectionHeads.slice(); let componentChild = componentNode.child; while (componentChild !== null) { const slotIndex = projectionSlots ? matchingProjectionSlotIndex(componentChild, projectionSlots) : 0; if (slotIndex !== null) { if (tails[slotIndex]) { tails[slotIndex].projectionNext = componentChild; } else { projectionHeads[slotIndex] = componentChild; } tails[slotIndex] = componentChild; } componentChild = componentChild.next; } } } /** * Inserts previously re-distributed projected nodes. This instruction must be preceded by a call * to the projectionDef instruction. * * @param nodeIndex * @param selectorIndex: * - 0 when the selector is `*` (or unspecified as this is the default value), * - 1 based index of the selector from the {@link projectionDef} * * @codeGenApi */ function ɵɵprojection(nodeIndex, selectorIndex = 0, attrs) { const lView = getLView(); const tView = getTView(); const tProjectionNode = getOrCreateTNode(tView, HEADER_OFFSET + nodeIndex, 16 /* TNodeType.Projection */, null, attrs || null); // We can't use viewData[HOST_NODE] because projection nodes can be nested in embedded views. if (tProjectionNode.projection === null) tProjectionNode.projection = selectorIndex; // `<ng-content>` has no content setCurrentTNodeAsNotParent(); if ((tProjectionNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) { // re-distribution of projectable nodes is stored on a component's view level applyProjection(tView, lView, tProjectionNode); } } /** * * Update an interpolated property on an element with a lone bound value * * Used when the value passed to a property has 1 interpolated value in it, an no additional text * surrounds that interpolated value: * * ```html * <div title="{{v0}}"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵpropertyInterpolate('title', v0); * ``` * * If the property name also exists as an input property on one of the element's directives, * the component property will be set instead of the element property. This check must * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled. * * @param propName The name of the property to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵpropertyInterpolate(propName, v0, sanitizer) { ɵɵpropertyInterpolate1(propName, '', v0, '', sanitizer); return ɵɵpropertyInterpolate; } /** * * Update an interpolated property on an element with single bound value surrounded by text. * * Used when the value passed to a property has 1 interpolated value in it: * * ```html * <div title="prefix{{v0}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵpropertyInterpolate1('title', 'prefix', v0, 'suffix'); * ``` * * If the property name also exists as an input property on one of the element's directives, * the component property will be set instead of the element property. This check must * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled. * * @param propName The name of the property to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵpropertyInterpolate1(propName, prefix, v0, suffix, sanitizer) { const lView = getLView(); const interpolatedValue = interpolation1(lView, prefix, v0, suffix); if (interpolatedValue !== NO_CHANGE) { const tView = getTView(); const tNode = getSelectedTNode(); elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false); ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 1, prefix, suffix); } return ɵɵpropertyInterpolate1; } /** * * Update an interpolated property on an element with 2 bound values surrounded by text. * * Used when the value passed to a property has 2 interpolated values in it: * * ```html * <div title="prefix{{v0}}-{{v1}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵpropertyInterpolate2('title', 'prefix', v0, '-', v1, 'suffix'); * ``` * * If the property name also exists as an input property on one of the element's directives, * the component property will be set instead of the element property. This check must * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled. * * @param propName The name of the property to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵpropertyInterpolate2(propName, prefix, v0, i0, v1, suffix, sanitizer) { const lView = getLView(); const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix); if (interpolatedValue !== NO_CHANGE) { const tView = getTView(); const tNode = getSelectedTNode(); elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false); ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 2, prefix, i0, suffix); } return ɵɵpropertyInterpolate2; } /** * * Update an interpolated property on an element with 3 bound values surrounded by text. * * Used when the value passed to a property has 3 interpolated values in it: * * ```html * <div title="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵpropertyInterpolate3( * 'title', 'prefix', v0, '-', v1, '-', v2, 'suffix'); * ``` * * If the property name also exists as an input property on one of the element's directives, * the component property will be set instead of the element property. This check must * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled. * * @param propName The name of the property to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵpropertyInterpolate3(propName, prefix, v0, i0, v1, i1, v2, suffix, sanitizer) { const lView = getLView(); const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix); if (interpolatedValue !== NO_CHANGE) { const tView = getTView(); const tNode = getSelectedTNode(); elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false); ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 3, prefix, i0, i1, suffix); } return ɵɵpropertyInterpolate3; } /** * * Update an interpolated property on an element with 4 bound values surrounded by text. * * Used when the value passed to a property has 4 interpolated values in it: * * ```html * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵpropertyInterpolate4( * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix'); * ``` * * If the property name also exists as an input property on one of the element's directives, * the component property will be set instead of the element property. This check must * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled. * * @param propName The name of the property to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵpropertyInterpolate4(propName, prefix, v0, i0, v1, i1, v2, i2, v3, suffix, sanitizer) { const lView = getLView(); const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix); if (interpolatedValue !== NO_CHANGE) { const tView = getTView(); const tNode = getSelectedTNode(); elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false); ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 4, prefix, i0, i1, i2, suffix); } return ɵɵpropertyInterpolate4; } /** * * Update an interpolated property on an element with 5 bound values surrounded by text. * * Used when the value passed to a property has 5 interpolated values in it: * * ```html * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵpropertyInterpolate5( * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix'); * ``` * * If the property name also exists as an input property on one of the element's directives, * the component property will be set instead of the element property. This check must * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled. * * @param propName The name of the property to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵpropertyInterpolate5(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix, sanitizer) { const lView = getLView(); const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix); if (interpolatedValue !== NO_CHANGE) { const tView = getTView(); const tNode = getSelectedTNode(); elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false); ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 5, prefix, i0, i1, i2, i3, suffix); } return ɵɵpropertyInterpolate5; } /** * * Update an interpolated property on an element with 6 bound values surrounded by text. * * Used when the value passed to a property has 6 interpolated values in it: * * ```html * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵpropertyInterpolate6( * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix'); * ``` * * If the property name also exists as an input property on one of the element's directives, * the component property will be set instead of the element property. This check must * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled. * * @param propName The name of the property to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵpropertyInterpolate6(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix, sanitizer) { const lView = getLView(); const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix); if (interpolatedValue !== NO_CHANGE) { const tView = getTView(); const tNode = getSelectedTNode(); elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false); ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 6, prefix, i0, i1, i2, i3, i4, suffix); } return ɵɵpropertyInterpolate6; } /** * * Update an interpolated property on an element with 7 bound values surrounded by text. * * Used when the value passed to a property has 7 interpolated values in it: * * ```html * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵpropertyInterpolate7( * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix'); * ``` * * If the property name also exists as an input property on one of the element's directives, * the component property will be set instead of the element property. This check must * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled. * * @param propName The name of the property to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param i5 Static value used for concatenation only. * @param v6 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵpropertyInterpolate7(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix, sanitizer) { const lView = getLView(); const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix); if (interpolatedValue !== NO_CHANGE) { const tView = getTView(); const tNode = getSelectedTNode(); elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false); ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 7, prefix, i0, i1, i2, i3, i4, i5, suffix); } return ɵɵpropertyInterpolate7; } /** * * Update an interpolated property on an element with 8 bound values surrounded by text. * * Used when the value passed to a property has 8 interpolated values in it: * * ```html * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵpropertyInterpolate8( * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix'); * ``` * * If the property name also exists as an input property on one of the element's directives, * the component property will be set instead of the element property. This check must * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled. * * @param propName The name of the property to update * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param i5 Static value used for concatenation only. * @param v6 Value checked for change. * @param i6 Static value used for concatenation only. * @param v7 Value checked for change. * @param suffix Static value used for concatenation only. * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵpropertyInterpolate8(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix, sanitizer) { const lView = getLView(); const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix); if (interpolatedValue !== NO_CHANGE) { const tView = getTView(); const tNode = getSelectedTNode(); elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false); ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 8, prefix, i0, i1, i2, i3, i4, i5, i6, suffix); } return ɵɵpropertyInterpolate8; } /** * Update an interpolated property on an element with 9 or more bound values surrounded by text. * * Used when the number of interpolated values exceeds 8. * * ```html * <div * title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"></div> * ``` * * Its compiled representation is:: * * ```ts * ɵɵpropertyInterpolateV( * 'title', ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9, * 'suffix']); * ``` * * If the property name also exists as an input property on one of the element's directives, * the component property will be set instead of the element property. This check must * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled. * * @param propName The name of the property to update. * @param values The collection of values and the strings in between those values, beginning with a * string prefix and ending with a string suffix. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`) * @param sanitizer An optional sanitizer function * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵpropertyInterpolateV(propName, values, sanitizer) { const lView = getLView(); const interpolatedValue = interpolationV(lView, values); if (interpolatedValue !== NO_CHANGE) { const tView = getTView(); const tNode = getSelectedTNode(); elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false); if (ngDevMode) { const interpolationInBetween = [values[0]]; // prefix for (let i = 2; i < values.length; i += 2) { interpolationInBetween.push(values[i]); } storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - interpolationInBetween.length + 1, ...interpolationInBetween); } } return ɵɵpropertyInterpolateV; } function toTStylingRange(prev, next) { ngDevMode && assertNumberInRange(prev, 0, 32767 /* StylingRange.UNSIGNED_MASK */); ngDevMode && assertNumberInRange(next, 0, 32767 /* StylingRange.UNSIGNED_MASK */); return prev << 17 /* StylingRange.PREV_SHIFT */ | next << 2 /* StylingRange.NEXT_SHIFT */; } function getTStylingRangePrev(tStylingRange) { ngDevMode && assertNumber(tStylingRange, 'expected number'); return tStylingRange >> 17 /* StylingRange.PREV_SHIFT */ & 32767 /* StylingRange.UNSIGNED_MASK */; } function getTStylingRangePrevDuplicate(tStylingRange) { ngDevMode && assertNumber(tStylingRange, 'expected number'); return (tStylingRange & 2 /* StylingRange.PREV_DUPLICATE */) == 2 /* StylingRange.PREV_DUPLICATE */; } function setTStylingRangePrev(tStylingRange, previous) { ngDevMode && assertNumber(tStylingRange, 'expected number'); ngDevMode && assertNumberInRange(previous, 0, 32767 /* StylingRange.UNSIGNED_MASK */); return tStylingRange & ~4294836224 /* StylingRange.PREV_MASK */ | previous << 17 /* StylingRange.PREV_SHIFT */; } function setTStylingRangePrevDuplicate(tStylingRange) { ngDevMode && assertNumber(tStylingRange, 'expected number'); return tStylingRange | 2 /* StylingRange.PREV_DUPLICATE */; } function getTStylingRangeNext(tStylingRange) { ngDevMode && assertNumber(tStylingRange, 'expected number'); return (tStylingRange & 131068 /* StylingRange.NEXT_MASK */) >> 2 /* StylingRange.NEXT_SHIFT */; } function setTStylingRangeNext(tStylingRange, next) { ngDevMode && assertNumber(tStylingRange, 'expected number'); ngDevMode && assertNumberInRange(next, 0, 32767 /* StylingRange.UNSIGNED_MASK */); return tStylingRange & ~131068 /* StylingRange.NEXT_MASK */ | // next << 2 /* StylingRange.NEXT_SHIFT */; } function getTStylingRangeNextDuplicate(tStylingRange) { ngDevMode && assertNumber(tStylingRange, 'expected number'); return (tStylingRange & 1 /* StylingRange.NEXT_DUPLICATE */) === 1 /* StylingRange.NEXT_DUPLICATE */; } function setTStylingRangeNextDuplicate(tStylingRange) { ngDevMode && assertNumber(tStylingRange, 'expected number'); return tStylingRange | 1 /* StylingRange.NEXT_DUPLICATE */; } function getTStylingRangeTail(tStylingRange) { ngDevMode && assertNumber(tStylingRange, 'expected number'); const next = getTStylingRangeNext(tStylingRange); return next === 0 ? getTStylingRangePrev(tStylingRange) : next; } /** * NOTE: The word `styling` is used interchangeably as style or class styling. * * This file contains code to link styling instructions together so that they can be replayed in * priority order. The file exists because Ivy styling instruction execution order does not match * that of the priority order. The purpose of this code is to create a linked list so that the * instructions can be traversed in priority order when computing the styles. * * Assume we are dealing with the following code: * ``` * @Component({ * template: ` * <my-cmp [style]=" {color: '#001'} " * [style.color]=" #002 " * dir-style-color-1 * dir-style-color-2> ` * }) * class ExampleComponent { * static ngComp = ... { * ... * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap` * ɵɵstyleMap({color: '#001'}); * ɵɵstyleProp('color', '#002'); * ... * } * } * * @Directive({ * selector: `[dir-style-color-1]', * }) * class Style1Directive { * @HostBinding('style') style = {color: '#005'}; * @HostBinding('style.color') color = '#006'; * * static ngDir = ... { * ... * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap` * ɵɵstyleMap({color: '#005'}); * ɵɵstyleProp('color', '#006'); * ... * } * } * * @Directive({ * selector: `[dir-style-color-2]', * }) * class Style2Directive { * @HostBinding('style') style = {color: '#007'}; * @HostBinding('style.color') color = '#008'; * * static ngDir = ... { * ... * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap` * ɵɵstyleMap({color: '#007'}); * ɵɵstyleProp('color', '#008'); * ... * } * } * * @Directive({ * selector: `my-cmp', * }) * class MyComponent { * @HostBinding('style') style = {color: '#003'}; * @HostBinding('style.color') color = '#004'; * * static ngComp = ... { * ... * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap` * ɵɵstyleMap({color: '#003'}); * ɵɵstyleProp('color', '#004'); * ... * } * } * ``` * * The Order of instruction execution is: * * NOTE: the comment binding location is for illustrative purposes only. * * ``` * // Template: (ExampleComponent) * ɵɵstyleMap({color: '#001'}); // Binding index: 10 * ɵɵstyleProp('color', '#002'); // Binding index: 12 * // MyComponent * ɵɵstyleMap({color: '#003'}); // Binding index: 20 * ɵɵstyleProp('color', '#004'); // Binding index: 22 * // Style1Directive * ɵɵstyleMap({color: '#005'}); // Binding index: 24 * ɵɵstyleProp('color', '#006'); // Binding index: 26 * // Style2Directive * ɵɵstyleMap({color: '#007'}); // Binding index: 28 * ɵɵstyleProp('color', '#008'); // Binding index: 30 * ``` * * The correct priority order of concatenation is: * * ``` * // MyComponent * ɵɵstyleMap({color: '#003'}); // Binding index: 20 * ɵɵstyleProp('color', '#004'); // Binding index: 22 * // Style1Directive * ɵɵstyleMap({color: '#005'}); // Binding index: 24 * ɵɵstyleProp('color', '#006'); // Binding index: 26 * // Style2Directive * ɵɵstyleMap({color: '#007'}); // Binding index: 28 * ɵɵstyleProp('color', '#008'); // Binding index: 30 * // Template: (ExampleComponent) * ɵɵstyleMap({color: '#001'}); // Binding index: 10 * ɵɵstyleProp('color', '#002'); // Binding index: 12 * ``` * * What color should be rendered? * * Once the items are correctly sorted in the list, the answer is simply the last item in the * concatenation list which is `#002`. * * To do so we keep a linked list of all of the bindings which pertain to this element. * Notice that the bindings are inserted in the order of execution, but the `TView.data` allows * us to traverse them in the order of priority. * * |Idx|`TView.data`|`LView` | Notes * |---|------------|-----------------|-------------- * |...| | | * |10 |`null` |`{color: '#001'}`| `ɵɵstyleMap('color', {color: '#001'})` * |11 |`30 | 12` | ... | * |12 |`color` |`'#002'` | `ɵɵstyleProp('color', '#002')` * |13 |`10 | 0` | ... | * |...| | | * |20 |`null` |`{color: '#003'}`| `ɵɵstyleMap('color', {color: '#003'})` * |21 |`0 | 22` | ... | * |22 |`color` |`'#004'` | `ɵɵstyleProp('color', '#004')` * |23 |`20 | 24` | ... | * |24 |`null` |`{color: '#005'}`| `ɵɵstyleMap('color', {color: '#005'})` * |25 |`22 | 26` | ... | * |26 |`color` |`'#006'` | `ɵɵstyleProp('color', '#006')` * |27 |`24 | 28` | ... | * |28 |`null` |`{color: '#007'}`| `ɵɵstyleMap('color', {color: '#007'})` * |29 |`26 | 30` | ... | * |30 |`color` |`'#008'` | `ɵɵstyleProp('color', '#008')` * |31 |`28 | 10` | ... | * * The above data structure allows us to re-concatenate the styling no matter which data binding * changes. * * NOTE: in addition to keeping track of next/previous index the `TView.data` also stores prev/next * duplicate bit. The duplicate bit if true says there either is a binding with the same name or * there is a map (which may contain the name). This information is useful in knowing if other * styles with higher priority need to be searched for overwrites. * * NOTE: See `should support example in 'tnode_linked_list.ts' documentation` in * `tnode_linked_list_spec.ts` for working example. */ let __unused_const_as_closure_does_not_like_standalone_comment_blocks__; /** * Insert new `tStyleValue` at `TData` and link existing style bindings such that we maintain linked * list of styles and compute the duplicate flag. * * Note: this function is executed during `firstUpdatePass` only to populate the `TView.data`. * * The function works by keeping track of `tStylingRange` which contains two pointers pointing to * the head/tail of the template portion of the styles. * - if `isHost === false` (we are template) then insertion is at tail of `TStylingRange` * - if `isHost === true` (we are host binding) then insertion is at head of `TStylingRange` * * @param tData The `TData` to insert into. * @param tNode `TNode` associated with the styling element. * @param tStylingKey See `TStylingKey`. * @param index location of where `tStyleValue` should be stored (and linked into list.) * @param isHostBinding `true` if the insertion is for a `hostBinding`. (insertion is in front of * template.) * @param isClassBinding True if the associated `tStylingKey` as a `class` styling. * `tNode.classBindings` should be used (or `tNode.styleBindings` otherwise.) */ function insertTStylingBinding(tData, tNode, tStylingKeyWithStatic, index, isHostBinding, isClassBinding) { ngDevMode && assertFirstUpdatePass(getTView()); let tBindings = isClassBinding ? tNode.classBindings : tNode.styleBindings; let tmplHead = getTStylingRangePrev(tBindings); let tmplTail = getTStylingRangeNext(tBindings); tData[index] = tStylingKeyWithStatic; let isKeyDuplicateOfStatic = false; let tStylingKey; if (Array.isArray(tStylingKeyWithStatic)) { // We are case when the `TStylingKey` contains static fields as well. const staticKeyValueArray = tStylingKeyWithStatic; tStylingKey = staticKeyValueArray[1]; // unwrap. // We need to check if our key is present in the static so that we can mark it as duplicate. if (tStylingKey === null || keyValueArrayIndexOf(staticKeyValueArray, tStylingKey) > 0) { // tStylingKey is present in the statics, need to mark it as duplicate. isKeyDuplicateOfStatic = true; } } else { tStylingKey = tStylingKeyWithStatic; } if (isHostBinding) { // We are inserting host bindings // If we don't have template bindings then `tail` is 0. const hasTemplateBindings = tmplTail !== 0; // This is important to know because that means that the `head` can't point to the first // template bindings (there are none.) Instead the head points to the tail of the template. if (hasTemplateBindings) { // template head's "prev" will point to last host binding or to 0 if no host bindings yet const previousNode = getTStylingRangePrev(tData[tmplHead + 1]); tData[index + 1] = toTStylingRange(previousNode, tmplHead); // if a host binding has already been registered, we need to update the next of that host // binding to point to this one if (previousNode !== 0) { // We need to update the template-tail value to point to us. tData[previousNode + 1] = setTStylingRangeNext(tData[previousNode + 1], index); } // The "previous" of the template binding head should point to this host binding tData[tmplHead + 1] = setTStylingRangePrev(tData[tmplHead + 1], index); } else { tData[index + 1] = toTStylingRange(tmplHead, 0); // if a host binding has already been registered, we need to update the next of that host // binding to point to this one if (tmplHead !== 0) { // We need to update the template-tail value to point to us. tData[tmplHead + 1] = setTStylingRangeNext(tData[tmplHead + 1], index); } // if we don't have template, the head points to template-tail, and needs to be advanced. tmplHead = index; } } else { // We are inserting in template section. // We need to set this binding's "previous" to the current template tail tData[index + 1] = toTStylingRange(tmplTail, 0); ngDevMode && assertEqual(tmplHead !== 0 && tmplTail === 0, false, 'Adding template bindings after hostBindings is not allowed.'); if (tmplHead === 0) { tmplHead = index; } else { // We need to update the previous value "next" to point to this binding tData[tmplTail + 1] = setTStylingRangeNext(tData[tmplTail + 1], index); } tmplTail = index; } // Now we need to update / compute the duplicates. // Starting with our location search towards head (least priority) if (isKeyDuplicateOfStatic) { tData[index + 1] = setTStylingRangePrevDuplicate(tData[index + 1]); } markDuplicates(tData, tStylingKey, index, true, isClassBinding); markDuplicates(tData, tStylingKey, index, false, isClassBinding); markDuplicateOfResidualStyling(tNode, tStylingKey, tData, index, isClassBinding); tBindings = toTStylingRange(tmplHead, tmplTail); if (isClassBinding) { tNode.classBindings = tBindings; } else { tNode.styleBindings = tBindings; } } /** * Look into the residual styling to see if the current `tStylingKey` is duplicate of residual. * * @param tNode `TNode` where the residual is stored. * @param tStylingKey `TStylingKey` to store. * @param tData `TData` associated with the current `LView`. * @param index location of where `tStyleValue` should be stored (and linked into list.) * @param isClassBinding True if the associated `tStylingKey` as a `class` styling. * `tNode.classBindings` should be used (or `tNode.styleBindings` otherwise.) */ function markDuplicateOfResidualStyling(tNode, tStylingKey, tData, index, isClassBinding) { const residual = isClassBinding ? tNode.residualClasses : tNode.residualStyles; if (residual != null /* or undefined */ && typeof tStylingKey == 'string' && keyValueArrayIndexOf(residual, tStylingKey) >= 0) { // We have duplicate in the residual so mark ourselves as duplicate. tData[index + 1] = setTStylingRangeNextDuplicate(tData[index + 1]); } } /** * Marks `TStyleValue`s as duplicates if another style binding in the list has the same * `TStyleValue`. * * NOTE: this function is intended to be called twice once with `isPrevDir` set to `true` and once * with it set to `false` to search both the previous as well as next items in the list. * * No duplicate case * ``` * [style.color] * [style.width.px] <<- index * [style.height.px] * ``` * * In the above case adding `[style.width.px]` to the existing `[style.color]` produces no * duplicates because `width` is not found in any other part of the linked list. * * Duplicate case * ``` * [style.color] * [style.width.em] * [style.width.px] <<- index * ``` * In the above case adding `[style.width.px]` will produce a duplicate with `[style.width.em]` * because `width` is found in the chain. * * Map case 1 * ``` * [style.width.px] * [style.color] * [style] <<- index * ``` * In the above case adding `[style]` will produce a duplicate with any other bindings because * `[style]` is a Map and as such is fully dynamic and could produce `color` or `width`. * * Map case 2 * ``` * [style] * [style.width.px] * [style.color] <<- index * ``` * In the above case adding `[style.color]` will produce a duplicate because there is already a * `[style]` binding which is a Map and as such is fully dynamic and could produce `color` or * `width`. * * NOTE: Once `[style]` (Map) is added into the system all things are mapped as duplicates. * NOTE: We use `style` as example, but same logic is applied to `class`es as well. * * @param tData `TData` where the linked list is stored. * @param tStylingKey `TStylingKeyPrimitive` which contains the value to compare to other keys in * the linked list. * @param index Starting location in the linked list to search from * @param isPrevDir Direction. * - `true` for previous (lower priority); * - `false` for next (higher priority). */ function markDuplicates(tData, tStylingKey, index, isPrevDir, isClassBinding) { const tStylingAtIndex = tData[index + 1]; const isMap = tStylingKey === null; let cursor = isPrevDir ? getTStylingRangePrev(tStylingAtIndex) : getTStylingRangeNext(tStylingAtIndex); let foundDuplicate = false; // We keep iterating as long as we have a cursor // AND either: // - we found what we are looking for, OR // - we are a map in which case we have to continue searching even after we find what we were // looking for since we are a wild card and everything needs to be flipped to duplicate. while (cursor !== 0 && (foundDuplicate === false || isMap)) { ngDevMode && assertIndexInRange(tData, cursor); const tStylingValueAtCursor = tData[cursor]; const tStyleRangeAtCursor = tData[cursor + 1]; if (isStylingMatch(tStylingValueAtCursor, tStylingKey)) { foundDuplicate = true; tData[cursor + 1] = isPrevDir ? setTStylingRangeNextDuplicate(tStyleRangeAtCursor) : setTStylingRangePrevDuplicate(tStyleRangeAtCursor); } cursor = isPrevDir ? getTStylingRangePrev(tStyleRangeAtCursor) : getTStylingRangeNext(tStyleRangeAtCursor); } if (foundDuplicate) { // if we found a duplicate, than mark ourselves. tData[index + 1] = isPrevDir ? setTStylingRangePrevDuplicate(tStylingAtIndex) : setTStylingRangeNextDuplicate(tStylingAtIndex); } } /** * Determines if two `TStylingKey`s are a match. * * When computing whether a binding contains a duplicate, we need to compare if the instruction * `TStylingKey` has a match. * * Here are examples of `TStylingKey`s which match given `tStylingKeyCursor` is: * - `color` * - `color` // Match another color * - `null` // That means that `tStylingKey` is a `classMap`/`styleMap` instruction * - `['', 'color', 'other', true]` // wrapped `color` so match * - `['', null, 'other', true]` // wrapped `null` so match * - `['', 'width', 'color', 'value']` // wrapped static value contains a match on `'color'` * - `null` // `tStylingKeyCursor` always match as it is `classMap`/`styleMap` instruction * * @param tStylingKeyCursor * @param tStylingKey */ function isStylingMatch(tStylingKeyCursor, tStylingKey) { ngDevMode && assertNotEqual(Array.isArray(tStylingKey), true, 'Expected that \'tStylingKey\' has been unwrapped'); if (tStylingKeyCursor === null || // If the cursor is `null` it means that we have map at that // location so we must assume that we have a match. tStylingKey == null || // If `tStylingKey` is `null` then it is a map therefor assume that it // contains a match. (Array.isArray(tStylingKeyCursor) ? tStylingKeyCursor[1] : tStylingKeyCursor) === tStylingKey // If the keys match explicitly than we are a match. ) { return true; } else if (Array.isArray(tStylingKeyCursor) && typeof tStylingKey === 'string') { // if we did not find a match, but `tStylingKeyCursor` is `KeyValueArray` that means cursor has // statics and we need to check those as well. return keyValueArrayIndexOf(tStylingKeyCursor, tStylingKey) >= 0; // see if we are matching the key } return false; } // Global state of the parser. (This makes parser non-reentrant, but that is not an issue) const parserState = { textEnd: 0, key: 0, keyEnd: 0, value: 0, valueEnd: 0 }; /** * Retrieves the last parsed `key` of style. * @param text the text to substring the key from. */ function getLastParsedKey(text) { return text.substring(parserState.key, parserState.keyEnd); } /** * Retrieves the last parsed `value` of style. * @param text the text to substring the key from. */ function getLastParsedValue(text) { return text.substring(parserState.value, parserState.valueEnd); } /** * Initializes `className` string for parsing and parses the first token. * * This function is intended to be used in this format: * ``` * for (let i = parseClassName(text); i >= 0; i = parseClassNameNext(text, i)) { * const key = getLastParsedKey(); * ... * } * ``` * @param text `className` to parse * @returns index where the next invocation of `parseClassNameNext` should resume. */ function parseClassName(text) { resetParserState(text); return parseClassNameNext(text, consumeWhitespace(text, 0, parserState.textEnd)); } /** * Parses next `className` token. * * This function is intended to be used in this format: * ``` * for (let i = parseClassName(text); i >= 0; i = parseClassNameNext(text, i)) { * const key = getLastParsedKey(); * ... * } * ``` * * @param text `className` to parse * @param index where the parsing should resume. * @returns index where the next invocation of `parseClassNameNext` should resume. */ function parseClassNameNext(text, index) { const end = parserState.textEnd; if (end === index) { return -1; } index = parserState.keyEnd = consumeClassToken(text, parserState.key = index, end); return consumeWhitespace(text, index, end); } /** * Initializes `cssText` string for parsing and parses the first key/values. * * This function is intended to be used in this format: * ``` * for (let i = parseStyle(text); i >= 0; i = parseStyleNext(text, i))) { * const key = getLastParsedKey(); * const value = getLastParsedValue(); * ... * } * ``` * @param text `cssText` to parse * @returns index where the next invocation of `parseStyleNext` should resume. */ function parseStyle(text) { resetParserState(text); return parseStyleNext(text, consumeWhitespace(text, 0, parserState.textEnd)); } /** * Parses the next `cssText` key/values. * * This function is intended to be used in this format: * ``` * for (let i = parseStyle(text); i >= 0; i = parseStyleNext(text, i))) { * const key = getLastParsedKey(); * const value = getLastParsedValue(); * ... * } * * @param text `cssText` to parse * @param index where the parsing should resume. * @returns index where the next invocation of `parseStyleNext` should resume. */ function parseStyleNext(text, startIndex) { const end = parserState.textEnd; let index = parserState.key = consumeWhitespace(text, startIndex, end); if (end === index) { // we reached an end so just quit return -1; } index = parserState.keyEnd = consumeStyleKey(text, index, end); index = consumeSeparator(text, index, end, 58 /* CharCode.COLON */); index = parserState.value = consumeWhitespace(text, index, end); index = parserState.valueEnd = consumeStyleValue(text, index, end); return consumeSeparator(text, index, end, 59 /* CharCode.SEMI_COLON */); } /** * Reset the global state of the styling parser. * @param text The styling text to parse. */ function resetParserState(text) { parserState.key = 0; parserState.keyEnd = 0; parserState.value = 0; parserState.valueEnd = 0; parserState.textEnd = text.length; } /** * Returns index of next non-whitespace character. * * @param text Text to scan * @param startIndex Starting index of character where the scan should start. * @param endIndex Ending index of character where the scan should end. * @returns Index of next non-whitespace character (May be the same as `start` if no whitespace at * that location.) */ function consumeWhitespace(text, startIndex, endIndex) { while (startIndex < endIndex && text.charCodeAt(startIndex) <= 32 /* CharCode.SPACE */) { startIndex++; } return startIndex; } /** * Returns index of last char in class token. * * @param text Text to scan * @param startIndex Starting index of character where the scan should start. * @param endIndex Ending index of character where the scan should end. * @returns Index after last char in class token. */ function consumeClassToken(text, startIndex, endIndex) { while (startIndex < endIndex && text.charCodeAt(startIndex) > 32 /* CharCode.SPACE */) { startIndex++; } return startIndex; } /** * Consumes all of the characters belonging to style key and token. * * @param text Text to scan * @param startIndex Starting index of character where the scan should start. * @param endIndex Ending index of character where the scan should end. * @returns Index after last style key character. */ function consumeStyleKey(text, startIndex, endIndex) { let ch; while (startIndex < endIndex && ((ch = text.charCodeAt(startIndex)) === 45 /* CharCode.DASH */ || ch === 95 /* CharCode.UNDERSCORE */ || (ch & -33 /* CharCode.UPPER_CASE */) >= 65 /* CharCode.A */ && (ch & -33 /* CharCode.UPPER_CASE */) <= 90 /* CharCode.Z */ || ch >= 48 /* CharCode.ZERO */ && ch <= 57 /* CharCode.NINE */)) { startIndex++; } return startIndex; } /** * Consumes all whitespace and the separator `:` after the style key. * * @param text Text to scan * @param startIndex Starting index of character where the scan should start. * @param endIndex Ending index of character where the scan should end. * @returns Index after separator and surrounding whitespace. */ function consumeSeparator(text, startIndex, endIndex, separator) { startIndex = consumeWhitespace(text, startIndex, endIndex); if (startIndex < endIndex) { if (ngDevMode && text.charCodeAt(startIndex) !== separator) { malformedStyleError(text, String.fromCharCode(separator), startIndex); } startIndex++; } return startIndex; } /** * Consumes style value honoring `url()` and `""` text. * * @param text Text to scan * @param startIndex Starting index of character where the scan should start. * @param endIndex Ending index of character where the scan should end. * @returns Index after last style value character. */ function consumeStyleValue(text, startIndex, endIndex) { let ch1 = -1; // 1st previous character let ch2 = -1; // 2nd previous character let ch3 = -1; // 3rd previous character let i = startIndex; let lastChIndex = i; while (i < endIndex) { const ch = text.charCodeAt(i++); if (ch === 59 /* CharCode.SEMI_COLON */) { return lastChIndex; } else if (ch === 34 /* CharCode.DOUBLE_QUOTE */ || ch === 39 /* CharCode.SINGLE_QUOTE */) { lastChIndex = i = consumeQuotedText(text, ch, i, endIndex); } else if (startIndex === i - 4 && // We have seen only 4 characters so far "URL(" (Ignore "foo_URL()") ch3 === 85 /* CharCode.U */ && ch2 === 82 /* CharCode.R */ && ch1 === 76 /* CharCode.L */ && ch === 40 /* CharCode.OPEN_PAREN */) { lastChIndex = i = consumeQuotedText(text, 41 /* CharCode.CLOSE_PAREN */, i, endIndex); } else if (ch > 32 /* CharCode.SPACE */) { // if we have a non-whitespace character then capture its location lastChIndex = i; } ch3 = ch2; ch2 = ch1; ch1 = ch & -33 /* CharCode.UPPER_CASE */; } return lastChIndex; } /** * Consumes all of the quoted characters. * * @param text Text to scan * @param quoteCharCode CharCode of either `"` or `'` quote or `)` for `url(...)`. * @param startIndex Starting index of character where the scan should start. * @param endIndex Ending index of character where the scan should end. * @returns Index after quoted characters. */ function consumeQuotedText(text, quoteCharCode, startIndex, endIndex) { let ch1 = -1; // 1st previous character let index = startIndex; while (index < endIndex) { const ch = text.charCodeAt(index++); if (ch == quoteCharCode && ch1 !== 92 /* CharCode.BACK_SLASH */) { return index; } if (ch == 92 /* CharCode.BACK_SLASH */ && ch1 === 92 /* CharCode.BACK_SLASH */) { // two back slashes cancel each other out. For example `"\\"` should properly end the // quotation. (It should not assume that the last `"` is escaped.) ch1 = 0; } else { ch1 = ch; } } throw ngDevMode ? malformedStyleError(text, String.fromCharCode(quoteCharCode), endIndex) : new Error(); } function malformedStyleError(text, expecting, index) { ngDevMode && assertEqual(typeof text === 'string', true, 'String expected here'); throw throwError(`Malformed style at location ${index} in string '` + text.substring(0, index) + '[>>' + text.substring(index, index + 1) + '<<]' + text.slice(index + 1) + `'. Expecting '${expecting}'.`); } /** * Update a style binding on an element with the provided value. * * If the style value is falsy then it will be removed from the element * (or assigned a different value depending if there are any styles placed * on the element with `styleMap` or any static styles that are * present from when the element was created with `styling`). * * Note that the styling element is updated as part of `stylingApply`. * * @param prop A valid CSS property. * @param value New value to write (`null` or an empty string to remove). * @param suffix Optional suffix. Used with scalar values to add unit such as `px`. * * Note that this will apply the provided style value to the host element if this function is called * within a host binding function. * * @codeGenApi */ function ɵɵstyleProp(prop, value, suffix) { checkStylingProperty(prop, value, suffix, false); return ɵɵstyleProp; } /** * Update a class binding on an element with the provided value. * * This instruction is meant to handle the `[class.foo]="exp"` case and, * therefore, the class binding itself must already be allocated using * `styling` within the creation block. * * @param prop A valid CSS class (only one). * @param value A true/false value which will turn the class on or off. * * Note that this will apply the provided class value to the host element if this function * is called within a host binding function. * * @codeGenApi */ function ɵɵclassProp(className, value) { checkStylingProperty(className, value, null, true); return ɵɵclassProp; } /** * Update style bindings using an object literal on an element. * * This instruction is meant to apply styling via the `[style]="exp"` template bindings. * When styles are applied to the element they will then be updated with respect to * any styles/classes set via `styleProp`. If any styles are set to falsy * then they will be removed from the element. * * Note that the styling instruction will not be applied until `stylingApply` is called. * * @param styles A key/value style map of the styles that will be applied to the given element. * Any missing styles (that have already been applied to the element beforehand) will be * removed (unset) from the element's styling. * * Note that this will apply the provided styleMap value to the host element if this function * is called within a host binding. * * @codeGenApi */ function ɵɵstyleMap(styles) { checkStylingMap(styleKeyValueArraySet, styleStringParser, styles, false); } /** * Parse text as style and add values to KeyValueArray. * * This code is pulled out to a separate function so that it can be tree shaken away if it is not * needed. It is only referenced from `ɵɵstyleMap`. * * @param keyValueArray KeyValueArray to add parsed values to. * @param text text to parse. */ function styleStringParser(keyValueArray, text) { for (let i = parseStyle(text); i >= 0; i = parseStyleNext(text, i)) { styleKeyValueArraySet(keyValueArray, getLastParsedKey(text), getLastParsedValue(text)); } } /** * Update class bindings using an object literal or class-string on an element. * * This instruction is meant to apply styling via the `[class]="exp"` template bindings. * When classes are applied to the element they will then be updated with * respect to any styles/classes set via `classProp`. If any * classes are set to falsy then they will be removed from the element. * * Note that the styling instruction will not be applied until `stylingApply` is called. * Note that this will the provided classMap value to the host element if this function is called * within a host binding. * * @param classes A key/value map or string of CSS classes that will be added to the * given element. Any missing classes (that have already been applied to the element * beforehand) will be removed (unset) from the element's list of CSS classes. * * @codeGenApi */ function ɵɵclassMap(classes) { checkStylingMap(classKeyValueArraySet, classStringParser, classes, true); } /** * Parse text as class and add values to KeyValueArray. * * This code is pulled out to a separate function so that it can be tree shaken away if it is not * needed. It is only referenced from `ɵɵclassMap`. * * @param keyValueArray KeyValueArray to add parsed values to. * @param text text to parse. */ function classStringParser(keyValueArray, text) { for (let i = parseClassName(text); i >= 0; i = parseClassNameNext(text, i)) { keyValueArraySet(keyValueArray, getLastParsedKey(text), true); } } /** * Common code between `ɵɵclassProp` and `ɵɵstyleProp`. * * @param prop property name. * @param value binding value. * @param suffix suffix for the property (e.g. `em` or `px`) * @param isClassBased `true` if `class` change (`false` if `style`) */ function checkStylingProperty(prop, value, suffix, isClassBased) { const lView = getLView(); const tView = getTView(); // Styling instructions use 2 slots per binding. // 1. one for the value / TStylingKey // 2. one for the intermittent-value / TStylingRange const bindingIndex = incrementBindingIndex(2); if (tView.firstUpdatePass) { stylingFirstUpdatePass(tView, prop, bindingIndex, isClassBased); } if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) { const tNode = tView.data[getSelectedIndex()]; updateStyling(tView, tNode, lView, lView[RENDERER], prop, lView[bindingIndex + 1] = normalizeSuffix(value, suffix), isClassBased, bindingIndex); } } /** * Common code between `ɵɵclassMap` and `ɵɵstyleMap`. * * @param keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a * function so that `style` can be processed. This is done for tree shaking purposes. * @param stringParser Parser used to parse `value` if `string`. (Passed in as `style` and `class` * have different parsers.) * @param value bound value from application * @param isClassBased `true` if `class` change (`false` if `style`) */ function checkStylingMap(keyValueArraySet, stringParser, value, isClassBased) { const tView = getTView(); const bindingIndex = incrementBindingIndex(2); if (tView.firstUpdatePass) { stylingFirstUpdatePass(tView, null, bindingIndex, isClassBased); } const lView = getLView(); if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) { // `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the // if so as not to read unnecessarily. const tNode = tView.data[getSelectedIndex()]; if (hasStylingInputShadow(tNode, isClassBased) && !isInHostBindings(tView, bindingIndex)) { if (ngDevMode) { // verify that if we are shadowing then `TData` is appropriately marked so that we skip // processing this binding in styling resolution. const tStylingKey = tView.data[bindingIndex]; assertEqual(Array.isArray(tStylingKey) ? tStylingKey[1] : tStylingKey, false, 'Styling linked list shadow input should be marked as \'false\''); } // VE does not concatenate the static portion like we are doing here. // Instead VE just ignores the static completely if dynamic binding is present. // Because of locality we have already set the static portion because we don't know if there // is a dynamic portion until later. If we would ignore the static portion it would look like // the binding has removed it. This would confuse `[ngStyle]`/`[ngClass]` to do the wrong // thing as it would think that the static portion was removed. For this reason we // concatenate it so that `[ngStyle]`/`[ngClass]` can continue to work on changed. let staticPrefix = isClassBased ? tNode.classesWithoutHost : tNode.stylesWithoutHost; ngDevMode && isClassBased === false && staticPrefix !== null && assertEqual(staticPrefix.endsWith(';'), true, 'Expecting static portion to end with \';\''); if (staticPrefix !== null) { // We want to make sure that falsy values of `value` become empty strings. value = concatStringsWithSpace(staticPrefix, value ? value : ''); } // Given `<div [style] my-dir>` such that `my-dir` has `@Input('style')`. // This takes over the `[style]` binding. (Same for `[class]`) setDirectiveInputsWhichShadowsStyling(tView, tNode, lView, value, isClassBased); } else { updateStylingMap(tView, tNode, lView, lView[RENDERER], lView[bindingIndex + 1], lView[bindingIndex + 1] = toStylingKeyValueArray(keyValueArraySet, stringParser, value), isClassBased, bindingIndex); } } } /** * Determines when the binding is in `hostBindings` section * * @param tView Current `TView` * @param bindingIndex index of binding which we would like if it is in `hostBindings` */ function isInHostBindings(tView, bindingIndex) { // All host bindings are placed after the expando section. return bindingIndex >= tView.expandoStartIndex; } /** * Collects the necessary information to insert the binding into a linked list of style bindings * using `insertTStylingBinding`. * * @param tView `TView` where the binding linked list will be stored. * @param tStylingKey Property/key of the binding. * @param bindingIndex Index of binding associated with the `prop` * @param isClassBased `true` if `class` change (`false` if `style`) */ function stylingFirstUpdatePass(tView, tStylingKey, bindingIndex, isClassBased) { ngDevMode && assertFirstUpdatePass(tView); const tData = tView.data; if (tData[bindingIndex + 1] === null) { // The above check is necessary because we don't clear first update pass until first successful // (no exception) template execution. This prevents the styling instruction from double adding // itself to the list. // `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the // if so as not to read unnecessarily. const tNode = tData[getSelectedIndex()]; ngDevMode && assertDefined(tNode, 'TNode expected'); const isHostBindings = isInHostBindings(tView, bindingIndex); if (hasStylingInputShadow(tNode, isClassBased) && tStylingKey === null && !isHostBindings) { // `tStylingKey === null` implies that we are either `[style]` or `[class]` binding. // If there is a directive which uses `@Input('style')` or `@Input('class')` than // we need to neutralize this binding since that directive is shadowing it. // We turn this into a noop by setting the key to `false` tStylingKey = false; } tStylingKey = wrapInStaticStylingKey(tData, tNode, tStylingKey, isClassBased); insertTStylingBinding(tData, tNode, tStylingKey, bindingIndex, isHostBindings, isClassBased); } } /** * Adds static styling information to the binding if applicable. * * The linked list of styles not only stores the list and keys, but also stores static styling * information on some of the keys. This function determines if the key should contain the styling * information and computes it. * * See `TStylingStatic` for more details. * * @param tData `TData` where the linked list is stored. * @param tNode `TNode` for which the styling is being computed. * @param stylingKey `TStylingKeyPrimitive` which may need to be wrapped into `TStylingKey` * @param isClassBased `true` if `class` (`false` if `style`) */ function wrapInStaticStylingKey(tData, tNode, stylingKey, isClassBased) { const hostDirectiveDef = getCurrentDirectiveDef(tData); let residual = isClassBased ? tNode.residualClasses : tNode.residualStyles; if (hostDirectiveDef === null) { // We are in template node. // If template node already had styling instruction then it has already collected the static // styling and there is no need to collect them again. We know that we are the first styling // instruction because the `TNode.*Bindings` points to 0 (nothing has been inserted yet). const isFirstStylingInstructionInTemplate = (isClassBased ? tNode.classBindings : tNode.styleBindings) === 0; if (isFirstStylingInstructionInTemplate) { // It would be nice to be able to get the statics from `mergeAttrs`, however, at this point // they are already merged and it would not be possible to figure which property belongs where // in the priority. stylingKey = collectStylingFromDirectives(null, tData, tNode, stylingKey, isClassBased); stylingKey = collectStylingFromTAttrs(stylingKey, tNode.attrs, isClassBased); // We know that if we have styling binding in template we can't have residual. residual = null; } } else { // We are in host binding node and there was no binding instruction in template node. // This means that we need to compute the residual. const directiveStylingLast = tNode.directiveStylingLast; const isFirstStylingInstructionInHostBinding = directiveStylingLast === -1 || tData[directiveStylingLast] !== hostDirectiveDef; if (isFirstStylingInstructionInHostBinding) { stylingKey = collectStylingFromDirectives(hostDirectiveDef, tData, tNode, stylingKey, isClassBased); if (residual === null) { // - If `null` than either: // - Template styling instruction already ran and it has consumed the static // styling into its `TStylingKey` and so there is no need to update residual. Instead // we need to update the `TStylingKey` associated with the first template node // instruction. OR // - Some other styling instruction ran and determined that there are no residuals let templateStylingKey = getTemplateHeadTStylingKey(tData, tNode, isClassBased); if (templateStylingKey !== undefined && Array.isArray(templateStylingKey)) { // Only recompute if `templateStylingKey` had static values. (If no static value found // then there is nothing to do since this operation can only produce less static keys, not // more.) templateStylingKey = collectStylingFromDirectives(null, tData, tNode, templateStylingKey[1] /* unwrap previous statics */, isClassBased); templateStylingKey = collectStylingFromTAttrs(templateStylingKey, tNode.attrs, isClassBased); setTemplateHeadTStylingKey(tData, tNode, isClassBased, templateStylingKey); } } else { // We only need to recompute residual if it is not `null`. // - If existing residual (implies there was no template styling). This means that some of // the statics may have moved from the residual to the `stylingKey` and so we have to // recompute. // - If `undefined` this is the first time we are running. residual = collectResidual(tData, tNode, isClassBased); } } } if (residual !== undefined) { isClassBased ? tNode.residualClasses = residual : tNode.residualStyles = residual; } return stylingKey; } /** * Retrieve the `TStylingKey` for the template styling instruction. * * This is needed since `hostBinding` styling instructions are inserted after the template * instruction. While the template instruction needs to update the residual in `TNode` the * `hostBinding` instructions need to update the `TStylingKey` of the template instruction because * the template instruction is downstream from the `hostBindings` instructions. * * @param tData `TData` where the linked list is stored. * @param tNode `TNode` for which the styling is being computed. * @param isClassBased `true` if `class` (`false` if `style`) * @return `TStylingKey` if found or `undefined` if not found. */ function getTemplateHeadTStylingKey(tData, tNode, isClassBased) { const bindings = isClassBased ? tNode.classBindings : tNode.styleBindings; if (getTStylingRangeNext(bindings) === 0) { // There does not seem to be a styling instruction in the `template`. return undefined; } return tData[getTStylingRangePrev(bindings)]; } /** * Update the `TStylingKey` of the first template instruction in `TNode`. * * Logically `hostBindings` styling instructions are of lower priority than that of the template. * However, they execute after the template styling instructions. This means that they get inserted * in front of the template styling instructions. * * If we have a template styling instruction and a new `hostBindings` styling instruction is * executed it means that it may need to steal static fields from the template instruction. This * method allows us to update the first template instruction `TStylingKey` with a new value. * * Assume: * ``` * <div my-dir style="color: red" [style.color]="tmplExp"></div> * * @Directive({ * host: { * 'style': 'width: 100px', * '[style.color]': 'dirExp', * } * }) * class MyDir {} * ``` * * when `[style.color]="tmplExp"` executes it creates this data structure. * ``` * ['', 'color', 'color', 'red', 'width', '100px'], * ``` * * The reason for this is that the template instruction does not know if there are styling * instructions and must assume that there are none and must collect all of the static styling. * (both * `color' and 'width`) * * When `'[style.color]': 'dirExp',` executes we need to insert a new data into the linked list. * ``` * ['', 'color', 'width', '100px'], // newly inserted * ['', 'color', 'color', 'red', 'width', '100px'], // this is wrong * ``` * * Notice that the template statics is now wrong as it incorrectly contains `width` so we need to * update it like so: * ``` * ['', 'color', 'width', '100px'], * ['', 'color', 'color', 'red'], // UPDATE * ``` * * @param tData `TData` where the linked list is stored. * @param tNode `TNode` for which the styling is being computed. * @param isClassBased `true` if `class` (`false` if `style`) * @param tStylingKey New `TStylingKey` which is replacing the old one. */ function setTemplateHeadTStylingKey(tData, tNode, isClassBased, tStylingKey) { const bindings = isClassBased ? tNode.classBindings : tNode.styleBindings; ngDevMode && assertNotEqual(getTStylingRangeNext(bindings), 0, 'Expecting to have at least one template styling binding.'); tData[getTStylingRangePrev(bindings)] = tStylingKey; } /** * Collect all static values after the current `TNode.directiveStylingLast` index. * * Collect the remaining styling information which has not yet been collected by an existing * styling instruction. * * @param tData `TData` where the `DirectiveDefs` are stored. * @param tNode `TNode` which contains the directive range. * @param isClassBased `true` if `class` (`false` if `style`) */ function collectResidual(tData, tNode, isClassBased) { let residual = undefined; const directiveEnd = tNode.directiveEnd; ngDevMode && assertNotEqual(tNode.directiveStylingLast, -1, 'By the time this function gets called at least one hostBindings-node styling instruction must have executed.'); // We add `1 + tNode.directiveStart` because we need to skip the current directive (as we are // collecting things after the last `hostBindings` directive which had a styling instruction.) for (let i = 1 + tNode.directiveStylingLast; i < directiveEnd; i++) { const attrs = tData[i].hostAttrs; residual = collectStylingFromTAttrs(residual, attrs, isClassBased); } return collectStylingFromTAttrs(residual, tNode.attrs, isClassBased); } /** * Collect the static styling information with lower priority than `hostDirectiveDef`. * * (This is opposite of residual styling.) * * @param hostDirectiveDef `DirectiveDef` for which we want to collect lower priority static * styling. (Or `null` if template styling) * @param tData `TData` where the linked list is stored. * @param tNode `TNode` for which the styling is being computed. * @param stylingKey Existing `TStylingKey` to update or wrap. * @param isClassBased `true` if `class` (`false` if `style`) */ function collectStylingFromDirectives(hostDirectiveDef, tData, tNode, stylingKey, isClassBased) { // We need to loop because there can be directives which have `hostAttrs` but don't have // `hostBindings` so this loop catches up to the current directive.. let currentDirective = null; const directiveEnd = tNode.directiveEnd; let directiveStylingLast = tNode.directiveStylingLast; if (directiveStylingLast === -1) { directiveStylingLast = tNode.directiveStart; } else { directiveStylingLast++; } while (directiveStylingLast < directiveEnd) { currentDirective = tData[directiveStylingLast]; ngDevMode && assertDefined(currentDirective, 'expected to be defined'); stylingKey = collectStylingFromTAttrs(stylingKey, currentDirective.hostAttrs, isClassBased); if (currentDirective === hostDirectiveDef) break; directiveStylingLast++; } if (hostDirectiveDef !== null) { // we only advance the styling cursor if we are collecting data from host bindings. // Template executes before host bindings and so if we would update the index, // host bindings would not get their statics. tNode.directiveStylingLast = directiveStylingLast; } return stylingKey; } /** * Convert `TAttrs` into `TStylingStatic`. * * @param stylingKey existing `TStylingKey` to update or wrap. * @param attrs `TAttributes` to process. * @param isClassBased `true` if `class` (`false` if `style`) */ function collectStylingFromTAttrs(stylingKey, attrs, isClassBased) { const desiredMarker = isClassBased ? 1 /* AttributeMarker.Classes */ : 2 /* AttributeMarker.Styles */; let currentMarker = -1 /* AttributeMarker.ImplicitAttributes */; if (attrs !== null) { for (let i = 0; i < attrs.length; i++) { const item = attrs[i]; if (typeof item === 'number') { currentMarker = item; } else { if (currentMarker === desiredMarker) { if (!Array.isArray(stylingKey)) { stylingKey = stylingKey === undefined ? [] : ['', stylingKey]; } keyValueArraySet(stylingKey, item, isClassBased ? true : attrs[++i]); } } } } return stylingKey === undefined ? null : stylingKey; } /** * Convert user input to `KeyValueArray`. * * This function takes user input which could be `string`, Object literal, or iterable and converts * it into a consistent representation. The output of this is `KeyValueArray` (which is an array * where * even indexes contain keys and odd indexes contain values for those keys). * * The advantage of converting to `KeyValueArray` is that we can perform diff in an input * independent * way. * (ie we can compare `foo bar` to `['bar', 'baz'] and determine a set of changes which need to be * applied) * * The fact that `KeyValueArray` is sorted is very important because it allows us to compute the * difference in linear fashion without the need to allocate any additional data. * * For example if we kept this as a `Map` we would have to iterate over previous `Map` to determine * which values need to be deleted, over the new `Map` to determine additions, and we would have to * keep additional `Map` to keep track of duplicates or items which have not yet been visited. * * @param keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a * function so that `style` can be processed. This is done * for tree shaking purposes. * @param stringParser The parser is passed in so that it will be tree shakable. See * `styleStringParser` and `classStringParser` * @param value The value to parse/convert to `KeyValueArray` */ function toStylingKeyValueArray(keyValueArraySet, stringParser, value) { if (value == null /*|| value === undefined */ || value === '') return EMPTY_ARRAY; const styleKeyValueArray = []; const unwrappedValue = unwrapSafeValue(value); if (Array.isArray(unwrappedValue)) { for (let i = 0; i < unwrappedValue.length; i++) { keyValueArraySet(styleKeyValueArray, unwrappedValue[i], true); } } else if (typeof unwrappedValue === 'object') { for (const key in unwrappedValue) { if (unwrappedValue.hasOwnProperty(key)) { keyValueArraySet(styleKeyValueArray, key, unwrappedValue[key]); } } } else if (typeof unwrappedValue === 'string') { stringParser(styleKeyValueArray, unwrappedValue); } else { ngDevMode && throwError('Unsupported styling type ' + typeof unwrappedValue + ': ' + unwrappedValue); } return styleKeyValueArray; } /** * Set a `value` for a `key`. * * See: `keyValueArraySet` for details * * @param keyValueArray KeyValueArray to add to. * @param key Style key to add. * @param value The value to set. */ function styleKeyValueArraySet(keyValueArray, key, value) { keyValueArraySet(keyValueArray, key, unwrapSafeValue(value)); } /** * Class-binding-specific function for setting the `value` for a `key`. * * See: `keyValueArraySet` for details * * @param keyValueArray KeyValueArray to add to. * @param key Style key to add. * @param value The value to set. */ function classKeyValueArraySet(keyValueArray, key, value) { // We use `classList.add` to eventually add the CSS classes to the DOM node. Any value passed into // `add` is stringified and added to the `class` attribute, e.g. even null, undefined or numbers // will be added. Stringify the key here so that our internal data structure matches the value in // the DOM. The only exceptions are empty strings and strings that contain spaces for which // the browser throws an error. We ignore such values, because the error is somewhat cryptic. const stringKey = String(key); if (stringKey !== '' && !stringKey.includes(' ')) { keyValueArraySet(keyValueArray, stringKey, value); } } /** * Update map based styling. * * Map based styling could be anything which contains more than one binding. For example `string`, * or object literal. Dealing with all of these types would complicate the logic so * instead this function expects that the complex input is first converted into normalized * `KeyValueArray`. The advantage of normalization is that we get the values sorted, which makes it * very cheap to compute deltas between the previous and current value. * * @param tView Associated `TView.data` contains the linked list of binding priorities. * @param tNode `TNode` where the binding is located. * @param lView `LView` contains the values associated with other styling binding at this `TNode`. * @param renderer Renderer to use if any updates. * @param oldKeyValueArray Previous value represented as `KeyValueArray` * @param newKeyValueArray Current value represented as `KeyValueArray` * @param isClassBased `true` if `class` (`false` if `style`) * @param bindingIndex Binding index of the binding. */ function updateStylingMap(tView, tNode, lView, renderer, oldKeyValueArray, newKeyValueArray, isClassBased, bindingIndex) { if (oldKeyValueArray === NO_CHANGE) { // On first execution the oldKeyValueArray is NO_CHANGE => treat it as empty KeyValueArray. oldKeyValueArray = EMPTY_ARRAY; } let oldIndex = 0; let newIndex = 0; let oldKey = 0 < oldKeyValueArray.length ? oldKeyValueArray[0] : null; let newKey = 0 < newKeyValueArray.length ? newKeyValueArray[0] : null; while (oldKey !== null || newKey !== null) { ngDevMode && assertLessThan(oldIndex, 999, 'Are we stuck in infinite loop?'); ngDevMode && assertLessThan(newIndex, 999, 'Are we stuck in infinite loop?'); const oldValue = oldIndex < oldKeyValueArray.length ? oldKeyValueArray[oldIndex + 1] : undefined; const newValue = newIndex < newKeyValueArray.length ? newKeyValueArray[newIndex + 1] : undefined; let setKey = null; let setValue = undefined; if (oldKey === newKey) { // UPDATE: Keys are equal => new value is overwriting old value. oldIndex += 2; newIndex += 2; if (oldValue !== newValue) { setKey = newKey; setValue = newValue; } } else if (newKey === null || oldKey !== null && oldKey < newKey) { // DELETE: oldKey key is missing or we did not find the oldKey in the newValue // (because the keyValueArray is sorted and `newKey` is found later alphabetically). // `"background" < "color"` so we need to delete `"background"` because it is not found in the // new array. oldIndex += 2; setKey = oldKey; } else { // CREATE: newKey's is earlier alphabetically than oldKey's (or no oldKey) => we have new key. // `"color" > "background"` so we need to add `color` because it is in new array but not in // old array. ngDevMode && assertDefined(newKey, 'Expecting to have a valid key'); newIndex += 2; setKey = newKey; setValue = newValue; } if (setKey !== null) { updateStyling(tView, tNode, lView, renderer, setKey, setValue, isClassBased, bindingIndex); } oldKey = oldIndex < oldKeyValueArray.length ? oldKeyValueArray[oldIndex] : null; newKey = newIndex < newKeyValueArray.length ? newKeyValueArray[newIndex] : null; } } /** * Update a simple (property name) styling. * * This function takes `prop` and updates the DOM to that value. The function takes the binding * value as well as binding priority into consideration to determine which value should be written * to DOM. (For example it may be determined that there is a higher priority overwrite which blocks * the DOM write, or if the value goes to `undefined` a lower priority overwrite may be consulted.) * * @param tView Associated `TView.data` contains the linked list of binding priorities. * @param tNode `TNode` where the binding is located. * @param lView `LView` contains the values associated with other styling binding at this `TNode`. * @param renderer Renderer to use if any updates. * @param prop Either style property name or a class name. * @param value Either style value for `prop` or `true`/`false` if `prop` is class. * @param isClassBased `true` if `class` (`false` if `style`) * @param bindingIndex Binding index of the binding. */ function updateStyling(tView, tNode, lView, renderer, prop, value, isClassBased, bindingIndex) { if (!(tNode.type & 3 /* TNodeType.AnyRNode */)) { // It is possible to have styling on non-elements (such as ng-container). // This is rare, but it does happen. In such a case, just ignore the binding. return; } const tData = tView.data; const tRange = tData[bindingIndex + 1]; const higherPriorityValue = getTStylingRangeNextDuplicate(tRange) ? findStylingValue(tData, tNode, lView, prop, getTStylingRangeNext(tRange), isClassBased) : undefined; if (!isStylingValuePresent(higherPriorityValue)) { // We don't have a next duplicate, or we did not find a duplicate value. if (!isStylingValuePresent(value)) { // We should delete current value or restore to lower priority value. if (getTStylingRangePrevDuplicate(tRange)) { // We have a possible prev duplicate, let's retrieve it. value = findStylingValue(tData, null, lView, prop, bindingIndex, isClassBased); } } const rNode = getNativeByIndex(getSelectedIndex(), lView); applyStyling(renderer, isClassBased, rNode, prop, value); } } /** * Search for styling value with higher priority which is overwriting current value, or a * value of lower priority to which we should fall back if the value is `undefined`. * * When value is being applied at a location, related values need to be consulted. * - If there is a higher priority binding, we should be using that one instead. * For example `<div [style]="{color:exp1}" [style.color]="exp2">` change to `exp1` * requires that we check `exp2` to see if it is set to value other than `undefined`. * - If there is a lower priority binding and we are changing to `undefined` * For example `<div [style]="{color:exp1}" [style.color]="exp2">` change to `exp2` to * `undefined` requires that we check `exp1` (and static values) and use that as new value. * * NOTE: The styling stores two values. * 1. The raw value which came from the application is stored at `index + 0` location. (This value * is used for dirty checking). * 2. The normalized value is stored at `index + 1`. * * @param tData `TData` used for traversing the priority. * @param tNode `TNode` to use for resolving static styling. Also controls search direction. * - `TNode` search next and quit as soon as `isStylingValuePresent(value)` is true. * If no value found consult `tNode.residualStyle`/`tNode.residualClass` for default value. * - `null` search prev and go all the way to end. Return last value where * `isStylingValuePresent(value)` is true. * @param lView `LView` used for retrieving the actual values. * @param prop Property which we are interested in. * @param index Starting index in the linked list of styling bindings where the search should start. * @param isClassBased `true` if `class` (`false` if `style`) */ function findStylingValue(tData, tNode, lView, prop, index, isClassBased) { // `TNode` to use for resolving static styling. Also controls search direction. // - `TNode` search next and quit as soon as `isStylingValuePresent(value)` is true. // If no value found consult `tNode.residualStyle`/`tNode.residualClass` for default value. // - `null` search prev and go all the way to end. Return last value where // `isStylingValuePresent(value)` is true. const isPrevDirection = tNode === null; let value = undefined; while (index > 0) { const rawKey = tData[index]; const containsStatics = Array.isArray(rawKey); // Unwrap the key if we contain static values. const key = containsStatics ? rawKey[1] : rawKey; const isStylingMap = key === null; let valueAtLViewIndex = lView[index + 1]; if (valueAtLViewIndex === NO_CHANGE) { // In firstUpdatePass the styling instructions create a linked list of styling. // On subsequent passes it is possible for a styling instruction to try to read a binding // which // has not yet executed. In that case we will find `NO_CHANGE` and we should assume that // we have `undefined` (or empty array in case of styling-map instruction) instead. This // allows the resolution to apply the value (which may later be overwritten when the // binding actually executes.) valueAtLViewIndex = isStylingMap ? EMPTY_ARRAY : undefined; } let currentValue = isStylingMap ? keyValueArrayGet(valueAtLViewIndex, prop) : key === prop ? valueAtLViewIndex : undefined; if (containsStatics && !isStylingValuePresent(currentValue)) { currentValue = keyValueArrayGet(rawKey, prop); } if (isStylingValuePresent(currentValue)) { value = currentValue; if (isPrevDirection) { return value; } } const tRange = tData[index + 1]; index = isPrevDirection ? getTStylingRangePrev(tRange) : getTStylingRangeNext(tRange); } if (tNode !== null) { // in case where we are going in next direction AND we did not find anything, we need to // consult residual styling let residual = isClassBased ? tNode.residualClasses : tNode.residualStyles; if (residual != null /** OR residual !=== undefined */) { value = keyValueArrayGet(residual, prop); } } return value; } /** * Determines if the binding value should be used (or if the value is 'undefined' and hence priority * resolution should be used.) * * @param value Binding style value. */ function isStylingValuePresent(value) { // Currently only `undefined` value is considered non-binding. That is `undefined` says I don't // have an opinion as to what this binding should be and you should consult other bindings by // priority to determine the valid value. // This is extracted into a single function so that we have a single place to control this. return value !== undefined; } /** * Normalizes and/or adds a suffix to the value. * * If value is `null`/`undefined` no suffix is added * @param value * @param suffix */ function normalizeSuffix(value, suffix) { if (value == null || value === '') { // do nothing // Do not add the suffix if the value is going to be empty. // As it produce invalid CSS, which the browsers will automatically omit but Domino will not. // Example: `"left": "px;"` instead of `"left": ""`. } else if (typeof suffix === 'string') { value = value + suffix; } else if (typeof value === 'object') { value = stringify(unwrapSafeValue(value)); } return value; } /** * Tests if the `TNode` has input shadow. * * An input shadow is when a directive steals (shadows) the input by using `@Input('style')` or * `@Input('class')` as input. * * @param tNode `TNode` which we would like to see if it has shadow. * @param isClassBased `true` if `class` (`false` if `style`) */ function hasStylingInputShadow(tNode, isClassBased) { return (tNode.flags & (isClassBased ? 8 /* TNodeFlags.hasClassInput */ : 16 /* TNodeFlags.hasStyleInput */)) !== 0; } /** * Create static text node * * @param index Index of the node in the data array * @param value Static string value to write. * * @codeGenApi */ function ɵɵtext(index, value = '') { const lView = getLView(); const tView = getTView(); const adjustedIndex = index + HEADER_OFFSET; ngDevMode && assertEqual(getBindingIndex(), tView.bindingStartIndex, 'text nodes should be created before any bindings'); ngDevMode && assertIndexInRange(lView, adjustedIndex); const tNode = tView.firstCreatePass ? getOrCreateTNode(tView, adjustedIndex, 1 /* TNodeType.Text */, value, null) : tView.data[adjustedIndex]; const textNative = lView[adjustedIndex] = createTextNode(lView[RENDERER], value); appendChild(tView, lView, textNative, tNode); // Text nodes are self closing. setCurrentTNode(tNode, false); } /** * * Update text content with a lone bound value * * Used when a text node has 1 interpolated value in it, an no additional text * surrounds that interpolated value: * * ```html * <div>{{v0}}</div> * ``` * * Its compiled representation is: * * ```ts * ɵɵtextInterpolate(v0); * ``` * @returns itself, so that it may be chained. * @see textInterpolateV * @codeGenApi */ function ɵɵtextInterpolate(v0) { ɵɵtextInterpolate1('', v0, ''); return ɵɵtextInterpolate; } /** * * Update text content with single bound value surrounded by other text. * * Used when a text node has 1 interpolated value in it: * * ```html * <div>prefix{{v0}}suffix</div> * ``` * * Its compiled representation is: * * ```ts * ɵɵtextInterpolate1('prefix', v0, 'suffix'); * ``` * @returns itself, so that it may be chained. * @see textInterpolateV * @codeGenApi */ function ɵɵtextInterpolate1(prefix, v0, suffix) { const lView = getLView(); const interpolated = interpolation1(lView, prefix, v0, suffix); if (interpolated !== NO_CHANGE) { textBindingInternal(lView, getSelectedIndex(), interpolated); } return ɵɵtextInterpolate1; } /** * * Update text content with 2 bound values surrounded by other text. * * Used when a text node has 2 interpolated values in it: * * ```html * <div>prefix{{v0}}-{{v1}}suffix</div> * ``` * * Its compiled representation is: * * ```ts * ɵɵtextInterpolate2('prefix', v0, '-', v1, 'suffix'); * ``` * @returns itself, so that it may be chained. * @see textInterpolateV * @codeGenApi */ function ɵɵtextInterpolate2(prefix, v0, i0, v1, suffix) { const lView = getLView(); const interpolated = interpolation2(lView, prefix, v0, i0, v1, suffix); if (interpolated !== NO_CHANGE) { textBindingInternal(lView, getSelectedIndex(), interpolated); } return ɵɵtextInterpolate2; } /** * * Update text content with 3 bound values surrounded by other text. * * Used when a text node has 3 interpolated values in it: * * ```html * <div>prefix{{v0}}-{{v1}}-{{v2}}suffix</div> * ``` * * Its compiled representation is: * * ```ts * ɵɵtextInterpolate3( * 'prefix', v0, '-', v1, '-', v2, 'suffix'); * ``` * @returns itself, so that it may be chained. * @see textInterpolateV * @codeGenApi */ function ɵɵtextInterpolate3(prefix, v0, i0, v1, i1, v2, suffix) { const lView = getLView(); const interpolated = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix); if (interpolated !== NO_CHANGE) { textBindingInternal(lView, getSelectedIndex(), interpolated); } return ɵɵtextInterpolate3; } /** * * Update text content with 4 bound values surrounded by other text. * * Used when a text node has 4 interpolated values in it: * * ```html * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix</div> * ``` * * Its compiled representation is: * * ```ts * ɵɵtextInterpolate4( * 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix'); * ``` * @returns itself, so that it may be chained. * @see ɵɵtextInterpolateV * @codeGenApi */ function ɵɵtextInterpolate4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix) { const lView = getLView(); const interpolated = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix); if (interpolated !== NO_CHANGE) { textBindingInternal(lView, getSelectedIndex(), interpolated); } return ɵɵtextInterpolate4; } /** * * Update text content with 5 bound values surrounded by other text. * * Used when a text node has 5 interpolated values in it: * * ```html * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix</div> * ``` * * Its compiled representation is: * * ```ts * ɵɵtextInterpolate5( * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix'); * ``` * @returns itself, so that it may be chained. * @see textInterpolateV * @codeGenApi */ function ɵɵtextInterpolate5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) { const lView = getLView(); const interpolated = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix); if (interpolated !== NO_CHANGE) { textBindingInternal(lView, getSelectedIndex(), interpolated); } return ɵɵtextInterpolate5; } /** * * Update text content with 6 bound values surrounded by other text. * * Used when a text node has 6 interpolated values in it: * * ```html * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix</div> * ``` * * Its compiled representation is: * * ```ts * ɵɵtextInterpolate6( * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix'); * ``` * * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. @returns itself, so that it may be chained. * @see textInterpolateV * @codeGenApi */ function ɵɵtextInterpolate6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) { const lView = getLView(); const interpolated = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix); if (interpolated !== NO_CHANGE) { textBindingInternal(lView, getSelectedIndex(), interpolated); } return ɵɵtextInterpolate6; } /** * * Update text content with 7 bound values surrounded by other text. * * Used when a text node has 7 interpolated values in it: * * ```html * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix</div> * ``` * * Its compiled representation is: * * ```ts * ɵɵtextInterpolate7( * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix'); * ``` * @returns itself, so that it may be chained. * @see textInterpolateV * @codeGenApi */ function ɵɵtextInterpolate7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) { const lView = getLView(); const interpolated = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix); if (interpolated !== NO_CHANGE) { textBindingInternal(lView, getSelectedIndex(), interpolated); } return ɵɵtextInterpolate7; } /** * * Update text content with 8 bound values surrounded by other text. * * Used when a text node has 8 interpolated values in it: * * ```html * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix</div> * ``` * * Its compiled representation is: * * ```ts * ɵɵtextInterpolate8( * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix'); * ``` * @returns itself, so that it may be chained. * @see textInterpolateV * @codeGenApi */ function ɵɵtextInterpolate8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) { const lView = getLView(); const interpolated = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix); if (interpolated !== NO_CHANGE) { textBindingInternal(lView, getSelectedIndex(), interpolated); } return ɵɵtextInterpolate8; } /** * Update text content with 9 or more bound values other surrounded by text. * * Used when the number of interpolated values exceeds 8. * * ```html * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix</div> * ``` * * Its compiled representation is: * * ```ts * ɵɵtextInterpolateV( * ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9, * 'suffix']); * ``` *. * @param values The collection of values and the strings in between those values, beginning with * a string prefix and ending with a string suffix. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`) * * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵtextInterpolateV(values) { const lView = getLView(); const interpolated = interpolationV(lView, values); if (interpolated !== NO_CHANGE) { textBindingInternal(lView, getSelectedIndex(), interpolated); } return ɵɵtextInterpolateV; } /** * * Update an interpolated class on an element with single bound value surrounded by text. * * Used when the value passed to a property has 1 interpolated value in it: * * ```html * <div class="prefix{{v0}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵclassMapInterpolate1('prefix', v0, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵclassMapInterpolate1(prefix, v0, suffix) { const lView = getLView(); const interpolatedValue = interpolation1(lView, prefix, v0, suffix); checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true); } /** * * Update an interpolated class on an element with 2 bound values surrounded by text. * * Used when the value passed to a property has 2 interpolated values in it: * * ```html * <div class="prefix{{v0}}-{{v1}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵclassMapInterpolate2('prefix', v0, '-', v1, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵclassMapInterpolate2(prefix, v0, i0, v1, suffix) { const lView = getLView(); const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix); checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true); } /** * * Update an interpolated class on an element with 3 bound values surrounded by text. * * Used when the value passed to a property has 3 interpolated values in it: * * ```html * <div class="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵclassMapInterpolate3( * 'prefix', v0, '-', v1, '-', v2, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵclassMapInterpolate3(prefix, v0, i0, v1, i1, v2, suffix) { const lView = getLView(); const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix); checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true); } /** * * Update an interpolated class on an element with 4 bound values surrounded by text. * * Used when the value passed to a property has 4 interpolated values in it: * * ```html * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵclassMapInterpolate4( * 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵclassMapInterpolate4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix) { const lView = getLView(); const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix); checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true); } /** * * Update an interpolated class on an element with 5 bound values surrounded by text. * * Used when the value passed to a property has 5 interpolated values in it: * * ```html * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵclassMapInterpolate5( * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵclassMapInterpolate5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) { const lView = getLView(); const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix); checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true); } /** * * Update an interpolated class on an element with 6 bound values surrounded by text. * * Used when the value passed to a property has 6 interpolated values in it: * * ```html * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵclassMapInterpolate6( * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵclassMapInterpolate6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) { const lView = getLView(); const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix); checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true); } /** * * Update an interpolated class on an element with 7 bound values surrounded by text. * * Used when the value passed to a property has 7 interpolated values in it: * * ```html * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵclassMapInterpolate7( * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param i5 Static value used for concatenation only. * @param v6 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵclassMapInterpolate7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) { const lView = getLView(); const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix); checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true); } /** * * Update an interpolated class on an element with 8 bound values surrounded by text. * * Used when the value passed to a property has 8 interpolated values in it: * * ```html * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵclassMapInterpolate8( * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param i5 Static value used for concatenation only. * @param v6 Value checked for change. * @param i6 Static value used for concatenation only. * @param v7 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵclassMapInterpolate8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) { const lView = getLView(); const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix); checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true); } /** * Update an interpolated class on an element with 9 or more bound values surrounded by text. * * Used when the number of interpolated values exceeds 8. * * ```html * <div * class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵclassMapInterpolateV( * ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9, * 'suffix']); * ``` *. * @param values The collection of values and the strings in-between those values, beginning with * a string prefix and ending with a string suffix. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`) * @codeGenApi */ function ɵɵclassMapInterpolateV(values) { const lView = getLView(); const interpolatedValue = interpolationV(lView, values); checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true); } /** * * Update an interpolated style on an element with single bound value surrounded by text. * * Used when the value passed to a property has 1 interpolated value in it: * * ```html * <div style="key: {{v0}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstyleMapInterpolate1('key: ', v0, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵstyleMapInterpolate1(prefix, v0, suffix) { const lView = getLView(); const interpolatedValue = interpolation1(lView, prefix, v0, suffix); ɵɵstyleMap(interpolatedValue); } /** * * Update an interpolated style on an element with 2 bound values surrounded by text. * * Used when the value passed to a property has 2 interpolated values in it: * * ```html * <div style="key: {{v0}}; key1: {{v1}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstyleMapInterpolate2('key: ', v0, '; key1: ', v1, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵstyleMapInterpolate2(prefix, v0, i0, v1, suffix) { const lView = getLView(); const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix); ɵɵstyleMap(interpolatedValue); } /** * * Update an interpolated style on an element with 3 bound values surrounded by text. * * Used when the value passed to a property has 3 interpolated values in it: * * ```html * <div style="key: {{v0}}; key2: {{v1}}; key2: {{v2}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstyleMapInterpolate3( * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵstyleMapInterpolate3(prefix, v0, i0, v1, i1, v2, suffix) { const lView = getLView(); const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix); ɵɵstyleMap(interpolatedValue); } /** * * Update an interpolated style on an element with 4 bound values surrounded by text. * * Used when the value passed to a property has 4 interpolated values in it: * * ```html * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstyleMapInterpolate4( * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵstyleMapInterpolate4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix) { const lView = getLView(); const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix); ɵɵstyleMap(interpolatedValue); } /** * * Update an interpolated style on an element with 5 bound values surrounded by text. * * Used when the value passed to a property has 5 interpolated values in it: * * ```html * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstyleMapInterpolate5( * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵstyleMapInterpolate5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) { const lView = getLView(); const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix); ɵɵstyleMap(interpolatedValue); } /** * * Update an interpolated style on an element with 6 bound values surrounded by text. * * Used when the value passed to a property has 6 interpolated values in it: * * ```html * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}; * key5: {{v5}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstyleMapInterpolate6( * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5, * 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵstyleMapInterpolate6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) { const lView = getLView(); const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix); ɵɵstyleMap(interpolatedValue); } /** * * Update an interpolated style on an element with 7 bound values surrounded by text. * * Used when the value passed to a property has 7 interpolated values in it: * * ```html * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}; key5: {{v5}}; * key6: {{v6}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstyleMapInterpolate7( * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5, * '; key6: ', v6, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param i5 Static value used for concatenation only. * @param v6 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵstyleMapInterpolate7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) { const lView = getLView(); const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix); ɵɵstyleMap(interpolatedValue); } /** * * Update an interpolated style on an element with 8 bound values surrounded by text. * * Used when the value passed to a property has 8 interpolated values in it: * * ```html * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}; key5: {{v5}}; * key6: {{v6}}; key7: {{v7}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstyleMapInterpolate8( * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5, * '; key6: ', v6, '; key7: ', v7, 'suffix'); * ``` * * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param i5 Static value used for concatenation only. * @param v6 Value checked for change. * @param i6 Static value used for concatenation only. * @param v7 Value checked for change. * @param suffix Static value used for concatenation only. * @codeGenApi */ function ɵɵstyleMapInterpolate8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) { const lView = getLView(); const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix); ɵɵstyleMap(interpolatedValue); } /** * Update an interpolated style on an element with 9 or more bound values surrounded by text. * * Used when the number of interpolated values exceeds 8. * * ```html * <div * class="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}; key5: {{v5}}; * key6: {{v6}}; key7: {{v7}}; key8: {{v8}}; key9: {{v9}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstyleMapInterpolateV( * ['key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5, * '; key6: ', v6, '; key7: ', v7, '; key8: ', v8, '; key9: ', v9, 'suffix']); * ``` *. * @param values The collection of values and the strings in-between those values, beginning with * a string prefix and ending with a string suffix. * (e.g. `['prefix', value0, '; key2: ', value1, '; key2: ', value2, ..., value99, 'suffix']`) * @codeGenApi */ function ɵɵstyleMapInterpolateV(values) { const lView = getLView(); const interpolatedValue = interpolationV(lView, values); ɵɵstyleMap(interpolatedValue); } /** * * Update an interpolated style property on an element with single bound value surrounded by text. * * Used when the value passed to a property has 1 interpolated value in it: * * ```html * <div style.color="prefix{{v0}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstylePropInterpolate1(0, 'prefix', v0, 'suffix'); * ``` * * @param styleIndex Index of style to update. This index value refers to the * index of the style in the style bindings array that was passed into * `styling`. * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param suffix Static value used for concatenation only. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`. * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵstylePropInterpolate1(prop, prefix, v0, suffix, valueSuffix) { const lView = getLView(); const interpolatedValue = interpolation1(lView, prefix, v0, suffix); checkStylingProperty(prop, interpolatedValue, valueSuffix, false); return ɵɵstylePropInterpolate1; } /** * * Update an interpolated style property on an element with 2 bound values surrounded by text. * * Used when the value passed to a property has 2 interpolated values in it: * * ```html * <div style.color="prefix{{v0}}-{{v1}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstylePropInterpolate2(0, 'prefix', v0, '-', v1, 'suffix'); * ``` * * @param styleIndex Index of style to update. This index value refers to the * index of the style in the style bindings array that was passed into * `styling`. * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param suffix Static value used for concatenation only. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`. * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵstylePropInterpolate2(prop, prefix, v0, i0, v1, suffix, valueSuffix) { const lView = getLView(); const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix); checkStylingProperty(prop, interpolatedValue, valueSuffix, false); return ɵɵstylePropInterpolate2; } /** * * Update an interpolated style property on an element with 3 bound values surrounded by text. * * Used when the value passed to a property has 3 interpolated values in it: * * ```html * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstylePropInterpolate3(0, 'prefix', v0, '-', v1, '-', v2, 'suffix'); * ``` * * @param styleIndex Index of style to update. This index value refers to the * index of the style in the style bindings array that was passed into * `styling`. * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param suffix Static value used for concatenation only. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`. * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵstylePropInterpolate3(prop, prefix, v0, i0, v1, i1, v2, suffix, valueSuffix) { const lView = getLView(); const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix); checkStylingProperty(prop, interpolatedValue, valueSuffix, false); return ɵɵstylePropInterpolate3; } /** * * Update an interpolated style property on an element with 4 bound values surrounded by text. * * Used when the value passed to a property has 4 interpolated values in it: * * ```html * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstylePropInterpolate4(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix'); * ``` * * @param styleIndex Index of style to update. This index value refers to the * index of the style in the style bindings array that was passed into * `styling`. * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param suffix Static value used for concatenation only. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`. * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵstylePropInterpolate4(prop, prefix, v0, i0, v1, i1, v2, i2, v3, suffix, valueSuffix) { const lView = getLView(); const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix); checkStylingProperty(prop, interpolatedValue, valueSuffix, false); return ɵɵstylePropInterpolate4; } /** * * Update an interpolated style property on an element with 5 bound values surrounded by text. * * Used when the value passed to a property has 5 interpolated values in it: * * ```html * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstylePropInterpolate5(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix'); * ``` * * @param styleIndex Index of style to update. This index value refers to the * index of the style in the style bindings array that was passed into * `styling`. * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param suffix Static value used for concatenation only. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`. * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵstylePropInterpolate5(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix, valueSuffix) { const lView = getLView(); const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix); checkStylingProperty(prop, interpolatedValue, valueSuffix, false); return ɵɵstylePropInterpolate5; } /** * * Update an interpolated style property on an element with 6 bound values surrounded by text. * * Used when the value passed to a property has 6 interpolated values in it: * * ```html * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstylePropInterpolate6(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix'); * ``` * * @param styleIndex Index of style to update. This index value refers to the * index of the style in the style bindings array that was passed into * `styling`. * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param suffix Static value used for concatenation only. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`. * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵstylePropInterpolate6(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix, valueSuffix) { const lView = getLView(); const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix); checkStylingProperty(prop, interpolatedValue, valueSuffix, false); return ɵɵstylePropInterpolate6; } /** * * Update an interpolated style property on an element with 7 bound values surrounded by text. * * Used when the value passed to a property has 7 interpolated values in it: * * ```html * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstylePropInterpolate7( * 0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix'); * ``` * * @param styleIndex Index of style to update. This index value refers to the * index of the style in the style bindings array that was passed into * `styling`. * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param i5 Static value used for concatenation only. * @param v6 Value checked for change. * @param suffix Static value used for concatenation only. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`. * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵstylePropInterpolate7(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix, valueSuffix) { const lView = getLView(); const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix); checkStylingProperty(prop, interpolatedValue, valueSuffix, false); return ɵɵstylePropInterpolate7; } /** * * Update an interpolated style property on an element with 8 bound values surrounded by text. * * Used when the value passed to a property has 8 interpolated values in it: * * ```html * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstylePropInterpolate8(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, * '-', v7, 'suffix'); * ``` * * @param styleIndex Index of style to update. This index value refers to the * index of the style in the style bindings array that was passed into * `styling`. * @param prefix Static value used for concatenation only. * @param v0 Value checked for change. * @param i0 Static value used for concatenation only. * @param v1 Value checked for change. * @param i1 Static value used for concatenation only. * @param v2 Value checked for change. * @param i2 Static value used for concatenation only. * @param v3 Value checked for change. * @param i3 Static value used for concatenation only. * @param v4 Value checked for change. * @param i4 Static value used for concatenation only. * @param v5 Value checked for change. * @param i5 Static value used for concatenation only. * @param v6 Value checked for change. * @param i6 Static value used for concatenation only. * @param v7 Value checked for change. * @param suffix Static value used for concatenation only. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`. * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵstylePropInterpolate8(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix, valueSuffix) { const lView = getLView(); const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix); checkStylingProperty(prop, interpolatedValue, valueSuffix, false); return ɵɵstylePropInterpolate8; } /** * Update an interpolated style property on an element with 9 or more bound values surrounded by * text. * * Used when the number of interpolated values exceeds 8. * * ```html * <div * style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"> * </div> * ``` * * Its compiled representation is: * * ```ts * ɵɵstylePropInterpolateV( * 0, ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9, * 'suffix']); * ``` * * @param styleIndex Index of style to update. This index value refers to the * index of the style in the style bindings array that was passed into * `styling`.. * @param values The collection of values and the strings in-between those values, beginning with * a string prefix and ending with a string suffix. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`) * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`. * @returns itself, so that it may be chained. * @codeGenApi */ function ɵɵstylePropInterpolateV(prop, values, valueSuffix) { const lView = getLView(); const interpolatedValue = interpolationV(lView, values); checkStylingProperty(prop, interpolatedValue, valueSuffix, false); return ɵɵstylePropInterpolateV; } /** * Update a property on a host element. Only applies to native node properties, not inputs. * * Operates on the element selected by index via the {@link select} instruction. * * @param propName Name of property. Because it is going to DOM, this is not subject to * renaming as part of minification. * @param value New value to write. * @param sanitizer An optional function used to sanitize the value. * @returns This function returns itself so that it may be chained * (e.g. `property('name', ctx.name)('title', ctx.title)`) * * @codeGenApi */ function ɵɵhostProperty(propName, value, sanitizer) { const lView = getLView(); const bindingIndex = nextBindingIndex(); if (bindingUpdated(lView, bindingIndex, value)) { const tView = getTView(); const tNode = getSelectedTNode(); elementPropertyInternal(tView, tNode, lView, propName, value, lView[RENDERER], sanitizer, true); ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, bindingIndex); } return ɵɵhostProperty; } /** * Updates a synthetic host binding (e.g. `[@foo]`) on a component or directive. * * This instruction is for compatibility purposes and is designed to ensure that a * synthetic host binding (e.g. `@HostBinding('@foo')`) properly gets rendered in * the component's renderer. Normally all host bindings are evaluated with the parent * component's renderer, but, in the case of animation @triggers, they need to be * evaluated with the sub component's renderer (because that's where the animation * triggers are defined). * * Do not use this instruction as a replacement for `elementProperty`. This instruction * only exists to ensure compatibility with the ViewEngine's host binding behavior. * * @param index The index of the element to update in the data array * @param propName Name of property. Because it is going to DOM, this is not subject to * renaming as part of minification. * @param value New value to write. * @param sanitizer An optional function used to sanitize the value. * * @codeGenApi */ function ɵɵsyntheticHostProperty(propName, value, sanitizer) { const lView = getLView(); const bindingIndex = nextBindingIndex(); if (bindingUpdated(lView, bindingIndex, value)) { const tView = getTView(); const tNode = getSelectedTNode(); const currentDef = getCurrentDirectiveDef(tView.data); const renderer = loadComponentRenderer(currentDef, tNode, lView); elementPropertyInternal(tView, tNode, lView, propName, value, renderer, sanitizer, true); ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, bindingIndex); } return ɵɵsyntheticHostProperty; } /** * NOTE: changes to the `ngI18nClosureMode` name must be synced with `compiler-cli/src/tooling.ts`. */ if (typeof ngI18nClosureMode === 'undefined') { // These property accesses can be ignored because ngI18nClosureMode will be set to false // when optimizing code and the whole if statement will be dropped. // Make sure to refer to ngI18nClosureMode as ['ngI18nClosureMode'] for closure. // NOTE: we need to have it in IIFE so that the tree-shaker is happy. (function () { // tslint:disable-next-line:no-toplevel-property-access _global['ngI18nClosureMode'] = // TODO(FW-1250): validate that this actually, you know, works. // tslint:disable-next-line:no-toplevel-property-access typeof goog !== 'undefined' && typeof goog.getMsg === 'function'; })(); } // THIS CODE IS GENERATED - DO NOT MODIFY. const u = undefined; function plural(val) { const n = val, i = Math.floor(Math.abs(val)), v = val.toString().replace(/^[^.]*\.?/, '').length; if (i === 1 && v === 0) return 1; return 5; } var localeEn = ["en", [["a", "p"], ["AM", "PM"], u], [["AM", "PM"], u, u], [["S", "M", "T", "W", "T", "F", "S"], ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]], u, [["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"], ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]], u, [["B", "A"], ["BC", "AD"], ["Before Christ", "Anno Domini"]], 0, [6, 0], ["M/d/yy", "MMM d, y", "MMMM d, y", "EEEE, MMMM d, y"], ["h:mm a", "h:mm:ss a", "h:mm:ss a z", "h:mm:ss a zzzz"], ["{1}, {0}", u, "{1} 'at' {0}", u], [".", ",", ";", "%", "+", "-", "E", "×", "‰", "∞", "NaN", ":"], ["#,##0.###", "#,##0%", "¤#,##0.00", "#E0"], "USD", "$", "US Dollar", {}, "ltr", plural]; /** * This const is used to store the locale data registered with `registerLocaleData` */ let LOCALE_DATA = {}; /** * Register locale data to be used internally by Angular. See the * ["I18n guide"](guide/i18n-common-format-data-locale) to know how to import additional locale * data. * * The signature `registerLocaleData(data: any, extraData?: any)` is deprecated since v5.1 */ function registerLocaleData(data, localeId, extraData) { if (typeof localeId !== 'string') { extraData = localeId; localeId = data[LocaleDataIndex.LocaleId]; } localeId = localeId.toLowerCase().replace(/_/g, '-'); LOCALE_DATA[localeId] = data; if (extraData) { LOCALE_DATA[localeId][LocaleDataIndex.ExtraData] = extraData; } } /** * Finds the locale data for a given locale. * * @param locale The locale code. * @returns The locale data. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) */ function findLocaleData(locale) { const normalizedLocale = normalizeLocale(locale); let match = getLocaleData(normalizedLocale); if (match) { return match; } // let's try to find a parent locale const parentLocale = normalizedLocale.split('-')[0]; match = getLocaleData(parentLocale); if (match) { return match; } if (parentLocale === 'en') { return localeEn; } throw new RuntimeError(701 /* RuntimeErrorCode.MISSING_LOCALE_DATA */, ngDevMode && `Missing locale data for the locale "${locale}".`); } /** * Retrieves the default currency code for the given locale. * * The default is defined as the first currency which is still in use. * * @param locale The code of the locale whose currency code we want. * @returns The code of the default currency for the given locale. * */ function getLocaleCurrencyCode(locale) { const data = findLocaleData(locale); return data[LocaleDataIndex.CurrencyCode] || null; } /** * Retrieves the plural function used by ICU expressions to determine the plural case to use * for a given locale. * @param locale A locale code for the locale format rules to use. * @returns The plural function for the locale. * @see `NgPlural` * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview) */ function getLocalePluralCase(locale) { const data = findLocaleData(locale); return data[LocaleDataIndex.PluralCase]; } /** * Helper function to get the given `normalizedLocale` from `LOCALE_DATA` * or from the global `ng.common.locale`. */ function getLocaleData(normalizedLocale) { if (!(normalizedLocale in LOCALE_DATA)) { LOCALE_DATA[normalizedLocale] = _global.ng && _global.ng.common && _global.ng.common.locales && _global.ng.common.locales[normalizedLocale]; } return LOCALE_DATA[normalizedLocale]; } /** * Helper function to remove all the locale data from `LOCALE_DATA`. */ function unregisterAllLocaleData() { LOCALE_DATA = {}; } /** * Index of each type of locale data from the locale data array */ var LocaleDataIndex; (function (LocaleDataIndex) { LocaleDataIndex[LocaleDataIndex["LocaleId"] = 0] = "LocaleId"; LocaleDataIndex[LocaleDataIndex["DayPeriodsFormat"] = 1] = "DayPeriodsFormat"; LocaleDataIndex[LocaleDataIndex["DayPeriodsStandalone"] = 2] = "DayPeriodsStandalone"; LocaleDataIndex[LocaleDataIndex["DaysFormat"] = 3] = "DaysFormat"; LocaleDataIndex[LocaleDataIndex["DaysStandalone"] = 4] = "DaysStandalone"; LocaleDataIndex[LocaleDataIndex["MonthsFormat"] = 5] = "MonthsFormat"; LocaleDataIndex[LocaleDataIndex["MonthsStandalone"] = 6] = "MonthsStandalone"; LocaleDataIndex[LocaleDataIndex["Eras"] = 7] = "Eras"; LocaleDataIndex[LocaleDataIndex["FirstDayOfWeek"] = 8] = "FirstDayOfWeek"; LocaleDataIndex[LocaleDataIndex["WeekendRange"] = 9] = "WeekendRange"; LocaleDataIndex[LocaleDataIndex["DateFormat"] = 10] = "DateFormat"; LocaleDataIndex[LocaleDataIndex["TimeFormat"] = 11] = "TimeFormat"; LocaleDataIndex[LocaleDataIndex["DateTimeFormat"] = 12] = "DateTimeFormat"; LocaleDataIndex[LocaleDataIndex["NumberSymbols"] = 13] = "NumberSymbols"; LocaleDataIndex[LocaleDataIndex["NumberFormats"] = 14] = "NumberFormats"; LocaleDataIndex[LocaleDataIndex["CurrencyCode"] = 15] = "CurrencyCode"; LocaleDataIndex[LocaleDataIndex["CurrencySymbol"] = 16] = "CurrencySymbol"; LocaleDataIndex[LocaleDataIndex["CurrencyName"] = 17] = "CurrencyName"; LocaleDataIndex[LocaleDataIndex["Currencies"] = 18] = "Currencies"; LocaleDataIndex[LocaleDataIndex["Directionality"] = 19] = "Directionality"; LocaleDataIndex[LocaleDataIndex["PluralCase"] = 20] = "PluralCase"; LocaleDataIndex[LocaleDataIndex["ExtraData"] = 21] = "ExtraData"; })(LocaleDataIndex || (LocaleDataIndex = {})); /** * Returns the canonical form of a locale name - lowercase with `_` replaced with `-`. */ function normalizeLocale(locale) { return locale.toLowerCase().replace(/_/g, '-'); } const pluralMapping = ['zero', 'one', 'two', 'few', 'many']; /** * Returns the plural case based on the locale */ function getPluralCase(value, locale) { const plural = getLocalePluralCase(locale)(parseInt(value, 10)); const result = pluralMapping[plural]; return result !== undefined ? result : 'other'; } /** * The locale id that the application is using by default (for translations and ICU expressions). */ const DEFAULT_LOCALE_ID = 'en-US'; /** * USD currency code that the application uses by default for CurrencyPipe when no * DEFAULT_CURRENCY_CODE is provided. */ const USD_CURRENCY_CODE = 'USD'; /** * Marks that the next string is an element name. * * See `I18nMutateOpCodes` documentation. */ const ELEMENT_MARKER = { marker: 'element' }; /** * Marks that the next string is comment text need for ICU. * * See `I18nMutateOpCodes` documentation. */ const ICU_MARKER = { marker: 'ICU' }; /** * See `I18nCreateOpCodes` */ var I18nCreateOpCode; (function (I18nCreateOpCode) { /** * Number of bits to shift index so that it can be combined with the `APPEND_EAGERLY` and * `COMMENT`. */ I18nCreateOpCode[I18nCreateOpCode["SHIFT"] = 2] = "SHIFT"; /** * Should the node be appended to parent immediately after creation. */ I18nCreateOpCode[I18nCreateOpCode["APPEND_EAGERLY"] = 1] = "APPEND_EAGERLY"; /** * If set the node should be comment (rather than a text) node. */ I18nCreateOpCode[I18nCreateOpCode["COMMENT"] = 2] = "COMMENT"; })(I18nCreateOpCode || (I18nCreateOpCode = {})); // Note: This hack is necessary so we don't erroneously get a circular dependency // failure based on types. const unusedValueExportToPlacateAjd = 1; /** * The locale id that the application is currently using (for translations and ICU expressions). * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine * but is now defined as a global value. */ let LOCALE_ID$1 = DEFAULT_LOCALE_ID; /** * Sets the locale id that will be used for translations and ICU expressions. * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine * but is now defined as a global value. * * @param localeId */ function setLocaleId(localeId) { assertDefined(localeId, `Expected localeId to be defined`); if (typeof localeId === 'string') { LOCALE_ID$1 = localeId.toLowerCase().replace(/_/g, '-'); } } /** * Gets the locale id that will be used for translations and ICU expressions. * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine * but is now defined as a global value. */ function getLocaleId() { return LOCALE_ID$1; } /** * Find a node in front of which `currentTNode` should be inserted (takes i18n into account). * * This method determines the `RNode` in front of which we should insert the `currentRNode`. This * takes `TNode.insertBeforeIndex` into account. * * @param parentTNode parent `TNode` * @param currentTNode current `TNode` (The node which we would like to insert into the DOM) * @param lView current `LView` */ function getInsertInFrontOfRNodeWithI18n(parentTNode, currentTNode, lView) { const tNodeInsertBeforeIndex = currentTNode.insertBeforeIndex; const insertBeforeIndex = Array.isArray(tNodeInsertBeforeIndex) ? tNodeInsertBeforeIndex[0] : tNodeInsertBeforeIndex; if (insertBeforeIndex === null) { return getInsertInFrontOfRNodeWithNoI18n(parentTNode, currentTNode, lView); } else { ngDevMode && assertIndexInRange(lView, insertBeforeIndex); return unwrapRNode(lView[insertBeforeIndex]); } } /** * Process `TNode.insertBeforeIndex` by adding i18n text nodes. * * See `TNode.insertBeforeIndex` */ function processI18nInsertBefore(renderer, childTNode, lView, childRNode, parentRElement) { const tNodeInsertBeforeIndex = childTNode.insertBeforeIndex; if (Array.isArray(tNodeInsertBeforeIndex)) { // An array indicates that there are i18n nodes that need to be added as children of this // `childRNode`. These i18n nodes were created before this `childRNode` was available and so // only now can be added. The first element of the array is the normal index where we should // insert the `childRNode`. Additional elements are the extra nodes to be added as children of // `childRNode`. ngDevMode && assertDomNode(childRNode); let i18nParent = childRNode; let anchorRNode = null; if (!(childTNode.type & 3 /* TNodeType.AnyRNode */)) { anchorRNode = i18nParent; i18nParent = parentRElement; } if (i18nParent !== null && childTNode.componentOffset === -1) { for (let i = 1; i < tNodeInsertBeforeIndex.length; i++) { // No need to `unwrapRNode` because all of the indexes point to i18n text nodes. // see `assertDomNode` below. const i18nChild = lView[tNodeInsertBeforeIndex[i]]; nativeInsertBefore(renderer, i18nParent, i18nChild, anchorRNode, false); } } } } /** * Add `tNode` to `previousTNodes` list and update relevant `TNode`s in `previousTNodes` list * `tNode.insertBeforeIndex`. * * Things to keep in mind: * 1. All i18n text nodes are encoded as `TNodeType.Element` and are created eagerly by the * `ɵɵi18nStart` instruction. * 2. All `TNodeType.Placeholder` `TNodes` are elements which will be created later by * `ɵɵelementStart` instruction. * 3. `ɵɵelementStart` instruction will create `TNode`s in the ascending `TNode.index` order. (So a * smaller index `TNode` is guaranteed to be created before a larger one) * * We use the above three invariants to determine `TNode.insertBeforeIndex`. * * In an ideal world `TNode.insertBeforeIndex` would always be `TNode.next.index`. However, * this will not work because `TNode.next.index` may be larger than `TNode.index` which means that * the next node is not yet created and therefore we can't insert in front of it. * * Rule1: `TNode.insertBeforeIndex = null` if `TNode.next === null` (Initial condition, as we don't * know if there will be further `TNode`s inserted after.) * Rule2: If `previousTNode` is created after the `tNode` being inserted, then * `previousTNode.insertBeforeNode = tNode.index` (So when a new `tNode` is added we check * previous to see if we can update its `insertBeforeTNode`) * * See `TNode.insertBeforeIndex` for more context. * * @param previousTNodes A list of previous TNodes so that we can easily traverse `TNode`s in * reverse order. (If `TNode` would have `previous` this would not be necessary.) * @param newTNode A TNode to add to the `previousTNodes` list. */ function addTNodeAndUpdateInsertBeforeIndex(previousTNodes, newTNode) { // Start with Rule1 ngDevMode && assertEqual(newTNode.insertBeforeIndex, null, 'We expect that insertBeforeIndex is not set'); previousTNodes.push(newTNode); if (previousTNodes.length > 1) { for (let i = previousTNodes.length - 2; i >= 0; i--) { const existingTNode = previousTNodes[i]; // Text nodes are created eagerly and so they don't need their `indexBeforeIndex` updated. // It is safe to ignore them. if (!isI18nText(existingTNode)) { if (isNewTNodeCreatedBefore(existingTNode, newTNode) && getInsertBeforeIndex(existingTNode) === null) { // If it was created before us in time, (and it does not yet have `insertBeforeIndex`) // then add the `insertBeforeIndex`. setInsertBeforeIndex(existingTNode, newTNode.index); } } } } } function isI18nText(tNode) { return !(tNode.type & 64 /* TNodeType.Placeholder */); } function isNewTNodeCreatedBefore(existingTNode, newTNode) { return isI18nText(newTNode) || existingTNode.index > newTNode.index; } function getInsertBeforeIndex(tNode) { const index = tNode.insertBeforeIndex; return Array.isArray(index) ? index[0] : index; } function setInsertBeforeIndex(tNode, value) { const index = tNode.insertBeforeIndex; if (Array.isArray(index)) { // Array is stored if we have to insert child nodes. See `TNode.insertBeforeIndex` index[0] = value; } else { setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore); tNode.insertBeforeIndex = value; } } /** * Retrieve `TIcu` at a given `index`. * * The `TIcu` can be stored either directly (if it is nested ICU) OR * it is stored inside tho `TIcuContainer` if it is top level ICU. * * The reason for this is that the top level ICU need a `TNode` so that they are part of the render * tree, but nested ICU's have no TNode, because we don't know ahead of time if the nested ICU is * expressed (parent ICU may have selected a case which does not contain it.) * * @param tView Current `TView`. * @param index Index where the value should be read from. */ function getTIcu(tView, index) { const value = tView.data[index]; if (value === null || typeof value === 'string') return null; if (ngDevMode && !(value.hasOwnProperty('tView') || value.hasOwnProperty('currentCaseLViewIndex'))) { throwError('We expect to get \'null\'|\'TIcu\'|\'TIcuContainer\', but got: ' + value); } // Here the `value.hasOwnProperty('currentCaseLViewIndex')` is a polymorphic read as it can be // either TIcu or TIcuContainerNode. This is not ideal, but we still think it is OK because it // will be just two cases which fits into the browser inline cache (inline cache can take up to // 4) const tIcu = value.hasOwnProperty('currentCaseLViewIndex') ? value : value.value; ngDevMode && assertTIcu(tIcu); return tIcu; } /** * Store `TIcu` at a give `index`. * * The `TIcu` can be stored either directly (if it is nested ICU) OR * it is stored inside tho `TIcuContainer` if it is top level ICU. * * The reason for this is that the top level ICU need a `TNode` so that they are part of the render * tree, but nested ICU's have no TNode, because we don't know ahead of time if the nested ICU is * expressed (parent ICU may have selected a case which does not contain it.) * * @param tView Current `TView`. * @param index Index where the value should be stored at in `Tview.data` * @param tIcu The TIcu to store. */ function setTIcu(tView, index, tIcu) { const tNode = tView.data[index]; ngDevMode && assertEqual(tNode === null || tNode.hasOwnProperty('tView'), true, 'We expect to get \'null\'|\'TIcuContainer\''); if (tNode === null) { tView.data[index] = tIcu; } else { ngDevMode && assertTNodeType(tNode, 32 /* TNodeType.Icu */); tNode.value = tIcu; } } /** * Set `TNode.insertBeforeIndex` taking the `Array` into account. * * See `TNode.insertBeforeIndex` */ function setTNodeInsertBeforeIndex(tNode, index) { ngDevMode && assertTNode(tNode); let insertBeforeIndex = tNode.insertBeforeIndex; if (insertBeforeIndex === null) { setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore); insertBeforeIndex = tNode.insertBeforeIndex = [null /* may be updated to number later */, index]; } else { assertEqual(Array.isArray(insertBeforeIndex), true, 'Expecting array here'); insertBeforeIndex.push(index); } } /** * Create `TNode.type=TNodeType.Placeholder` node. * * See `TNodeType.Placeholder` for more information. */ function createTNodePlaceholder(tView, previousTNodes, index) { const tNode = createTNodeAtIndex(tView, index, 64 /* TNodeType.Placeholder */, null, null); addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tNode); return tNode; } /** * Returns current ICU case. * * ICU cases are stored as index into the `TIcu.cases`. * At times it is necessary to communicate that the ICU case just switched and that next ICU update * should update all bindings regardless of the mask. In such a case the we store negative numbers * for cases which have just been switched. This function removes the negative flag. */ function getCurrentICUCaseIndex(tIcu, lView) { const currentCase = lView[tIcu.currentCaseLViewIndex]; return currentCase === null ? currentCase : currentCase < 0 ? ~currentCase : currentCase; } function getParentFromIcuCreateOpCode(mergedCode) { return mergedCode >>> 17 /* IcuCreateOpCode.SHIFT_PARENT */; } function getRefFromIcuCreateOpCode(mergedCode) { return (mergedCode & 131070 /* IcuCreateOpCode.MASK_REF */) >>> 1 /* IcuCreateOpCode.SHIFT_REF */; } function getInstructionFromIcuCreateOpCode(mergedCode) { return mergedCode & 1 /* IcuCreateOpCode.MASK_INSTRUCTION */; } function icuCreateOpCode(opCode, parentIdx, refIdx) { ngDevMode && assertGreaterThanOrEqual(parentIdx, 0, 'Missing parent index'); ngDevMode && assertGreaterThan(refIdx, 0, 'Missing ref index'); return opCode | parentIdx << 17 /* IcuCreateOpCode.SHIFT_PARENT */ | refIdx << 1 /* IcuCreateOpCode.SHIFT_REF */; } /** * Keep track of which input bindings in `ɵɵi18nExp` have changed. * * This is used to efficiently update expressions in i18n only when the corresponding input has * changed. * * 1) Each bit represents which of the `ɵɵi18nExp` has changed. * 2) There are 32 bits allowed in JS. * 3) Bit 32 is special as it is shared for all changes past 32. (In other words if you have more * than 32 `ɵɵi18nExp` then all changes past 32nd `ɵɵi18nExp` will be mapped to same bit. This means * that we may end up changing more than we need to. But i18n expressions with 32 bindings is rare * so in practice it should not be an issue.) */ let changeMask = 0b0; /** * Keeps track of which bit needs to be updated in `changeMask` * * This value gets incremented on every call to `ɵɵi18nExp` */ let changeMaskCounter = 0; /** * Keep track of which input bindings in `ɵɵi18nExp` have changed. * * `setMaskBit` gets invoked by each call to `ɵɵi18nExp`. * * @param hasChange did `ɵɵi18nExp` detect a change. */ function setMaskBit(hasChange) { if (hasChange) { changeMask = changeMask | 1 << Math.min(changeMaskCounter, 31); } changeMaskCounter++; } function applyI18n(tView, lView, index) { if (changeMaskCounter > 0) { ngDevMode && assertDefined(tView, `tView should be defined`); const tI18n = tView.data[index]; // When `index` points to an `ɵɵi18nAttributes` then we have an array otherwise `TI18n` const updateOpCodes = Array.isArray(tI18n) ? tI18n : tI18n.update; const bindingsStartIndex = getBindingIndex() - changeMaskCounter - 1; applyUpdateOpCodes(tView, lView, updateOpCodes, bindingsStartIndex, changeMask); } // Reset changeMask & maskBit to default for the next update cycle changeMask = 0b0; changeMaskCounter = 0; } /** * Apply `I18nCreateOpCodes` op-codes as stored in `TI18n.create`. * * Creates text (and comment) nodes which are internationalized. * * @param lView Current lView * @param createOpCodes Set of op-codes to apply * @param parentRNode Parent node (so that direct children can be added eagerly) or `null` if it is * a root node. * @param insertInFrontOf DOM node that should be used as an anchor. */ function applyCreateOpCodes(lView, createOpCodes, parentRNode, insertInFrontOf) { const renderer = lView[RENDERER]; for (let i = 0; i < createOpCodes.length; i++) { const opCode = createOpCodes[i++]; const text = createOpCodes[i]; const isComment = (opCode & I18nCreateOpCode.COMMENT) === I18nCreateOpCode.COMMENT; const appendNow = (opCode & I18nCreateOpCode.APPEND_EAGERLY) === I18nCreateOpCode.APPEND_EAGERLY; const index = opCode >>> I18nCreateOpCode.SHIFT; let rNode = lView[index]; if (rNode === null) { // We only create new DOM nodes if they don't already exist: If ICU switches case back to a // case which was already instantiated, no need to create new DOM nodes. rNode = lView[index] = isComment ? renderer.createComment(text) : createTextNode(renderer, text); } if (appendNow && parentRNode !== null) { nativeInsertBefore(renderer, parentRNode, rNode, insertInFrontOf, false); } } } /** * Apply `I18nMutateOpCodes` OpCodes. * * @param tView Current `TView` * @param mutableOpCodes Mutable OpCodes to process * @param lView Current `LView` * @param anchorRNode place where the i18n node should be inserted. */ function applyMutableOpCodes(tView, mutableOpCodes, lView, anchorRNode) { ngDevMode && assertDomNode(anchorRNode); const renderer = lView[RENDERER]; // `rootIdx` represents the node into which all inserts happen. let rootIdx = null; // `rootRNode` represents the real node into which we insert. This can be different from // `lView[rootIdx]` if we have projection. // - null we don't have a parent (as can be the case in when we are inserting into a root of // LView which has no parent.) // - `RElement` The element representing the root after taking projection into account. let rootRNode; for (let i = 0; i < mutableOpCodes.length; i++) { const opCode = mutableOpCodes[i]; if (typeof opCode == 'string') { const textNodeIndex = mutableOpCodes[++i]; if (lView[textNodeIndex] === null) { ngDevMode && ngDevMode.rendererCreateTextNode++; ngDevMode && assertIndexInRange(lView, textNodeIndex); lView[textNodeIndex] = createTextNode(renderer, opCode); } } else if (typeof opCode == 'number') { switch (opCode & 1 /* IcuCreateOpCode.MASK_INSTRUCTION */) { case 0 /* IcuCreateOpCode.AppendChild */: const parentIdx = getParentFromIcuCreateOpCode(opCode); if (rootIdx === null) { // The first operation should save the `rootIdx` because the first operation // must insert into the root. (Only subsequent operations can insert into a dynamic // parent) rootIdx = parentIdx; rootRNode = nativeParentNode(renderer, anchorRNode); } let insertInFrontOf; let parentRNode; if (parentIdx === rootIdx) { insertInFrontOf = anchorRNode; parentRNode = rootRNode; } else { insertInFrontOf = null; parentRNode = unwrapRNode(lView[parentIdx]); } // FIXME(misko): Refactor with `processI18nText` if (parentRNode !== null) { // This can happen if the `LView` we are adding to is not attached to a parent `LView`. // In such a case there is no "root" we can attach to. This is fine, as we still need to // create the elements. When the `LView` gets later added to a parent these "root" nodes // get picked up and added. ngDevMode && assertDomNode(parentRNode); const refIdx = getRefFromIcuCreateOpCode(opCode); ngDevMode && assertGreaterThan(refIdx, HEADER_OFFSET, 'Missing ref'); // `unwrapRNode` is not needed here as all of these point to RNodes as part of the i18n // which can't have components. const child = lView[refIdx]; ngDevMode && assertDomNode(child); nativeInsertBefore(renderer, parentRNode, child, insertInFrontOf, false); const tIcu = getTIcu(tView, refIdx); if (tIcu !== null && typeof tIcu === 'object') { // If we just added a comment node which has ICU then that ICU may have already been // rendered and therefore we need to re-add it here. ngDevMode && assertTIcu(tIcu); const caseIndex = getCurrentICUCaseIndex(tIcu, lView); if (caseIndex !== null) { applyMutableOpCodes(tView, tIcu.create[caseIndex], lView, lView[tIcu.anchorIdx]); } } } break; case 1 /* IcuCreateOpCode.Attr */: const elementNodeIndex = opCode >>> 1 /* IcuCreateOpCode.SHIFT_REF */; const attrName = mutableOpCodes[++i]; const attrValue = mutableOpCodes[++i]; // This code is used for ICU expressions only, since we don't support // directives/components in ICUs, we don't need to worry about inputs here setElementAttribute(renderer, getNativeByIndex(elementNodeIndex, lView), null, null, attrName, attrValue, null); break; default: if (ngDevMode) { throw new RuntimeError(700 /* RuntimeErrorCode.INVALID_I18N_STRUCTURE */, `Unable to determine the type of mutate operation for "${opCode}"`); } } } else { switch (opCode) { case ICU_MARKER: const commentValue = mutableOpCodes[++i]; const commentNodeIndex = mutableOpCodes[++i]; if (lView[commentNodeIndex] === null) { ngDevMode && assertEqual(typeof commentValue, 'string', `Expected "${commentValue}" to be a comment node value`); ngDevMode && ngDevMode.rendererCreateComment++; ngDevMode && assertIndexInExpandoRange(lView, commentNodeIndex); const commentRNode = lView[commentNodeIndex] = createCommentNode(renderer, commentValue); // FIXME(misko): Attaching patch data is only needed for the root (Also add tests) attachPatchData(commentRNode, lView); } break; case ELEMENT_MARKER: const tagName = mutableOpCodes[++i]; const elementNodeIndex = mutableOpCodes[++i]; if (lView[elementNodeIndex] === null) { ngDevMode && assertEqual(typeof tagName, 'string', `Expected "${tagName}" to be an element node tag name`); ngDevMode && ngDevMode.rendererCreateElement++; ngDevMode && assertIndexInExpandoRange(lView, elementNodeIndex); const elementRNode = lView[elementNodeIndex] = createElementNode(renderer, tagName, null); // FIXME(misko): Attaching patch data is only needed for the root (Also add tests) attachPatchData(elementRNode, lView); } break; default: ngDevMode && throwError(`Unable to determine the type of mutate operation for "${opCode}"`); } } } } /** * Apply `I18nUpdateOpCodes` OpCodes * * @param tView Current `TView` * @param lView Current `LView` * @param updateOpCodes OpCodes to process * @param bindingsStartIndex Location of the first `ɵɵi18nApply` * @param changeMask Each bit corresponds to a `ɵɵi18nExp` (Counting backwards from * `bindingsStartIndex`) */ function applyUpdateOpCodes(tView, lView, updateOpCodes, bindingsStartIndex, changeMask) { for (let i = 0; i < updateOpCodes.length; i++) { // bit code to check if we should apply the next update const checkBit = updateOpCodes[i]; // Number of opCodes to skip until next set of update codes const skipCodes = updateOpCodes[++i]; if (checkBit & changeMask) { // The value has been updated since last checked let value = ''; for (let j = i + 1; j <= i + skipCodes; j++) { const opCode = updateOpCodes[j]; if (typeof opCode == 'string') { value += opCode; } else if (typeof opCode == 'number') { if (opCode < 0) { // Negative opCode represent `i18nExp` values offset. value += renderStringify(lView[bindingsStartIndex - opCode]); } else { const nodeIndex = opCode >>> 2 /* I18nUpdateOpCode.SHIFT_REF */; switch (opCode & 3 /* I18nUpdateOpCode.MASK_OPCODE */) { case 1 /* I18nUpdateOpCode.Attr */: const propName = updateOpCodes[++j]; const sanitizeFn = updateOpCodes[++j]; const tNodeOrTagName = tView.data[nodeIndex]; ngDevMode && assertDefined(tNodeOrTagName, 'Experting TNode or string'); if (typeof tNodeOrTagName === 'string') { // IF we don't have a `TNode`, then we are an element in ICU (as ICU content does // not have TNode), in which case we know that there are no directives, and hence // we use attribute setting. setElementAttribute(lView[RENDERER], lView[nodeIndex], null, tNodeOrTagName, propName, value, sanitizeFn); } else { elementPropertyInternal(tView, tNodeOrTagName, lView, propName, value, lView[RENDERER], sanitizeFn, false); } break; case 0 /* I18nUpdateOpCode.Text */: const rText = lView[nodeIndex]; rText !== null && updateTextNode(lView[RENDERER], rText, value); break; case 2 /* I18nUpdateOpCode.IcuSwitch */: applyIcuSwitchCase(tView, getTIcu(tView, nodeIndex), lView, value); break; case 3 /* I18nUpdateOpCode.IcuUpdate */: applyIcuUpdateCase(tView, getTIcu(tView, nodeIndex), bindingsStartIndex, lView); break; } } } } } else { const opCode = updateOpCodes[i + 1]; if (opCode > 0 && (opCode & 3 /* I18nUpdateOpCode.MASK_OPCODE */) === 3 /* I18nUpdateOpCode.IcuUpdate */) { // Special case for the `icuUpdateCase`. It could be that the mask did not match, but // we still need to execute `icuUpdateCase` because the case has changed recently due to // previous `icuSwitchCase` instruction. (`icuSwitchCase` and `icuUpdateCase` always come in // pairs.) const nodeIndex = opCode >>> 2 /* I18nUpdateOpCode.SHIFT_REF */; const tIcu = getTIcu(tView, nodeIndex); const currentIndex = lView[tIcu.currentCaseLViewIndex]; if (currentIndex < 0) { applyIcuUpdateCase(tView, tIcu, bindingsStartIndex, lView); } } } i += skipCodes; } } /** * Apply OpCodes associated with updating an existing ICU. * * @param tView Current `TView` * @param tIcu Current `TIcu` * @param bindingsStartIndex Location of the first `ɵɵi18nApply` * @param lView Current `LView` */ function applyIcuUpdateCase(tView, tIcu, bindingsStartIndex, lView) { ngDevMode && assertIndexInRange(lView, tIcu.currentCaseLViewIndex); let activeCaseIndex = lView[tIcu.currentCaseLViewIndex]; if (activeCaseIndex !== null) { let mask = changeMask; if (activeCaseIndex < 0) { // Clear the flag. // Negative number means that the ICU was freshly created and we need to force the update. activeCaseIndex = lView[tIcu.currentCaseLViewIndex] = ~activeCaseIndex; // -1 is same as all bits on, which simulates creation since it marks all bits dirty mask = -1; } applyUpdateOpCodes(tView, lView, tIcu.update[activeCaseIndex], bindingsStartIndex, mask); } } /** * Apply OpCodes associated with switching a case on ICU. * * This involves tearing down existing case and than building up a new case. * * @param tView Current `TView` * @param tIcu Current `TIcu` * @param lView Current `LView` * @param value Value of the case to update to. */ function applyIcuSwitchCase(tView, tIcu, lView, value) { // Rebuild a new case for this ICU const caseIndex = getCaseIndex(tIcu, value); let activeCaseIndex = getCurrentICUCaseIndex(tIcu, lView); if (activeCaseIndex !== caseIndex) { applyIcuSwitchCaseRemove(tView, tIcu, lView); lView[tIcu.currentCaseLViewIndex] = caseIndex === null ? null : ~caseIndex; if (caseIndex !== null) { // Add the nodes for the new case const anchorRNode = lView[tIcu.anchorIdx]; if (anchorRNode) { ngDevMode && assertDomNode(anchorRNode); applyMutableOpCodes(tView, tIcu.create[caseIndex], lView, anchorRNode); } } } } /** * Apply OpCodes associated with tearing ICU case. * * This involves tearing down existing case and than building up a new case. * * @param tView Current `TView` * @param tIcu Current `TIcu` * @param lView Current `LView` */ function applyIcuSwitchCaseRemove(tView, tIcu, lView) { let activeCaseIndex = getCurrentICUCaseIndex(tIcu, lView); if (activeCaseIndex !== null) { const removeCodes = tIcu.remove[activeCaseIndex]; for (let i = 0; i < removeCodes.length; i++) { const nodeOrIcuIndex = removeCodes[i]; if (nodeOrIcuIndex > 0) { // Positive numbers are `RNode`s. const rNode = getNativeByIndex(nodeOrIcuIndex, lView); rNode !== null && nativeRemoveNode(lView[RENDERER], rNode); } else { // Negative numbers are ICUs applyIcuSwitchCaseRemove(tView, getTIcu(tView, ~nodeOrIcuIndex), lView); } } } } /** * Returns the index of the current case of an ICU expression depending on the main binding value * * @param icuExpression * @param bindingValue The value of the main binding used by this ICU expression */ function getCaseIndex(icuExpression, bindingValue) { let index = icuExpression.cases.indexOf(bindingValue); if (index === -1) { switch (icuExpression.type) { case 1 /* IcuType.plural */: { const resolvedCase = getPluralCase(bindingValue, getLocaleId()); index = icuExpression.cases.indexOf(resolvedCase); if (index === -1 && resolvedCase !== 'other') { index = icuExpression.cases.indexOf('other'); } break; } case 0 /* IcuType.select */: { index = icuExpression.cases.indexOf('other'); break; } } } return index === -1 ? null : index; } function loadIcuContainerVisitor() { const _stack = []; let _index = -1; let _lView; let _removes; /** * Retrieves a set of root nodes from `TIcu.remove`. Used by `TNodeType.ICUContainer` * to determine which root belong to the ICU. * * Example of usage. * ``` * const nextRNode = icuContainerIteratorStart(tIcuContainerNode, lView); * let rNode: RNode|null; * while(rNode = nextRNode()) { * console.log(rNode); * } * ``` * * @param tIcuContainerNode Current `TIcuContainerNode` * @param lView `LView` where the `RNode`s should be looked up. */ function icuContainerIteratorStart(tIcuContainerNode, lView) { _lView = lView; while (_stack.length) _stack.pop(); ngDevMode && assertTNodeForLView(tIcuContainerNode, lView); enterIcu(tIcuContainerNode.value, lView); return icuContainerIteratorNext; } function enterIcu(tIcu, lView) { _index = 0; const currentCase = getCurrentICUCaseIndex(tIcu, lView); if (currentCase !== null) { ngDevMode && assertNumberInRange(currentCase, 0, tIcu.cases.length - 1); _removes = tIcu.remove[currentCase]; } else { _removes = EMPTY_ARRAY; } } function icuContainerIteratorNext() { if (_index < _removes.length) { const removeOpCode = _removes[_index++]; ngDevMode && assertNumber(removeOpCode, 'Expecting OpCode number'); if (removeOpCode > 0) { const rNode = _lView[removeOpCode]; ngDevMode && assertDomNode(rNode); return rNode; } else { _stack.push(_index, _removes); // ICUs are represented by negative indices const tIcuIndex = ~removeOpCode; const tIcu = _lView[TVIEW].data[tIcuIndex]; ngDevMode && assertTIcu(tIcu); enterIcu(tIcu, _lView); return icuContainerIteratorNext(); } } else { if (_stack.length === 0) { return null; } else { _removes = _stack.pop(); _index = _stack.pop(); return icuContainerIteratorNext(); } } } return icuContainerIteratorStart; } /** * Converts `I18nCreateOpCodes` array into a human readable format. * * This function is attached to the `I18nCreateOpCodes.debug` property if `ngDevMode` is enabled. * This function provides a human readable view of the opcodes. This is useful when debugging the * application as well as writing more readable tests. * * @param this `I18nCreateOpCodes` if attached as a method. * @param opcodes `I18nCreateOpCodes` if invoked as a function. */ function i18nCreateOpCodesToString(opcodes) { const createOpCodes = opcodes || (Array.isArray(this) ? this : []); let lines = []; for (let i = 0; i < createOpCodes.length; i++) { const opCode = createOpCodes[i++]; const text = createOpCodes[i]; const isComment = (opCode & I18nCreateOpCode.COMMENT) === I18nCreateOpCode.COMMENT; const appendNow = (opCode & I18nCreateOpCode.APPEND_EAGERLY) === I18nCreateOpCode.APPEND_EAGERLY; const index = opCode >>> I18nCreateOpCode.SHIFT; lines.push(`lView[${index}] = document.${isComment ? 'createComment' : 'createText'}(${JSON.stringify(text)});`); if (appendNow) { lines.push(`parent.appendChild(lView[${index}]);`); } } return lines; } /** * Converts `I18nUpdateOpCodes` array into a human readable format. * * This function is attached to the `I18nUpdateOpCodes.debug` property if `ngDevMode` is enabled. * This function provides a human readable view of the opcodes. This is useful when debugging the * application as well as writing more readable tests. * * @param this `I18nUpdateOpCodes` if attached as a method. * @param opcodes `I18nUpdateOpCodes` if invoked as a function. */ function i18nUpdateOpCodesToString(opcodes) { const parser = new OpCodeParser(opcodes || (Array.isArray(this) ? this : [])); let lines = []; function consumeOpCode(value) { const ref = value >>> 2 /* I18nUpdateOpCode.SHIFT_REF */; const opCode = value & 3 /* I18nUpdateOpCode.MASK_OPCODE */; switch (opCode) { case 0 /* I18nUpdateOpCode.Text */: return `(lView[${ref}] as Text).textContent = $$$`; case 1 /* I18nUpdateOpCode.Attr */: const attrName = parser.consumeString(); const sanitizationFn = parser.consumeFunction(); const value = sanitizationFn ? `(${sanitizationFn})($$$)` : '$$$'; return `(lView[${ref}] as Element).setAttribute('${attrName}', ${value})`; case 2 /* I18nUpdateOpCode.IcuSwitch */: return `icuSwitchCase(${ref}, $$$)`; case 3 /* I18nUpdateOpCode.IcuUpdate */: return `icuUpdateCase(${ref})`; } throw new Error('unexpected OpCode'); } while (parser.hasMore()) { let mask = parser.consumeNumber(); let size = parser.consumeNumber(); const end = parser.i + size; const statements = []; let statement = ''; while (parser.i < end) { let value = parser.consumeNumberOrString(); if (typeof value === 'string') { statement += value; } else if (value < 0) { // Negative numbers are ref indexes // Here `i` refers to current binding index. It is to signify that the value is relative, // rather than absolute. statement += '${lView[i' + value + ']}'; } else { // Positive numbers are operations. const opCodeText = consumeOpCode(value); statements.push(opCodeText.replace('$$$', '`' + statement + '`') + ';'); statement = ''; } } lines.push(`if (mask & 0b${mask.toString(2)}) { ${statements.join(' ')} }`); } return lines; } /** * Converts `I18nCreateOpCodes` array into a human readable format. * * This function is attached to the `I18nCreateOpCodes.debug` if `ngDevMode` is enabled. This * function provides a human readable view of the opcodes. This is useful when debugging the * application as well as writing more readable tests. * * @param this `I18nCreateOpCodes` if attached as a method. * @param opcodes `I18nCreateOpCodes` if invoked as a function. */ function icuCreateOpCodesToString(opcodes) { const parser = new OpCodeParser(opcodes || (Array.isArray(this) ? this : [])); let lines = []; function consumeOpCode(opCode) { const parent = getParentFromIcuCreateOpCode(opCode); const ref = getRefFromIcuCreateOpCode(opCode); switch (getInstructionFromIcuCreateOpCode(opCode)) { case 0 /* IcuCreateOpCode.AppendChild */: return `(lView[${parent}] as Element).appendChild(lView[${lastRef}])`; case 1 /* IcuCreateOpCode.Attr */: return `(lView[${ref}] as Element).setAttribute("${parser.consumeString()}", "${parser.consumeString()}")`; } throw new Error('Unexpected OpCode: ' + getInstructionFromIcuCreateOpCode(opCode)); } let lastRef = -1; while (parser.hasMore()) { let value = parser.consumeNumberStringOrMarker(); if (value === ICU_MARKER) { const text = parser.consumeString(); lastRef = parser.consumeNumber(); lines.push(`lView[${lastRef}] = document.createComment("${text}")`); } else if (value === ELEMENT_MARKER) { const text = parser.consumeString(); lastRef = parser.consumeNumber(); lines.push(`lView[${lastRef}] = document.createElement("${text}")`); } else if (typeof value === 'string') { lastRef = parser.consumeNumber(); lines.push(`lView[${lastRef}] = document.createTextNode("${value}")`); } else if (typeof value === 'number') { const line = consumeOpCode(value); line && lines.push(line); } else { throw new Error('Unexpected value'); } } return lines; } /** * Converts `I18nRemoveOpCodes` array into a human readable format. * * This function is attached to the `I18nRemoveOpCodes.debug` if `ngDevMode` is enabled. This * function provides a human readable view of the opcodes. This is useful when debugging the * application as well as writing more readable tests. * * @param this `I18nRemoveOpCodes` if attached as a method. * @param opcodes `I18nRemoveOpCodes` if invoked as a function. */ function i18nRemoveOpCodesToString(opcodes) { const removeCodes = opcodes || (Array.isArray(this) ? this : []); let lines = []; for (let i = 0; i < removeCodes.length; i++) { const nodeOrIcuIndex = removeCodes[i]; if (nodeOrIcuIndex > 0) { // Positive numbers are `RNode`s. lines.push(`remove(lView[${nodeOrIcuIndex}])`); } else { // Negative numbers are ICUs lines.push(`removeNestedICU(${~nodeOrIcuIndex})`); } } return lines; } class OpCodeParser { constructor(codes) { this.i = 0; this.codes = codes; } hasMore() { return this.i < this.codes.length; } consumeNumber() { let value = this.codes[this.i++]; assertNumber(value, 'expecting number in OpCode'); return value; } consumeString() { let value = this.codes[this.i++]; assertString(value, 'expecting string in OpCode'); return value; } consumeFunction() { let value = this.codes[this.i++]; if (value === null || typeof value === 'function') { return value; } throw new Error('expecting function in OpCode'); } consumeNumberOrString() { let value = this.codes[this.i++]; if (typeof value === 'string') { return value; } assertNumber(value, 'expecting number or string in OpCode'); return value; } consumeNumberStringOrMarker() { let value = this.codes[this.i++]; if (typeof value === 'string' || typeof value === 'number' || value == ICU_MARKER || value == ELEMENT_MARKER) { return value; } assertNumber(value, 'expecting number, string, ICU_MARKER or ELEMENT_MARKER in OpCode'); return value; } } const BINDING_REGEXP = /�(\d+):?\d*�/gi; const ICU_REGEXP = /({\s*�\d+:?\d*�\s*,\s*\S{6}\s*,[\s\S]*})/gi; const NESTED_ICU = /�(\d+)�/; const ICU_BLOCK_REGEXP = /^\s*(�\d+:?\d*�)\s*,\s*(select|plural)\s*,/; const MARKER = `�`; const SUBTEMPLATE_REGEXP = /�\/?\*(\d+:\d+)�/gi; const PH_REGEXP = /�(\/?[#*]\d+):?\d*�/gi; /** * Angular Dart introduced &ngsp; as a placeholder for non-removable space, see: * https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart#L25-L32 * In Angular Dart &ngsp; is converted to the 0xE500 PUA (Private Use Areas) unicode character * and later on replaced by a space. We are re-implementing the same idea here, since translations * might contain this special character. */ const NGSP_UNICODE_REGEXP = /\uE500/g; function replaceNgsp(value) { return value.replace(NGSP_UNICODE_REGEXP, ' '); } /** * Patch a `debug` property getter on top of the existing object. * * NOTE: always call this method with `ngDevMode && attachDebugObject(...)` * * @param obj Object to patch * @param debugGetter Getter returning a value to patch */ function attachDebugGetter(obj, debugGetter) { if (ngDevMode) { Object.defineProperty(obj, 'debug', { get: debugGetter, enumerable: false }); } else { throw new Error('This method should be guarded with `ngDevMode` so that it can be tree shaken in production!'); } } /** * Create dynamic nodes from i18n translation block. * * - Text nodes are created synchronously * - TNodes are linked into tree lazily * * @param tView Current `TView` * @parentTNodeIndex index to the parent TNode of this i18n block * @param lView Current `LView` * @param index Index of `ɵɵi18nStart` instruction. * @param message Message to translate. * @param subTemplateIndex Index into the sub template of message translation. (ie in case of * `ngIf`) (-1 otherwise) */ function i18nStartFirstCreatePass(tView, parentTNodeIndex, lView, index, message, subTemplateIndex) { const rootTNode = getCurrentParentTNode(); const createOpCodes = []; const updateOpCodes = []; const existingTNodeStack = [[]]; if (ngDevMode) { attachDebugGetter(createOpCodes, i18nCreateOpCodesToString); attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString); } message = getTranslationForTemplate(message, subTemplateIndex); const msgParts = replaceNgsp(message).split(PH_REGEXP); for (let i = 0; i < msgParts.length; i++) { let value = msgParts[i]; if ((i & 1) === 0) { // Even indexes are text (including bindings & ICU expressions) const parts = i18nParseTextIntoPartsAndICU(value); for (let j = 0; j < parts.length; j++) { let part = parts[j]; if ((j & 1) === 0) { // `j` is odd therefore `part` is string const text = part; ngDevMode && assertString(text, 'Parsed ICU part should be string'); if (text !== '') { i18nStartFirstCreatePassProcessTextNode(tView, rootTNode, existingTNodeStack[0], createOpCodes, updateOpCodes, lView, text); } } else { // `j` is Even therefor `part` is an `ICUExpression` const icuExpression = part; // Verify that ICU expression has the right shape. Translations might contain invalid // constructions (while original messages were correct), so ICU parsing at runtime may // not succeed (thus `icuExpression` remains a string). // Note: we intentionally retain the error here by not using `ngDevMode`, because // the value can change based on the locale and users aren't guaranteed to hit // an invalid string while they're developing. if (typeof icuExpression !== 'object') { throw new Error(`Unable to parse ICU expression in "${message}" message.`); } const icuContainerTNode = createTNodeAndAddOpCode(tView, rootTNode, existingTNodeStack[0], lView, createOpCodes, ngDevMode ? `ICU ${index}:${icuExpression.mainBinding}` : '', true); const icuNodeIndex = icuContainerTNode.index; ngDevMode && assertGreaterThanOrEqual(icuNodeIndex, HEADER_OFFSET, 'Index must be in absolute LView offset'); icuStart(tView, lView, updateOpCodes, parentTNodeIndex, icuExpression, icuNodeIndex); } } } else { // Odd indexes are placeholders (elements and sub-templates) // At this point value is something like: '/#1:2' (originally coming from '�/#1:2�') const isClosing = value.charCodeAt(0) === 47 /* CharCode.SLASH */; const type = value.charCodeAt(isClosing ? 1 : 0); ngDevMode && assertOneOf(type, 42 /* CharCode.STAR */, 35 /* CharCode.HASH */); const index = HEADER_OFFSET + Number.parseInt(value.substring(isClosing ? 2 : 1)); if (isClosing) { existingTNodeStack.shift(); setCurrentTNode(getCurrentParentTNode(), false); } else { const tNode = createTNodePlaceholder(tView, existingTNodeStack[0], index); existingTNodeStack.unshift([]); setCurrentTNode(tNode, true); } } } tView.data[index] = { create: createOpCodes, update: updateOpCodes }; } /** * Allocate space in i18n Range add create OpCode instruction to create a text or comment node. * * @param tView Current `TView` needed to allocate space in i18n range. * @param rootTNode Root `TNode` of the i18n block. This node determines if the new TNode will be * added as part of the `i18nStart` instruction or as part of the `TNode.insertBeforeIndex`. * @param existingTNodes internal state for `addTNodeAndUpdateInsertBeforeIndex`. * @param lView Current `LView` needed to allocate space in i18n range. * @param createOpCodes Array storing `I18nCreateOpCodes` where new opCodes will be added. * @param text Text to be added when the `Text` or `Comment` node will be created. * @param isICU true if a `Comment` node for ICU (instead of `Text`) node should be created. */ function createTNodeAndAddOpCode(tView, rootTNode, existingTNodes, lView, createOpCodes, text, isICU) { const i18nNodeIdx = allocExpando(tView, lView, 1, null); let opCode = i18nNodeIdx << I18nCreateOpCode.SHIFT; let parentTNode = getCurrentParentTNode(); if (rootTNode === parentTNode) { // FIXME(misko): A null `parentTNode` should represent when we fall of the `LView` boundary. // (there is no parent), but in some circumstances (because we are inconsistent about how we set // `previousOrParentTNode`) it could point to `rootTNode` So this is a work around. parentTNode = null; } if (parentTNode === null) { // If we don't have a parent that means that we can eagerly add nodes. // If we have a parent than these nodes can't be added now (as the parent has not been created // yet) and instead the `parentTNode` is responsible for adding it. See // `TNode.insertBeforeIndex` opCode |= I18nCreateOpCode.APPEND_EAGERLY; } if (isICU) { opCode |= I18nCreateOpCode.COMMENT; ensureIcuContainerVisitorLoaded(loadIcuContainerVisitor); } createOpCodes.push(opCode, text === null ? '' : text); // We store `{{?}}` so that when looking at debug `TNodeType.template` we can see where the // bindings are. const tNode = createTNodeAtIndex(tView, i18nNodeIdx, isICU ? 32 /* TNodeType.Icu */ : 1 /* TNodeType.Text */, text === null ? ngDevMode ? '{{?}}' : '' : text, null); addTNodeAndUpdateInsertBeforeIndex(existingTNodes, tNode); const tNodeIdx = tNode.index; setCurrentTNode(tNode, false /* Text nodes are self closing */); if (parentTNode !== null && rootTNode !== parentTNode) { // We are a child of deeper node (rather than a direct child of `i18nStart` instruction.) // We have to make sure to add ourselves to the parent. setTNodeInsertBeforeIndex(parentTNode, tNodeIdx); } return tNode; } /** * Processes text node in i18n block. * * Text nodes can have: * - Create instruction in `createOpCodes` for creating the text node. * - Allocate spec for text node in i18n range of `LView` * - If contains binding: * - bindings => allocate space in i18n range of `LView` to store the binding value. * - populate `updateOpCodes` with update instructions. * * @param tView Current `TView` * @param rootTNode Root `TNode` of the i18n block. This node determines if the new TNode will * be added as part of the `i18nStart` instruction or as part of the * `TNode.insertBeforeIndex`. * @param existingTNodes internal state for `addTNodeAndUpdateInsertBeforeIndex`. * @param createOpCodes Location where the creation OpCodes will be stored. * @param lView Current `LView` * @param text The translated text (which may contain binding) */ function i18nStartFirstCreatePassProcessTextNode(tView, rootTNode, existingTNodes, createOpCodes, updateOpCodes, lView, text) { const hasBinding = text.match(BINDING_REGEXP); const tNode = createTNodeAndAddOpCode(tView, rootTNode, existingTNodes, lView, createOpCodes, hasBinding ? null : text, false); if (hasBinding) { generateBindingUpdateOpCodes(updateOpCodes, text, tNode.index, null, 0, null); } } /** * See `i18nAttributes` above. */ function i18nAttributesFirstPass(tView, index, values) { const previousElement = getCurrentTNode(); const previousElementIndex = previousElement.index; const updateOpCodes = []; if (ngDevMode) { attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString); } if (tView.firstCreatePass && tView.data[index] === null) { for (let i = 0; i < values.length; i += 2) { const attrName = values[i]; const message = values[i + 1]; if (message !== '') { // Check if attribute value contains an ICU and throw an error if that's the case. // ICUs in element attributes are not supported. // Note: we intentionally retain the error here by not using `ngDevMode`, because // the `value` can change based on the locale and users aren't guaranteed to hit // an invalid string while they're developing. if (ICU_REGEXP.test(message)) { throw new Error(`ICU expressions are not supported in attributes. Message: "${message}".`); } // i18n attributes that hit this code path are guaranteed to have bindings, because // the compiler treats static i18n attributes as regular attribute bindings. // Since this may not be the first i18n attribute on this element we need to pass in how // many previous bindings there have already been. generateBindingUpdateOpCodes(updateOpCodes, message, previousElementIndex, attrName, countBindings(updateOpCodes), null); } } tView.data[index] = updateOpCodes; } } /** * Generate the OpCodes to update the bindings of a string. * * @param updateOpCodes Place where the update opcodes will be stored. * @param str The string containing the bindings. * @param destinationNode Index of the destination node which will receive the binding. * @param attrName Name of the attribute, if the string belongs to an attribute. * @param sanitizeFn Sanitization function used to sanitize the string after update, if necessary. * @param bindingStart The lView index of the next expression that can be bound via an opCode. * @returns The mask value for these bindings */ function generateBindingUpdateOpCodes(updateOpCodes, str, destinationNode, attrName, bindingStart, sanitizeFn) { ngDevMode && assertGreaterThanOrEqual(destinationNode, HEADER_OFFSET, 'Index must be in absolute LView offset'); const maskIndex = updateOpCodes.length; // Location of mask const sizeIndex = maskIndex + 1; // location of size for skipping updateOpCodes.push(null, null); // Alloc space for mask and size const startIndex = maskIndex + 2; // location of first allocation. if (ngDevMode) { attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString); } const textParts = str.split(BINDING_REGEXP); let mask = 0; for (let j = 0; j < textParts.length; j++) { const textValue = textParts[j]; if (j & 1) { // Odd indexes are bindings const bindingIndex = bindingStart + parseInt(textValue, 10); updateOpCodes.push(-1 - bindingIndex); mask = mask | toMaskBit(bindingIndex); } else if (textValue !== '') { // Even indexes are text updateOpCodes.push(textValue); } } updateOpCodes.push(destinationNode << 2 /* I18nUpdateOpCode.SHIFT_REF */ | (attrName ? 1 /* I18nUpdateOpCode.Attr */ : 0 /* I18nUpdateOpCode.Text */)); if (attrName) { updateOpCodes.push(attrName, sanitizeFn); } updateOpCodes[maskIndex] = mask; updateOpCodes[sizeIndex] = updateOpCodes.length - startIndex; return mask; } /** * Count the number of bindings in the given `opCodes`. * * It could be possible to speed this up, by passing the number of bindings found back from * `generateBindingUpdateOpCodes()` to `i18nAttributesFirstPass()` but this would then require more * complexity in the code and/or transient objects to be created. * * Since this function is only called once when the template is instantiated, is trivial in the * first instance (since `opCodes` will be an empty array), and it is not common for elements to * contain multiple i18n bound attributes, it seems like this is a reasonable compromise. */ function countBindings(opCodes) { let count = 0; for (let i = 0; i < opCodes.length; i++) { const opCode = opCodes[i]; // Bindings are negative numbers. if (typeof opCode === 'number' && opCode < 0) { count++; } } return count; } /** * Convert binding index to mask bit. * * Each index represents a single bit on the bit-mask. Because bit-mask only has 32 bits, we make * the 32nd bit share all masks for all bindings higher than 32. Since it is extremely rare to * have more than 32 bindings this will be hit very rarely. The downside of hitting this corner * case is that we will execute binding code more often than necessary. (penalty of performance) */ function toMaskBit(bindingIndex) { return 1 << Math.min(bindingIndex, 31); } function isRootTemplateMessage(subTemplateIndex) { return subTemplateIndex === -1; } /** * Removes everything inside the sub-templates of a message. */ function removeInnerTemplateTranslation(message) { let match; let res = ''; let index = 0; let inTemplate = false; let tagMatched; while ((match = SUBTEMPLATE_REGEXP.exec(message)) !== null) { if (!inTemplate) { res += message.substring(index, match.index + match[0].length); tagMatched = match[1]; inTemplate = true; } else { if (match[0] === `${MARKER}/*${tagMatched}${MARKER}`) { index = match.index; inTemplate = false; } } } ngDevMode && assertEqual(inTemplate, false, `Tag mismatch: unable to find the end of the sub-template in the translation "${message}"`); res += message.slice(index); return res; } /** * Extracts a part of a message and removes the rest. * * This method is used for extracting a part of the message associated with a template. A * translated message can span multiple templates. * * Example: * ``` * <div i18n>Translate <span *ngIf>me</span>!</div> * ``` * * @param message The message to crop * @param subTemplateIndex Index of the sub-template to extract. If undefined it returns the * external template and removes all sub-templates. */ function getTranslationForTemplate(message, subTemplateIndex) { if (isRootTemplateMessage(subTemplateIndex)) { // We want the root template message, ignore all sub-templates return removeInnerTemplateTranslation(message); } else { // We want a specific sub-template const start = message.indexOf(`:${subTemplateIndex}${MARKER}`) + 2 + subTemplateIndex.toString().length; const end = message.search(new RegExp(`${MARKER}\\/\\*\\d+:${subTemplateIndex}${MARKER}`)); return removeInnerTemplateTranslation(message.substring(start, end)); } } /** * Generate the OpCodes for ICU expressions. * * @param icuExpression * @param index Index where the anchor is stored and an optional `TIcuContainerNode` * - `lView[anchorIdx]` points to a `Comment` node representing the anchor for the ICU. * - `tView.data[anchorIdx]` points to the `TIcuContainerNode` if ICU is root (`null` otherwise) */ function icuStart(tView, lView, updateOpCodes, parentIdx, icuExpression, anchorIdx) { ngDevMode && assertDefined(icuExpression, 'ICU expression must be defined'); let bindingMask = 0; const tIcu = { type: icuExpression.type, currentCaseLViewIndex: allocExpando(tView, lView, 1, null), anchorIdx, cases: [], create: [], remove: [], update: [] }; addUpdateIcuSwitch(updateOpCodes, icuExpression, anchorIdx); setTIcu(tView, anchorIdx, tIcu); const values = icuExpression.values; for (let i = 0; i < values.length; i++) { // Each value is an array of strings & other ICU expressions const valueArr = values[i]; const nestedIcus = []; for (let j = 0; j < valueArr.length; j++) { const value = valueArr[j]; if (typeof value !== 'string') { // It is an nested ICU expression const icuIndex = nestedIcus.push(value) - 1; // Replace nested ICU expression by a comment node valueArr[j] = `<!--�${icuIndex}�-->`; } } bindingMask = parseIcuCase(tView, tIcu, lView, updateOpCodes, parentIdx, icuExpression.cases[i], valueArr.join(''), nestedIcus) | bindingMask; } if (bindingMask) { addUpdateIcuUpdate(updateOpCodes, bindingMask, anchorIdx); } } /** * Parses text containing an ICU expression and produces a JSON object for it. * Original code from closure library, modified for Angular. * * @param pattern Text containing an ICU expression that needs to be parsed. * */ function parseICUBlock(pattern) { const cases = []; const values = []; let icuType = 1 /* IcuType.plural */; let mainBinding = 0; pattern = pattern.replace(ICU_BLOCK_REGEXP, function (str, binding, type) { if (type === 'select') { icuType = 0 /* IcuType.select */; } else { icuType = 1 /* IcuType.plural */; } mainBinding = parseInt(binding.slice(1), 10); return ''; }); const parts = i18nParseTextIntoPartsAndICU(pattern); // Looking for (key block)+ sequence. One of the keys has to be "other". for (let pos = 0; pos < parts.length;) { let key = parts[pos++].trim(); if (icuType === 1 /* IcuType.plural */) { // Key can be "=x", we just want "x" key = key.replace(/\s*(?:=)?(\w+)\s*/, '$1'); } if (key.length) { cases.push(key); } const blocks = i18nParseTextIntoPartsAndICU(parts[pos++]); if (cases.length > values.length) { values.push(blocks); } } // TODO(ocombe): support ICU expressions in attributes, see #21615 return { type: icuType, mainBinding: mainBinding, cases, values }; } /** * Breaks pattern into strings and top level {...} blocks. * Can be used to break a message into text and ICU expressions, or to break an ICU expression * into keys and cases. Original code from closure library, modified for Angular. * * @param pattern (sub)Pattern to be broken. * @returns An `Array<string|IcuExpression>` where: * - odd positions: `string` => text between ICU expressions * - even positions: `ICUExpression` => ICU expression parsed into `ICUExpression` record. */ function i18nParseTextIntoPartsAndICU(pattern) { if (!pattern) { return []; } let prevPos = 0; const braceStack = []; const results = []; const braces = /[{}]/g; // lastIndex doesn't get set to 0 so we have to. braces.lastIndex = 0; let match; while (match = braces.exec(pattern)) { const pos = match.index; if (match[0] == '}') { braceStack.pop(); if (braceStack.length == 0) { // End of the block. const block = pattern.substring(prevPos, pos); if (ICU_BLOCK_REGEXP.test(block)) { results.push(parseICUBlock(block)); } else { results.push(block); } prevPos = pos + 1; } } else { if (braceStack.length == 0) { const substring = pattern.substring(prevPos, pos); results.push(substring); prevPos = pos + 1; } braceStack.push('{'); } } const substring = pattern.substring(prevPos); results.push(substring); return results; } /** * Parses a node, its children and its siblings, and generates the mutate & update OpCodes. * */ function parseIcuCase(tView, tIcu, lView, updateOpCodes, parentIdx, caseName, unsafeCaseHtml, nestedIcus) { const create = []; const remove = []; const update = []; if (ngDevMode) { attachDebugGetter(create, icuCreateOpCodesToString); attachDebugGetter(remove, i18nRemoveOpCodesToString); attachDebugGetter(update, i18nUpdateOpCodesToString); } tIcu.cases.push(caseName); tIcu.create.push(create); tIcu.remove.push(remove); tIcu.update.push(update); const inertBodyHelper = getInertBodyHelper(getDocument()); const inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeCaseHtml); ngDevMode && assertDefined(inertBodyElement, 'Unable to generate inert body element'); const inertRootNode = getTemplateContent(inertBodyElement) || inertBodyElement; if (inertRootNode) { return walkIcuTree(tView, tIcu, lView, updateOpCodes, create, remove, update, inertRootNode, parentIdx, nestedIcus, 0); } else { return 0; } } function walkIcuTree(tView, tIcu, lView, sharedUpdateOpCodes, create, remove, update, parentNode, parentIdx, nestedIcus, depth) { let bindingMask = 0; let currentNode = parentNode.firstChild; while (currentNode) { const newIndex = allocExpando(tView, lView, 1, null); switch (currentNode.nodeType) { case Node.ELEMENT_NODE: const element = currentNode; const tagName = element.tagName.toLowerCase(); if (VALID_ELEMENTS.hasOwnProperty(tagName)) { addCreateNodeAndAppend(create, ELEMENT_MARKER, tagName, parentIdx, newIndex); tView.data[newIndex] = tagName; const elAttrs = element.attributes; for (let i = 0; i < elAttrs.length; i++) { const attr = elAttrs.item(i); const lowerAttrName = attr.name.toLowerCase(); const hasBinding = !!attr.value.match(BINDING_REGEXP); // we assume the input string is safe, unless it's using a binding if (hasBinding) { if (VALID_ATTRS.hasOwnProperty(lowerAttrName)) { if (URI_ATTRS[lowerAttrName]) { generateBindingUpdateOpCodes(update, attr.value, newIndex, attr.name, 0, _sanitizeUrl); } else { generateBindingUpdateOpCodes(update, attr.value, newIndex, attr.name, 0, null); } } else { ngDevMode && console.warn(`WARNING: ignoring unsafe attribute value ` + `${lowerAttrName} on element ${tagName} ` + `(see ${XSS_SECURITY_URL})`); } } else { addCreateAttribute(create, newIndex, attr); } } // Parse the children of this node (if any) bindingMask = walkIcuTree(tView, tIcu, lView, sharedUpdateOpCodes, create, remove, update, currentNode, newIndex, nestedIcus, depth + 1) | bindingMask; addRemoveNode(remove, newIndex, depth); } break; case Node.TEXT_NODE: const value = currentNode.textContent || ''; const hasBinding = value.match(BINDING_REGEXP); addCreateNodeAndAppend(create, null, hasBinding ? '' : value, parentIdx, newIndex); addRemoveNode(remove, newIndex, depth); if (hasBinding) { bindingMask = generateBindingUpdateOpCodes(update, value, newIndex, null, 0, null) | bindingMask; } break; case Node.COMMENT_NODE: // Check if the comment node is a placeholder for a nested ICU const isNestedIcu = NESTED_ICU.exec(currentNode.textContent || ''); if (isNestedIcu) { const nestedIcuIndex = parseInt(isNestedIcu[1], 10); const icuExpression = nestedIcus[nestedIcuIndex]; // Create the comment node that will anchor the ICU expression addCreateNodeAndAppend(create, ICU_MARKER, ngDevMode ? `nested ICU ${nestedIcuIndex}` : '', parentIdx, newIndex); icuStart(tView, lView, sharedUpdateOpCodes, parentIdx, icuExpression, newIndex); addRemoveNestedIcu(remove, newIndex, depth); } break; } currentNode = currentNode.nextSibling; } return bindingMask; } function addRemoveNode(remove, index, depth) { if (depth === 0) { remove.push(index); } } function addRemoveNestedIcu(remove, index, depth) { if (depth === 0) { remove.push(~index); // remove ICU at `index` remove.push(index); // remove ICU comment at `index` } } function addUpdateIcuSwitch(update, icuExpression, index) { update.push(toMaskBit(icuExpression.mainBinding), 2, -1 - icuExpression.mainBinding, index << 2 /* I18nUpdateOpCode.SHIFT_REF */ | 2 /* I18nUpdateOpCode.IcuSwitch */); } function addUpdateIcuUpdate(update, bindingMask, index) { update.push(bindingMask, 1, index << 2 /* I18nUpdateOpCode.SHIFT_REF */ | 3 /* I18nUpdateOpCode.IcuUpdate */); } function addCreateNodeAndAppend(create, marker, text, appendToParentIdx, createAtIdx) { if (marker !== null) { create.push(marker); } create.push(text, createAtIdx, icuCreateOpCode(0 /* IcuCreateOpCode.AppendChild */, appendToParentIdx, createAtIdx)); } function addCreateAttribute(create, newIndex, attr) { create.push(newIndex << 1 /* IcuCreateOpCode.SHIFT_REF */ | 1 /* IcuCreateOpCode.Attr */, attr.name, attr.value); } // i18nPostprocess consts const ROOT_TEMPLATE_ID = 0; const PP_MULTI_VALUE_PLACEHOLDERS_REGEXP = /\[(�.+?�?)\]/; const PP_PLACEHOLDERS_REGEXP = /\[(�.+?�?)\]|(�\/?\*\d+:\d+�)/g; const PP_ICU_VARS_REGEXP = /({\s*)(VAR_(PLURAL|SELECT)(_\d+)?)(\s*,)/g; const PP_ICU_PLACEHOLDERS_REGEXP = /{([A-Z0-9_]+)}/g; const PP_ICUS_REGEXP = /�I18N_EXP_(ICU(_\d+)?)�/g; const PP_CLOSE_TEMPLATE_REGEXP = /\/\*/; const PP_TEMPLATE_ID_REGEXP = /\d+\:(\d+)/; /** * Handles message string post-processing for internationalization. * * Handles message string post-processing by transforming it from intermediate * format (that might contain some markers that we need to replace) to the final * form, consumable by i18nStart instruction. Post processing steps include: * * 1. Resolve all multi-value cases (like [�*1:1��#2:1�|�#4:1�|�5�]) * 2. Replace all ICU vars (like "VAR_PLURAL") * 3. Replace all placeholders used inside ICUs in a form of {PLACEHOLDER} * 4. Replace all ICU references with corresponding values (like �ICU_EXP_ICU_1�) * in case multiple ICUs have the same placeholder name * * @param message Raw translation string for post processing * @param replacements Set of replacements that should be applied * * @returns Transformed string that can be consumed by i18nStart instruction * * @codeGenApi */ function i18nPostprocess(message, replacements = {}) { /** * Step 1: resolve all multi-value placeholders like [�#5�|�*1:1��#2:1�|�#4:1�] * * Note: due to the way we process nested templates (BFS), multi-value placeholders are typically * grouped by templates, for example: [�#5�|�#6�|�#1:1�|�#3:2�] where �#5� and �#6� belong to root * template, �#1:1� belong to nested template with index 1 and �#1:2� - nested template with index * 3. However in real templates the order might be different: i.e. �#1:1� and/or �#3:2� may go in * front of �#6�. The post processing step restores the right order by keeping track of the * template id stack and looks for placeholders that belong to the currently active template. */ let result = message; if (PP_MULTI_VALUE_PLACEHOLDERS_REGEXP.test(message)) { const matches = {}; const templateIdsStack = [ROOT_TEMPLATE_ID]; result = result.replace(PP_PLACEHOLDERS_REGEXP, (m, phs, tmpl) => { const content = phs || tmpl; const placeholders = matches[content] || []; if (!placeholders.length) { content.split('|').forEach(placeholder => { const match = placeholder.match(PP_TEMPLATE_ID_REGEXP); const templateId = match ? parseInt(match[1], 10) : ROOT_TEMPLATE_ID; const isCloseTemplateTag = PP_CLOSE_TEMPLATE_REGEXP.test(placeholder); placeholders.push([templateId, isCloseTemplateTag, placeholder]); }); matches[content] = placeholders; } if (!placeholders.length) { throw new Error(`i18n postprocess: unmatched placeholder - ${content}`); } const currentTemplateId = templateIdsStack[templateIdsStack.length - 1]; let idx = 0; // find placeholder index that matches current template id for (let i = 0; i < placeholders.length; i++) { if (placeholders[i][0] === currentTemplateId) { idx = i; break; } } // update template id stack based on the current tag extracted const [templateId, isCloseTemplateTag, placeholder] = placeholders[idx]; if (isCloseTemplateTag) { templateIdsStack.pop(); } else if (currentTemplateId !== templateId) { templateIdsStack.push(templateId); } // remove processed tag from the list placeholders.splice(idx, 1); return placeholder; }); } // return current result if no replacements specified if (!Object.keys(replacements).length) { return result; } /** * Step 2: replace all ICU vars (like "VAR_PLURAL") */ result = result.replace(PP_ICU_VARS_REGEXP, (match, start, key, _type, _idx, end) => { return replacements.hasOwnProperty(key) ? `${start}${replacements[key]}${end}` : match; }); /** * Step 3: replace all placeholders used inside ICUs in a form of {PLACEHOLDER} */ result = result.replace(PP_ICU_PLACEHOLDERS_REGEXP, (match, key) => { return replacements.hasOwnProperty(key) ? replacements[key] : match; }); /** * Step 4: replace all ICU references with corresponding values (like �ICU_EXP_ICU_1�) in case * multiple ICUs have the same placeholder name */ result = result.replace(PP_ICUS_REGEXP, (match, key) => { if (replacements.hasOwnProperty(key)) { const list = replacements[key]; if (!list.length) { throw new Error(`i18n postprocess: unmatched ICU - ${match} with key: ${key}`); } return list.shift(); } return match; }); return result; } /** * Marks a block of text as translatable. * * The instructions `i18nStart` and `i18nEnd` mark the translation block in the template. * The translation `message` is the value which is locale specific. The translation string may * contain placeholders which associate inner elements and sub-templates within the translation. * * The translation `message` placeholders are: * - `�{index}(:{block})�`: *Binding Placeholder*: Marks a location where an expression will be * interpolated into. The placeholder `index` points to the expression binding index. An optional * `block` that matches the sub-template in which it was declared. * - `�#{index}(:{block})�`/`�/#{index}(:{block})�`: *Element Placeholder*: Marks the beginning * and end of DOM element that were embedded in the original translation block. The placeholder * `index` points to the element index in the template instructions set. An optional `block` that * matches the sub-template in which it was declared. * - `�*{index}:{block}�`/`�/*{index}:{block}�`: *Sub-template Placeholder*: Sub-templates must be * split up and translated separately in each angular template function. The `index` points to the * `template` instruction index. A `block` that matches the sub-template in which it was declared. * * @param index A unique index of the translation in the static block. * @param messageIndex An index of the translation message from the `def.consts` array. * @param subTemplateIndex Optional sub-template index in the `message`. * * @codeGenApi */ function ɵɵi18nStart(index, messageIndex, subTemplateIndex = -1) { const tView = getTView(); const lView = getLView(); const adjustedIndex = HEADER_OFFSET + index; ngDevMode && assertDefined(tView, `tView should be defined`); const message = getConstant(tView.consts, messageIndex); const parentTNode = getCurrentParentTNode(); if (tView.firstCreatePass) { i18nStartFirstCreatePass(tView, parentTNode === null ? 0 : parentTNode.index, lView, adjustedIndex, message, subTemplateIndex); } const tI18n = tView.data[adjustedIndex]; const sameViewParentTNode = parentTNode === lView[T_HOST] ? null : parentTNode; const parentRNode = getClosestRElement(tView, sameViewParentTNode, lView); // If `parentTNode` is an `ElementContainer` than it has `<!--ng-container--->`. // When we do inserts we have to make sure to insert in front of `<!--ng-container--->`. const insertInFrontOf = parentTNode && parentTNode.type & 8 /* TNodeType.ElementContainer */ ? lView[parentTNode.index] : null; applyCreateOpCodes(lView, tI18n.create, parentRNode, insertInFrontOf); setInI18nBlock(true); } /** * Translates a translation block marked by `i18nStart` and `i18nEnd`. It inserts the text/ICU nodes * into the render tree, moves the placeholder nodes and removes the deleted nodes. * * @codeGenApi */ function ɵɵi18nEnd() { setInI18nBlock(false); } /** * * Use this instruction to create a translation block that doesn't contain any placeholder. * It calls both {@link i18nStart} and {@link i18nEnd} in one instruction. * * The translation `message` is the value which is locale specific. The translation string may * contain placeholders which associate inner elements and sub-templates within the translation. * * The translation `message` placeholders are: * - `�{index}(:{block})�`: *Binding Placeholder*: Marks a location where an expression will be * interpolated into. The placeholder `index` points to the expression binding index. An optional * `block` that matches the sub-template in which it was declared. * - `�#{index}(:{block})�`/`�/#{index}(:{block})�`: *Element Placeholder*: Marks the beginning * and end of DOM element that were embedded in the original translation block. The placeholder * `index` points to the element index in the template instructions set. An optional `block` that * matches the sub-template in which it was declared. * - `�*{index}:{block}�`/`�/*{index}:{block}�`: *Sub-template Placeholder*: Sub-templates must be * split up and translated separately in each angular template function. The `index` points to the * `template` instruction index. A `block` that matches the sub-template in which it was declared. * * @param index A unique index of the translation in the static block. * @param messageIndex An index of the translation message from the `def.consts` array. * @param subTemplateIndex Optional sub-template index in the `message`. * * @codeGenApi */ function ɵɵi18n(index, messageIndex, subTemplateIndex) { ɵɵi18nStart(index, messageIndex, subTemplateIndex); ɵɵi18nEnd(); } /** * Marks a list of attributes as translatable. * * @param index A unique index in the static block * @param values * * @codeGenApi */ function ɵɵi18nAttributes(index, attrsIndex) { const tView = getTView(); ngDevMode && assertDefined(tView, `tView should be defined`); const attrs = getConstant(tView.consts, attrsIndex); i18nAttributesFirstPass(tView, index + HEADER_OFFSET, attrs); } /** * Stores the values of the bindings during each update cycle in order to determine if we need to * update the translated nodes. * * @param value The binding's value * @returns This function returns itself so that it may be chained * (e.g. `i18nExp(ctx.name)(ctx.title)`) * * @codeGenApi */ function ɵɵi18nExp(value) { const lView = getLView(); setMaskBit(bindingUpdated(lView, nextBindingIndex(), value)); return ɵɵi18nExp; } /** * Updates a translation block or an i18n attribute when the bindings have changed. * * @param index Index of either {@link i18nStart} (translation block) or {@link i18nAttributes} * (i18n attribute) on which it should update the content. * * @codeGenApi */ function ɵɵi18nApply(index) { applyI18n(getTView(), getLView(), index + HEADER_OFFSET); } /** * Handles message string post-processing for internationalization. * * Handles message string post-processing by transforming it from intermediate * format (that might contain some markers that we need to replace) to the final * form, consumable by i18nStart instruction. Post processing steps include: * * 1. Resolve all multi-value cases (like [�*1:1��#2:1�|�#4:1�|�5�]) * 2. Replace all ICU vars (like "VAR_PLURAL") * 3. Replace all placeholders used inside ICUs in a form of {PLACEHOLDER} * 4. Replace all ICU references with corresponding values (like �ICU_EXP_ICU_1�) * in case multiple ICUs have the same placeholder name * * @param message Raw translation string for post processing * @param replacements Set of replacements that should be applied * * @returns Transformed string that can be consumed by i18nStart instruction * * @codeGenApi */ function ɵɵi18nPostprocess(message, replacements = {}) { return i18nPostprocess(message, replacements); } /* * This file re-exports all symbols contained in this directory. * * Why is this file not `index.ts`? * * There seems to be an inconsistent path resolution of an `index.ts` file * when only the parent directory is referenced. This could be due to the * node module resolution configuration differing from rollup and/or typescript. * * With commit * https://github.com/angular/angular/commit/d5e3f2c64bd13ce83e7c70788b7fc514ca4a9918 * the `instructions.ts` file was moved to `instructions/instructions.ts` and an * `index.ts` file was used to re-export everything. Having had file names that were * importing from `instructions' directly (not the from the sub file or the `index.ts` * file) caused strange CI issues. `index.ts` had to be renamed to `all.ts` for this * to work. * * Jira Issue = FW-1184 */ /** * Resolves the providers which are defined in the DirectiveDef. * * When inserting the tokens and the factories in their respective arrays, we can assume that * this method is called first for the component (if any), and then for other directives on the same * node. * As a consequence,the providers are always processed in that order: * 1) The view providers of the component * 2) The providers of the component * 3) The providers of the other directives * This matches the structure of the injectables arrays of a view (for each node). * So the tokens and the factories can be pushed at the end of the arrays, except * in one case for multi providers. * * @param def the directive definition * @param providers: Array of `providers`. * @param viewProviders: Array of `viewProviders`. */ function providersResolver(def, providers, viewProviders) { const tView = getTView(); if (tView.firstCreatePass) { const isComponent = isComponentDef(def); // The list of view providers is processed first, and the flags are updated resolveProvider(viewProviders, tView.data, tView.blueprint, isComponent, true); // Then, the list of providers is processed, and the flags are updated resolveProvider(providers, tView.data, tView.blueprint, isComponent, false); } } /** * Resolves a provider and publishes it to the DI system. */ function resolveProvider(provider, tInjectables, lInjectablesBlueprint, isComponent, isViewProvider) { provider = resolveForwardRef(provider); if (Array.isArray(provider)) { // Recursively call `resolveProvider` // Recursion is OK in this case because this code will not be in hot-path once we implement // cloning of the initial state. for (let i = 0; i < provider.length; i++) { resolveProvider(provider[i], tInjectables, lInjectablesBlueprint, isComponent, isViewProvider); } } else { const tView = getTView(); const lView = getLView(); let token = isTypeProvider(provider) ? provider : resolveForwardRef(provider.provide); let providerFactory = providerToFactory(provider); const tNode = getCurrentTNode(); const beginIndex = tNode.providerIndexes & 1048575 /* TNodeProviderIndexes.ProvidersStartIndexMask */; const endIndex = tNode.directiveStart; const cptViewProvidersCount = tNode.providerIndexes >> 20 /* TNodeProviderIndexes.CptViewProvidersCountShift */; if (isTypeProvider(provider) || !provider.multi) { // Single provider case: the factory is created and pushed immediately const factory = new NodeInjectorFactory(providerFactory, isViewProvider, ɵɵdirectiveInject); const existingFactoryIndex = indexOf(token, tInjectables, isViewProvider ? beginIndex : beginIndex + cptViewProvidersCount, endIndex); if (existingFactoryIndex === -1) { diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, token); registerDestroyHooksIfSupported(tView, provider, tInjectables.length); tInjectables.push(token); tNode.directiveStart++; tNode.directiveEnd++; if (isViewProvider) { tNode.providerIndexes += 1048576 /* TNodeProviderIndexes.CptViewProvidersCountShifter */; } lInjectablesBlueprint.push(factory); lView.push(factory); } else { lInjectablesBlueprint[existingFactoryIndex] = factory; lView[existingFactoryIndex] = factory; } } else { // Multi provider case: // We create a multi factory which is going to aggregate all the values. // Since the output of such a factory depends on content or view injection, // we create two of them, which are linked together. // // The first one (for view providers) is always in the first block of the injectables array, // and the second one (for providers) is always in the second block. // This is important because view providers have higher priority. When a multi token // is being looked up, the view providers should be found first. // Note that it is not possible to have a multi factory in the third block (directive block). // // The algorithm to process multi providers is as follows: // 1) If the multi provider comes from the `viewProviders` of the component: // a) If the special view providers factory doesn't exist, it is created and pushed. // b) Else, the multi provider is added to the existing multi factory. // 2) If the multi provider comes from the `providers` of the component or of another // directive: // a) If the multi factory doesn't exist, it is created and provider pushed into it. // It is also linked to the multi factory for view providers, if it exists. // b) Else, the multi provider is added to the existing multi factory. const existingProvidersFactoryIndex = indexOf(token, tInjectables, beginIndex + cptViewProvidersCount, endIndex); const existingViewProvidersFactoryIndex = indexOf(token, tInjectables, beginIndex, beginIndex + cptViewProvidersCount); const doesProvidersFactoryExist = existingProvidersFactoryIndex >= 0 && lInjectablesBlueprint[existingProvidersFactoryIndex]; const doesViewProvidersFactoryExist = existingViewProvidersFactoryIndex >= 0 && lInjectablesBlueprint[existingViewProvidersFactoryIndex]; if (isViewProvider && !doesViewProvidersFactoryExist || !isViewProvider && !doesProvidersFactoryExist) { // Cases 1.a and 2.a diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, token); const factory = multiFactory(isViewProvider ? multiViewProvidersFactoryResolver : multiProvidersFactoryResolver, lInjectablesBlueprint.length, isViewProvider, isComponent, providerFactory); if (!isViewProvider && doesViewProvidersFactoryExist) { lInjectablesBlueprint[existingViewProvidersFactoryIndex].providerFactory = factory; } registerDestroyHooksIfSupported(tView, provider, tInjectables.length, 0); tInjectables.push(token); tNode.directiveStart++; tNode.directiveEnd++; if (isViewProvider) { tNode.providerIndexes += 1048576 /* TNodeProviderIndexes.CptViewProvidersCountShifter */; } lInjectablesBlueprint.push(factory); lView.push(factory); } else { // Cases 1.b and 2.b const indexInFactory = multiFactoryAdd(lInjectablesBlueprint[isViewProvider ? existingViewProvidersFactoryIndex : existingProvidersFactoryIndex], providerFactory, !isViewProvider && isComponent); registerDestroyHooksIfSupported(tView, provider, existingProvidersFactoryIndex > -1 ? existingProvidersFactoryIndex : existingViewProvidersFactoryIndex, indexInFactory); } if (!isViewProvider && isComponent && doesViewProvidersFactoryExist) { lInjectablesBlueprint[existingViewProvidersFactoryIndex].componentProviders++; } } } } /** * Registers the `ngOnDestroy` hook of a provider, if the provider supports destroy hooks. * @param tView `TView` in which to register the hook. * @param provider Provider whose hook should be registered. * @param contextIndex Index under which to find the context for the hook when it's being invoked. * @param indexInFactory Only required for `multi` providers. Index of the provider in the multi * provider factory. */ function registerDestroyHooksIfSupported(tView, provider, contextIndex, indexInFactory) { const providerIsTypeProvider = isTypeProvider(provider); const providerIsClassProvider = isClassProvider(provider); if (providerIsTypeProvider || providerIsClassProvider) { // Resolve forward references as `useClass` can hold a forward reference. const classToken = providerIsClassProvider ? resolveForwardRef(provider.useClass) : provider; const prototype = classToken.prototype; const ngOnDestroy = prototype.ngOnDestroy; if (ngOnDestroy) { const hooks = tView.destroyHooks || (tView.destroyHooks = []); if (!providerIsTypeProvider && provider.multi) { ngDevMode && assertDefined(indexInFactory, 'indexInFactory when registering multi factory destroy hook'); const existingCallbacksIndex = hooks.indexOf(contextIndex); if (existingCallbacksIndex === -1) { hooks.push(contextIndex, [indexInFactory, ngOnDestroy]); } else { hooks[existingCallbacksIndex + 1].push(indexInFactory, ngOnDestroy); } } else { hooks.push(contextIndex, ngOnDestroy); } } } } /** * Add a factory in a multi factory. * @returns Index at which the factory was inserted. */ function multiFactoryAdd(multiFactory, factory, isComponentProvider) { if (isComponentProvider) { multiFactory.componentProviders++; } return multiFactory.multi.push(factory) - 1; } /** * Returns the index of item in the array, but only in the begin to end range. */ function indexOf(item, arr, begin, end) { for (let i = begin; i < end; i++) { if (arr[i] === item) return i; } return -1; } /** * Use this with `multi` `providers`. */ function multiProvidersFactoryResolver(_, tData, lData, tNode) { return multiResolve(this.multi, []); } /** * Use this with `multi` `viewProviders`. * * This factory knows how to concatenate itself with the existing `multi` `providers`. */ function multiViewProvidersFactoryResolver(_, tData, lView, tNode) { const factories = this.multi; let result; if (this.providerFactory) { const componentCount = this.providerFactory.componentProviders; const multiProviders = getNodeInjectable(lView, lView[TVIEW], this.providerFactory.index, tNode); // Copy the section of the array which contains `multi` `providers` from the component result = multiProviders.slice(0, componentCount); // Insert the `viewProvider` instances. multiResolve(factories, result); // Copy the section of the array which contains `multi` `providers` from other directives for (let i = componentCount; i < multiProviders.length; i++) { result.push(multiProviders[i]); } } else { result = []; // Insert the `viewProvider` instances. multiResolve(factories, result); } return result; } /** * Maps an array of factories into an array of values. */ function multiResolve(factories, result) { for (let i = 0; i < factories.length; i++) { const factory = factories[i]; result.push(factory()); } return result; } /** * Creates a multi factory. */ function multiFactory(factoryFn, index, isViewProvider, isComponent, f) { const factory = new NodeInjectorFactory(factoryFn, isViewProvider, ɵɵdirectiveInject); factory.multi = []; factory.index = index; factory.componentProviders = 0; multiFactoryAdd(factory, f, isComponent && !isViewProvider); return factory; } /** * This feature resolves the providers of a directive (or component), * and publish them into the DI system, making it visible to others for injection. * * For example: * ```ts * class ComponentWithProviders { * constructor(private greeter: GreeterDE) {} * * static ɵcmp = defineComponent({ * type: ComponentWithProviders, * selectors: [['component-with-providers']], * factory: () => new ComponentWithProviders(directiveInject(GreeterDE as any)), * decls: 1, * vars: 1, * template: function(fs: RenderFlags, ctx: ComponentWithProviders) { * if (fs & RenderFlags.Create) { * ɵɵtext(0); * } * if (fs & RenderFlags.Update) { * ɵɵtextInterpolate(ctx.greeter.greet()); * } * }, * features: [ɵɵProvidersFeature([GreeterDE])] * }); * } * ``` * * @param definition * * @codeGenApi */ function ɵɵProvidersFeature(providers, viewProviders = []) { return definition => { definition.providersResolver = (def, processProvidersFn) => { return providersResolver(def, // processProvidersFn ? processProvidersFn(providers) : providers, // viewProviders); }; }; } /** * Represents an instance of an `NgModule` created by an `NgModuleFactory`. * Provides access to the `NgModule` instance and related objects. * * @publicApi */ class NgModuleRef$1 {} /** * @publicApi * * @deprecated * This class was mostly used as a part of ViewEngine-based JIT API and is no longer needed in Ivy * JIT mode. See [JIT API changes due to ViewEngine deprecation](guide/deprecations#jit-api-changes) * for additional context. Angular provides APIs that accept NgModule classes directly (such as * [PlatformRef.bootstrapModule](api/core/PlatformRef#bootstrapModule) and * [createNgModule](api/core/createNgModule)), consider switching to those APIs instead of * using factory-based ones. */ class NgModuleFactory$1 {} /** * Returns a new NgModuleRef instance based on the NgModule class and parent injector provided. * * @param ngModule NgModule class. * @param parentInjector Optional injector instance to use as a parent for the module injector. If * not provided, `NullInjector` will be used instead. * @returns NgModuleRef that represents an NgModule instance. * * @publicApi */ function createNgModule(ngModule, parentInjector) { return new NgModuleRef(ngModule, parentInjector ?? null); } /** * The `createNgModule` function alias for backwards-compatibility. * Please avoid using it directly and use `createNgModule` instead. * * @deprecated Use `createNgModule` instead. */ const createNgModuleRef = createNgModule; class NgModuleRef extends NgModuleRef$1 { constructor(ngModuleType, _parent) { super(); this._parent = _parent; // tslint:disable-next-line:require-internal-with-underscore this._bootstrapComponents = []; this.destroyCbs = []; // When bootstrapping a module we have a dependency graph that looks like this: // ApplicationRef -> ComponentFactoryResolver -> NgModuleRef. The problem is that if the // module being resolved tries to inject the ComponentFactoryResolver, it'll create a // circular dependency which will result in a runtime error, because the injector doesn't // exist yet. We work around the issue by creating the ComponentFactoryResolver ourselves // and providing it, rather than letting the injector resolve it. this.componentFactoryResolver = new ComponentFactoryResolver(this); const ngModuleDef = getNgModuleDef(ngModuleType); ngDevMode && assertDefined(ngModuleDef, `NgModule '${stringify(ngModuleType)}' is not a subtype of 'NgModuleType'.`); this._bootstrapComponents = maybeUnwrapFn(ngModuleDef.bootstrap); this._r3Injector = createInjectorWithoutInjectorInstances(ngModuleType, _parent, [{ provide: NgModuleRef$1, useValue: this }, { provide: ComponentFactoryResolver$1, useValue: this.componentFactoryResolver }], stringify(ngModuleType), new Set(['environment'])); // We need to resolve the injector types separately from the injector creation, because // the module might be trying to use this ref in its constructor for DI which will cause a // circular error that will eventually error out, because the injector isn't created yet. this._r3Injector.resolveInjectorInitializers(); this.instance = this._r3Injector.get(ngModuleType); } get injector() { return this._r3Injector; } destroy() { ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed'); const injector = this._r3Injector; !injector.destroyed && injector.destroy(); this.destroyCbs.forEach(fn => fn()); this.destroyCbs = null; } onDestroy(callback) { ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed'); this.destroyCbs.push(callback); } } class NgModuleFactory extends NgModuleFactory$1 { constructor(moduleType) { super(); this.moduleType = moduleType; } create(parentInjector) { return new NgModuleRef(this.moduleType, parentInjector); } } class EnvironmentNgModuleRefAdapter extends NgModuleRef$1 { constructor(providers, parent, source) { super(); this.componentFactoryResolver = new ComponentFactoryResolver(this); this.instance = null; const injector = new R3Injector([...providers, { provide: NgModuleRef$1, useValue: this }, { provide: ComponentFactoryResolver$1, useValue: this.componentFactoryResolver }], parent || getNullInjector(), source, new Set(['environment'])); this.injector = injector; injector.resolveInjectorInitializers(); } destroy() { this.injector.destroy(); } onDestroy(callback) { this.injector.onDestroy(callback); } } /** * Create a new environment injector. * * Learn more about environment injectors in * [this guide](guide/standalone-components#environment-injectors). * * @param providers An array of providers. * @param parent A parent environment injector. * @param debugName An optional name for this injector instance, which will be used in error * messages. * * @publicApi */ function createEnvironmentInjector(providers, parent, debugName = null) { const adapter = new EnvironmentNgModuleRefAdapter(providers, parent, debugName); return adapter.injector; } /** * A service used by the framework to create instances of standalone injectors. Those injectors are * created on demand in case of dynamic component instantiation and contain ambient providers * collected from the imports graph rooted at a given standalone component. */ class StandaloneService { constructor(_injector) { this._injector = _injector; this.cachedInjectors = new Map(); } getOrCreateStandaloneInjector(componentDef) { if (!componentDef.standalone) { return null; } if (!this.cachedInjectors.has(componentDef.id)) { const providers = internalImportProvidersFrom(false, componentDef.type); const standaloneInjector = providers.length > 0 ? createEnvironmentInjector([providers], this._injector, `Standalone[${componentDef.type.name}]`) : null; this.cachedInjectors.set(componentDef.id, standaloneInjector); } return this.cachedInjectors.get(componentDef.id); } ngOnDestroy() { try { for (const injector of this.cachedInjectors.values()) { if (injector !== null) { injector.destroy(); } } } finally { this.cachedInjectors.clear(); } } } /** @nocollapse */ StandaloneService.ɵprov = ɵɵdefineInjectable({ token: StandaloneService, providedIn: 'environment', factory: () => new StandaloneService(ɵɵinject(EnvironmentInjector)) }); /** * A feature that acts as a setup code for the {@link StandaloneService}. * * The most important responsibility of this feature is to expose the "getStandaloneInjector" * function (an entry points to a standalone injector creation) on a component definition object. We * go through the features infrastructure to make sure that the standalone injector creation logic * is tree-shakable and not included in applications that don't use standalone components. * * @codeGenApi */ function ɵɵStandaloneFeature(definition) { definition.getStandaloneInjector = parentInjector => { return parentInjector.get(StandaloneService).getOrCreateStandaloneInjector(definition); }; } /** * Retrieves the component instance associated with a given DOM element. * * @usageNotes * Given the following DOM structure: * * ```html * <app-root> * <div> * <child-comp></child-comp> * </div> * </app-root> * ``` * * Calling `getComponent` on `<child-comp>` will return the instance of `ChildComponent` * associated with this DOM element. * * Calling the function on `<app-root>` will return the `MyApp` instance. * * * @param element DOM element from which the component should be retrieved. * @returns Component instance associated with the element or `null` if there * is no component associated with it. * * @publicApi * @globalApi ng */ function getComponent(element) { ngDevMode && assertDomElement(element); const context = getLContext(element); if (context === null) return null; if (context.component === undefined) { const lView = context.lView; if (lView === null) { return null; } context.component = getComponentAtNodeIndex(context.nodeIndex, lView); } return context.component; } /** * If inside an embedded view (e.g. `*ngIf` or `*ngFor`), retrieves the context of the embedded * view that the element is part of. Otherwise retrieves the instance of the component whose view * owns the element (in this case, the result is the same as calling `getOwningComponent`). * * @param element Element for which to get the surrounding component instance. * @returns Instance of the component that is around the element or null if the element isn't * inside any component. * * @publicApi * @globalApi ng */ function getContext(element) { assertDomElement(element); const context = getLContext(element); const lView = context ? context.lView : null; return lView === null ? null : lView[CONTEXT]; } /** * Retrieves the component instance whose view contains the DOM element. * * For example, if `<child-comp>` is used in the template of `<app-comp>` * (i.e. a `ViewChild` of `<app-comp>`), calling `getOwningComponent` on `<child-comp>` * would return `<app-comp>`. * * @param elementOrDir DOM element, component or directive instance * for which to retrieve the root components. * @returns Component instance whose view owns the DOM element or null if the element is not * part of a component view. * * @publicApi * @globalApi ng */ function getOwningComponent(elementOrDir) { const context = getLContext(elementOrDir); let lView = context ? context.lView : null; if (lView === null) return null; let parent; while (lView[TVIEW].type === 2 /* TViewType.Embedded */ && (parent = getLViewParent(lView))) { lView = parent; } return lView[FLAGS] & 256 /* LViewFlags.IsRoot */ ? null : lView[CONTEXT]; } /** * Retrieves all root components associated with a DOM element, directive or component instance. * Root components are those which have been bootstrapped by Angular. * * @param elementOrDir DOM element, component or directive instance * for which to retrieve the root components. * @returns Root components associated with the target object. * * @publicApi * @globalApi ng */ function getRootComponents(elementOrDir) { const lView = readPatchedLView(elementOrDir); return lView !== null ? [getRootContext(lView)] : []; } /** * Retrieves an `Injector` associated with an element, component or directive instance. * * @param elementOrDir DOM element, component or directive instance for which to * retrieve the injector. * @returns Injector associated with the element, component or directive instance. * * @publicApi * @globalApi ng */ function getInjector(elementOrDir) { const context = getLContext(elementOrDir); const lView = context ? context.lView : null; if (lView === null) return Injector.NULL; const tNode = lView[TVIEW].data[context.nodeIndex]; return new NodeInjector(tNode, lView); } /** * Retrieve a set of injection tokens at a given DOM node. * * @param element Element for which the injection tokens should be retrieved. */ function getInjectionTokens(element) { const context = getLContext(element); const lView = context ? context.lView : null; if (lView === null) return []; const tView = lView[TVIEW]; const tNode = tView.data[context.nodeIndex]; const providerTokens = []; const startIndex = tNode.providerIndexes & 1048575 /* TNodeProviderIndexes.ProvidersStartIndexMask */; const endIndex = tNode.directiveEnd; for (let i = startIndex; i < endIndex; i++) { let value = tView.data[i]; if (isDirectiveDefHack(value)) { // The fact that we sometimes store Type and sometimes DirectiveDef in this location is a // design flaw. We should always store same type so that we can be monomorphic. The issue // is that for Components/Directives we store the def instead the type. The correct behavior // is that we should always be storing injectable type in this location. value = value.type; } providerTokens.push(value); } return providerTokens; } /** * Retrieves directive instances associated with a given DOM node. Does not include * component instances. * * @usageNotes * Given the following DOM structure: * * ```html * <app-root> * <button my-button></button> * <my-comp></my-comp> * </app-root> * ``` * * Calling `getDirectives` on `<button>` will return an array with an instance of the `MyButton` * directive that is associated with the DOM node. * * Calling `getDirectives` on `<my-comp>` will return an empty array. * * @param node DOM node for which to get the directives. * @returns Array of directives associated with the node. * * @publicApi * @globalApi ng */ function getDirectives(node) { // Skip text nodes because we can't have directives associated with them. if (node instanceof Text) { return []; } const context = getLContext(node); const lView = context ? context.lView : null; if (lView === null) { return []; } const tView = lView[TVIEW]; const nodeIndex = context.nodeIndex; if (!tView?.data[nodeIndex]) { return []; } if (context.directives === undefined) { context.directives = getDirectivesAtNodeIndex(nodeIndex, lView); } // The `directives` in this case are a named array called `LComponentView`. Clone the // result so we don't expose an internal data structure in the user's console. return context.directives === null ? [] : [...context.directives]; } /** * Returns the debug (partial) metadata for a particular directive or component instance. * The function accepts an instance of a directive or component and returns the corresponding * metadata. * * @param directiveOrComponentInstance Instance of a directive or component * @returns metadata of the passed directive or component * * @publicApi * @globalApi ng */ function getDirectiveMetadata$1(directiveOrComponentInstance) { const { constructor } = directiveOrComponentInstance; if (!constructor) { throw new Error('Unable to find the instance constructor'); } // In case a component inherits from a directive, we may have component and directive metadata // To ensure we don't get the metadata of the directive, we want to call `getComponentDef` first. const componentDef = getComponentDef(constructor); if (componentDef) { return { inputs: componentDef.inputs, outputs: componentDef.outputs, encapsulation: componentDef.encapsulation, changeDetection: componentDef.onPush ? ChangeDetectionStrategy.OnPush : ChangeDetectionStrategy.Default }; } const directiveDef = getDirectiveDef(constructor); if (directiveDef) { return { inputs: directiveDef.inputs, outputs: directiveDef.outputs }; } return null; } /** * Retrieve map of local references. * * The references are retrieved as a map of local reference name to element or directive instance. * * @param target DOM element, component or directive instance for which to retrieve * the local references. */ function getLocalRefs(target) { const context = getLContext(target); if (context === null) return {}; if (context.localRefs === undefined) { const lView = context.lView; if (lView === null) { return {}; } context.localRefs = discoverLocalRefs(lView, context.nodeIndex); } return context.localRefs || {}; } /** * Retrieves the host element of a component or directive instance. * The host element is the DOM element that matched the selector of the directive. * * @param componentOrDirective Component or directive instance for which the host * element should be retrieved. * @returns Host element of the target. * * @publicApi * @globalApi ng */ function getHostElement(componentOrDirective) { return getLContext(componentOrDirective).native; } /** * Retrieves the rendered text for a given component. * * This function retrieves the host element of a component and * and then returns the `textContent` for that element. This implies * that the text returned will include re-projected content of * the component as well. * * @param component The component to return the content text for. */ function getRenderedText(component) { const hostElement = getHostElement(component); return hostElement.textContent || ''; } /** * Retrieves a list of event listeners associated with a DOM element. The list does include host * listeners, but it does not include event listeners defined outside of the Angular context * (e.g. through `addEventListener`). * * @usageNotes * Given the following DOM structure: * * ```html * <app-root> * <div (click)="doSomething()"></div> * </app-root> * ``` * * Calling `getListeners` on `<div>` will return an object that looks as follows: * * ```ts * { * name: 'click', * element: <div>, * callback: () => doSomething(), * useCapture: false * } * ``` * * @param element Element for which the DOM listeners should be retrieved. * @returns Array of event listeners on the DOM element. * * @publicApi * @globalApi ng */ function getListeners(element) { ngDevMode && assertDomElement(element); const lContext = getLContext(element); const lView = lContext === null ? null : lContext.lView; if (lView === null) return []; const tView = lView[TVIEW]; const lCleanup = lView[CLEANUP]; const tCleanup = tView.cleanup; const listeners = []; if (tCleanup && lCleanup) { for (let i = 0; i < tCleanup.length;) { const firstParam = tCleanup[i++]; const secondParam = tCleanup[i++]; if (typeof firstParam === 'string') { const name = firstParam; const listenerElement = unwrapRNode(lView[secondParam]); const callback = lCleanup[tCleanup[i++]]; const useCaptureOrIndx = tCleanup[i++]; // if useCaptureOrIndx is boolean then report it as is. // if useCaptureOrIndx is positive number then it in unsubscribe method // if useCaptureOrIndx is negative number then it is a Subscription const type = typeof useCaptureOrIndx === 'boolean' || useCaptureOrIndx >= 0 ? 'dom' : 'output'; const useCapture = typeof useCaptureOrIndx === 'boolean' ? useCaptureOrIndx : false; if (element == listenerElement) { listeners.push({ element, name, callback, useCapture, type }); } } } } listeners.sort(sortListeners); return listeners; } function sortListeners(a, b) { if (a.name == b.name) return 0; return a.name < b.name ? -1 : 1; } /** * This function should not exist because it is megamorphic and only mostly correct. * * See call site for more info. */ function isDirectiveDefHack(obj) { return obj.type !== undefined && obj.declaredInputs !== undefined && obj.findHostDirectiveDefs !== undefined; } /** * Retrieve the component `LView` from component/element. * * NOTE: `LView` is a private and should not be leaked outside. * Don't export this method to `ng.*` on window. * * @param target DOM element or component instance for which to retrieve the LView. */ function getComponentLView(target) { const lContext = getLContext(target); const nodeIndx = lContext.nodeIndex; const lView = lContext.lView; ngDevMode && assertLView(lView); const componentLView = lView[nodeIndx]; ngDevMode && assertLView(componentLView); return componentLView; } /** Asserts that a value is a DOM Element. */ function assertDomElement(value) { if (typeof Element !== 'undefined' && !(value instanceof Element)) { throw new Error('Expecting instance of DOM Element'); } } /** * Adds decorator, constructor, and property metadata to a given type via static metadata fields * on the type. * * These metadata fields can later be read with Angular's `ReflectionCapabilities` API. * * Calls to `setClassMetadata` can be guarded by ngDevMode, resulting in the metadata assignments * being tree-shaken away during production builds. */ function setClassMetadata(type, decorators, ctorParameters, propDecorators) { return noSideEffects(() => { const clazz = type; if (decorators !== null) { if (clazz.hasOwnProperty('decorators') && clazz.decorators !== undefined) { clazz.decorators.push(...decorators); } else { clazz.decorators = decorators; } } if (ctorParameters !== null) { // Rather than merging, clobber the existing parameters. If other projects exist which // use tsickle-style annotations and reflect over them in the same way, this could // cause issues, but that is vanishingly unlikely. clazz.ctorParameters = ctorParameters; } if (propDecorators !== null) { // The property decorator objects are merged as it is possible different fields have // different decorator types. Decorators on individual fields are not merged, as it's // also incredibly unlikely that a field will be decorated both with an Angular // decorator and a non-Angular decorator that's also been downleveled. if (clazz.hasOwnProperty('propDecorators') && clazz.propDecorators !== undefined) { clazz.propDecorators = { ...clazz.propDecorators, ...propDecorators }; } else { clazz.propDecorators = propDecorators; } } }); } /** * Bindings for pure functions are stored after regular bindings. * * |-------decls------|---------vars---------| |----- hostVars (dir1) ------| * ------------------------------------------------------------------------------------------ * | nodes/refs/pipes | bindings | fn slots | injector | dir1 | host bindings | host slots | * ------------------------------------------------------------------------------------------ * ^ ^ * TView.bindingStartIndex TView.expandoStartIndex * * Pure function instructions are given an offset from the binding root. Adding the offset to the * binding root gives the first index where the bindings are stored. In component views, the binding * root is the bindingStartIndex. In host bindings, the binding root is the expandoStartIndex + * any directive instances + any hostVars in directives evaluated before it. * * See VIEW_DATA.md for more information about host binding resolution. */ /** * If the value hasn't been saved, calls the pure function to store and return the * value. If it has been saved, returns the saved value. * * @param slotOffset the offset from binding root to the reserved slot * @param pureFn Function that returns a value * @param thisArg Optional calling context of pureFn * @returns value * * @codeGenApi */ function ɵɵpureFunction0(slotOffset, pureFn, thisArg) { const bindingIndex = getBindingRoot() + slotOffset; const lView = getLView(); return lView[bindingIndex] === NO_CHANGE ? updateBinding(lView, bindingIndex, thisArg ? pureFn.call(thisArg) : pureFn()) : getBinding(lView, bindingIndex); } /** * If the value of the provided exp has changed, calls the pure function to return * an updated value. Or if the value has not changed, returns cached value. * * @param slotOffset the offset from binding root to the reserved slot * @param pureFn Function that returns an updated value * @param exp Updated expression value * @param thisArg Optional calling context of pureFn * @returns Updated or cached value * * @codeGenApi */ function ɵɵpureFunction1(slotOffset, pureFn, exp, thisArg) { return pureFunction1Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp, thisArg); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 * @param thisArg Optional calling context of pureFn * @returns Updated or cached value * * @codeGenApi */ function ɵɵpureFunction2(slotOffset, pureFn, exp1, exp2, thisArg) { return pureFunction2Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, thisArg); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 * @param exp3 * @param thisArg Optional calling context of pureFn * @returns Updated or cached value * * @codeGenApi */ function ɵɵpureFunction3(slotOffset, pureFn, exp1, exp2, exp3, thisArg) { return pureFunction3Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, exp3, thisArg); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 * @param exp3 * @param exp4 * @param thisArg Optional calling context of pureFn * @returns Updated or cached value * * @codeGenApi */ function ɵɵpureFunction4(slotOffset, pureFn, exp1, exp2, exp3, exp4, thisArg) { return pureFunction4Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, exp3, exp4, thisArg); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 * @param exp3 * @param exp4 * @param exp5 * @param thisArg Optional calling context of pureFn * @returns Updated or cached value * * @codeGenApi */ function ɵɵpureFunction5(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, thisArg) { const bindingIndex = getBindingRoot() + slotOffset; const lView = getLView(); const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4); return bindingUpdated(lView, bindingIndex + 4, exp5) || different ? updateBinding(lView, bindingIndex + 5, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5) : pureFn(exp1, exp2, exp3, exp4, exp5)) : getBinding(lView, bindingIndex + 5); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 * @param exp3 * @param exp4 * @param exp5 * @param exp6 * @param thisArg Optional calling context of pureFn * @returns Updated or cached value * * @codeGenApi */ function ɵɵpureFunction6(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, exp6, thisArg) { const bindingIndex = getBindingRoot() + slotOffset; const lView = getLView(); const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4); return bindingUpdated2(lView, bindingIndex + 4, exp5, exp6) || different ? updateBinding(lView, bindingIndex + 6, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6) : pureFn(exp1, exp2, exp3, exp4, exp5, exp6)) : getBinding(lView, bindingIndex + 6); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 * @param exp3 * @param exp4 * @param exp5 * @param exp6 * @param exp7 * @param thisArg Optional calling context of pureFn * @returns Updated or cached value * * @codeGenApi */ function ɵɵpureFunction7(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, exp6, exp7, thisArg) { const bindingIndex = getBindingRoot() + slotOffset; const lView = getLView(); let different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4); return bindingUpdated3(lView, bindingIndex + 4, exp5, exp6, exp7) || different ? updateBinding(lView, bindingIndex + 7, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7) : pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7)) : getBinding(lView, bindingIndex + 7); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 * @param exp3 * @param exp4 * @param exp5 * @param exp6 * @param exp7 * @param exp8 * @param thisArg Optional calling context of pureFn * @returns Updated or cached value * * @codeGenApi */ function ɵɵpureFunction8(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8, thisArg) { const bindingIndex = getBindingRoot() + slotOffset; const lView = getLView(); const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4); return bindingUpdated4(lView, bindingIndex + 4, exp5, exp6, exp7, exp8) || different ? updateBinding(lView, bindingIndex + 8, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8) : pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8)) : getBinding(lView, bindingIndex + 8); } /** * pureFunction instruction that can support any number of bindings. * * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * * @param slotOffset the offset from binding root to the reserved slot * @param pureFn A pure function that takes binding values and builds an object or array * containing those values. * @param exps An array of binding values * @param thisArg Optional calling context of pureFn * @returns Updated or cached value * * @codeGenApi */ function ɵɵpureFunctionV(slotOffset, pureFn, exps, thisArg) { return pureFunctionVInternal(getLView(), getBindingRoot(), slotOffset, pureFn, exps, thisArg); } /** * Results of a pure function invocation are stored in LView in a dedicated slot that is initialized * to NO_CHANGE. In rare situations a pure pipe might throw an exception on the very first * invocation and not produce any valid results. In this case LView would keep holding the NO_CHANGE * value. The NO_CHANGE is not something that we can use in expressions / bindings thus we convert * it to `undefined`. */ function getPureFunctionReturnValue(lView, returnValueIndex) { ngDevMode && assertIndexInRange(lView, returnValueIndex); const lastReturnValue = lView[returnValueIndex]; return lastReturnValue === NO_CHANGE ? undefined : lastReturnValue; } /** * If the value of the provided exp has changed, calls the pure function to return * an updated value. Or if the value has not changed, returns cached value. * * @param lView LView in which the function is being executed. * @param bindingRoot Binding root index. * @param slotOffset the offset from binding root to the reserved slot * @param pureFn Function that returns an updated value * @param exp Updated expression value * @param thisArg Optional calling context of pureFn * @returns Updated or cached value */ function pureFunction1Internal(lView, bindingRoot, slotOffset, pureFn, exp, thisArg) { const bindingIndex = bindingRoot + slotOffset; return bindingUpdated(lView, bindingIndex, exp) ? updateBinding(lView, bindingIndex + 1, thisArg ? pureFn.call(thisArg, exp) : pureFn(exp)) : getPureFunctionReturnValue(lView, bindingIndex + 1); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * * @param lView LView in which the function is being executed. * @param bindingRoot Binding root index. * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 * @param thisArg Optional calling context of pureFn * @returns Updated or cached value */ function pureFunction2Internal(lView, bindingRoot, slotOffset, pureFn, exp1, exp2, thisArg) { const bindingIndex = bindingRoot + slotOffset; return bindingUpdated2(lView, bindingIndex, exp1, exp2) ? updateBinding(lView, bindingIndex + 2, thisArg ? pureFn.call(thisArg, exp1, exp2) : pureFn(exp1, exp2)) : getPureFunctionReturnValue(lView, bindingIndex + 2); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * * @param lView LView in which the function is being executed. * @param bindingRoot Binding root index. * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 * @param exp3 * @param thisArg Optional calling context of pureFn * @returns Updated or cached value */ function pureFunction3Internal(lView, bindingRoot, slotOffset, pureFn, exp1, exp2, exp3, thisArg) { const bindingIndex = bindingRoot + slotOffset; return bindingUpdated3(lView, bindingIndex, exp1, exp2, exp3) ? updateBinding(lView, bindingIndex + 3, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3) : pureFn(exp1, exp2, exp3)) : getPureFunctionReturnValue(lView, bindingIndex + 3); } /** * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * * @param lView LView in which the function is being executed. * @param bindingRoot Binding root index. * @param slotOffset the offset from binding root to the reserved slot * @param pureFn * @param exp1 * @param exp2 * @param exp3 * @param exp4 * @param thisArg Optional calling context of pureFn * @returns Updated or cached value * */ function pureFunction4Internal(lView, bindingRoot, slotOffset, pureFn, exp1, exp2, exp3, exp4, thisArg) { const bindingIndex = bindingRoot + slotOffset; return bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4) ? updateBinding(lView, bindingIndex + 4, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4) : pureFn(exp1, exp2, exp3, exp4)) : getPureFunctionReturnValue(lView, bindingIndex + 4); } /** * pureFunction instruction that can support any number of bindings. * * If the value of any provided exp has changed, calls the pure function to return * an updated value. Or if no values have changed, returns cached value. * * @param lView LView in which the function is being executed. * @param bindingRoot Binding root index. * @param slotOffset the offset from binding root to the reserved slot * @param pureFn A pure function that takes binding values and builds an object or array * containing those values. * @param exps An array of binding values * @param thisArg Optional calling context of pureFn * @returns Updated or cached value */ function pureFunctionVInternal(lView, bindingRoot, slotOffset, pureFn, exps, thisArg) { let bindingIndex = bindingRoot + slotOffset; let different = false; for (let i = 0; i < exps.length; i++) { bindingUpdated(lView, bindingIndex++, exps[i]) && (different = true); } return different ? updateBinding(lView, bindingIndex, pureFn.apply(thisArg, exps)) : getPureFunctionReturnValue(lView, bindingIndex); } /** * Create a pipe. * * @param index Pipe index where the pipe will be stored. * @param pipeName The name of the pipe * @returns T the instance of the pipe. * * @codeGenApi */ function ɵɵpipe(index, pipeName) { const tView = getTView(); let pipeDef; const adjustedIndex = index + HEADER_OFFSET; if (tView.firstCreatePass) { // The `getPipeDef` throws if a pipe with a given name is not found // (so we use non-null assertion below). pipeDef = getPipeDef(pipeName, tView.pipeRegistry); tView.data[adjustedIndex] = pipeDef; if (pipeDef.onDestroy) { (tView.destroyHooks ?? (tView.destroyHooks = [])).push(adjustedIndex, pipeDef.onDestroy); } } else { pipeDef = tView.data[adjustedIndex]; } const pipeFactory = pipeDef.factory || (pipeDef.factory = getFactoryDef(pipeDef.type, true)); const previousInjectImplementation = setInjectImplementation(ɵɵdirectiveInject); try { // DI for pipes is supposed to behave like directives when placed on a component // host node, which means that we have to disable access to `viewProviders`. const previousIncludeViewProviders = setIncludeViewProviders(false); const pipeInstance = pipeFactory(); setIncludeViewProviders(previousIncludeViewProviders); store(tView, getLView(), adjustedIndex, pipeInstance); return pipeInstance; } finally { // we have to restore the injector implementation in finally, just in case the creation of the // pipe throws an error. setInjectImplementation(previousInjectImplementation); } } /** * Searches the pipe registry for a pipe with the given name. If one is found, * returns the pipe. Otherwise, an error is thrown because the pipe cannot be resolved. * * @param name Name of pipe to resolve * @param registry Full list of available pipes * @returns Matching PipeDef */ function getPipeDef(name, registry) { if (registry) { for (let i = registry.length - 1; i >= 0; i--) { const pipeDef = registry[i]; if (name === pipeDef.name) { return pipeDef; } } } if (ngDevMode) { throw new RuntimeError(-302 /* RuntimeErrorCode.PIPE_NOT_FOUND */, getPipeNotFoundErrorMessage(name)); } } /** * Generates a helpful error message for the user when a pipe is not found. * * @param name Name of the missing pipe * @returns The error message */ function getPipeNotFoundErrorMessage(name) { const lView = getLView(); const declarationLView = lView[DECLARATION_COMPONENT_VIEW]; const context = declarationLView[CONTEXT]; const hostIsStandalone = isHostComponentStandalone(lView); const componentInfoMessage = context ? ` in the '${context.constructor.name}' component` : ''; const verifyMessage = `Verify that it is ${hostIsStandalone ? 'included in the \'@Component.imports\' of this component' : 'declared or imported in this module'}`; const errorMessage = `The pipe '${name}' could not be found${componentInfoMessage}. ${verifyMessage}`; return errorMessage; } /** * Invokes a pipe with 1 arguments. * * This instruction acts as a guard to {@link PipeTransform#transform} invoking * the pipe only when an input to the pipe changes. * * @param index Pipe index where the pipe was stored on creation. * @param slotOffset the offset in the reserved slot space * @param v1 1st argument to {@link PipeTransform#transform}. * * @codeGenApi */ function ɵɵpipeBind1(index, slotOffset, v1) { const adjustedIndex = index + HEADER_OFFSET; const lView = getLView(); const pipeInstance = load(lView, adjustedIndex); return isPure(lView, adjustedIndex) ? pureFunction1Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, pipeInstance) : pipeInstance.transform(v1); } /** * Invokes a pipe with 2 arguments. * * This instruction acts as a guard to {@link PipeTransform#transform} invoking * the pipe only when an input to the pipe changes. * * @param index Pipe index where the pipe was stored on creation. * @param slotOffset the offset in the reserved slot space * @param v1 1st argument to {@link PipeTransform#transform}. * @param v2 2nd argument to {@link PipeTransform#transform}. * * @codeGenApi */ function ɵɵpipeBind2(index, slotOffset, v1, v2) { const adjustedIndex = index + HEADER_OFFSET; const lView = getLView(); const pipeInstance = load(lView, adjustedIndex); return isPure(lView, adjustedIndex) ? pureFunction2Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, pipeInstance) : pipeInstance.transform(v1, v2); } /** * Invokes a pipe with 3 arguments. * * This instruction acts as a guard to {@link PipeTransform#transform} invoking * the pipe only when an input to the pipe changes. * * @param index Pipe index where the pipe was stored on creation. * @param slotOffset the offset in the reserved slot space * @param v1 1st argument to {@link PipeTransform#transform}. * @param v2 2nd argument to {@link PipeTransform#transform}. * @param v3 4rd argument to {@link PipeTransform#transform}. * * @codeGenApi */ function ɵɵpipeBind3(index, slotOffset, v1, v2, v3) { const adjustedIndex = index + HEADER_OFFSET; const lView = getLView(); const pipeInstance = load(lView, adjustedIndex); return isPure(lView, adjustedIndex) ? pureFunction3Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, v3, pipeInstance) : pipeInstance.transform(v1, v2, v3); } /** * Invokes a pipe with 4 arguments. * * This instruction acts as a guard to {@link PipeTransform#transform} invoking * the pipe only when an input to the pipe changes. * * @param index Pipe index where the pipe was stored on creation. * @param slotOffset the offset in the reserved slot space * @param v1 1st argument to {@link PipeTransform#transform}. * @param v2 2nd argument to {@link PipeTransform#transform}. * @param v3 3rd argument to {@link PipeTransform#transform}. * @param v4 4th argument to {@link PipeTransform#transform}. * * @codeGenApi */ function ɵɵpipeBind4(index, slotOffset, v1, v2, v3, v4) { const adjustedIndex = index + HEADER_OFFSET; const lView = getLView(); const pipeInstance = load(lView, adjustedIndex); return isPure(lView, adjustedIndex) ? pureFunction4Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, v3, v4, pipeInstance) : pipeInstance.transform(v1, v2, v3, v4); } /** * Invokes a pipe with variable number of arguments. * * This instruction acts as a guard to {@link PipeTransform#transform} invoking * the pipe only when an input to the pipe changes. * * @param index Pipe index where the pipe was stored on creation. * @param slotOffset the offset in the reserved slot space * @param values Array of arguments to pass to {@link PipeTransform#transform} method. * * @codeGenApi */ function ɵɵpipeBindV(index, slotOffset, values) { const adjustedIndex = index + HEADER_OFFSET; const lView = getLView(); const pipeInstance = load(lView, adjustedIndex); return isPure(lView, adjustedIndex) ? pureFunctionVInternal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, values, pipeInstance) : pipeInstance.transform.apply(pipeInstance, values); } function isPure(lView, index) { return lView[TVIEW].data[index].pure; } /// <reference types="rxjs" /> class EventEmitter_ extends rxjs__WEBPACK_IMPORTED_MODULE_0__.Subject { constructor(isAsync = false) { super(); this.__isAsync = isAsync; } emit(value) { super.next(value); } subscribe(observerOrNext, error, complete) { let nextFn = observerOrNext; let errorFn = error || (() => null); let completeFn = complete; if (observerOrNext && typeof observerOrNext === 'object') { const observer = observerOrNext; nextFn = observer.next?.bind(observer); errorFn = observer.error?.bind(observer); completeFn = observer.complete?.bind(observer); } if (this.__isAsync) { errorFn = _wrapInTimeout(errorFn); if (nextFn) { nextFn = _wrapInTimeout(nextFn); } if (completeFn) { completeFn = _wrapInTimeout(completeFn); } } const sink = super.subscribe({ next: nextFn, error: errorFn, complete: completeFn }); if (observerOrNext instanceof rxjs__WEBPACK_IMPORTED_MODULE_1__.Subscription) { observerOrNext.add(sink); } return sink; } } function _wrapInTimeout(fn) { return value => { setTimeout(fn, undefined, value); }; } /** * @publicApi */ const EventEmitter = EventEmitter_; function symbolIterator() { // @ts-expect-error accessing a private member return this._results[Symbol.iterator](); } /** * An unmodifiable list of items that Angular keeps up to date when the state * of the application changes. * * The type of object that {@link ViewChildren}, {@link ContentChildren}, and {@link QueryList} * provide. * * Implements an iterable interface, therefore it can be used in both ES6 * javascript `for (var i of items)` loops as well as in Angular templates with * `*ngFor="let i of myList"`. * * Changes can be observed by subscribing to the changes `Observable`. * * NOTE: In the future this class will implement an `Observable` interface. * * @usageNotes * ### Example * ```typescript * @Component({...}) * class Container { * @ViewChildren(Item) items:QueryList<Item>; * } * ``` * * @publicApi */ class QueryList { /** * Returns `Observable` of `QueryList` notifying the subscriber of changes. */ get changes() { return this._changes || (this._changes = new EventEmitter()); } /** * @param emitDistinctChangesOnly Whether `QueryList.changes` should fire only when actual change * has occurred. Or if it should fire when query is recomputed. (recomputing could resolve in * the same result) */ constructor(_emitDistinctChangesOnly = false) { this._emitDistinctChangesOnly = _emitDistinctChangesOnly; this.dirty = true; this._results = []; this._changesDetected = false; this._changes = null; this.length = 0; this.first = undefined; this.last = undefined; // This function should be declared on the prototype, but doing so there will cause the class // declaration to have side-effects and become not tree-shakable. For this reason we do it in // the constructor. // [Symbol.iterator](): Iterator<T> { ... } const proto = QueryList.prototype; if (!proto[Symbol.iterator]) proto[Symbol.iterator] = symbolIterator; } /** * Returns the QueryList entry at `index`. */ get(index) { return this._results[index]; } /** * See * [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) */ map(fn) { return this._results.map(fn); } /** * See * [Array.filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) */ filter(fn) { return this._results.filter(fn); } /** * See * [Array.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) */ find(fn) { return this._results.find(fn); } /** * See * [Array.reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) */ reduce(fn, init) { return this._results.reduce(fn, init); } /** * See * [Array.forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) */ forEach(fn) { this._results.forEach(fn); } /** * See * [Array.some](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some) */ some(fn) { return this._results.some(fn); } /** * Returns a copy of the internal results list as an Array. */ toArray() { return this._results.slice(); } toString() { return this._results.toString(); } /** * Updates the stored data of the query list, and resets the `dirty` flag to `false`, so that * on change detection, it will not notify of changes to the queries, unless a new change * occurs. * * @param resultsTree The query results to store * @param identityAccessor Optional function for extracting stable object identity from a value * in the array. This function is executed for each element of the query result list while * comparing current query list with the new one (provided as a first argument of the `reset` * function) to detect if the lists are different. If the function is not provided, elements * are compared as is (without any pre-processing). */ reset(resultsTree, identityAccessor) { // Cast to `QueryListInternal` so that we can mutate fields which are readonly for the usage of // QueryList (but not for QueryList itself.) const self = this; self.dirty = false; const newResultFlat = flatten(resultsTree); if (this._changesDetected = !arrayEquals(self._results, newResultFlat, identityAccessor)) { self._results = newResultFlat; self.length = newResultFlat.length; self.last = newResultFlat[this.length - 1]; self.first = newResultFlat[0]; } } /** * Triggers a change event by emitting on the `changes` {@link EventEmitter}. */ notifyOnChanges() { if (this._changes && (this._changesDetected || !this._emitDistinctChangesOnly)) this._changes.emit(this); } /** internal */ setDirty() { this.dirty = true; } /** internal */ destroy() { this.changes.complete(); this.changes.unsubscribe(); } } Symbol.iterator; /** * Represents an embedded template that can be used to instantiate embedded views. * To instantiate embedded views based on a template, use the `ViewContainerRef` * method `createEmbeddedView()`. * * Access a `TemplateRef` instance by placing a directive on an `<ng-template>` * element (or directive prefixed with `*`). The `TemplateRef` for the embedded view * is injected into the constructor of the directive, * using the `TemplateRef` token. * * You can also use a `Query` to find a `TemplateRef` associated with * a component or a directive. * * @see `ViewContainerRef` * @see [Navigate the Component Tree with DI](guide/dependency-injection-navtree) * * @publicApi */ class TemplateRef {} /** * @internal * @nocollapse */ TemplateRef.__NG_ELEMENT_ID__ = injectTemplateRef; const ViewEngineTemplateRef = TemplateRef; // TODO(alxhub): combine interface and implementation. Currently this is challenging since something // in g3 depends on them being separate. const R3TemplateRef = class TemplateRef extends ViewEngineTemplateRef { constructor(_declarationLView, _declarationTContainer, elementRef) { super(); this._declarationLView = _declarationLView; this._declarationTContainer = _declarationTContainer; this.elementRef = elementRef; } createEmbeddedView(context, injector) { const embeddedTView = this._declarationTContainer.tView; const embeddedLView = createLView(this._declarationLView, embeddedTView, context, 16 /* LViewFlags.CheckAlways */, null, embeddedTView.declTNode, null, null, null, null, injector || null); const declarationLContainer = this._declarationLView[this._declarationTContainer.index]; ngDevMode && assertLContainer(declarationLContainer); embeddedLView[DECLARATION_LCONTAINER] = declarationLContainer; const declarationViewLQueries = this._declarationLView[QUERIES]; if (declarationViewLQueries !== null) { embeddedLView[QUERIES] = declarationViewLQueries.createEmbeddedView(embeddedTView); } renderView(embeddedTView, embeddedLView, context); return new ViewRef$1(embeddedLView); } }; /** * Creates a TemplateRef given a node. * * @returns The TemplateRef instance to use */ function injectTemplateRef() { return createTemplateRef(getCurrentTNode(), getLView()); } /** * Creates a TemplateRef and stores it on the injector. * * @param hostTNode The node on which a TemplateRef is requested * @param hostLView The `LView` to which the node belongs * @returns The TemplateRef instance or null if we can't create a TemplateRef on a given node type */ function createTemplateRef(hostTNode, hostLView) { if (hostTNode.type & 4 /* TNodeType.Container */) { ngDevMode && assertDefined(hostTNode.tView, 'TView must be allocated'); return new R3TemplateRef(hostLView, hostTNode, createElementRef(hostTNode, hostLView)); } return null; } /** * Represents a container where one or more views can be attached to a component. * * Can contain *host views* (created by instantiating a * component with the `createComponent()` method), and *embedded views* * (created by instantiating a `TemplateRef` with the `createEmbeddedView()` method). * * A view container instance can contain other view containers, * creating a [view hierarchy](guide/glossary#view-hierarchy). * * @see `ComponentRef` * @see `EmbeddedViewRef` * * @publicApi */ class ViewContainerRef {} /** * @internal * @nocollapse */ ViewContainerRef.__NG_ELEMENT_ID__ = injectViewContainerRef; /** * Creates a ViewContainerRef and stores it on the injector. Or, if the ViewContainerRef * already exists, retrieves the existing ViewContainerRef. * * @returns The ViewContainerRef instance to use */ function injectViewContainerRef() { const previousTNode = getCurrentTNode(); return createContainerRef(previousTNode, getLView()); } const VE_ViewContainerRef = ViewContainerRef; // TODO(alxhub): cleaning up this indirection triggers a subtle bug in Closure in g3. Once the fix // for that lands, this can be cleaned up. const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef { constructor(_lContainer, _hostTNode, _hostLView) { super(); this._lContainer = _lContainer; this._hostTNode = _hostTNode; this._hostLView = _hostLView; } get element() { return createElementRef(this._hostTNode, this._hostLView); } get injector() { return new NodeInjector(this._hostTNode, this._hostLView); } /** @deprecated No replacement */ get parentInjector() { const parentLocation = getParentInjectorLocation(this._hostTNode, this._hostLView); if (hasParentInjector(parentLocation)) { const parentView = getParentInjectorView(parentLocation, this._hostLView); const injectorIndex = getParentInjectorIndex(parentLocation); ngDevMode && assertNodeInjector(parentView, injectorIndex); const parentTNode = parentView[TVIEW].data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */]; return new NodeInjector(parentTNode, parentView); } else { return new NodeInjector(null, this._hostLView); } } clear() { while (this.length > 0) { this.remove(this.length - 1); } } get(index) { const viewRefs = getViewRefs(this._lContainer); return viewRefs !== null && viewRefs[index] || null; } get length() { return this._lContainer.length - CONTAINER_HEADER_OFFSET; } createEmbeddedView(templateRef, context, indexOrOptions) { let index; let injector; if (typeof indexOrOptions === 'number') { index = indexOrOptions; } else if (indexOrOptions != null) { index = indexOrOptions.index; injector = indexOrOptions.injector; } const viewRef = templateRef.createEmbeddedView(context || {}, injector); this.insert(viewRef, index); return viewRef; } createComponent(componentFactoryOrType, indexOrOptions, injector, projectableNodes, environmentInjector) { const isComponentFactory = componentFactoryOrType && !isType(componentFactoryOrType); let index; // This function supports 2 signatures and we need to handle options correctly for both: // 1. When first argument is a Component type. This signature also requires extra // options to be provided as as object (more ergonomic option). // 2. First argument is a Component factory. In this case extra options are represented as // positional arguments. This signature is less ergonomic and will be deprecated. if (isComponentFactory) { if (ngDevMode) { assertEqual(typeof indexOrOptions !== 'object', true, 'It looks like Component factory was provided as the first argument ' + 'and an options object as the second argument. This combination of arguments ' + 'is incompatible. You can either change the first argument to provide Component ' + 'type or change the second argument to be a number (representing an index at ' + 'which to insert the new component\'s host view into this container)'); } index = indexOrOptions; } else { if (ngDevMode) { assertDefined(getComponentDef(componentFactoryOrType), `Provided Component class doesn't contain Component definition. ` + `Please check whether provided class has @Component decorator.`); assertEqual(typeof indexOrOptions !== 'number', true, 'It looks like Component type was provided as the first argument ' + 'and a number (representing an index at which to insert the new component\'s ' + 'host view into this container as the second argument. This combination of arguments ' + 'is incompatible. Please use an object as the second argument instead.'); } const options = indexOrOptions || {}; if (ngDevMode && options.environmentInjector && options.ngModuleRef) { throwError(`Cannot pass both environmentInjector and ngModuleRef options to createComponent().`); } index = options.index; injector = options.injector; projectableNodes = options.projectableNodes; environmentInjector = options.environmentInjector || options.ngModuleRef; } const componentFactory = isComponentFactory ? componentFactoryOrType : new ComponentFactory(getComponentDef(componentFactoryOrType)); const contextInjector = injector || this.parentInjector; // If an `NgModuleRef` is not provided explicitly, try retrieving it from the DI tree. if (!environmentInjector && componentFactory.ngModule == null) { // For the `ComponentFactory` case, entering this logic is very unlikely, since we expect that // an instance of a `ComponentFactory`, resolved via `ComponentFactoryResolver` would have an // `ngModule` field. This is possible in some test scenarios and potentially in some JIT-based // use-cases. For the `ComponentFactory` case we preserve backwards-compatibility and try // using a provided injector first, then fall back to the parent injector of this // `ViewContainerRef` instance. // // For the factory-less case, it's critical to establish a connection with the module // injector tree (by retrieving an instance of an `NgModuleRef` and accessing its injector), // so that a component can use DI tokens provided in MgModules. For this reason, we can not // rely on the provided injector, since it might be detached from the DI tree (for example, if // it was created via `Injector.create` without specifying a parent injector, or if an // injector is retrieved from an `NgModuleRef` created via `createNgModule` using an // NgModule outside of a module tree). Instead, we always use `ViewContainerRef`'s parent // injector, which is normally connected to the DI tree, which includes module injector // subtree. const _injector = isComponentFactory ? contextInjector : this.parentInjector; // DO NOT REFACTOR. The code here used to have a `injector.get(NgModuleRef, null) || // undefined` expression which seems to cause internal google apps to fail. This is documented // in the following internal bug issue: go/b/142967802 const result = _injector.get(EnvironmentInjector, null); if (result) { environmentInjector = result; } } const componentRef = componentFactory.create(contextInjector, projectableNodes, undefined, environmentInjector); this.insert(componentRef.hostView, index); return componentRef; } insert(viewRef, index) { const lView = viewRef._lView; const tView = lView[TVIEW]; if (ngDevMode && viewRef.destroyed) { throw new Error('Cannot insert a destroyed View in a ViewContainer!'); } if (viewAttachedToContainer(lView)) { // If view is already attached, detach it first so we clean up references appropriately. const prevIdx = this.indexOf(viewRef); // A view might be attached either to this or a different container. The `prevIdx` for // those cases will be: // equal to -1 for views attached to this ViewContainerRef // >= 0 for views attached to a different ViewContainerRef if (prevIdx !== -1) { this.detach(prevIdx); } else { const prevLContainer = lView[PARENT]; ngDevMode && assertEqual(isLContainer(prevLContainer), true, 'An attached view should have its PARENT point to a container.'); // We need to re-create a R3ViewContainerRef instance since those are not stored on // LView (nor anywhere else). const prevVCRef = new R3ViewContainerRef(prevLContainer, prevLContainer[T_HOST], prevLContainer[PARENT]); prevVCRef.detach(prevVCRef.indexOf(viewRef)); } } // Logical operation of adding `LView` to `LContainer` const adjustedIdx = this._adjustIndex(index); const lContainer = this._lContainer; insertView(tView, lView, lContainer, adjustedIdx); // Physical operation of adding the DOM nodes. const beforeNode = getBeforeNodeForView(adjustedIdx, lContainer); const renderer = lView[RENDERER]; const parentRNode = nativeParentNode(renderer, lContainer[NATIVE]); if (parentRNode !== null) { addViewToContainer(tView, lContainer[T_HOST], renderer, lView, parentRNode, beforeNode); } viewRef.attachToViewContainerRef(); addToArray(getOrCreateViewRefs(lContainer), adjustedIdx, viewRef); return viewRef; } move(viewRef, newIndex) { if (ngDevMode && viewRef.destroyed) { throw new Error('Cannot move a destroyed View in a ViewContainer!'); } return this.insert(viewRef, newIndex); } indexOf(viewRef) { const viewRefsArr = getViewRefs(this._lContainer); return viewRefsArr !== null ? viewRefsArr.indexOf(viewRef) : -1; } remove(index) { const adjustedIdx = this._adjustIndex(index, -1); const detachedView = detachView(this._lContainer, adjustedIdx); if (detachedView) { // Before destroying the view, remove it from the container's array of `ViewRef`s. // This ensures the view container length is updated before calling // `destroyLView`, which could recursively call view container methods that // rely on an accurate container length. // (e.g. a method on this view container being called by a child directive's OnDestroy // lifecycle hook) removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx); destroyLView(detachedView[TVIEW], detachedView); } } detach(index) { const adjustedIdx = this._adjustIndex(index, -1); const view = detachView(this._lContainer, adjustedIdx); const wasDetached = view && removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx) != null; return wasDetached ? new ViewRef$1(view) : null; } _adjustIndex(index, shift = 0) { if (index == null) { return this.length + shift; } if (ngDevMode) { assertGreaterThan(index, -1, `ViewRef index must be positive, got ${index}`); // +1 because it's legal to insert at the end. assertLessThan(index, this.length + 1 + shift, 'index'); } return index; } }; function getViewRefs(lContainer) { return lContainer[VIEW_REFS]; } function getOrCreateViewRefs(lContainer) { return lContainer[VIEW_REFS] || (lContainer[VIEW_REFS] = []); } /** * Creates a ViewContainerRef and stores it on the injector. * * @param ViewContainerRefToken The ViewContainerRef type * @param ElementRefToken The ElementRef type * @param hostTNode The node that is requesting a ViewContainerRef * @param hostLView The view to which the node belongs * @returns The ViewContainerRef instance to use */ function createContainerRef(hostTNode, hostLView) { ngDevMode && assertTNodeType(hostTNode, 12 /* TNodeType.AnyContainer */ | 3 /* TNodeType.AnyRNode */); let lContainer; const slotValue = hostLView[hostTNode.index]; if (isLContainer(slotValue)) { // If the host is a container, we don't need to create a new LContainer lContainer = slotValue; } else { let commentNode; // If the host is an element container, the native host element is guaranteed to be a // comment and we can reuse that comment as anchor element for the new LContainer. // The comment node in question is already part of the DOM structure so we don't need to append // it again. if (hostTNode.type & 8 /* TNodeType.ElementContainer */) { commentNode = unwrapRNode(slotValue); } else { // If the host is a regular element, we have to insert a comment node manually which will // be used as an anchor when inserting elements. In this specific case we use low-level DOM // manipulation to insert it. const renderer = hostLView[RENDERER]; ngDevMode && ngDevMode.rendererCreateComment++; commentNode = renderer.createComment(ngDevMode ? 'container' : ''); const hostNative = getNativeByTNode(hostTNode, hostLView); const parentOfHostNative = nativeParentNode(renderer, hostNative); nativeInsertBefore(renderer, parentOfHostNative, commentNode, nativeNextSibling(renderer, hostNative), false); } hostLView[hostTNode.index] = lContainer = createLContainer(slotValue, hostLView, commentNode, hostTNode); addToViewTree(hostLView, lContainer); } return new R3ViewContainerRef(lContainer, hostTNode, hostLView); } class LQuery_ { constructor(queryList) { this.queryList = queryList; this.matches = null; } clone() { return new LQuery_(this.queryList); } setDirty() { this.queryList.setDirty(); } } class LQueries_ { constructor(queries = []) { this.queries = queries; } createEmbeddedView(tView) { const tQueries = tView.queries; if (tQueries !== null) { const noOfInheritedQueries = tView.contentQueries !== null ? tView.contentQueries[0] : tQueries.length; const viewLQueries = []; // An embedded view has queries propagated from a declaration view at the beginning of the // TQueries collection and up until a first content query declared in the embedded view. Only // propagated LQueries are created at this point (LQuery corresponding to declared content // queries will be instantiated from the content query instructions for each directive). for (let i = 0; i < noOfInheritedQueries; i++) { const tQuery = tQueries.getByIndex(i); const parentLQuery = this.queries[tQuery.indexInDeclarationView]; viewLQueries.push(parentLQuery.clone()); } return new LQueries_(viewLQueries); } return null; } insertView(tView) { this.dirtyQueriesWithMatches(tView); } detachView(tView) { this.dirtyQueriesWithMatches(tView); } dirtyQueriesWithMatches(tView) { for (let i = 0; i < this.queries.length; i++) { if (getTQuery(tView, i).matches !== null) { this.queries[i].setDirty(); } } } } class TQueryMetadata_ { constructor(predicate, flags, read = null) { this.predicate = predicate; this.flags = flags; this.read = read; } } class TQueries_ { constructor(queries = []) { this.queries = queries; } elementStart(tView, tNode) { ngDevMode && assertFirstCreatePass(tView, 'Queries should collect results on the first template pass only'); for (let i = 0; i < this.queries.length; i++) { this.queries[i].elementStart(tView, tNode); } } elementEnd(tNode) { for (let i = 0; i < this.queries.length; i++) { this.queries[i].elementEnd(tNode); } } embeddedTView(tNode) { let queriesForTemplateRef = null; for (let i = 0; i < this.length; i++) { const childQueryIndex = queriesForTemplateRef !== null ? queriesForTemplateRef.length : 0; const tqueryClone = this.getByIndex(i).embeddedTView(tNode, childQueryIndex); if (tqueryClone) { tqueryClone.indexInDeclarationView = i; if (queriesForTemplateRef !== null) { queriesForTemplateRef.push(tqueryClone); } else { queriesForTemplateRef = [tqueryClone]; } } } return queriesForTemplateRef !== null ? new TQueries_(queriesForTemplateRef) : null; } template(tView, tNode) { ngDevMode && assertFirstCreatePass(tView, 'Queries should collect results on the first template pass only'); for (let i = 0; i < this.queries.length; i++) { this.queries[i].template(tView, tNode); } } getByIndex(index) { ngDevMode && assertIndexInRange(this.queries, index); return this.queries[index]; } get length() { return this.queries.length; } track(tquery) { this.queries.push(tquery); } } class TQuery_ { constructor(metadata, nodeIndex = -1) { this.metadata = metadata; this.matches = null; this.indexInDeclarationView = -1; this.crossesNgTemplate = false; /** * A flag indicating if a given query still applies to nodes it is crossing. We use this flag * (alongside with _declarationNodeIndex) to know when to stop applying content queries to * elements in a template. */ this._appliesToNextNode = true; this._declarationNodeIndex = nodeIndex; } elementStart(tView, tNode) { if (this.isApplyingToNode(tNode)) { this.matchTNode(tView, tNode); } } elementEnd(tNode) { if (this._declarationNodeIndex === tNode.index) { this._appliesToNextNode = false; } } template(tView, tNode) { this.elementStart(tView, tNode); } embeddedTView(tNode, childQueryIndex) { if (this.isApplyingToNode(tNode)) { this.crossesNgTemplate = true; // A marker indicating a `<ng-template>` element (a placeholder for query results from // embedded views created based on this `<ng-template>`). this.addMatch(-tNode.index, childQueryIndex); return new TQuery_(this.metadata); } return null; } isApplyingToNode(tNode) { if (this._appliesToNextNode && (this.metadata.flags & 1 /* QueryFlags.descendants */) !== 1 /* QueryFlags.descendants */) { const declarationNodeIdx = this._declarationNodeIndex; let parent = tNode.parent; // Determine if a given TNode is a "direct" child of a node on which a content query was // declared (only direct children of query's host node can match with the descendants: false // option). There are 3 main use-case / conditions to consider here: // - <needs-target><i #target></i></needs-target>: here <i #target> parent node is a query // host node; // - <needs-target><ng-template [ngIf]="true"><i #target></i></ng-template></needs-target>: // here <i #target> parent node is null; // - <needs-target><ng-container><i #target></i></ng-container></needs-target>: here we need // to go past `<ng-container>` to determine <i #target> parent node (but we shouldn't traverse // up past the query's host node!). while (parent !== null && parent.type & 8 /* TNodeType.ElementContainer */ && parent.index !== declarationNodeIdx) { parent = parent.parent; } return declarationNodeIdx === (parent !== null ? parent.index : -1); } return this._appliesToNextNode; } matchTNode(tView, tNode) { const predicate = this.metadata.predicate; if (Array.isArray(predicate)) { for (let i = 0; i < predicate.length; i++) { const name = predicate[i]; this.matchTNodeWithReadOption(tView, tNode, getIdxOfMatchingSelector(tNode, name)); // Also try matching the name to a provider since strings can be used as DI tokens too. this.matchTNodeWithReadOption(tView, tNode, locateDirectiveOrProvider(tNode, tView, name, false, false)); } } else { if (predicate === TemplateRef) { if (tNode.type & 4 /* TNodeType.Container */) { this.matchTNodeWithReadOption(tView, tNode, -1); } } else { this.matchTNodeWithReadOption(tView, tNode, locateDirectiveOrProvider(tNode, tView, predicate, false, false)); } } } matchTNodeWithReadOption(tView, tNode, nodeMatchIdx) { if (nodeMatchIdx !== null) { const read = this.metadata.read; if (read !== null) { if (read === ElementRef || read === ViewContainerRef || read === TemplateRef && tNode.type & 4 /* TNodeType.Container */) { this.addMatch(tNode.index, -2); } else { const directiveOrProviderIdx = locateDirectiveOrProvider(tNode, tView, read, false, false); if (directiveOrProviderIdx !== null) { this.addMatch(tNode.index, directiveOrProviderIdx); } } } else { this.addMatch(tNode.index, nodeMatchIdx); } } } addMatch(tNodeIdx, matchIdx) { if (this.matches === null) { this.matches = [tNodeIdx, matchIdx]; } else { this.matches.push(tNodeIdx, matchIdx); } } } /** * Iterates over local names for a given node and returns directive index * (or -1 if a local name points to an element). * * @param tNode static data of a node to check * @param selector selector to match * @returns directive index, -1 or null if a selector didn't match any of the local names */ function getIdxOfMatchingSelector(tNode, selector) { const localNames = tNode.localNames; if (localNames !== null) { for (let i = 0; i < localNames.length; i += 2) { if (localNames[i] === selector) { return localNames[i + 1]; } } } return null; } function createResultByTNodeType(tNode, currentView) { if (tNode.type & (3 /* TNodeType.AnyRNode */ | 8 /* TNodeType.ElementContainer */)) { return createElementRef(tNode, currentView); } else if (tNode.type & 4 /* TNodeType.Container */) { return createTemplateRef(tNode, currentView); } return null; } function createResultForNode(lView, tNode, matchingIdx, read) { if (matchingIdx === -1) { // if read token and / or strategy is not specified, detect it using appropriate tNode type return createResultByTNodeType(tNode, lView); } else if (matchingIdx === -2) { // read a special token from a node injector return createSpecialToken(lView, tNode, read); } else { // read a token return getNodeInjectable(lView, lView[TVIEW], matchingIdx, tNode); } } function createSpecialToken(lView, tNode, read) { if (read === ElementRef) { return createElementRef(tNode, lView); } else if (read === TemplateRef) { return createTemplateRef(tNode, lView); } else if (read === ViewContainerRef) { ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */); return createContainerRef(tNode, lView); } else { ngDevMode && throwError(`Special token to read should be one of ElementRef, TemplateRef or ViewContainerRef but got ${stringify(read)}.`); } } /** * A helper function that creates query results for a given view. This function is meant to do the * processing once and only once for a given view instance (a set of results for a given view * doesn't change). */ function materializeViewResults(tView, lView, tQuery, queryIndex) { const lQuery = lView[QUERIES].queries[queryIndex]; if (lQuery.matches === null) { const tViewData = tView.data; const tQueryMatches = tQuery.matches; const result = []; for (let i = 0; i < tQueryMatches.length; i += 2) { const matchedNodeIdx = tQueryMatches[i]; if (matchedNodeIdx < 0) { // we at the <ng-template> marker which might have results in views created based on this // <ng-template> - those results will be in separate views though, so here we just leave // null as a placeholder result.push(null); } else { ngDevMode && assertIndexInRange(tViewData, matchedNodeIdx); const tNode = tViewData[matchedNodeIdx]; result.push(createResultForNode(lView, tNode, tQueryMatches[i + 1], tQuery.metadata.read)); } } lQuery.matches = result; } return lQuery.matches; } /** * A helper function that collects (already materialized) query results from a tree of views, * starting with a provided LView. */ function collectQueryResults(tView, lView, queryIndex, result) { const tQuery = tView.queries.getByIndex(queryIndex); const tQueryMatches = tQuery.matches; if (tQueryMatches !== null) { const lViewResults = materializeViewResults(tView, lView, tQuery, queryIndex); for (let i = 0; i < tQueryMatches.length; i += 2) { const tNodeIdx = tQueryMatches[i]; if (tNodeIdx > 0) { result.push(lViewResults[i / 2]); } else { const childQueryIndex = tQueryMatches[i + 1]; const declarationLContainer = lView[-tNodeIdx]; ngDevMode && assertLContainer(declarationLContainer); // collect matches for views inserted in this container for (let i = CONTAINER_HEADER_OFFSET; i < declarationLContainer.length; i++) { const embeddedLView = declarationLContainer[i]; if (embeddedLView[DECLARATION_LCONTAINER] === embeddedLView[PARENT]) { collectQueryResults(embeddedLView[TVIEW], embeddedLView, childQueryIndex, result); } } // collect matches for views created from this declaration container and inserted into // different containers if (declarationLContainer[MOVED_VIEWS] !== null) { const embeddedLViews = declarationLContainer[MOVED_VIEWS]; for (let i = 0; i < embeddedLViews.length; i++) { const embeddedLView = embeddedLViews[i]; collectQueryResults(embeddedLView[TVIEW], embeddedLView, childQueryIndex, result); } } } } } return result; } /** * Refreshes a query by combining matches from all active views and removing matches from deleted * views. * * @returns `true` if a query got dirty during change detection or if this is a static query * resolving in creation mode, `false` otherwise. * * @codeGenApi */ function ɵɵqueryRefresh(queryList) { const lView = getLView(); const tView = getTView(); const queryIndex = getCurrentQueryIndex(); setCurrentQueryIndex(queryIndex + 1); const tQuery = getTQuery(tView, queryIndex); if (queryList.dirty && isCreationMode(lView) === ((tQuery.metadata.flags & 2 /* QueryFlags.isStatic */) === 2 /* QueryFlags.isStatic */)) { if (tQuery.matches === null) { queryList.reset([]); } else { const result = tQuery.crossesNgTemplate ? collectQueryResults(tView, lView, queryIndex, []) : materializeViewResults(tView, lView, tQuery, queryIndex); queryList.reset(result, unwrapElementRef); queryList.notifyOnChanges(); } return true; } return false; } /** * Creates new QueryList, stores the reference in LView and returns QueryList. * * @param predicate The type for which the query will search * @param flags Flags associated with the query * @param read What to save in the query * * @codeGenApi */ function ɵɵviewQuery(predicate, flags, read) { ngDevMode && assertNumber(flags, 'Expecting flags'); const tView = getTView(); if (tView.firstCreatePass) { createTQuery(tView, new TQueryMetadata_(predicate, flags, read), -1); if ((flags & 2 /* QueryFlags.isStatic */) === 2 /* QueryFlags.isStatic */) { tView.staticViewQueries = true; } } createLQuery(tView, getLView(), flags); } /** * Registers a QueryList, associated with a content query, for later refresh (part of a view * refresh). * * @param directiveIndex Current directive index * @param predicate The type for which the query will search * @param flags Flags associated with the query * @param read What to save in the query * @returns QueryList<T> * * @codeGenApi */ function ɵɵcontentQuery(directiveIndex, predicate, flags, read) { ngDevMode && assertNumber(flags, 'Expecting flags'); const tView = getTView(); if (tView.firstCreatePass) { const tNode = getCurrentTNode(); createTQuery(tView, new TQueryMetadata_(predicate, flags, read), tNode.index); saveContentQueryAndDirectiveIndex(tView, directiveIndex); if ((flags & 2 /* QueryFlags.isStatic */) === 2 /* QueryFlags.isStatic */) { tView.staticContentQueries = true; } } createLQuery(tView, getLView(), flags); } /** * Loads a QueryList corresponding to the current view or content query. * * @codeGenApi */ function ɵɵloadQuery() { return loadQueryInternal(getLView(), getCurrentQueryIndex()); } function loadQueryInternal(lView, queryIndex) { ngDevMode && assertDefined(lView[QUERIES], 'LQueries should be defined when trying to load a query'); ngDevMode && assertIndexInRange(lView[QUERIES].queries, queryIndex); return lView[QUERIES].queries[queryIndex].queryList; } function createLQuery(tView, lView, flags) { const queryList = new QueryList((flags & 4 /* QueryFlags.emitDistinctChangesOnly */) === 4 /* QueryFlags.emitDistinctChangesOnly */); storeCleanupWithContext(tView, lView, queryList, queryList.destroy); if (lView[QUERIES] === null) lView[QUERIES] = new LQueries_(); lView[QUERIES].queries.push(new LQuery_(queryList)); } function createTQuery(tView, metadata, nodeIndex) { if (tView.queries === null) tView.queries = new TQueries_(); tView.queries.track(new TQuery_(metadata, nodeIndex)); } function saveContentQueryAndDirectiveIndex(tView, directiveIndex) { const tViewContentQueries = tView.contentQueries || (tView.contentQueries = []); const lastSavedDirectiveIndex = tViewContentQueries.length ? tViewContentQueries[tViewContentQueries.length - 1] : -1; if (directiveIndex !== lastSavedDirectiveIndex) { tViewContentQueries.push(tView.queries.length - 1, directiveIndex); } } function getTQuery(tView, index) { ngDevMode && assertDefined(tView.queries, 'TQueries must be defined to retrieve a TQuery'); return tView.queries.getByIndex(index); } /** * Retrieves `TemplateRef` instance from `Injector` when a local reference is placed on the * `<ng-template>` element. * * @codeGenApi */ function ɵɵtemplateRefExtractor(tNode, lView) { return createTemplateRef(tNode, lView); } /** * A mapping of the @angular/core API surface used in generated expressions to the actual symbols. * * This should be kept up to date with the public exports of @angular/core. */ const angularCoreEnv = (() => ({ 'ɵɵattribute': ɵɵattribute, 'ɵɵattributeInterpolate1': ɵɵattributeInterpolate1, 'ɵɵattributeInterpolate2': ɵɵattributeInterpolate2, 'ɵɵattributeInterpolate3': ɵɵattributeInterpolate3, 'ɵɵattributeInterpolate4': ɵɵattributeInterpolate4, 'ɵɵattributeInterpolate5': ɵɵattributeInterpolate5, 'ɵɵattributeInterpolate6': ɵɵattributeInterpolate6, 'ɵɵattributeInterpolate7': ɵɵattributeInterpolate7, 'ɵɵattributeInterpolate8': ɵɵattributeInterpolate8, 'ɵɵattributeInterpolateV': ɵɵattributeInterpolateV, 'ɵɵdefineComponent': ɵɵdefineComponent, 'ɵɵdefineDirective': ɵɵdefineDirective, 'ɵɵdefineInjectable': ɵɵdefineInjectable, 'ɵɵdefineInjector': ɵɵdefineInjector, 'ɵɵdefineNgModule': ɵɵdefineNgModule, 'ɵɵdefinePipe': ɵɵdefinePipe, 'ɵɵdirectiveInject': ɵɵdirectiveInject, 'ɵɵgetInheritedFactory': ɵɵgetInheritedFactory, 'ɵɵinject': ɵɵinject, 'ɵɵinjectAttribute': ɵɵinjectAttribute, 'ɵɵinvalidFactory': ɵɵinvalidFactory, 'ɵɵinvalidFactoryDep': ɵɵinvalidFactoryDep, 'ɵɵtemplateRefExtractor': ɵɵtemplateRefExtractor, 'ɵɵresetView': ɵɵresetView, 'ɵɵHostDirectivesFeature': ɵɵHostDirectivesFeature, 'ɵɵNgOnChangesFeature': ɵɵNgOnChangesFeature, 'ɵɵProvidersFeature': ɵɵProvidersFeature, 'ɵɵCopyDefinitionFeature': ɵɵCopyDefinitionFeature, 'ɵɵInheritDefinitionFeature': ɵɵInheritDefinitionFeature, 'ɵɵStandaloneFeature': ɵɵStandaloneFeature, 'ɵɵnextContext': ɵɵnextContext, 'ɵɵnamespaceHTML': ɵɵnamespaceHTML, 'ɵɵnamespaceMathML': ɵɵnamespaceMathML, 'ɵɵnamespaceSVG': ɵɵnamespaceSVG, 'ɵɵenableBindings': ɵɵenableBindings, 'ɵɵdisableBindings': ɵɵdisableBindings, 'ɵɵelementStart': ɵɵelementStart, 'ɵɵelementEnd': ɵɵelementEnd, 'ɵɵelement': ɵɵelement, 'ɵɵelementContainerStart': ɵɵelementContainerStart, 'ɵɵelementContainerEnd': ɵɵelementContainerEnd, 'ɵɵelementContainer': ɵɵelementContainer, 'ɵɵpureFunction0': ɵɵpureFunction0, 'ɵɵpureFunction1': ɵɵpureFunction1, 'ɵɵpureFunction2': ɵɵpureFunction2, 'ɵɵpureFunction3': ɵɵpureFunction3, 'ɵɵpureFunction4': ɵɵpureFunction4, 'ɵɵpureFunction5': ɵɵpureFunction5, 'ɵɵpureFunction6': ɵɵpureFunction6, 'ɵɵpureFunction7': ɵɵpureFunction7, 'ɵɵpureFunction8': ɵɵpureFunction8, 'ɵɵpureFunctionV': ɵɵpureFunctionV, 'ɵɵgetCurrentView': ɵɵgetCurrentView, 'ɵɵrestoreView': ɵɵrestoreView, 'ɵɵlistener': ɵɵlistener, 'ɵɵprojection': ɵɵprojection, 'ɵɵsyntheticHostProperty': ɵɵsyntheticHostProperty, 'ɵɵsyntheticHostListener': ɵɵsyntheticHostListener, 'ɵɵpipeBind1': ɵɵpipeBind1, 'ɵɵpipeBind2': ɵɵpipeBind2, 'ɵɵpipeBind3': ɵɵpipeBind3, 'ɵɵpipeBind4': ɵɵpipeBind4, 'ɵɵpipeBindV': ɵɵpipeBindV, 'ɵɵprojectionDef': ɵɵprojectionDef, 'ɵɵhostProperty': ɵɵhostProperty, 'ɵɵproperty': ɵɵproperty, 'ɵɵpropertyInterpolate': ɵɵpropertyInterpolate, 'ɵɵpropertyInterpolate1': ɵɵpropertyInterpolate1, 'ɵɵpropertyInterpolate2': ɵɵpropertyInterpolate2, 'ɵɵpropertyInterpolate3': ɵɵpropertyInterpolate3, 'ɵɵpropertyInterpolate4': ɵɵpropertyInterpolate4, 'ɵɵpropertyInterpolate5': ɵɵpropertyInterpolate5, 'ɵɵpropertyInterpolate6': ɵɵpropertyInterpolate6, 'ɵɵpropertyInterpolate7': ɵɵpropertyInterpolate7, 'ɵɵpropertyInterpolate8': ɵɵpropertyInterpolate8, 'ɵɵpropertyInterpolateV': ɵɵpropertyInterpolateV, 'ɵɵpipe': ɵɵpipe, 'ɵɵqueryRefresh': ɵɵqueryRefresh, 'ɵɵviewQuery': ɵɵviewQuery, 'ɵɵloadQuery': ɵɵloadQuery, 'ɵɵcontentQuery': ɵɵcontentQuery, 'ɵɵreference': ɵɵreference, 'ɵɵclassMap': ɵɵclassMap, 'ɵɵclassMapInterpolate1': ɵɵclassMapInterpolate1, 'ɵɵclassMapInterpolate2': ɵɵclassMapInterpolate2, 'ɵɵclassMapInterpolate3': ɵɵclassMapInterpolate3, 'ɵɵclassMapInterpolate4': ɵɵclassMapInterpolate4, 'ɵɵclassMapInterpolate5': ɵɵclassMapInterpolate5, 'ɵɵclassMapInterpolate6': ɵɵclassMapInterpolate6, 'ɵɵclassMapInterpolate7': ɵɵclassMapInterpolate7, 'ɵɵclassMapInterpolate8': ɵɵclassMapInterpolate8, 'ɵɵclassMapInterpolateV': ɵɵclassMapInterpolateV, 'ɵɵstyleMap': ɵɵstyleMap, 'ɵɵstyleMapInterpolate1': ɵɵstyleMapInterpolate1, 'ɵɵstyleMapInterpolate2': ɵɵstyleMapInterpolate2, 'ɵɵstyleMapInterpolate3': ɵɵstyleMapInterpolate3, 'ɵɵstyleMapInterpolate4': ɵɵstyleMapInterpolate4, 'ɵɵstyleMapInterpolate5': ɵɵstyleMapInterpolate5, 'ɵɵstyleMapInterpolate6': ɵɵstyleMapInterpolate6, 'ɵɵstyleMapInterpolate7': ɵɵstyleMapInterpolate7, 'ɵɵstyleMapInterpolate8': ɵɵstyleMapInterpolate8, 'ɵɵstyleMapInterpolateV': ɵɵstyleMapInterpolateV, 'ɵɵstyleProp': ɵɵstyleProp, 'ɵɵstylePropInterpolate1': ɵɵstylePropInterpolate1, 'ɵɵstylePropInterpolate2': ɵɵstylePropInterpolate2, 'ɵɵstylePropInterpolate3': ɵɵstylePropInterpolate3, 'ɵɵstylePropInterpolate4': ɵɵstylePropInterpolate4, 'ɵɵstylePropInterpolate5': ɵɵstylePropInterpolate5, 'ɵɵstylePropInterpolate6': ɵɵstylePropInterpolate6, 'ɵɵstylePropInterpolate7': ɵɵstylePropInterpolate7, 'ɵɵstylePropInterpolate8': ɵɵstylePropInterpolate8, 'ɵɵstylePropInterpolateV': ɵɵstylePropInterpolateV, 'ɵɵclassProp': ɵɵclassProp, 'ɵɵadvance': ɵɵadvance, 'ɵɵtemplate': ɵɵtemplate, 'ɵɵtext': ɵɵtext, 'ɵɵtextInterpolate': ɵɵtextInterpolate, 'ɵɵtextInterpolate1': ɵɵtextInterpolate1, 'ɵɵtextInterpolate2': ɵɵtextInterpolate2, 'ɵɵtextInterpolate3': ɵɵtextInterpolate3, 'ɵɵtextInterpolate4': ɵɵtextInterpolate4, 'ɵɵtextInterpolate5': ɵɵtextInterpolate5, 'ɵɵtextInterpolate6': ɵɵtextInterpolate6, 'ɵɵtextInterpolate7': ɵɵtextInterpolate7, 'ɵɵtextInterpolate8': ɵɵtextInterpolate8, 'ɵɵtextInterpolateV': ɵɵtextInterpolateV, 'ɵɵi18n': ɵɵi18n, 'ɵɵi18nAttributes': ɵɵi18nAttributes, 'ɵɵi18nExp': ɵɵi18nExp, 'ɵɵi18nStart': ɵɵi18nStart, 'ɵɵi18nEnd': ɵɵi18nEnd, 'ɵɵi18nApply': ɵɵi18nApply, 'ɵɵi18nPostprocess': ɵɵi18nPostprocess, 'ɵɵresolveWindow': ɵɵresolveWindow, 'ɵɵresolveDocument': ɵɵresolveDocument, 'ɵɵresolveBody': ɵɵresolveBody, 'ɵɵsetComponentScope': ɵɵsetComponentScope, 'ɵɵsetNgModuleScope': ɵɵsetNgModuleScope, 'ɵɵregisterNgModuleType': registerNgModuleType, 'ɵɵsanitizeHtml': ɵɵsanitizeHtml, 'ɵɵsanitizeStyle': ɵɵsanitizeStyle, 'ɵɵsanitizeResourceUrl': ɵɵsanitizeResourceUrl, 'ɵɵsanitizeScript': ɵɵsanitizeScript, 'ɵɵsanitizeUrl': ɵɵsanitizeUrl, 'ɵɵsanitizeUrlOrResourceUrl': ɵɵsanitizeUrlOrResourceUrl, 'ɵɵtrustConstantHtml': ɵɵtrustConstantHtml, 'ɵɵtrustConstantResourceUrl': ɵɵtrustConstantResourceUrl, 'ɵɵvalidateIframeAttribute': ɵɵvalidateIframeAttribute, 'forwardRef': forwardRef, 'resolveForwardRef': resolveForwardRef }))(); let jitOptions = null; function setJitOptions(options) { if (jitOptions !== null) { if (options.defaultEncapsulation !== jitOptions.defaultEncapsulation) { ngDevMode && console.error('Provided value for `defaultEncapsulation` can not be changed once it has been set.'); return; } if (options.preserveWhitespaces !== jitOptions.preserveWhitespaces) { ngDevMode && console.error('Provided value for `preserveWhitespaces` can not be changed once it has been set.'); return; } } jitOptions = options; } function getJitOptions() { return jitOptions; } function resetJitOptions() { jitOptions = null; } function patchModuleCompilation() { // Does nothing, but exists as a target for patching. } function isModuleWithProviders(value) { return value.ngModule !== undefined; } function isNgModule(value) { return !!getNgModuleDef(value); } const moduleQueue = []; /** * Enqueues moduleDef to be checked later to see if scope can be set on its * component declarations. */ function enqueueModuleForDelayedScoping(moduleType, ngModule) { moduleQueue.push({ moduleType, ngModule }); } let flushingModuleQueue = false; /** * Loops over queued module definitions, if a given module definition has all of its * declarations resolved, it dequeues that module definition and sets the scope on * its declarations. */ function flushModuleScopingQueueAsMuchAsPossible() { if (!flushingModuleQueue) { flushingModuleQueue = true; try { for (let i = moduleQueue.length - 1; i >= 0; i--) { const { moduleType, ngModule } = moduleQueue[i]; if (ngModule.declarations && ngModule.declarations.every(isResolvedDeclaration)) { // dequeue moduleQueue.splice(i, 1); setScopeOnDeclaredComponents(moduleType, ngModule); } } } finally { flushingModuleQueue = false; } } } /** * Returns truthy if a declaration has resolved. If the declaration happens to be * an array of declarations, it will recurse to check each declaration in that array * (which may also be arrays). */ function isResolvedDeclaration(declaration) { if (Array.isArray(declaration)) { return declaration.every(isResolvedDeclaration); } return !!resolveForwardRef(declaration); } /** * Compiles a module in JIT mode. * * This function automatically gets called when a class has a `@NgModule` decorator. */ function compileNgModule(moduleType, ngModule = {}) { patchModuleCompilation(); compileNgModuleDefs(moduleType, ngModule); if (ngModule.id !== undefined) { registerNgModuleType(moduleType, ngModule.id); } // Because we don't know if all declarations have resolved yet at the moment the // NgModule decorator is executing, we're enqueueing the setting of module scope // on its declarations to be run at a later time when all declarations for the module, // including forward refs, have resolved. enqueueModuleForDelayedScoping(moduleType, ngModule); } /** * Compiles and adds the `ɵmod`, `ɵfac` and `ɵinj` properties to the module class. * * It's possible to compile a module via this API which will allow duplicate declarations in its * root. */ function compileNgModuleDefs(moduleType, ngModule, allowDuplicateDeclarationsInRoot = false) { ngDevMode && assertDefined(moduleType, 'Required value moduleType'); ngDevMode && assertDefined(ngModule, 'Required value ngModule'); const declarations = flatten(ngModule.declarations || EMPTY_ARRAY); let ngModuleDef = null; Object.defineProperty(moduleType, NG_MOD_DEF, { configurable: true, get: () => { if (ngModuleDef === null) { if (ngDevMode && ngModule.imports && ngModule.imports.indexOf(moduleType) > -1) { // We need to assert this immediately, because allowing it to continue will cause it to // go into an infinite loop before we've reached the point where we throw all the errors. throw new Error(`'${stringifyForError(moduleType)}' module can't import itself`); } const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'NgModule', type: moduleType }); ngModuleDef = compiler.compileNgModule(angularCoreEnv, `ng:///${moduleType.name}/ɵmod.js`, { type: moduleType, bootstrap: flatten(ngModule.bootstrap || EMPTY_ARRAY).map(resolveForwardRef), declarations: declarations.map(resolveForwardRef), imports: flatten(ngModule.imports || EMPTY_ARRAY).map(resolveForwardRef).map(expandModuleWithProviders), exports: flatten(ngModule.exports || EMPTY_ARRAY).map(resolveForwardRef).map(expandModuleWithProviders), schemas: ngModule.schemas ? flatten(ngModule.schemas) : null, id: ngModule.id || null }); // Set `schemas` on ngModuleDef to an empty array in JIT mode to indicate that runtime // should verify that there are no unknown elements in a template. In AOT mode, that check // happens at compile time and `schemas` information is not present on Component and Module // defs after compilation (so the check doesn't happen the second time at runtime). if (!ngModuleDef.schemas) { ngModuleDef.schemas = []; } } return ngModuleDef; } }); let ngFactoryDef = null; Object.defineProperty(moduleType, NG_FACTORY_DEF, { get: () => { if (ngFactoryDef === null) { const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'NgModule', type: moduleType }); ngFactoryDef = compiler.compileFactory(angularCoreEnv, `ng:///${moduleType.name}/ɵfac.js`, { name: moduleType.name, type: moduleType, deps: reflectDependencies(moduleType), target: compiler.FactoryTarget.NgModule, typeArgumentCount: 0 }); } return ngFactoryDef; }, // Make the property configurable in dev mode to allow overriding in tests configurable: !!ngDevMode }); let ngInjectorDef = null; Object.defineProperty(moduleType, NG_INJ_DEF, { get: () => { if (ngInjectorDef === null) { ngDevMode && verifySemanticsOfNgModuleDef(moduleType, allowDuplicateDeclarationsInRoot); const meta = { name: moduleType.name, type: moduleType, providers: ngModule.providers || EMPTY_ARRAY, imports: [(ngModule.imports || EMPTY_ARRAY).map(resolveForwardRef), (ngModule.exports || EMPTY_ARRAY).map(resolveForwardRef)] }; const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'NgModule', type: moduleType }); ngInjectorDef = compiler.compileInjector(angularCoreEnv, `ng:///${moduleType.name}/ɵinj.js`, meta); } return ngInjectorDef; }, // Make the property configurable in dev mode to allow overriding in tests configurable: !!ngDevMode }); } function generateStandaloneInDeclarationsError(type, location) { const prefix = `Unexpected "${stringifyForError(type)}" found in the "declarations" array of the`; const suffix = `"${stringifyForError(type)}" is marked as standalone and can't be declared ` + 'in any NgModule - did you intend to import it instead (by adding it to the "imports" array)?'; return `${prefix} ${location}, ${suffix}`; } function verifySemanticsOfNgModuleDef(moduleType, allowDuplicateDeclarationsInRoot, importingModule) { if (verifiedNgModule.get(moduleType)) return; // skip verifications of standalone components, directives, and pipes if (isStandalone(moduleType)) return; verifiedNgModule.set(moduleType, true); moduleType = resolveForwardRef(moduleType); let ngModuleDef; if (importingModule) { ngModuleDef = getNgModuleDef(moduleType); if (!ngModuleDef) { throw new Error(`Unexpected value '${moduleType.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`); } } else { ngModuleDef = getNgModuleDef(moduleType, true); } const errors = []; const declarations = maybeUnwrapFn(ngModuleDef.declarations); const imports = maybeUnwrapFn(ngModuleDef.imports); flatten(imports).map(unwrapModuleWithProvidersImports).forEach(modOrStandaloneCmpt => { verifySemanticsOfNgModuleImport(modOrStandaloneCmpt, moduleType); verifySemanticsOfNgModuleDef(modOrStandaloneCmpt, false, moduleType); }); const exports = maybeUnwrapFn(ngModuleDef.exports); declarations.forEach(verifyDeclarationsHaveDefinitions); declarations.forEach(verifyDirectivesHaveSelector); declarations.forEach(declarationType => verifyNotStandalone(declarationType, moduleType)); const combinedDeclarations = [...declarations.map(resolveForwardRef), ...flatten(imports.map(computeCombinedExports)).map(resolveForwardRef)]; exports.forEach(verifyExportsAreDeclaredOrReExported); declarations.forEach(decl => verifyDeclarationIsUnique(decl, allowDuplicateDeclarationsInRoot)); declarations.forEach(verifyComponentEntryComponentsIsPartOfNgModule); const ngModule = getAnnotation(moduleType, 'NgModule'); if (ngModule) { ngModule.imports && flatten(ngModule.imports).map(unwrapModuleWithProvidersImports).forEach(mod => { verifySemanticsOfNgModuleImport(mod, moduleType); verifySemanticsOfNgModuleDef(mod, false, moduleType); }); ngModule.bootstrap && deepForEach(ngModule.bootstrap, verifyCorrectBootstrapType); ngModule.bootstrap && deepForEach(ngModule.bootstrap, verifyComponentIsPartOfNgModule); ngModule.entryComponents && deepForEach(ngModule.entryComponents, verifyComponentIsPartOfNgModule); } // Throw Error if any errors were detected. if (errors.length) { throw new Error(errors.join('\n')); } //////////////////////////////////////////////////////////////////////////////////////////////// function verifyDeclarationsHaveDefinitions(type) { type = resolveForwardRef(type); const def = getComponentDef(type) || getDirectiveDef(type) || getPipeDef$1(type); if (!def) { errors.push(`Unexpected value '${stringifyForError(type)}' declared by the module '${stringifyForError(moduleType)}'. Please add a @Pipe/@Directive/@Component annotation.`); } } function verifyDirectivesHaveSelector(type) { type = resolveForwardRef(type); const def = getDirectiveDef(type); if (!getComponentDef(type) && def && def.selectors.length == 0) { errors.push(`Directive ${stringifyForError(type)} has no selector, please add it!`); } } function verifyNotStandalone(type, moduleType) { type = resolveForwardRef(type); const def = getComponentDef(type) || getDirectiveDef(type) || getPipeDef$1(type); if (def?.standalone) { const location = `"${stringifyForError(moduleType)}" NgModule`; errors.push(generateStandaloneInDeclarationsError(type, location)); } } function verifyExportsAreDeclaredOrReExported(type) { type = resolveForwardRef(type); const kind = getComponentDef(type) && 'component' || getDirectiveDef(type) && 'directive' || getPipeDef$1(type) && 'pipe'; if (kind) { // only checked if we are declared as Component, Directive, or Pipe // Modules don't need to be declared or imported. if (combinedDeclarations.lastIndexOf(type) === -1) { // We are exporting something which we don't explicitly declare or import. errors.push(`Can't export ${kind} ${stringifyForError(type)} from ${stringifyForError(moduleType)} as it was neither declared nor imported!`); } } } function verifyDeclarationIsUnique(type, suppressErrors) { type = resolveForwardRef(type); const existingModule = ownerNgModule.get(type); if (existingModule && existingModule !== moduleType) { if (!suppressErrors) { const modules = [existingModule, moduleType].map(stringifyForError).sort(); errors.push(`Type ${stringifyForError(type)} is part of the declarations of 2 modules: ${modules[0]} and ${modules[1]}! ` + `Please consider moving ${stringifyForError(type)} to a higher module that imports ${modules[0]} and ${modules[1]}. ` + `You can also create a new NgModule that exports and includes ${stringifyForError(type)} then import that NgModule in ${modules[0]} and ${modules[1]}.`); } } else { // Mark type as having owner. ownerNgModule.set(type, moduleType); } } function verifyComponentIsPartOfNgModule(type) { type = resolveForwardRef(type); const existingModule = ownerNgModule.get(type); if (!existingModule && !isStandalone(type)) { errors.push(`Component ${stringifyForError(type)} is not part of any NgModule or the module has not been imported into your module.`); } } function verifyCorrectBootstrapType(type) { type = resolveForwardRef(type); if (!getComponentDef(type)) { errors.push(`${stringifyForError(type)} cannot be used as an entry component.`); } if (isStandalone(type)) { // Note: this error should be the same as the // `NGMODULE_BOOTSTRAP_IS_STANDALONE` one in AOT compiler. errors.push(`The \`${stringifyForError(type)}\` class is a standalone component, which can ` + `not be used in the \`@NgModule.bootstrap\` array. Use the \`bootstrapApplication\` ` + `function for bootstrap instead.`); } } function verifyComponentEntryComponentsIsPartOfNgModule(type) { type = resolveForwardRef(type); if (getComponentDef(type)) { // We know we are component const component = getAnnotation(type, 'Component'); if (component && component.entryComponents) { deepForEach(component.entryComponents, verifyComponentIsPartOfNgModule); } } } function verifySemanticsOfNgModuleImport(type, importingModule) { type = resolveForwardRef(type); const directiveDef = getComponentDef(type) || getDirectiveDef(type); if (directiveDef !== null && !directiveDef.standalone) { throw new Error(`Unexpected directive '${type.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`); } const pipeDef = getPipeDef$1(type); if (pipeDef !== null && !pipeDef.standalone) { throw new Error(`Unexpected pipe '${type.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`); } } } function unwrapModuleWithProvidersImports(typeOrWithProviders) { typeOrWithProviders = resolveForwardRef(typeOrWithProviders); return typeOrWithProviders.ngModule || typeOrWithProviders; } function getAnnotation(type, name) { let annotation = null; collect(type.__annotations__); collect(type.decorators); return annotation; function collect(annotations) { if (annotations) { annotations.forEach(readAnnotation); } } function readAnnotation(decorator) { if (!annotation) { const proto = Object.getPrototypeOf(decorator); if (proto.ngMetadataName == name) { annotation = decorator; } else if (decorator.type) { const proto = Object.getPrototypeOf(decorator.type); if (proto.ngMetadataName == name) { annotation = decorator.args[0]; } } } } } /** * Keep track of compiled components. This is needed because in tests we often want to compile the * same component with more than one NgModule. This would cause an error unless we reset which * NgModule the component belongs to. We keep the list of compiled components here so that the * TestBed can reset it later. */ let ownerNgModule = new WeakMap(); let verifiedNgModule = new WeakMap(); function resetCompiledComponents() { ownerNgModule = new WeakMap(); verifiedNgModule = new WeakMap(); moduleQueue.length = 0; } /** * Computes the combined declarations of explicit declarations, as well as declarations inherited by * traversing the exports of imported modules. * @param type */ function computeCombinedExports(type) { type = resolveForwardRef(type); const ngModuleDef = getNgModuleDef(type); // a standalone component, directive or pipe if (ngModuleDef === null) { return [type]; } return [...flatten(maybeUnwrapFn(ngModuleDef.exports).map(type => { const ngModuleDef = getNgModuleDef(type); if (ngModuleDef) { verifySemanticsOfNgModuleDef(type, false); return computeCombinedExports(type); } else { return type; } }))]; } /** * Some declared components may be compiled asynchronously, and thus may not have their * ɵcmp set yet. If this is the case, then a reference to the module is written into * the `ngSelectorScope` property of the declared type. */ function setScopeOnDeclaredComponents(moduleType, ngModule) { const declarations = flatten(ngModule.declarations || EMPTY_ARRAY); const transitiveScopes = transitiveScopesFor(moduleType); declarations.forEach(declaration => { declaration = resolveForwardRef(declaration); if (declaration.hasOwnProperty(NG_COMP_DEF)) { // A `ɵcmp` field exists - go ahead and patch the component directly. const component = declaration; const componentDef = getComponentDef(component); patchComponentDefWithScope(componentDef, transitiveScopes); } else if (!declaration.hasOwnProperty(NG_DIR_DEF) && !declaration.hasOwnProperty(NG_PIPE_DEF)) { // Set `ngSelectorScope` for future reference when the component compilation finishes. declaration.ngSelectorScope = moduleType; } }); } /** * Patch the definition of a component with directives and pipes from the compilation scope of * a given module. */ function patchComponentDefWithScope(componentDef, transitiveScopes) { componentDef.directiveDefs = () => Array.from(transitiveScopes.compilation.directives).map(dir => dir.hasOwnProperty(NG_COMP_DEF) ? getComponentDef(dir) : getDirectiveDef(dir)).filter(def => !!def); componentDef.pipeDefs = () => Array.from(transitiveScopes.compilation.pipes).map(pipe => getPipeDef$1(pipe)); componentDef.schemas = transitiveScopes.schemas; // Since we avoid Components/Directives/Pipes recompiling in case there are no overrides, we // may face a problem where previously compiled defs available to a given Component/Directive // are cached in TView and may become stale (in case any of these defs gets recompiled). In // order to avoid this problem, we force fresh TView to be created. componentDef.tView = null; } /** * Compute the pair of transitive scopes (compilation scope and exported scope) for a given type * (either a NgModule or a standalone component / directive / pipe). */ function transitiveScopesFor(type) { if (isNgModule(type)) { return transitiveScopesForNgModule(type); } else if (isStandalone(type)) { const directiveDef = getComponentDef(type) || getDirectiveDef(type); if (directiveDef !== null) { return { schemas: null, compilation: { directives: new Set(), pipes: new Set() }, exported: { directives: new Set([type]), pipes: new Set() } }; } const pipeDef = getPipeDef$1(type); if (pipeDef !== null) { return { schemas: null, compilation: { directives: new Set(), pipes: new Set() }, exported: { directives: new Set(), pipes: new Set([type]) } }; } } // TODO: change the error message to be more user-facing and take standalone into account throw new Error(`${type.name} does not have a module def (ɵmod property)`); } /** * Compute the pair of transitive scopes (compilation scope and exported scope) for a given module. * * This operation is memoized and the result is cached on the module's definition. This function can * be called on modules with components that have not fully compiled yet, but the result should not * be used until they have. * * @param moduleType module that transitive scope should be calculated for. */ function transitiveScopesForNgModule(moduleType) { const def = getNgModuleDef(moduleType, true); if (def.transitiveCompileScopes !== null) { return def.transitiveCompileScopes; } const scopes = { schemas: def.schemas || null, compilation: { directives: new Set(), pipes: new Set() }, exported: { directives: new Set(), pipes: new Set() } }; maybeUnwrapFn(def.imports).forEach(imported => { // When this module imports another, the imported module's exported directives and pipes are // added to the compilation scope of this module. const importedScope = transitiveScopesFor(imported); importedScope.exported.directives.forEach(entry => scopes.compilation.directives.add(entry)); importedScope.exported.pipes.forEach(entry => scopes.compilation.pipes.add(entry)); }); maybeUnwrapFn(def.declarations).forEach(declared => { const declaredWithDefs = declared; if (getPipeDef$1(declaredWithDefs)) { scopes.compilation.pipes.add(declared); } else { // Either declared has a ɵcmp or ɵdir, or it's a component which hasn't // had its template compiled yet. In either case, it gets added to the compilation's // directives. scopes.compilation.directives.add(declared); } }); maybeUnwrapFn(def.exports).forEach(exported => { const exportedType = exported; // Either the type is a module, a pipe, or a component/directive (which may not have a // ɵcmp as it might be compiled asynchronously). if (isNgModule(exportedType)) { // When this module exports another, the exported module's exported directives and pipes are // added to both the compilation and exported scopes of this module. const exportedScope = transitiveScopesFor(exportedType); exportedScope.exported.directives.forEach(entry => { scopes.compilation.directives.add(entry); scopes.exported.directives.add(entry); }); exportedScope.exported.pipes.forEach(entry => { scopes.compilation.pipes.add(entry); scopes.exported.pipes.add(entry); }); } else if (getPipeDef$1(exportedType)) { scopes.exported.pipes.add(exportedType); } else { scopes.exported.directives.add(exportedType); } }); def.transitiveCompileScopes = scopes; return scopes; } function expandModuleWithProviders(value) { if (isModuleWithProviders(value)) { return value.ngModule; } return value; } /** * Keep track of the compilation depth to avoid reentrancy issues during JIT compilation. This * matters in the following scenario: * * Consider a component 'A' that extends component 'B', both declared in module 'M'. During * the compilation of 'A' the definition of 'B' is requested to capture the inheritance chain, * potentially triggering compilation of 'B'. If this nested compilation were to trigger * `flushModuleScopingQueueAsMuchAsPossible` it may happen that module 'M' is still pending in the * queue, resulting in 'A' and 'B' to be patched with the NgModule scope. As the compilation of * 'A' is still in progress, this would introduce a circular dependency on its compilation. To avoid * this issue, the module scope queue is only flushed for compilations at the depth 0, to ensure * all compilations have finished. */ let compilationDepth = 0; /** * Compile an Angular component according to its decorator metadata, and patch the resulting * component def (ɵcmp) onto the component type. * * Compilation may be asynchronous (due to the need to resolve URLs for the component template or * other resources, for example). In the event that compilation is not immediate, `compileComponent` * will enqueue resource resolution into a global queue and will fail to return the `ɵcmp` * until the global queue has been resolved with a call to `resolveComponentResources`. */ function compileComponent(type, metadata) { // Initialize ngDevMode. This must be the first statement in compileComponent. // See the `initNgDevMode` docstring for more information. (typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode(); let ngComponentDef = null; // Metadata may have resources which need to be resolved. maybeQueueResolutionOfComponentResources(type, metadata); // Note that we're using the same function as `Directive`, because that's only subset of metadata // that we need to create the ngFactoryDef. We're avoiding using the component metadata // because we'd have to resolve the asynchronous templates. addDirectiveFactoryDef(type, metadata); Object.defineProperty(type, NG_COMP_DEF, { get: () => { if (ngComponentDef === null) { const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'component', type: type }); if (componentNeedsResolution(metadata)) { const error = [`Component '${type.name}' is not resolved:`]; if (metadata.templateUrl) { error.push(` - templateUrl: ${metadata.templateUrl}`); } if (metadata.styleUrls && metadata.styleUrls.length) { error.push(` - styleUrls: ${JSON.stringify(metadata.styleUrls)}`); } error.push(`Did you run and wait for 'resolveComponentResources()'?`); throw new Error(error.join('\n')); } // This const was called `jitOptions` previously but had to be renamed to `options` because // of a bug with Terser that caused optimized JIT builds to throw a `ReferenceError`. // This bug was investigated in https://github.com/angular/angular-cli/issues/17264. // We should not rename it back until https://github.com/terser/terser/issues/615 is fixed. const options = getJitOptions(); let preserveWhitespaces = metadata.preserveWhitespaces; if (preserveWhitespaces === undefined) { if (options !== null && options.preserveWhitespaces !== undefined) { preserveWhitespaces = options.preserveWhitespaces; } else { preserveWhitespaces = false; } } let encapsulation = metadata.encapsulation; if (encapsulation === undefined) { if (options !== null && options.defaultEncapsulation !== undefined) { encapsulation = options.defaultEncapsulation; } else { encapsulation = ViewEncapsulation$1.Emulated; } } const templateUrl = metadata.templateUrl || `ng:///${type.name}/template.html`; const meta = { ...directiveMetadata(type, metadata), typeSourceSpan: compiler.createParseSourceSpan('Component', type.name, templateUrl), template: metadata.template || '', preserveWhitespaces, styles: metadata.styles || EMPTY_ARRAY, animations: metadata.animations, // JIT components are always compiled against an empty set of `declarations`. Instead, the // `directiveDefs` and `pipeDefs` are updated at a later point: // * for NgModule-based components, they're set when the NgModule which declares the // component resolves in the module scoping queue // * for standalone components, they're set just below, after `compileComponent`. declarations: [], changeDetection: metadata.changeDetection, encapsulation, interpolation: metadata.interpolation, viewProviders: metadata.viewProviders || null }; compilationDepth++; try { if (meta.usesInheritance) { addDirectiveDefToUndecoratedParents(type); } ngComponentDef = compiler.compileComponent(angularCoreEnv, templateUrl, meta); if (metadata.standalone) { // Patch the component definition for standalone components with `directiveDefs` and // `pipeDefs` functions which lazily compute the directives/pipes available in the // standalone component. Also set `dependencies` to the lazily resolved list of imports. const imports = flatten(metadata.imports || EMPTY_ARRAY); const { directiveDefs, pipeDefs } = getStandaloneDefFunctions(type, imports); ngComponentDef.directiveDefs = directiveDefs; ngComponentDef.pipeDefs = pipeDefs; ngComponentDef.dependencies = () => imports.map(resolveForwardRef); } } finally { // Ensure that the compilation depth is decremented even when the compilation failed. compilationDepth--; } if (compilationDepth === 0) { // When NgModule decorator executed, we enqueued the module definition such that // it would only dequeue and add itself as module scope to all of its declarations, // but only if if all of its declarations had resolved. This call runs the check // to see if any modules that are in the queue can be dequeued and add scope to // their declarations. flushModuleScopingQueueAsMuchAsPossible(); } // If component compilation is async, then the @NgModule annotation which declares the // component may execute and set an ngSelectorScope property on the component type. This // allows the component to patch itself with directiveDefs from the module after it // finishes compiling. if (hasSelectorScope(type)) { const scopes = transitiveScopesFor(type.ngSelectorScope); patchComponentDefWithScope(ngComponentDef, scopes); } if (metadata.schemas) { if (metadata.standalone) { ngComponentDef.schemas = metadata.schemas; } else { throw new Error(`The 'schemas' was specified for the ${stringifyForError(type)} but is only valid on a component that is standalone.`); } } else if (metadata.standalone) { ngComponentDef.schemas = []; } } return ngComponentDef; }, // Make the property configurable in dev mode to allow overriding in tests configurable: !!ngDevMode }); } function getDependencyTypeForError(type) { if (getComponentDef(type)) return 'component'; if (getDirectiveDef(type)) return 'directive'; if (getPipeDef$1(type)) return 'pipe'; return 'type'; } function verifyStandaloneImport(depType, importingType) { if (isForwardRef(depType)) { depType = resolveForwardRef(depType); if (!depType) { throw new Error(`Expected forwardRef function, imported from "${stringifyForError(importingType)}", to return a standalone entity or NgModule but got "${stringifyForError(depType) || depType}".`); } } if (getNgModuleDef(depType) == null) { const def = getComponentDef(depType) || getDirectiveDef(depType) || getPipeDef$1(depType); if (def != null) { // if a component, directive or pipe is imported make sure that it is standalone if (!def.standalone) { throw new Error(`The "${stringifyForError(depType)}" ${getDependencyTypeForError(depType)}, imported from "${stringifyForError(importingType)}", is not standalone. Did you forget to add the standalone: true flag?`); } } else { // it can be either a module with provider or an unknown (not annotated) type if (isModuleWithProviders(depType)) { throw new Error(`A module with providers was imported from "${stringifyForError(importingType)}". Modules with providers are not supported in standalone components imports.`); } else { throw new Error(`The "${stringifyForError(depType)}" type, imported from "${stringifyForError(importingType)}", must be a standalone component / directive / pipe or an NgModule. Did you forget to add the required @Component / @Directive / @Pipe or @NgModule annotation?`); } } } } /** * Build memoized `directiveDefs` and `pipeDefs` functions for the component definition of a * standalone component, which process `imports` and filter out directives and pipes. The use of * memoized functions here allows for the delayed resolution of any `forwardRef`s present in the * component's `imports`. */ function getStandaloneDefFunctions(type, imports) { let cachedDirectiveDefs = null; let cachedPipeDefs = null; const directiveDefs = () => { if (cachedDirectiveDefs === null) { // Standalone components are always able to self-reference, so include the component's own // definition in its `directiveDefs`. cachedDirectiveDefs = [getComponentDef(type)]; const seen = new Set(); for (const rawDep of imports) { ngDevMode && verifyStandaloneImport(rawDep, type); const dep = resolveForwardRef(rawDep); if (seen.has(dep)) { continue; } seen.add(dep); if (!!getNgModuleDef(dep)) { const scope = transitiveScopesFor(dep); for (const dir of scope.exported.directives) { const def = getComponentDef(dir) || getDirectiveDef(dir); if (def && !seen.has(dir)) { seen.add(dir); cachedDirectiveDefs.push(def); } } } else { const def = getComponentDef(dep) || getDirectiveDef(dep); if (def) { cachedDirectiveDefs.push(def); } } } } return cachedDirectiveDefs; }; const pipeDefs = () => { if (cachedPipeDefs === null) { cachedPipeDefs = []; const seen = new Set(); for (const rawDep of imports) { const dep = resolveForwardRef(rawDep); if (seen.has(dep)) { continue; } seen.add(dep); if (!!getNgModuleDef(dep)) { const scope = transitiveScopesFor(dep); for (const pipe of scope.exported.pipes) { const def = getPipeDef$1(pipe); if (def && !seen.has(pipe)) { seen.add(pipe); cachedPipeDefs.push(def); } } } else { const def = getPipeDef$1(dep); if (def) { cachedPipeDefs.push(def); } } } } return cachedPipeDefs; }; return { directiveDefs, pipeDefs }; } function hasSelectorScope(component) { return component.ngSelectorScope !== undefined; } /** * Compile an Angular directive according to its decorator metadata, and patch the resulting * directive def onto the component type. * * In the event that compilation is not immediate, `compileDirective` will return a `Promise` which * will resolve when compilation completes and the directive becomes usable. */ function compileDirective(type, directive) { let ngDirectiveDef = null; addDirectiveFactoryDef(type, directive || {}); Object.defineProperty(type, NG_DIR_DEF, { get: () => { if (ngDirectiveDef === null) { // `directive` can be null in the case of abstract directives as a base class // that use `@Directive()` with no selector. In that case, pass empty object to the // `directiveMetadata` function instead of null. const meta = getDirectiveMetadata(type, directive || {}); const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'directive', type }); ngDirectiveDef = compiler.compileDirective(angularCoreEnv, meta.sourceMapUrl, meta.metadata); } return ngDirectiveDef; }, // Make the property configurable in dev mode to allow overriding in tests configurable: !!ngDevMode }); } function getDirectiveMetadata(type, metadata) { const name = type && type.name; const sourceMapUrl = `ng:///${name}/ɵdir.js`; const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'directive', type }); const facade = directiveMetadata(type, metadata); facade.typeSourceSpan = compiler.createParseSourceSpan('Directive', name, sourceMapUrl); if (facade.usesInheritance) { addDirectiveDefToUndecoratedParents(type); } return { metadata: facade, sourceMapUrl }; } function addDirectiveFactoryDef(type, metadata) { let ngFactoryDef = null; Object.defineProperty(type, NG_FACTORY_DEF, { get: () => { if (ngFactoryDef === null) { const meta = getDirectiveMetadata(type, metadata); const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'directive', type }); ngFactoryDef = compiler.compileFactory(angularCoreEnv, `ng:///${type.name}/ɵfac.js`, { name: meta.metadata.name, type: meta.metadata.type, typeArgumentCount: 0, deps: reflectDependencies(type), target: compiler.FactoryTarget.Directive }); } return ngFactoryDef; }, // Make the property configurable in dev mode to allow overriding in tests configurable: !!ngDevMode }); } function extendsDirectlyFromObject(type) { return Object.getPrototypeOf(type.prototype) === Object.prototype; } /** * Extract the `R3DirectiveMetadata` for a particular directive (either a `Directive` or a * `Component`). */ function directiveMetadata(type, metadata) { // Reflect inputs and outputs. const reflect = getReflect(); const propMetadata = reflect.ownPropMetadata(type); return { name: type.name, type: type, selector: metadata.selector !== undefined ? metadata.selector : null, host: metadata.host || EMPTY_OBJ, propMetadata: propMetadata, inputs: metadata.inputs || EMPTY_ARRAY, outputs: metadata.outputs || EMPTY_ARRAY, queries: extractQueriesMetadata(type, propMetadata, isContentQuery), lifecycle: { usesOnChanges: reflect.hasLifecycleHook(type, 'ngOnChanges') }, typeSourceSpan: null, usesInheritance: !extendsDirectlyFromObject(type), exportAs: extractExportAs(metadata.exportAs), providers: metadata.providers || null, viewQueries: extractQueriesMetadata(type, propMetadata, isViewQuery), isStandalone: !!metadata.standalone, hostDirectives: metadata.hostDirectives?.map(directive => typeof directive === 'function' ? { directive } : directive) || null }; } /** * Adds a directive definition to all parent classes of a type that don't have an Angular decorator. */ function addDirectiveDefToUndecoratedParents(type) { const objPrototype = Object.prototype; let parent = Object.getPrototypeOf(type.prototype).constructor; // Go up the prototype until we hit `Object`. while (parent && parent !== objPrototype) { // Since inheritance works if the class was annotated already, we only need to add // the def if there are no annotations and the def hasn't been created already. if (!getDirectiveDef(parent) && !getComponentDef(parent) && shouldAddAbstractDirective(parent)) { compileDirective(parent, null); } parent = Object.getPrototypeOf(parent); } } function convertToR3QueryPredicate(selector) { return typeof selector === 'string' ? splitByComma(selector) : resolveForwardRef(selector); } function convertToR3QueryMetadata(propertyName, ann) { return { propertyName: propertyName, predicate: convertToR3QueryPredicate(ann.selector), descendants: ann.descendants, first: ann.first, read: ann.read ? ann.read : null, static: !!ann.static, emitDistinctChangesOnly: !!ann.emitDistinctChangesOnly }; } function extractQueriesMetadata(type, propMetadata, isQueryAnn) { const queriesMeta = []; for (const field in propMetadata) { if (propMetadata.hasOwnProperty(field)) { const annotations = propMetadata[field]; annotations.forEach(ann => { if (isQueryAnn(ann)) { if (!ann.selector) { throw new Error(`Can't construct a query for the property "${field}" of ` + `"${stringifyForError(type)}" since the query selector wasn't defined.`); } if (annotations.some(isInputAnnotation)) { throw new Error(`Cannot combine @Input decorators with query decorators`); } queriesMeta.push(convertToR3QueryMetadata(field, ann)); } }); } } return queriesMeta; } function extractExportAs(exportAs) { return exportAs === undefined ? null : splitByComma(exportAs); } function isContentQuery(value) { const name = value.ngMetadataName; return name === 'ContentChild' || name === 'ContentChildren'; } function isViewQuery(value) { const name = value.ngMetadataName; return name === 'ViewChild' || name === 'ViewChildren'; } function isInputAnnotation(value) { return value.ngMetadataName === 'Input'; } function splitByComma(value) { return value.split(',').map(piece => piece.trim()); } const LIFECYCLE_HOOKS = ['ngOnChanges', 'ngOnInit', 'ngOnDestroy', 'ngDoCheck', 'ngAfterViewInit', 'ngAfterViewChecked', 'ngAfterContentInit', 'ngAfterContentChecked']; function shouldAddAbstractDirective(type) { const reflect = getReflect(); if (LIFECYCLE_HOOKS.some(hookName => reflect.hasLifecycleHook(type, hookName))) { return true; } const propMetadata = reflect.propMetadata(type); for (const field in propMetadata) { const annotations = propMetadata[field]; for (let i = 0; i < annotations.length; i++) { const current = annotations[i]; const metadataName = current.ngMetadataName; if (isInputAnnotation(current) || isContentQuery(current) || isViewQuery(current) || metadataName === 'Output' || metadataName === 'HostBinding' || metadataName === 'HostListener') { return true; } } } return false; } function compilePipe(type, meta) { let ngPipeDef = null; let ngFactoryDef = null; Object.defineProperty(type, NG_FACTORY_DEF, { get: () => { if (ngFactoryDef === null) { const metadata = getPipeMetadata(type, meta); const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'pipe', type: metadata.type }); ngFactoryDef = compiler.compileFactory(angularCoreEnv, `ng:///${metadata.name}/ɵfac.js`, { name: metadata.name, type: metadata.type, typeArgumentCount: 0, deps: reflectDependencies(type), target: compiler.FactoryTarget.Pipe }); } return ngFactoryDef; }, // Make the property configurable in dev mode to allow overriding in tests configurable: !!ngDevMode }); Object.defineProperty(type, NG_PIPE_DEF, { get: () => { if (ngPipeDef === null) { const metadata = getPipeMetadata(type, meta); const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'pipe', type: metadata.type }); ngPipeDef = compiler.compilePipe(angularCoreEnv, `ng:///${metadata.name}/ɵpipe.js`, metadata); } return ngPipeDef; }, // Make the property configurable in dev mode to allow overriding in tests configurable: !!ngDevMode }); } function getPipeMetadata(type, meta) { return { type: type, name: type.name, pipeName: meta.name, pure: meta.pure !== undefined ? meta.pure : true, isStandalone: !!meta.standalone }; } /** * Type of the Directive metadata. * * @publicApi */ const Directive = makeDecorator('Directive', (dir = {}) => dir, undefined, undefined, (type, meta) => compileDirective(type, meta)); /** * Component decorator and metadata. * * @Annotation * @publicApi */ const Component = makeDecorator('Component', (c = {}) => ({ changeDetection: ChangeDetectionStrategy.Default, ...c }), Directive, undefined, (type, meta) => compileComponent(type, meta)); /** * @Annotation * @publicApi */ const Pipe = makeDecorator('Pipe', p => ({ pure: true, ...p }), undefined, undefined, (type, meta) => compilePipe(type, meta)); /** * @Annotation * @publicApi */ const Input = makePropDecorator('Input', bindingPropertyName => ({ bindingPropertyName })); /** * @Annotation * @publicApi */ const Output = makePropDecorator('Output', bindingPropertyName => ({ bindingPropertyName })); /** * @Annotation * @publicApi */ const HostBinding = makePropDecorator('HostBinding', hostPropertyName => ({ hostPropertyName })); /** * Decorator that binds a DOM event to a host listener and supplies configuration metadata. * Angular invokes the supplied handler method when the host element emits the specified event, * and updates the bound element with the result. * * If the handler method returns false, applies `preventDefault` on the bound element. * * @usageNotes * * The following example declares a directive * that attaches a click listener to a button and counts clicks. * * ```ts * @Directive({selector: 'button[counting]'}) * class CountClicks { * numberOfClicks = 0; * * @HostListener('click', ['$event.target']) * onClick(btn) { * console.log('button', btn, 'number of clicks:', this.numberOfClicks++); * } * } * * @Component({ * selector: 'app', * template: '<button counting>Increment</button>', * }) * class App {} * * ``` * * The following example registers another DOM event handler that listens for `Enter` key-press * events on the global `window`. * ``` ts * import { HostListener, Component } from "@angular/core"; * * @Component({ * selector: 'app', * template: `<h1>Hello, you have pressed enter {{counter}} number of times!</h1> Press enter key * to increment the counter. * <button (click)="resetCounter()">Reset Counter</button>` * }) * class AppComponent { * counter = 0; * @HostListener('window:keydown.enter', ['$event']) * handleKeyDown(event: KeyboardEvent) { * this.counter++; * } * resetCounter() { * this.counter = 0; * } * } * ``` * The list of valid key names for `keydown` and `keyup` events * can be found here: * https://www.w3.org/TR/DOM-Level-3-Events-key/#named-key-attribute-values * * Note that keys can also be combined, e.g. `@HostListener('keydown.shift.a')`. * * The global target names that can be used to prefix an event name are * `document:`, `window:` and `body:`. * * @Annotation * @publicApi */ const HostListener = makePropDecorator('HostListener', (eventName, args) => ({ eventName, args })); /** * @Annotation * @publicApi */ const NgModule = makeDecorator('NgModule', ngModule => ngModule, undefined, undefined, /** * Decorator that marks the following class as an NgModule, and supplies * configuration metadata for it. * * * The `declarations` and `entryComponents` options configure the compiler * with information about what belongs to the NgModule. * * The `providers` options configures the NgModule's injector to provide * dependencies the NgModule members. * * The `imports` and `exports` options bring in members from other modules, and make * this module's members available to others. */ (type, meta) => compileNgModule(type, meta)); /** * This indirection is needed to free up Component, etc symbols in the public API * to be used by the decorator versions of these annotations. */ function noop(...args) { // Do nothing. } /* * This file exists to support compilation of @angular/core in Ivy mode. * * When the Angular compiler processes a compilation unit, it normally writes imports to * @angular/core. When compiling the core package itself this strategy isn't usable. Instead, the * compiler writes imports to this file. * * Only a subset of such imports are supported - core is not allowed to declare components or pipes. * A check in ngtsc's `R3SymbolsImportRewriter` validates this condition. The rewriter is only used * when compiling @angular/core and is responsible for translating an external name (prefixed with * ɵ) to the internal symbol name as exported below. * * The below symbols are used for @Injectable and @NgModule compilation. */ /** * The existence of this constant (in this particular file) informs the Angular compiler that the * current program is actually @angular/core, which needs to be compiled specially. */ const ITS_JUST_ANGULAR = true; /** * A [DI token](guide/glossary#di-token "DI token definition") that you can use to provide * one or more initialization functions. * * The provided functions are injected at application startup and executed during * app initialization. If any of these functions returns a Promise or an Observable, initialization * does not complete until the Promise is resolved or the Observable is completed. * * You can, for example, create a factory function that loads language data * or an external configuration, and provide that function to the `APP_INITIALIZER` token. * The function is executed during the application bootstrap process, * and the needed data is available on startup. * * @see `ApplicationInitStatus` * * @usageNotes * * The following example illustrates how to configure a multi-provider using `APP_INITIALIZER` token * and a function returning a promise. * * ``` * function initializeApp(): Promise<any> { * return new Promise((resolve, reject) => { * // Do some asynchronous stuff * resolve(); * }); * } * * @NgModule({ * imports: [BrowserModule], * declarations: [AppComponent], * bootstrap: [AppComponent], * providers: [{ * provide: APP_INITIALIZER, * useFactory: () => initializeApp, * multi: true * }] * }) * export class AppModule {} * ``` * * It's also possible to configure a multi-provider using `APP_INITIALIZER` token and a function * returning an observable, see an example below. Note: the `HttpClient` in this example is used for * demo purposes to illustrate how the factory function can work with other providers available * through DI. * * ``` * function initializeAppFactory(httpClient: HttpClient): () => Observable<any> { * return () => httpClient.get("https://someUrl.com/api/user") * .pipe( * tap(user => { ... }) * ); * } * * @NgModule({ * imports: [BrowserModule, HttpClientModule], * declarations: [AppComponent], * bootstrap: [AppComponent], * providers: [{ * provide: APP_INITIALIZER, * useFactory: initializeAppFactory, * deps: [HttpClient], * multi: true * }] * }) * export class AppModule {} * ``` * * @publicApi */ const APP_INITIALIZER = new InjectionToken('Application Initializer'); /** * A class that reflects the state of running {@link APP_INITIALIZER} functions. * * @publicApi */ class ApplicationInitStatus { constructor(appInits) { this.appInits = appInits; this.resolve = noop; this.reject = noop; this.initialized = false; this.done = false; // TODO: Throw RuntimeErrorCode.INVALID_MULTI_PROVIDER if appInits is not an array this.donePromise = new Promise((res, rej) => { this.resolve = res; this.reject = rej; }); } /** @internal */ runInitializers() { if (this.initialized) { return; } const asyncInitPromises = []; const complete = () => { this.done = true; this.resolve(); }; if (this.appInits) { for (let i = 0; i < this.appInits.length; i++) { const initResult = this.appInits[i](); if (isPromise(initResult)) { asyncInitPromises.push(initResult); } else if (isObservable(initResult)) { const observableAsPromise = new Promise((resolve, reject) => { initResult.subscribe({ complete: resolve, error: reject }); }); asyncInitPromises.push(observableAsPromise); } } } Promise.all(asyncInitPromises).then(() => { complete(); }).catch(e => { this.reject(e); }); if (asyncInitPromises.length === 0) { complete(); } this.initialized = true; } } ApplicationInitStatus.ɵfac = function ApplicationInitStatus_Factory(t) { return new (t || ApplicationInitStatus)(ɵɵinject(APP_INITIALIZER, 8)); }; ApplicationInitStatus.ɵprov = /*@__PURE__*/ɵɵdefineInjectable({ token: ApplicationInitStatus, factory: ApplicationInitStatus.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ApplicationInitStatus, [{ type: Injectable, args: [{ providedIn: 'root' }] }], function () { return [{ type: undefined, decorators: [{ type: Inject, args: [APP_INITIALIZER] }, { type: Optional }] }]; }, null); })(); /** * A [DI token](guide/glossary#di-token "DI token definition") representing a unique string ID, used * primarily for prefixing application attributes and CSS styles when * {@link ViewEncapsulation#Emulated ViewEncapsulation.Emulated} is being used. * * BY default, the value is randomly generated and assigned to the application by Angular. * To provide a custom ID value, use a DI provider <!-- TODO: provider --> to configure * the root {@link Injector} that uses this token. * * @publicApi */ const APP_ID = new InjectionToken('AppId', { providedIn: 'root', factory: _appIdRandomProviderFactory }); function _appIdRandomProviderFactory() { return `${_randomChar()}${_randomChar()}${_randomChar()}`; } /** * Providers that generate a random `APP_ID_TOKEN`. * @publicApi */ const APP_ID_RANDOM_PROVIDER = { provide: APP_ID, useFactory: _appIdRandomProviderFactory, deps: [] }; function _randomChar() { return String.fromCharCode(97 + Math.floor(Math.random() * 25)); } /** * A function that is executed when a platform is initialized. * @publicApi */ const PLATFORM_INITIALIZER = new InjectionToken('Platform Initializer'); /** * A token that indicates an opaque platform ID. * @publicApi */ const PLATFORM_ID = new InjectionToken('Platform ID', { providedIn: 'platform', factory: () => 'unknown' // set a default platform name, when none set explicitly }); /** * A [DI token](guide/glossary#di-token "DI token definition") that indicates the root directory of * the application * @publicApi */ const PACKAGE_ROOT_URL = new InjectionToken('Application Packages Root URL'); // We keep this token here, rather than the animations package, so that modules that only care // about which animations module is loaded (e.g. the CDK) can retrieve it without having to // include extra dependencies. See #44970 for more context. /** * A [DI token](guide/glossary#di-token "DI token definition") that indicates which animations * module has been loaded. * @publicApi */ const ANIMATION_MODULE_TYPE = new InjectionToken('AnimationModuleType'); class Console { log(message) { // tslint:disable-next-line:no-console console.log(message); } // Note: for reporting errors use `DOM.logError()` as it is platform specific warn(message) { // tslint:disable-next-line:no-console console.warn(message); } } Console.ɵfac = function Console_Factory(t) { return new (t || Console)(); }; Console.ɵprov = /*@__PURE__*/ɵɵdefineInjectable({ token: Console, factory: Console.ɵfac, providedIn: 'platform' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Console, [{ type: Injectable, args: [{ providedIn: 'platform' }] }], null, null); })(); /** * Work out the locale from the potential global properties. * * * Closure Compiler: use `goog.LOCALE`. * * Ivy enabled: use `$localize.locale` */ function getGlobalLocale() { if (typeof ngI18nClosureMode !== 'undefined' && ngI18nClosureMode && typeof goog !== 'undefined' && goog.LOCALE !== 'en') { // * The default `goog.LOCALE` value is `en`, while Angular used `en-US`. // * In order to preserve backwards compatibility, we use Angular default value over // Closure Compiler's one. return goog.LOCALE; } else { // KEEP `typeof $localize !== 'undefined' && $localize.locale` IN SYNC WITH THE LOCALIZE // COMPILE-TIME INLINER. // // * During compile time inlining of translations the expression will be replaced // with a string literal that is the current locale. Other forms of this expression are not // guaranteed to be replaced. // // * During runtime translation evaluation, the developer is required to set `$localize.locale` // if required, or just to provide their own `LOCALE_ID` provider. return typeof $localize !== 'undefined' && $localize.locale || DEFAULT_LOCALE_ID; } } /** * Provide this token to set the locale of your application. * It is used for i18n extraction, by i18n pipes (DatePipe, I18nPluralPipe, CurrencyPipe, * DecimalPipe and PercentPipe) and by ICU expressions. * * See the [i18n guide](guide/i18n-common-locale-id) for more information. * * @usageNotes * ### Example * * ```typescript * import { LOCALE_ID } from '@angular/core'; * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; * import { AppModule } from './app/app.module'; * * platformBrowserDynamic().bootstrapModule(AppModule, { * providers: [{provide: LOCALE_ID, useValue: 'en-US' }] * }); * ``` * * @publicApi */ const LOCALE_ID = new InjectionToken('LocaleId', { providedIn: 'root', factory: () => inject(LOCALE_ID, InjectFlags.Optional | InjectFlags.SkipSelf) || getGlobalLocale() }); /** * Provide this token to set the default currency code your application uses for * CurrencyPipe when there is no currency code passed into it. This is only used by * CurrencyPipe and has no relation to locale currency. Defaults to USD if not configured. * * See the [i18n guide](guide/i18n-common-locale-id) for more information. * * <div class="alert is-helpful"> * * **Deprecation notice:** * * The default currency code is currently always `USD` but this is deprecated from v9. * * **In v10 the default currency code will be taken from the current locale.** * * If you need the previous behavior then set it by creating a `DEFAULT_CURRENCY_CODE` provider in * your application `NgModule`: * * ```ts * {provide: DEFAULT_CURRENCY_CODE, useValue: 'USD'} * ``` * * </div> * * @usageNotes * ### Example * * ```typescript * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; * import { AppModule } from './app/app.module'; * * platformBrowserDynamic().bootstrapModule(AppModule, { * providers: [{provide: DEFAULT_CURRENCY_CODE, useValue: 'EUR' }] * }); * ``` * * @publicApi */ const DEFAULT_CURRENCY_CODE = new InjectionToken('DefaultCurrencyCode', { providedIn: 'root', factory: () => USD_CURRENCY_CODE }); /** * Use this token at bootstrap to provide the content of your translation file (`xtb`, * `xlf` or `xlf2`) when you want to translate your application in another language. * * See the [i18n guide](guide/i18n-common-merge) for more information. * * @usageNotes * ### Example * * ```typescript * import { TRANSLATIONS } from '@angular/core'; * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; * import { AppModule } from './app/app.module'; * * // content of your translation file * const translations = '....'; * * platformBrowserDynamic().bootstrapModule(AppModule, { * providers: [{provide: TRANSLATIONS, useValue: translations }] * }); * ``` * * @publicApi */ const TRANSLATIONS = new InjectionToken('Translations'); /** * Provide this token at bootstrap to set the format of your {@link TRANSLATIONS}: `xtb`, * `xlf` or `xlf2`. * * See the [i18n guide](guide/i18n-common-merge) for more information. * * @usageNotes * ### Example * * ```typescript * import { TRANSLATIONS_FORMAT } from '@angular/core'; * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; * import { AppModule } from './app/app.module'; * * platformBrowserDynamic().bootstrapModule(AppModule, { * providers: [{provide: TRANSLATIONS_FORMAT, useValue: 'xlf' }] * }); * ``` * * @publicApi */ const TRANSLATIONS_FORMAT = new InjectionToken('TranslationsFormat'); /** * Use this enum at bootstrap as an option of `bootstrapModule` to define the strategy * that the compiler should use in case of missing translations: * - Error: throw if you have missing translations. * - Warning (default): show a warning in the console and/or shell. * - Ignore: do nothing. * * See the [i18n guide](guide/i18n-common-merge#report-missing-translations) for more information. * * @usageNotes * ### Example * ```typescript * import { MissingTranslationStrategy } from '@angular/core'; * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; * import { AppModule } from './app/app.module'; * * platformBrowserDynamic().bootstrapModule(AppModule, { * missingTranslation: MissingTranslationStrategy.Error * }); * ``` * * @publicApi */ var MissingTranslationStrategy; (function (MissingTranslationStrategy) { MissingTranslationStrategy[MissingTranslationStrategy["Error"] = 0] = "Error"; MissingTranslationStrategy[MissingTranslationStrategy["Warning"] = 1] = "Warning"; MissingTranslationStrategy[MissingTranslationStrategy["Ignore"] = 2] = "Ignore"; })(MissingTranslationStrategy || (MissingTranslationStrategy = {})); /** * Combination of NgModuleFactory and ComponentFactories. * * @publicApi * * @deprecated * Ivy JIT mode doesn't require accessing this symbol. * See [JIT API changes due to ViewEngine deprecation](guide/deprecations#jit-api-changes) for * additional context. */ class ModuleWithComponentFactories { constructor(ngModuleFactory, componentFactories) { this.ngModuleFactory = ngModuleFactory; this.componentFactories = componentFactories; } } /** * Low-level service for running the angular compiler during runtime * to create {@link ComponentFactory}s, which * can later be used to create and render a Component instance. * * Each `@NgModule` provides an own `Compiler` to its injector, * that will use the directives/pipes of the ng module for compilation * of components. * * @publicApi * * @deprecated * Ivy JIT mode doesn't require accessing this symbol. * See [JIT API changes due to ViewEngine deprecation](guide/deprecations#jit-api-changes) for * additional context. */ class Compiler { /** * Compiles the given NgModule and all of its components. All templates of the components listed * in `entryComponents` have to be inlined. */ compileModuleSync(moduleType) { return new NgModuleFactory(moduleType); } /** * Compiles the given NgModule and all of its components */ compileModuleAsync(moduleType) { return Promise.resolve(this.compileModuleSync(moduleType)); } /** * Same as {@link #compileModuleSync} but also creates ComponentFactories for all components. */ compileModuleAndAllComponentsSync(moduleType) { const ngModuleFactory = this.compileModuleSync(moduleType); const moduleDef = getNgModuleDef(moduleType); const componentFactories = maybeUnwrapFn(moduleDef.declarations).reduce((factories, declaration) => { const componentDef = getComponentDef(declaration); componentDef && factories.push(new ComponentFactory(componentDef)); return factories; }, []); return new ModuleWithComponentFactories(ngModuleFactory, componentFactories); } /** * Same as {@link #compileModuleAsync} but also creates ComponentFactories for all components. */ compileModuleAndAllComponentsAsync(moduleType) { return Promise.resolve(this.compileModuleAndAllComponentsSync(moduleType)); } /** * Clears all caches. */ clearCache() {} /** * Clears the cache for the given component/ngModule. */ clearCacheFor(type) {} /** * Returns the id for a given NgModule, if one is defined and known to the compiler. */ getModuleId(moduleType) { return undefined; } } Compiler.ɵfac = function Compiler_Factory(t) { return new (t || Compiler)(); }; Compiler.ɵprov = /*@__PURE__*/ɵɵdefineInjectable({ token: Compiler, factory: Compiler.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Compiler, [{ type: Injectable, args: [{ providedIn: 'root' }] }], null, null); })(); /** * Token to provide CompilerOptions in the platform injector. * * @publicApi */ const COMPILER_OPTIONS = new InjectionToken('compilerOptions'); /** * A factory for creating a Compiler * * @publicApi * * @deprecated * Ivy JIT mode doesn't require accessing this symbol. * See [JIT API changes due to ViewEngine deprecation](guide/deprecations#jit-api-changes) for * additional context. */ class CompilerFactory {} /** * Marks a component for check (in case of OnPush components) and synchronously * performs change detection on the application this component belongs to. * * @param component Component to {@link ChangeDetectorRef#markForCheck mark for check}. * * @publicApi * @globalApi ng */ function applyChanges(component) { ngDevMode && assertDefined(component, 'component'); markViewDirty(getComponentViewByInstance(component)); getRootComponents(component).forEach(rootComponent => detectChanges(rootComponent)); } /** * This file introduces series of globally accessible debug tools * to allow for the Angular debugging story to function. * * To see this in action run the following command: * * bazel run //packages/core/test/bundling/todo:devserver * * Then load `localhost:5432` and start using the console tools. */ /** * This value reflects the property on the window where the dev * tools are patched (window.ng). * */ const GLOBAL_PUBLISH_EXPANDO_KEY = 'ng'; let _published = false; /** * Publishes a collection of default debug tools onto`window.ng`. * * These functions are available globally when Angular is in development * mode and are automatically stripped away from prod mode is on. */ function publishDefaultGlobalUtils$1() { if (!_published) { _published = true; /** * Warning: this function is *INTERNAL* and should not be relied upon in application's code. * The contract of the function might be changed in any release and/or the function can be * removed completely. */ publishGlobalUtil('ɵsetProfiler', setProfiler); publishGlobalUtil('getDirectiveMetadata', getDirectiveMetadata$1); publishGlobalUtil('getComponent', getComponent); publishGlobalUtil('getContext', getContext); publishGlobalUtil('getListeners', getListeners); publishGlobalUtil('getOwningComponent', getOwningComponent); publishGlobalUtil('getHostElement', getHostElement); publishGlobalUtil('getInjector', getInjector); publishGlobalUtil('getRootComponents', getRootComponents); publishGlobalUtil('getDirectives', getDirectives); publishGlobalUtil('applyChanges', applyChanges); } } /** * Publishes the given function to `window.ng` so that it can be * used from the browser console when an application is not in production. */ function publishGlobalUtil(name, fn) { if (typeof COMPILED === 'undefined' || !COMPILED) { // Note: we can't export `ng` when using closure enhanced optimization as: // - closure declares globals itself for minified names, which sometimes clobber our `ng` global // - we can't declare a closure extern as the namespace `ng` is already used within Google // for typings for AngularJS (via `goog.provide('ng....')`). const w = _global; ngDevMode && assertDefined(fn, 'function not defined'); if (w) { let container = w[GLOBAL_PUBLISH_EXPANDO_KEY]; if (!container) { container = w[GLOBAL_PUBLISH_EXPANDO_KEY] = {}; } container[name] = fn; } } } const promise = (() => Promise.resolve(0))(); function scheduleMicroTask(fn) { if (typeof Zone === 'undefined') { // use promise to schedule microTask instead of use Zone promise.then(() => { fn && fn.apply(null, null); }); } else { Zone.current.scheduleMicroTask('scheduleMicrotask', fn); } } function getNativeRequestAnimationFrame() { let nativeRequestAnimationFrame = _global['requestAnimationFrame']; let nativeCancelAnimationFrame = _global['cancelAnimationFrame']; if (typeof Zone !== 'undefined' && nativeRequestAnimationFrame && nativeCancelAnimationFrame) { // use unpatched version of requestAnimationFrame(native delegate) if possible // to avoid another Change detection const unpatchedRequestAnimationFrame = nativeRequestAnimationFrame[Zone.__symbol__('OriginalDelegate')]; if (unpatchedRequestAnimationFrame) { nativeRequestAnimationFrame = unpatchedRequestAnimationFrame; } const unpatchedCancelAnimationFrame = nativeCancelAnimationFrame[Zone.__symbol__('OriginalDelegate')]; if (unpatchedCancelAnimationFrame) { nativeCancelAnimationFrame = unpatchedCancelAnimationFrame; } } return { nativeRequestAnimationFrame, nativeCancelAnimationFrame }; } class AsyncStackTaggingZoneSpec { constructor(namePrefix, consoleAsyncStackTaggingImpl = console) { this.name = 'asyncStackTagging for ' + namePrefix; this.createTask = consoleAsyncStackTaggingImpl?.createTask ?? (() => null); } onScheduleTask(delegate, _current, target, task) { task.consoleTask = this.createTask(`Zone - ${task.source || task.type}`); return delegate.scheduleTask(target, task); } onInvokeTask(delegate, _currentZone, targetZone, task, applyThis, applyArgs) { let ret; if (task.consoleTask) { ret = task.consoleTask.run(() => delegate.invokeTask(targetZone, task, applyThis, applyArgs)); } else { ret = delegate.invokeTask(targetZone, task, applyThis, applyArgs); } return ret; } } /** * An injectable service for executing work inside or outside of the Angular zone. * * The most common use of this service is to optimize performance when starting a work consisting of * one or more asynchronous tasks that don't require UI updates or error handling to be handled by * Angular. Such tasks can be kicked off via {@link #runOutsideAngular} and if needed, these tasks * can reenter the Angular zone via {@link #run}. * * <!-- TODO: add/fix links to: * - docs explaining zones and the use of zones in Angular and change-detection * - link to runOutsideAngular/run (throughout this file!) * --> * * @usageNotes * ### Example * * ``` * import {Component, NgZone} from '@angular/core'; * import {NgIf} from '@angular/common'; * * @Component({ * selector: 'ng-zone-demo', * template: ` * <h2>Demo: NgZone</h2> * * <p>Progress: {{progress}}%</p> * <p *ngIf="progress >= 100">Done processing {{label}} of Angular zone!</p> * * <button (click)="processWithinAngularZone()">Process within Angular zone</button> * <button (click)="processOutsideOfAngularZone()">Process outside of Angular zone</button> * `, * }) * export class NgZoneDemo { * progress: number = 0; * label: string; * * constructor(private _ngZone: NgZone) {} * * // Loop inside the Angular zone * // so the UI DOES refresh after each setTimeout cycle * processWithinAngularZone() { * this.label = 'inside'; * this.progress = 0; * this._increaseProgress(() => console.log('Inside Done!')); * } * * // Loop outside of the Angular zone * // so the UI DOES NOT refresh after each setTimeout cycle * processOutsideOfAngularZone() { * this.label = 'outside'; * this.progress = 0; * this._ngZone.runOutsideAngular(() => { * this._increaseProgress(() => { * // reenter the Angular zone and display done * this._ngZone.run(() => { console.log('Outside Done!'); }); * }); * }); * } * * _increaseProgress(doneCallback: () => void) { * this.progress += 1; * console.log(`Current progress: ${this.progress}%`); * * if (this.progress < 100) { * window.setTimeout(() => this._increaseProgress(doneCallback), 10); * } else { * doneCallback(); * } * } * } * ``` * * @publicApi */ class NgZone { constructor({ enableLongStackTrace = false, shouldCoalesceEventChangeDetection = false, shouldCoalesceRunChangeDetection = false }) { this.hasPendingMacrotasks = false; this.hasPendingMicrotasks = false; /** * Whether there are no outstanding microtasks or macrotasks. */ this.isStable = true; /** * Notifies when code enters Angular Zone. This gets fired first on VM Turn. */ this.onUnstable = new EventEmitter(false); /** * Notifies when there is no more microtasks enqueued in the current VM Turn. * This is a hint for Angular to do change detection, which may enqueue more microtasks. * For this reason this event can fire multiple times per VM Turn. */ this.onMicrotaskEmpty = new EventEmitter(false); /** * Notifies when the last `onMicrotaskEmpty` has run and there are no more microtasks, which * implies we are about to relinquish VM turn. * This event gets called just once. */ this.onStable = new EventEmitter(false); /** * Notifies that an error has been delivered. */ this.onError = new EventEmitter(false); if (typeof Zone == 'undefined') { throw new RuntimeError(908 /* RuntimeErrorCode.MISSING_ZONEJS */, ngDevMode && `In this configuration Angular requires Zone.js`); } Zone.assertZonePatched(); const self = this; self._nesting = 0; self._outer = self._inner = Zone.current; // AsyncStackTaggingZoneSpec provides `linked stack traces` to show // where the async operation is scheduled. For more details, refer // to this article, https://developer.chrome.com/blog/devtools-better-angular-debugging/ // And we only import this AsyncStackTaggingZoneSpec in development mode, // in the production mode, the AsyncStackTaggingZoneSpec will be tree shaken away. if (ngDevMode) { self._inner = self._inner.fork(new AsyncStackTaggingZoneSpec('Angular')); } if (Zone['TaskTrackingZoneSpec']) { self._inner = self._inner.fork(new Zone['TaskTrackingZoneSpec']()); } if (enableLongStackTrace && Zone['longStackTraceZoneSpec']) { self._inner = self._inner.fork(Zone['longStackTraceZoneSpec']); } // if shouldCoalesceRunChangeDetection is true, all tasks including event tasks will be // coalesced, so shouldCoalesceEventChangeDetection option is not necessary and can be skipped. self.shouldCoalesceEventChangeDetection = !shouldCoalesceRunChangeDetection && shouldCoalesceEventChangeDetection; self.shouldCoalesceRunChangeDetection = shouldCoalesceRunChangeDetection; self.lastRequestAnimationFrameId = -1; self.nativeRequestAnimationFrame = getNativeRequestAnimationFrame().nativeRequestAnimationFrame; forkInnerZoneWithAngularBehavior(self); } static isInAngularZone() { // Zone needs to be checked, because this method might be called even when NoopNgZone is used. return typeof Zone !== 'undefined' && Zone.current.get('isAngularZone') === true; } static assertInAngularZone() { if (!NgZone.isInAngularZone()) { throw new RuntimeError(909 /* RuntimeErrorCode.UNEXPECTED_ZONE_STATE */, ngDevMode && 'Expected to be in Angular Zone, but it is not!'); } } static assertNotInAngularZone() { if (NgZone.isInAngularZone()) { throw new RuntimeError(909 /* RuntimeErrorCode.UNEXPECTED_ZONE_STATE */, ngDevMode && 'Expected to not be in Angular Zone, but it is!'); } } /** * Executes the `fn` function synchronously within the Angular zone and returns value returned by * the function. * * Running functions via `run` allows you to reenter Angular zone from a task that was executed * outside of the Angular zone (typically started via {@link #runOutsideAngular}). * * Any future tasks or microtasks scheduled from within this function will continue executing from * within the Angular zone. * * If a synchronous error happens it will be rethrown and not reported via `onError`. */ run(fn, applyThis, applyArgs) { return this._inner.run(fn, applyThis, applyArgs); } /** * Executes the `fn` function synchronously within the Angular zone as a task and returns value * returned by the function. * * Running functions via `run` allows you to reenter Angular zone from a task that was executed * outside of the Angular zone (typically started via {@link #runOutsideAngular}). * * Any future tasks or microtasks scheduled from within this function will continue executing from * within the Angular zone. * * If a synchronous error happens it will be rethrown and not reported via `onError`. */ runTask(fn, applyThis, applyArgs, name) { const zone = this._inner; const task = zone.scheduleEventTask('NgZoneEvent: ' + name, fn, EMPTY_PAYLOAD, noop, noop); try { return zone.runTask(task, applyThis, applyArgs); } finally { zone.cancelTask(task); } } /** * Same as `run`, except that synchronous errors are caught and forwarded via `onError` and not * rethrown. */ runGuarded(fn, applyThis, applyArgs) { return this._inner.runGuarded(fn, applyThis, applyArgs); } /** * Executes the `fn` function synchronously in Angular's parent zone and returns value returned by * the function. * * Running functions via {@link #runOutsideAngular} allows you to escape Angular's zone and do * work that * doesn't trigger Angular change-detection or is subject to Angular's error handling. * * Any future tasks or microtasks scheduled from within this function will continue executing from * outside of the Angular zone. * * Use {@link #run} to reenter the Angular zone and do work that updates the application model. */ runOutsideAngular(fn) { return this._outer.run(fn); } } const EMPTY_PAYLOAD = {}; function checkStable(zone) { // TODO: @JiaLiPassion, should check zone.isCheckStableRunning to prevent // re-entry. The case is: // // @Component({...}) // export class AppComponent { // constructor(private ngZone: NgZone) { // this.ngZone.onStable.subscribe(() => { // this.ngZone.run(() => console.log('stable');); // }); // } // // The onStable subscriber run another function inside ngZone // which causes `checkStable()` re-entry. // But this fix causes some issues in g3, so this fix will be // launched in another PR. if (zone._nesting == 0 && !zone.hasPendingMicrotasks && !zone.isStable) { try { zone._nesting++; zone.onMicrotaskEmpty.emit(null); } finally { zone._nesting--; if (!zone.hasPendingMicrotasks) { try { zone.runOutsideAngular(() => zone.onStable.emit(null)); } finally { zone.isStable = true; } } } } } function delayChangeDetectionForEvents(zone) { /** * We also need to check _nesting here * Consider the following case with shouldCoalesceRunChangeDetection = true * * ngZone.run(() => {}); * ngZone.run(() => {}); * * We want the two `ngZone.run()` only trigger one change detection * when shouldCoalesceRunChangeDetection is true. * And because in this case, change detection run in async way(requestAnimationFrame), * so we also need to check the _nesting here to prevent multiple * change detections. */ if (zone.isCheckStableRunning || zone.lastRequestAnimationFrameId !== -1) { return; } zone.lastRequestAnimationFrameId = zone.nativeRequestAnimationFrame.call(_global, () => { // This is a work around for https://github.com/angular/angular/issues/36839. // The core issue is that when event coalescing is enabled it is possible for microtasks // to get flushed too early (As is the case with `Promise.then`) between the // coalescing eventTasks. // // To workaround this we schedule a "fake" eventTask before we process the // coalescing eventTasks. The benefit of this is that the "fake" container eventTask // will prevent the microtasks queue from getting drained in between the coalescing // eventTask execution. if (!zone.fakeTopEventTask) { zone.fakeTopEventTask = Zone.root.scheduleEventTask('fakeTopEventTask', () => { zone.lastRequestAnimationFrameId = -1; updateMicroTaskStatus(zone); zone.isCheckStableRunning = true; checkStable(zone); zone.isCheckStableRunning = false; }, undefined, () => {}, () => {}); } zone.fakeTopEventTask.invoke(); }); updateMicroTaskStatus(zone); } function forkInnerZoneWithAngularBehavior(zone) { const delayChangeDetectionForEventsDelegate = () => { delayChangeDetectionForEvents(zone); }; zone._inner = zone._inner.fork({ name: 'angular', properties: { 'isAngularZone': true }, onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => { try { onEnter(zone); return delegate.invokeTask(target, task, applyThis, applyArgs); } finally { if (zone.shouldCoalesceEventChangeDetection && task.type === 'eventTask' || zone.shouldCoalesceRunChangeDetection) { delayChangeDetectionForEventsDelegate(); } onLeave(zone); } }, onInvoke: (delegate, current, target, callback, applyThis, applyArgs, source) => { try { onEnter(zone); return delegate.invoke(target, callback, applyThis, applyArgs, source); } finally { if (zone.shouldCoalesceRunChangeDetection) { delayChangeDetectionForEventsDelegate(); } onLeave(zone); } }, onHasTask: (delegate, current, target, hasTaskState) => { delegate.hasTask(target, hasTaskState); if (current === target) { // We are only interested in hasTask events which originate from our zone // (A child hasTask event is not interesting to us) if (hasTaskState.change == 'microTask') { zone._hasPendingMicrotasks = hasTaskState.microTask; updateMicroTaskStatus(zone); checkStable(zone); } else if (hasTaskState.change == 'macroTask') { zone.hasPendingMacrotasks = hasTaskState.macroTask; } } }, onHandleError: (delegate, current, target, error) => { delegate.handleError(target, error); zone.runOutsideAngular(() => zone.onError.emit(error)); return false; } }); } function updateMicroTaskStatus(zone) { if (zone._hasPendingMicrotasks || (zone.shouldCoalesceEventChangeDetection || zone.shouldCoalesceRunChangeDetection) && zone.lastRequestAnimationFrameId !== -1) { zone.hasPendingMicrotasks = true; } else { zone.hasPendingMicrotasks = false; } } function onEnter(zone) { zone._nesting++; if (zone.isStable) { zone.isStable = false; zone.onUnstable.emit(null); } } function onLeave(zone) { zone._nesting--; checkStable(zone); } /** * Provides a noop implementation of `NgZone` which does nothing. This zone requires explicit calls * to framework to perform rendering. */ class NoopNgZone { constructor() { this.hasPendingMicrotasks = false; this.hasPendingMacrotasks = false; this.isStable = true; this.onUnstable = new EventEmitter(); this.onMicrotaskEmpty = new EventEmitter(); this.onStable = new EventEmitter(); this.onError = new EventEmitter(); } run(fn, applyThis, applyArgs) { return fn.apply(applyThis, applyArgs); } runGuarded(fn, applyThis, applyArgs) { return fn.apply(applyThis, applyArgs); } runOutsideAngular(fn) { return fn(); } runTask(fn, applyThis, applyArgs, name) { return fn.apply(applyThis, applyArgs); } } /** * Internal injection token that can used to access an instance of a Testability class. * * This token acts as a bridge between the core bootstrap code and the `Testability` class. This is * needed to ensure that there are no direct references to the `Testability` class, so it can be * tree-shaken away (if not referenced). For the environments/setups when the `Testability` class * should be available, this token is used to add a provider that references the `Testability` * class. Otherwise, only this token is retained in a bundle, but the `Testability` class is not. */ const TESTABILITY = new InjectionToken(''); /** * Internal injection token to retrieve Testability getter class instance. */ const TESTABILITY_GETTER = new InjectionToken(''); /** * The Testability service provides testing hooks that can be accessed from * the browser. * * Angular applications bootstrapped using an NgModule (via `@NgModule.bootstrap` field) will also * instantiate Testability by default (in both development and production modes). * * For applications bootstrapped using the `bootstrapApplication` function, Testability is not * included by default. You can include it into your applications by getting the list of necessary * providers using the `provideProtractorTestingSupport()` function and adding them into the * `options.providers` array. Example: * * ```typescript * import {provideProtractorTestingSupport} from '@angular/platform-browser'; * * await bootstrapApplication(RootComponent, providers: [provideProtractorTestingSupport()]); * ``` * * @publicApi */ class Testability { constructor(_ngZone, registry, testabilityGetter) { this._ngZone = _ngZone; this.registry = registry; this._pendingCount = 0; this._isZoneStable = true; /** * Whether any work was done since the last 'whenStable' callback. This is * useful to detect if this could have potentially destabilized another * component while it is stabilizing. * @internal */ this._didWork = false; this._callbacks = []; this.taskTrackingZone = null; // If there was no Testability logic registered in the global scope // before, register the current testability getter as a global one. if (!_testabilityGetter) { setTestabilityGetter(testabilityGetter); testabilityGetter.addToWindow(registry); } this._watchAngularEvents(); _ngZone.run(() => { this.taskTrackingZone = typeof Zone == 'undefined' ? null : Zone.current.get('TaskTrackingZone'); }); } _watchAngularEvents() { this._ngZone.onUnstable.subscribe({ next: () => { this._didWork = true; this._isZoneStable = false; } }); this._ngZone.runOutsideAngular(() => { this._ngZone.onStable.subscribe({ next: () => { NgZone.assertNotInAngularZone(); scheduleMicroTask(() => { this._isZoneStable = true; this._runCallbacksIfReady(); }); } }); }); } /** * Increases the number of pending request * @deprecated pending requests are now tracked with zones. */ increasePendingRequestCount() { this._pendingCount += 1; this._didWork = true; return this._pendingCount; } /** * Decreases the number of pending request * @deprecated pending requests are now tracked with zones */ decreasePendingRequestCount() { this._pendingCount -= 1; if (this._pendingCount < 0) { throw new Error('pending async requests below zero'); } this._runCallbacksIfReady(); return this._pendingCount; } /** * Whether an associated application is stable */ isStable() { return this._isZoneStable && this._pendingCount === 0 && !this._ngZone.hasPendingMacrotasks; } _runCallbacksIfReady() { if (this.isStable()) { // Schedules the call backs in a new frame so that it is always async. scheduleMicroTask(() => { while (this._callbacks.length !== 0) { let cb = this._callbacks.pop(); clearTimeout(cb.timeoutId); cb.doneCb(this._didWork); } this._didWork = false; }); } else { // Still not stable, send updates. let pending = this.getPendingTasks(); this._callbacks = this._callbacks.filter(cb => { if (cb.updateCb && cb.updateCb(pending)) { clearTimeout(cb.timeoutId); return false; } return true; }); this._didWork = true; } } getPendingTasks() { if (!this.taskTrackingZone) { return []; } // Copy the tasks data so that we don't leak tasks. return this.taskTrackingZone.macroTasks.map(t => { return { source: t.source, // From TaskTrackingZone: // https://github.com/angular/zone.js/blob/master/lib/zone-spec/task-tracking.ts#L40 creationLocation: t.creationLocation, data: t.data }; }); } addCallback(cb, timeout, updateCb) { let timeoutId = -1; if (timeout && timeout > 0) { timeoutId = setTimeout(() => { this._callbacks = this._callbacks.filter(cb => cb.timeoutId !== timeoutId); cb(this._didWork, this.getPendingTasks()); }, timeout); } this._callbacks.push({ doneCb: cb, timeoutId: timeoutId, updateCb: updateCb }); } /** * Wait for the application to be stable with a timeout. If the timeout is reached before that * happens, the callback receives a list of the macro tasks that were pending, otherwise null. * * @param doneCb The callback to invoke when Angular is stable or the timeout expires * whichever comes first. * @param timeout Optional. The maximum time to wait for Angular to become stable. If not * specified, whenStable() will wait forever. * @param updateCb Optional. If specified, this callback will be invoked whenever the set of * pending macrotasks changes. If this callback returns true doneCb will not be invoked * and no further updates will be issued. */ whenStable(doneCb, timeout, updateCb) { if (updateCb && !this.taskTrackingZone) { throw new Error('Task tracking zone is required when passing an update callback to ' + 'whenStable(). Is "zone.js/plugins/task-tracking" loaded?'); } // These arguments are 'Function' above to keep the public API simple. this.addCallback(doneCb, timeout, updateCb); this._runCallbacksIfReady(); } /** * Get the number of pending requests * @deprecated pending requests are now tracked with zones */ getPendingRequestCount() { return this._pendingCount; } /** * Registers an application with a testability hook so that it can be tracked. * @param token token of application, root element * * @internal */ registerApplication(token) { this.registry.registerApplication(token, this); } /** * Unregisters an application. * @param token token of application, root element * * @internal */ unregisterApplication(token) { this.registry.unregisterApplication(token); } /** * Find providers by name * @param using The root element to search from * @param provider The name of binding variable * @param exactMatch Whether using exactMatch */ findProviders(using, provider, exactMatch) { // TODO(juliemr): implement. return []; } } Testability.ɵfac = function Testability_Factory(t) { return new (t || Testability)(ɵɵinject(NgZone), ɵɵinject(TestabilityRegistry), ɵɵinject(TESTABILITY_GETTER)); }; Testability.ɵprov = /*@__PURE__*/ɵɵdefineInjectable({ token: Testability, factory: Testability.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Testability, [{ type: Injectable }], function () { return [{ type: NgZone }, { type: TestabilityRegistry }, { type: undefined, decorators: [{ type: Inject, args: [TESTABILITY_GETTER] }] }]; }, null); })(); /** * A global registry of {@link Testability} instances for specific elements. * @publicApi */ class TestabilityRegistry { constructor() { /** @internal */ this._applications = new Map(); } /** * Registers an application with a testability hook so that it can be tracked * @param token token of application, root element * @param testability Testability hook */ registerApplication(token, testability) { this._applications.set(token, testability); } /** * Unregisters an application. * @param token token of application, root element */ unregisterApplication(token) { this._applications.delete(token); } /** * Unregisters all applications */ unregisterAllApplications() { this._applications.clear(); } /** * Get a testability hook associated with the application * @param elem root element */ getTestability(elem) { return this._applications.get(elem) || null; } /** * Get all registered testabilities */ getAllTestabilities() { return Array.from(this._applications.values()); } /** * Get all registered applications(root elements) */ getAllRootElements() { return Array.from(this._applications.keys()); } /** * Find testability of a node in the Tree * @param elem node * @param findInAncestors whether finding testability in ancestors if testability was not found in * current node */ findTestabilityInTree(elem, findInAncestors = true) { return _testabilityGetter?.findTestabilityInTree(this, elem, findInAncestors) ?? null; } } TestabilityRegistry.ɵfac = function TestabilityRegistry_Factory(t) { return new (t || TestabilityRegistry)(); }; TestabilityRegistry.ɵprov = /*@__PURE__*/ɵɵdefineInjectable({ token: TestabilityRegistry, factory: TestabilityRegistry.ɵfac, providedIn: 'platform' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(TestabilityRegistry, [{ type: Injectable, args: [{ providedIn: 'platform' }] }], null, null); })(); /** * Set the {@link GetTestability} implementation used by the Angular testing framework. * @publicApi */ function setTestabilityGetter(getter) { _testabilityGetter = getter; } let _testabilityGetter; const NG_DEV_MODE = typeof ngDevMode === 'undefined' || ngDevMode; let _platformInjector = null; /** * Internal token to indicate whether having multiple bootstrapped platform should be allowed (only * one bootstrapped platform is allowed by default). This token helps to support SSR scenarios. */ const ALLOW_MULTIPLE_PLATFORMS = new InjectionToken('AllowMultipleToken'); /** * Internal token that allows to register extra callbacks that should be invoked during the * `PlatformRef.destroy` operation. This token is needed to avoid a direct reference to the * `PlatformRef` class (i.e. register the callback via `PlatformRef.onDestroy`), thus making the * entire class tree-shakeable. */ const PLATFORM_DESTROY_LISTENERS = new InjectionToken('PlatformDestroyListeners'); /** * A [DI token](guide/glossary#di-token "DI token definition") that provides a set of callbacks to * be called for every component that is bootstrapped. * * Each callback must take a `ComponentRef` instance and return nothing. * * `(componentRef: ComponentRef) => void` * * @publicApi */ const APP_BOOTSTRAP_LISTENER = new InjectionToken('appBootstrapListener'); function compileNgModuleFactory(injector, options, moduleType) { ngDevMode && assertNgModuleType(moduleType); const moduleFactory = new NgModuleFactory(moduleType); // All of the logic below is irrelevant for AOT-compiled code. if (typeof ngJitMode !== 'undefined' && !ngJitMode) { return Promise.resolve(moduleFactory); } const compilerOptions = injector.get(COMPILER_OPTIONS, []).concat(options); // Configure the compiler to use the provided options. This call may fail when multiple modules // are bootstrapped with incompatible options, as a component can only be compiled according to // a single set of options. setJitOptions({ defaultEncapsulation: _lastDefined(compilerOptions.map(opts => opts.defaultEncapsulation)), preserveWhitespaces: _lastDefined(compilerOptions.map(opts => opts.preserveWhitespaces)) }); if (isComponentResourceResolutionQueueEmpty()) { return Promise.resolve(moduleFactory); } const compilerProviders = _mergeArrays(compilerOptions.map(o => o.providers)); // In case there are no compiler providers, we just return the module factory as // there won't be any resource loader. This can happen with Ivy, because AOT compiled // modules can be still passed through "bootstrapModule". In that case we shouldn't // unnecessarily require the JIT compiler. if (compilerProviders.length === 0) { return Promise.resolve(moduleFactory); } const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'NgModule', type: moduleType }); const compilerInjector = Injector.create({ providers: compilerProviders }); const resourceLoader = compilerInjector.get(compiler.ResourceLoader); // The resource loader can also return a string while the "resolveComponentResources" // always expects a promise. Therefore we need to wrap the returned value in a promise. return resolveComponentResources(url => Promise.resolve(resourceLoader.get(url))).then(() => moduleFactory); } function publishDefaultGlobalUtils() { ngDevMode && publishDefaultGlobalUtils$1(); } function isBoundToModule(cf) { return cf.isBoundToModule; } /** * A token for third-party components that can register themselves with NgProbe. * * @publicApi */ class NgProbeToken { constructor(name, token) { this.name = name; this.token = token; } } /** * Creates a platform. * Platforms must be created on launch using this function. * * @publicApi */ function createPlatform(injector) { if (_platformInjector && !_platformInjector.get(ALLOW_MULTIPLE_PLATFORMS, false)) { throw new RuntimeError(400 /* RuntimeErrorCode.MULTIPLE_PLATFORMS */, ngDevMode && 'There can be only one platform. Destroy the previous one to create a new one.'); } publishDefaultGlobalUtils(); _platformInjector = injector; const platform = injector.get(PlatformRef); runPlatformInitializers(injector); return platform; } /** * The goal of this function is to bootstrap a platform injector, * but avoid referencing `PlatformRef` class. * This function is needed for bootstrapping a Standalone Component. */ function createOrReusePlatformInjector(providers = []) { // If a platform injector already exists, it means that the platform // is already bootstrapped and no additional actions are required. if (_platformInjector) return _platformInjector; // Otherwise, setup a new platform injector and run platform initializers. const injector = createPlatformInjector(providers); _platformInjector = injector; publishDefaultGlobalUtils(); runPlatformInitializers(injector); return injector; } function runPlatformInitializers(injector) { const inits = injector.get(PLATFORM_INITIALIZER, null); if (inits) { inits.forEach(init => init()); } } /** * Internal create application API that implements the core application creation logic and optional * bootstrap logic. * * Platforms (such as `platform-browser`) may require different set of application and platform * providers for an application to function correctly. As a result, platforms may use this function * internally and supply the necessary providers during the bootstrap, while exposing * platform-specific APIs as a part of their public API. * * @returns A promise that returns an `ApplicationRef` instance once resolved. */ function internalCreateApplication(config) { const { rootComponent, appProviders, platformProviders } = config; if (NG_DEV_MODE && rootComponent !== undefined) { assertStandaloneComponentType(rootComponent); } const platformInjector = createOrReusePlatformInjector(platformProviders); const ngZone = getNgZone('zone.js', getNgZoneOptions()); return ngZone.run(() => { // Create root application injector based on a set of providers configured at the platform // bootstrap level as well as providers passed to the bootstrap call by a user. const allAppProviders = [{ provide: NgZone, useValue: ngZone }, ...(appProviders || []) // ]; const envInjector = createEnvironmentInjector(allAppProviders, platformInjector, 'Environment Injector'); const exceptionHandler = envInjector.get(ErrorHandler, null); if (NG_DEV_MODE && !exceptionHandler) { throw new RuntimeError(402 /* RuntimeErrorCode.ERROR_HANDLER_NOT_FOUND */, 'No `ErrorHandler` found in the Dependency Injection tree.'); } let onErrorSubscription; ngZone.runOutsideAngular(() => { onErrorSubscription = ngZone.onError.subscribe({ next: error => { exceptionHandler.handleError(error); } }); }); // If the whole platform is destroyed, invoke the `destroy` method // for all bootstrapped applications as well. const destroyListener = () => envInjector.destroy(); const onPlatformDestroyListeners = platformInjector.get(PLATFORM_DESTROY_LISTENERS); onPlatformDestroyListeners.add(destroyListener); envInjector.onDestroy(() => { onErrorSubscription.unsubscribe(); onPlatformDestroyListeners.delete(destroyListener); }); return _callAndReportToErrorHandler(exceptionHandler, ngZone, () => { const initStatus = envInjector.get(ApplicationInitStatus); initStatus.runInitializers(); return initStatus.donePromise.then(() => { const localeId = envInjector.get(LOCALE_ID, DEFAULT_LOCALE_ID); setLocaleId(localeId || DEFAULT_LOCALE_ID); const appRef = envInjector.get(ApplicationRef); if (rootComponent !== undefined) { appRef.bootstrap(rootComponent); } return appRef; }); }); }); } /** * Creates a factory for a platform. Can be used to provide or override `Providers` specific to * your application's runtime needs, such as `PLATFORM_INITIALIZER` and `PLATFORM_ID`. * @param parentPlatformFactory Another platform factory to modify. Allows you to compose factories * to build up configurations that might be required by different libraries or parts of the * application. * @param name Identifies the new platform factory. * @param providers A set of dependency providers for platforms created with the new factory. * * @publicApi */ function createPlatformFactory(parentPlatformFactory, name, providers = []) { const desc = `Platform: ${name}`; const marker = new InjectionToken(desc); return (extraProviders = []) => { let platform = getPlatform(); if (!platform || platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) { const platformProviders = [...providers, ...extraProviders, { provide: marker, useValue: true }]; if (parentPlatformFactory) { parentPlatformFactory(platformProviders); } else { createPlatform(createPlatformInjector(platformProviders, desc)); } } return assertPlatform(marker); }; } /** * Checks that there is currently a platform that contains the given token as a provider. * * @publicApi */ function assertPlatform(requiredToken) { const platform = getPlatform(); if (!platform) { throw new RuntimeError(401 /* RuntimeErrorCode.PLATFORM_NOT_FOUND */, ngDevMode && 'No platform exists!'); } if ((typeof ngDevMode === 'undefined' || ngDevMode) && !platform.injector.get(requiredToken, null)) { throw new RuntimeError(400 /* RuntimeErrorCode.MULTIPLE_PLATFORMS */, 'A platform with a different configuration has been created. Please destroy it first.'); } return platform; } /** * Helper function to create an instance of a platform injector (that maintains the 'platform' * scope). */ function createPlatformInjector(providers = [], name) { return Injector.create({ name, providers: [{ provide: INJECTOR_SCOPE, useValue: 'platform' }, { provide: PLATFORM_DESTROY_LISTENERS, useValue: new Set([() => _platformInjector = null]) }, ...providers] }); } /** * Destroys the current Angular platform and all Angular applications on the page. * Destroys all modules and listeners registered with the platform. * * @publicApi */ function destroyPlatform() { getPlatform()?.destroy(); } /** * Returns the current platform. * * @publicApi */ function getPlatform() { return _platformInjector?.get(PlatformRef) ?? null; } /** * The Angular platform is the entry point for Angular on a web page. * Each page has exactly one platform. Services (such as reflection) which are common * to every Angular application running on the page are bound in its scope. * A page's platform is initialized implicitly when a platform is created using a platform * factory such as `PlatformBrowser`, or explicitly by calling the `createPlatform()` function. * * @publicApi */ class PlatformRef { /** @internal */ constructor(_injector) { this._injector = _injector; this._modules = []; this._destroyListeners = []; this._destroyed = false; } /** * Creates an instance of an `@NgModule` for the given platform. * * @deprecated Passing NgModule factories as the `PlatformRef.bootstrapModuleFactory` function * argument is deprecated. Use the `PlatformRef.bootstrapModule` API instead. */ bootstrapModuleFactory(moduleFactory, options) { // Note: We need to create the NgZone _before_ we instantiate the module, // as instantiating the module creates some providers eagerly. // So we create a mini parent injector that just contains the new NgZone and // pass that as parent to the NgModuleFactory. const ngZone = getNgZone(options?.ngZone, getNgZoneOptions(options)); const providers = [{ provide: NgZone, useValue: ngZone }]; // Note: Create ngZoneInjector within ngZone.run so that all of the instantiated services are // created within the Angular zone // Do not try to replace ngZone.run with ApplicationRef#run because ApplicationRef would then be // created outside of the Angular zone. return ngZone.run(() => { const ngZoneInjector = Injector.create({ providers: providers, parent: this.injector, name: moduleFactory.moduleType.name }); const moduleRef = moduleFactory.create(ngZoneInjector); const exceptionHandler = moduleRef.injector.get(ErrorHandler, null); if (!exceptionHandler) { throw new RuntimeError(402 /* RuntimeErrorCode.ERROR_HANDLER_NOT_FOUND */, ngDevMode && 'No ErrorHandler. Is platform module (BrowserModule) included?'); } ngZone.runOutsideAngular(() => { const subscription = ngZone.onError.subscribe({ next: error => { exceptionHandler.handleError(error); } }); moduleRef.onDestroy(() => { remove(this._modules, moduleRef); subscription.unsubscribe(); }); }); return _callAndReportToErrorHandler(exceptionHandler, ngZone, () => { const initStatus = moduleRef.injector.get(ApplicationInitStatus); initStatus.runInitializers(); return initStatus.donePromise.then(() => { // If the `LOCALE_ID` provider is defined at bootstrap then we set the value for ivy const localeId = moduleRef.injector.get(LOCALE_ID, DEFAULT_LOCALE_ID); setLocaleId(localeId || DEFAULT_LOCALE_ID); this._moduleDoBootstrap(moduleRef); return moduleRef; }); }); }); } /** * Creates an instance of an `@NgModule` for a given platform. * * @usageNotes * ### Simple Example * * ```typescript * @NgModule({ * imports: [BrowserModule] * }) * class MyModule {} * * let moduleRef = platformBrowser().bootstrapModule(MyModule); * ``` * */ bootstrapModule(moduleType, compilerOptions = []) { const options = optionsReducer({}, compilerOptions); return compileNgModuleFactory(this.injector, options, moduleType).then(moduleFactory => this.bootstrapModuleFactory(moduleFactory, options)); } _moduleDoBootstrap(moduleRef) { const appRef = moduleRef.injector.get(ApplicationRef); if (moduleRef._bootstrapComponents.length > 0) { moduleRef._bootstrapComponents.forEach(f => appRef.bootstrap(f)); } else if (moduleRef.instance.ngDoBootstrap) { moduleRef.instance.ngDoBootstrap(appRef); } else { throw new RuntimeError(-403 /* RuntimeErrorCode.BOOTSTRAP_COMPONENTS_NOT_FOUND */, ngDevMode && `The module ${stringify(moduleRef.instance.constructor)} was bootstrapped, ` + `but it does not declare "@NgModule.bootstrap" components nor a "ngDoBootstrap" method. ` + `Please define one of these.`); } this._modules.push(moduleRef); } /** * Registers a listener to be called when the platform is destroyed. */ onDestroy(callback) { this._destroyListeners.push(callback); } /** * Retrieves the platform {@link Injector}, which is the parent injector for * every Angular application on the page and provides singleton providers. */ get injector() { return this._injector; } /** * Destroys the current Angular platform and all Angular applications on the page. * Destroys all modules and listeners registered with the platform. */ destroy() { if (this._destroyed) { throw new RuntimeError(404 /* RuntimeErrorCode.PLATFORM_ALREADY_DESTROYED */, ngDevMode && 'The platform has already been destroyed!'); } this._modules.slice().forEach(module => module.destroy()); this._destroyListeners.forEach(listener => listener()); const destroyListeners = this._injector.get(PLATFORM_DESTROY_LISTENERS, null); if (destroyListeners) { destroyListeners.forEach(listener => listener()); destroyListeners.clear(); } this._destroyed = true; } /** * Indicates whether this instance was destroyed. */ get destroyed() { return this._destroyed; } } PlatformRef.ɵfac = function PlatformRef_Factory(t) { return new (t || PlatformRef)(ɵɵinject(Injector)); }; PlatformRef.ɵprov = /*@__PURE__*/ɵɵdefineInjectable({ token: PlatformRef, factory: PlatformRef.ɵfac, providedIn: 'platform' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(PlatformRef, [{ type: Injectable, args: [{ providedIn: 'platform' }] }], function () { return [{ type: Injector }]; }, null); })(); // Transforms a set of `BootstrapOptions` (supported by the NgModule-based bootstrap APIs) -> // `NgZoneOptions` that are recognized by the NgZone constructor. Passing no options will result in // a set of default options returned. function getNgZoneOptions(options) { return { enableLongStackTrace: typeof ngDevMode === 'undefined' ? false : !!ngDevMode, shouldCoalesceEventChangeDetection: !!(options && options.ngZoneEventCoalescing) || false, shouldCoalesceRunChangeDetection: !!(options && options.ngZoneRunCoalescing) || false }; } function getNgZone(ngZoneToUse, options) { let ngZone; if (ngZoneToUse === 'noop') { ngZone = new NoopNgZone(); } else { ngZone = (ngZoneToUse === 'zone.js' ? undefined : ngZoneToUse) || new NgZone(options); } return ngZone; } function _callAndReportToErrorHandler(errorHandler, ngZone, callback) { try { const result = callback(); if (isPromise(result)) { return result.catch(e => { ngZone.runOutsideAngular(() => errorHandler.handleError(e)); // rethrow as the exception handler might not do it throw e; }); } return result; } catch (e) { ngZone.runOutsideAngular(() => errorHandler.handleError(e)); // rethrow as the exception handler might not do it throw e; } } function optionsReducer(dst, objs) { if (Array.isArray(objs)) { dst = objs.reduce(optionsReducer, dst); } else { dst = { ...dst, ...objs }; } return dst; } /** * A reference to an Angular application running on a page. * * @usageNotes * * {@a is-stable-examples} * ### isStable examples and caveats * * Note two important points about `isStable`, demonstrated in the examples below: * - the application will never be stable if you start any kind * of recurrent asynchronous task when the application starts * (for example for a polling process, started with a `setInterval`, a `setTimeout` * or using RxJS operators like `interval`); * - the `isStable` Observable runs outside of the Angular zone. * * Let's imagine that you start a recurrent task * (here incrementing a counter, using RxJS `interval`), * and at the same time subscribe to `isStable`. * * ``` * constructor(appRef: ApplicationRef) { * appRef.isStable.pipe( * filter(stable => stable) * ).subscribe(() => console.log('App is stable now'); * interval(1000).subscribe(counter => console.log(counter)); * } * ``` * In this example, `isStable` will never emit `true`, * and the trace "App is stable now" will never get logged. * * If you want to execute something when the app is stable, * you have to wait for the application to be stable * before starting your polling process. * * ``` * constructor(appRef: ApplicationRef) { * appRef.isStable.pipe( * first(stable => stable), * tap(stable => console.log('App is stable now')), * switchMap(() => interval(1000)) * ).subscribe(counter => console.log(counter)); * } * ``` * In this example, the trace "App is stable now" will be logged * and then the counter starts incrementing every second. * * Note also that this Observable runs outside of the Angular zone, * which means that the code in the subscription * to this Observable will not trigger the change detection. * * Let's imagine that instead of logging the counter value, * you update a field of your component * and display it in its template. * * ``` * constructor(appRef: ApplicationRef) { * appRef.isStable.pipe( * first(stable => stable), * switchMap(() => interval(1000)) * ).subscribe(counter => this.value = counter); * } * ``` * As the `isStable` Observable runs outside the zone, * the `value` field will be updated properly, * but the template will not be refreshed! * * You'll have to manually trigger the change detection to update the template. * * ``` * constructor(appRef: ApplicationRef, cd: ChangeDetectorRef) { * appRef.isStable.pipe( * first(stable => stable), * switchMap(() => interval(1000)) * ).subscribe(counter => { * this.value = counter; * cd.detectChanges(); * }); * } * ``` * * Or make the subscription callback run inside the zone. * * ``` * constructor(appRef: ApplicationRef, zone: NgZone) { * appRef.isStable.pipe( * first(stable => stable), * switchMap(() => interval(1000)) * ).subscribe(counter => zone.run(() => this.value = counter)); * } * ``` * * @publicApi */ class ApplicationRef { /** * Indicates whether this instance was destroyed. */ get destroyed() { return this._destroyed; } /** * The `EnvironmentInjector` used to create this application. */ get injector() { return this._injector; } /** @internal */ constructor(_zone, _injector, _exceptionHandler) { this._zone = _zone; this._injector = _injector; this._exceptionHandler = _exceptionHandler; /** @internal */ this._bootstrapListeners = []; this._views = []; this._runningTick = false; this._stable = true; this._destroyed = false; this._destroyListeners = []; /** * Get a list of component types registered to this application. * This list is populated even before the component is created. */ this.componentTypes = []; /** * Get a list of components registered to this application. */ this.components = []; this._onMicrotaskEmptySubscription = this._zone.onMicrotaskEmpty.subscribe({ next: () => { this._zone.run(() => { this.tick(); }); } }); const isCurrentlyStable = new rxjs__WEBPACK_IMPORTED_MODULE_2__.Observable(observer => { this._stable = this._zone.isStable && !this._zone.hasPendingMacrotasks && !this._zone.hasPendingMicrotasks; this._zone.runOutsideAngular(() => { observer.next(this._stable); observer.complete(); }); }); const isStable = new rxjs__WEBPACK_IMPORTED_MODULE_2__.Observable(observer => { // Create the subscription to onStable outside the Angular Zone so that // the callback is run outside the Angular Zone. let stableSub; this._zone.runOutsideAngular(() => { stableSub = this._zone.onStable.subscribe(() => { NgZone.assertNotInAngularZone(); // Check whether there are no pending macro/micro tasks in the next tick // to allow for NgZone to update the state. scheduleMicroTask(() => { if (!this._stable && !this._zone.hasPendingMacrotasks && !this._zone.hasPendingMicrotasks) { this._stable = true; observer.next(true); } }); }); }); const unstableSub = this._zone.onUnstable.subscribe(() => { NgZone.assertInAngularZone(); if (this._stable) { this._stable = false; this._zone.runOutsideAngular(() => { observer.next(false); }); } }); return () => { stableSub.unsubscribe(); unstableSub.unsubscribe(); }; }); this.isStable = (0,rxjs__WEBPACK_IMPORTED_MODULE_3__.merge)(isCurrentlyStable, isStable.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.share)())); } /** * Bootstrap a component onto the element identified by its selector or, optionally, to a * specified element. * * @usageNotes * ### Bootstrap process * * When bootstrapping a component, Angular mounts it onto a target DOM element * and kicks off automatic change detection. The target DOM element can be * provided using the `rootSelectorOrNode` argument. * * If the target DOM element is not provided, Angular tries to find one on a page * using the `selector` of the component that is being bootstrapped * (first matched element is used). * * ### Example * * Generally, we define the component to bootstrap in the `bootstrap` array of `NgModule`, * but it requires us to know the component while writing the application code. * * Imagine a situation where we have to wait for an API call to decide about the component to * bootstrap. We can use the `ngDoBootstrap` hook of the `NgModule` and call this method to * dynamically bootstrap a component. * * {@example core/ts/platform/platform.ts region='componentSelector'} * * Optionally, a component can be mounted onto a DOM element that does not match the * selector of the bootstrapped component. * * In the following example, we are providing a CSS selector to match the target element. * * {@example core/ts/platform/platform.ts region='cssSelector'} * * While in this example, we are providing reference to a DOM node. * * {@example core/ts/platform/platform.ts region='domNode'} */ bootstrap(componentOrFactory, rootSelectorOrNode) { NG_DEV_MODE && this.warnIfDestroyed(); const isComponentFactory = componentOrFactory instanceof ComponentFactory$1; const initStatus = this._injector.get(ApplicationInitStatus); if (!initStatus.done) { const standalone = !isComponentFactory && isStandalone(componentOrFactory); const errorMessage = 'Cannot bootstrap as there are still asynchronous initializers running.' + (standalone ? '' : ' Bootstrap components in the `ngDoBootstrap` method of the root module.'); throw new RuntimeError(405 /* RuntimeErrorCode.ASYNC_INITIALIZERS_STILL_RUNNING */, NG_DEV_MODE && errorMessage); } let componentFactory; if (isComponentFactory) { componentFactory = componentOrFactory; } else { const resolver = this._injector.get(ComponentFactoryResolver$1); componentFactory = resolver.resolveComponentFactory(componentOrFactory); } this.componentTypes.push(componentFactory.componentType); // Create a factory associated with the current module if it's not bound to some other const ngModule = isBoundToModule(componentFactory) ? undefined : this._injector.get(NgModuleRef$1); const selectorOrNode = rootSelectorOrNode || componentFactory.selector; const compRef = componentFactory.create(Injector.NULL, [], selectorOrNode, ngModule); const nativeElement = compRef.location.nativeElement; const testability = compRef.injector.get(TESTABILITY, null); testability?.registerApplication(nativeElement); compRef.onDestroy(() => { this.detachView(compRef.hostView); remove(this.components, compRef); testability?.unregisterApplication(nativeElement); }); this._loadComponent(compRef); if (typeof ngDevMode === 'undefined' || ngDevMode) { const _console = this._injector.get(Console); _console.log(`Angular is running in development mode. Call enableProdMode() to enable production mode.`); } return compRef; } /** * Invoke this method to explicitly process change detection and its side-effects. * * In development mode, `tick()` also performs a second change detection cycle to ensure that no * further changes are detected. If additional changes are picked up during this second cycle, * bindings in the app have side-effects that cannot be resolved in a single change detection * pass. * In this case, Angular throws an error, since an Angular application can only have one change * detection pass during which all change detection must complete. */ tick() { NG_DEV_MODE && this.warnIfDestroyed(); if (this._runningTick) { throw new RuntimeError(101 /* RuntimeErrorCode.RECURSIVE_APPLICATION_REF_TICK */, ngDevMode && 'ApplicationRef.tick is called recursively'); } try { this._runningTick = true; for (let view of this._views) { view.detectChanges(); } if (typeof ngDevMode === 'undefined' || ngDevMode) { for (let view of this._views) { view.checkNoChanges(); } } } catch (e) { // Attention: Don't rethrow as it could cancel subscriptions to Observables! this._zone.runOutsideAngular(() => this._exceptionHandler.handleError(e)); } finally { this._runningTick = false; } } /** * Attaches a view so that it will be dirty checked. * The view will be automatically detached when it is destroyed. * This will throw if the view is already attached to a ViewContainer. */ attachView(viewRef) { NG_DEV_MODE && this.warnIfDestroyed(); const view = viewRef; this._views.push(view); view.attachToAppRef(this); } /** * Detaches a view from dirty checking again. */ detachView(viewRef) { NG_DEV_MODE && this.warnIfDestroyed(); const view = viewRef; remove(this._views, view); view.detachFromAppRef(); } _loadComponent(componentRef) { this.attachView(componentRef.hostView); this.tick(); this.components.push(componentRef); // Get the listeners lazily to prevent DI cycles. const listeners = this._injector.get(APP_BOOTSTRAP_LISTENER, []); if (ngDevMode && !Array.isArray(listeners)) { throw new RuntimeError(-209 /* RuntimeErrorCode.INVALID_MULTI_PROVIDER */, 'Unexpected type of the `APP_BOOTSTRAP_LISTENER` token value ' + `(expected an array, but got ${typeof listeners}). ` + 'Please check that the `APP_BOOTSTRAP_LISTENER` token is configured as a ' + '`multi: true` provider.'); } listeners.push(...this._bootstrapListeners); listeners.forEach(listener => listener(componentRef)); } /** @internal */ ngOnDestroy() { if (this._destroyed) return; try { // Call all the lifecycle hooks. this._destroyListeners.forEach(listener => listener()); // Destroy all registered views. this._views.slice().forEach(view => view.destroy()); this._onMicrotaskEmptySubscription.unsubscribe(); } finally { // Indicate that this instance is destroyed. this._destroyed = true; // Release all references. this._views = []; this._bootstrapListeners = []; this._destroyListeners = []; } } /** * Registers a listener to be called when an instance is destroyed. * * @param callback A callback function to add as a listener. * @returns A function which unregisters a listener. * * @internal */ onDestroy(callback) { NG_DEV_MODE && this.warnIfDestroyed(); this._destroyListeners.push(callback); return () => remove(this._destroyListeners, callback); } /** * Destroys an Angular application represented by this `ApplicationRef`. Calling this function * will destroy the associated environment injectors as well as all the bootstrapped components * with their views. */ destroy() { if (this._destroyed) { throw new RuntimeError(406 /* RuntimeErrorCode.APPLICATION_REF_ALREADY_DESTROYED */, ngDevMode && 'This instance of the `ApplicationRef` has already been destroyed.'); } const injector = this._injector; // Check that this injector instance supports destroy operation. if (injector.destroy && !injector.destroyed) { // Destroying an underlying injector will trigger the `ngOnDestroy` lifecycle // hook, which invokes the remaining cleanup actions. injector.destroy(); } } /** * Returns the number of attached views. */ get viewCount() { return this._views.length; } warnIfDestroyed() { if (NG_DEV_MODE && this._destroyed) { console.warn(formatRuntimeError(406 /* RuntimeErrorCode.APPLICATION_REF_ALREADY_DESTROYED */, 'This instance of the `ApplicationRef` has already been destroyed.')); } } } ApplicationRef.ɵfac = function ApplicationRef_Factory(t) { return new (t || ApplicationRef)(ɵɵinject(NgZone), ɵɵinject(EnvironmentInjector), ɵɵinject(ErrorHandler)); }; ApplicationRef.ɵprov = /*@__PURE__*/ɵɵdefineInjectable({ token: ApplicationRef, factory: ApplicationRef.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ApplicationRef, [{ type: Injectable, args: [{ providedIn: 'root' }] }], function () { return [{ type: NgZone }, { type: EnvironmentInjector }, { type: ErrorHandler }]; }, null); })(); function remove(list, el) { const index = list.indexOf(el); if (index > -1) { list.splice(index, 1); } } function _lastDefined(args) { for (let i = args.length - 1; i >= 0; i--) { if (args[i] !== undefined) { return args[i]; } } return undefined; } function _mergeArrays(parts) { const result = []; parts.forEach(part => part && result.push(...part)); return result; } /** * Returns whether Angular is in development mode. * * By default, this is true, unless `enableProdMode` is invoked prior to calling this method or the * application is built using the Angular CLI with the `optimization` option. * @see {@link cli/build ng build} * * @publicApi */ function isDevMode() { return typeof ngDevMode === 'undefined' || !!ngDevMode; } /** * Disable Angular's development mode, which turns off assertions and other * checks within the framework. * * One important assertion this disables verifies that a change detection pass * does not result in additional changes to any bindings (also known as * unidirectional data flow). * * Using this method is discouraged as the Angular CLI will set production mode when using the * `optimization` option. * @see {@link cli/build ng build} * * @publicApi */ function enableProdMode() { // The below check is there so when ngDevMode is set via terser // `global['ngDevMode'] = false;` is also dropped. if (typeof ngDevMode === 'undefined' || ngDevMode) { _global['ngDevMode'] = false; } } // Public API for Zone // Public API for render /** * Returns the NgModuleFactory with the given id (specified using [@NgModule.id * field](api/core/NgModule#id)), if it exists and has been loaded. Factories for NgModules that do * not specify an `id` cannot be retrieved. Throws if an NgModule cannot be found. * @publicApi * @deprecated Use `getNgModuleById` instead. */ function getModuleFactory(id) { const type = getRegisteredNgModuleType(id); if (!type) throw noModuleError(id); return new NgModuleFactory(type); } /** * Returns the NgModule class with the given id (specified using [@NgModule.id * field](api/core/NgModule#id)), if it exists and has been loaded. Classes for NgModules that do * not specify an `id` cannot be retrieved. Throws if an NgModule cannot be found. * @publicApi */ function getNgModuleById(id) { const type = getRegisteredNgModuleType(id); if (!type) throw noModuleError(id); return type; } function noModuleError(id) { return new Error(`No module with ID ${id} loaded`); } /** * Base class that provides change detection functionality. * A change-detection tree collects all views that are to be checked for changes. * Use the methods to add and remove views from the tree, initiate change-detection, * and explicitly mark views as _dirty_, meaning that they have changed and need to be re-rendered. * * @see [Using change detection hooks](guide/lifecycle-hooks#using-change-detection-hooks) * @see [Defining custom change detection](guide/lifecycle-hooks#defining-custom-change-detection) * * @usageNotes * * The following examples demonstrate how to modify default change-detection behavior * to perform explicit detection when needed. * * ### Use `markForCheck()` with `CheckOnce` strategy * * The following example sets the `OnPush` change-detection strategy for a component * (`CheckOnce`, rather than the default `CheckAlways`), then forces a second check * after an interval. See [live demo](https://plnkr.co/edit/GC512b?p=preview). * * <code-example path="core/ts/change_detect/change-detection.ts" * region="mark-for-check"></code-example> * * ### Detach change detector to limit how often check occurs * * The following example defines a component with a large list of read-only data * that is expected to change constantly, many times per second. * To improve performance, we want to check and update the list * less often than the changes actually occur. To do that, we detach * the component's change detector and perform an explicit local check every five seconds. * * <code-example path="core/ts/change_detect/change-detection.ts" region="detach"></code-example> * * * ### Reattaching a detached component * * The following example creates a component displaying live data. * The component detaches its change detector from the main change detector tree * when the `live` property is set to false, and reattaches it when the property * becomes true. * * <code-example path="core/ts/change_detect/change-detection.ts" region="reattach"></code-example> * * @publicApi */ class ChangeDetectorRef {} /** * @internal * @nocollapse */ ChangeDetectorRef.__NG_ELEMENT_ID__ = injectChangeDetectorRef; /** Returns a ChangeDetectorRef (a.k.a. a ViewRef) */ function injectChangeDetectorRef(flags) { return createViewRef(getCurrentTNode(), getLView(), (flags & 16 /* InternalInjectFlags.ForPipe */) === 16 /* InternalInjectFlags.ForPipe */); } /** * Creates a ViewRef and stores it on the injector as ChangeDetectorRef (public alias). * * @param tNode The node that is requesting a ChangeDetectorRef * @param lView The view to which the node belongs * @param isPipe Whether the view is being injected into a pipe. * @returns The ChangeDetectorRef to use */ function createViewRef(tNode, lView, isPipe) { if (isComponentHost(tNode) && !isPipe) { // The LView represents the location where the component is declared. // Instead we want the LView for the component View and so we need to look it up. const componentView = getComponentLViewByIndex(tNode.index, lView); // look down return new ViewRef$1(componentView, componentView); } else if (tNode.type & (3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 32 /* TNodeType.Icu */)) { // The LView represents the location where the injection is requested from. // We need to locate the containing LView (in case where the `lView` is an embedded view) const hostComponentView = lView[DECLARATION_COMPONENT_VIEW]; // look up return new ViewRef$1(hostComponentView, lView); } return null; } /** * Represents an Angular [view](guide/glossary#view "Definition"). * * @see {@link ChangeDetectorRef#usage-notes Change detection usage} * * @publicApi */ class ViewRef extends ChangeDetectorRef {} /** * Represents an Angular [view](guide/glossary#view) in a view container. * An [embedded view](guide/glossary#view-hierarchy) can be referenced from a component * other than the hosting component whose template defines it, or it can be defined * independently by a `TemplateRef`. * * Properties of elements in a view can change, but the structure (number and order) of elements in * a view cannot. Change the structure of elements by inserting, moving, or * removing nested views in a view container. * * @see `ViewContainerRef` * * @usageNotes * * The following template breaks down into two separate `TemplateRef` instances, * an outer one and an inner one. * * ``` * Count: {{items.length}} * <ul> * <li *ngFor="let item of items">{{item}}</li> * </ul> * ``` * * This is the outer `TemplateRef`: * * ``` * Count: {{items.length}} * <ul> * <ng-template ngFor let-item [ngForOf]="items"></ng-template> * </ul> * ``` * * This is the inner `TemplateRef`: * * ``` * <li>{{item}}</li> * ``` * * The outer and inner `TemplateRef` instances are assembled into views as follows: * * ``` * <!-- ViewRef: outer-0 --> * Count: 2 * <ul> * <ng-template view-container-ref></ng-template> * <!-- ViewRef: inner-1 --><li>first</li><!-- /ViewRef: inner-1 --> * <!-- ViewRef: inner-2 --><li>second</li><!-- /ViewRef: inner-2 --> * </ul> * <!-- /ViewRef: outer-0 --> * ``` * @publicApi */ class EmbeddedViewRef extends ViewRef {} // Public API for compiler // This file exists for easily patching NgModuleFactoryLoader in g3 var ng_module_factory_loader_impl = {}; /** * @publicApi */ class DebugEventListener { constructor(name, callback) { this.name = name; this.callback = callback; } } /** * @publicApi */ function asNativeElements(debugEls) { return debugEls.map(el => el.nativeElement); } /** * @publicApi */ class DebugNode { constructor(nativeNode) { this.nativeNode = nativeNode; } /** * The `DebugElement` parent. Will be `null` if this is the root element. */ get parent() { const parent = this.nativeNode.parentNode; return parent ? new DebugElement(parent) : null; } /** * The host dependency injector. For example, the root element's component instance injector. */ get injector() { return getInjector(this.nativeNode); } /** * The element's own component instance, if it has one. */ get componentInstance() { const nativeElement = this.nativeNode; return nativeElement && (getComponent(nativeElement) || getOwningComponent(nativeElement)); } /** * An object that provides parent context for this element. Often an ancestor component instance * that governs this element. * * When an element is repeated within *ngFor, the context is an `NgForOf` whose `$implicit` * property is the value of the row instance value. For example, the `hero` in `*ngFor="let hero * of heroes"`. */ get context() { return getComponent(this.nativeNode) || getContext(this.nativeNode); } /** * The callbacks attached to the component's @Output properties and/or the element's event * properties. */ get listeners() { return getListeners(this.nativeNode).filter(listener => listener.type === 'dom'); } /** * Dictionary of objects associated with template local variables (e.g. #foo), keyed by the local * variable name. */ get references() { return getLocalRefs(this.nativeNode); } /** * This component's injector lookup tokens. Includes the component itself plus the tokens that the * component lists in its providers metadata. */ get providerTokens() { return getInjectionTokens(this.nativeNode); } } /** * @publicApi * * @see [Component testing scenarios](guide/testing-components-scenarios) * @see [Basics of testing components](guide/testing-components-basics) * @see [Testing utility APIs](guide/testing-utility-apis) */ class DebugElement extends DebugNode { constructor(nativeNode) { ngDevMode && assertDomNode(nativeNode); super(nativeNode); } /** * The underlying DOM element at the root of the component. */ get nativeElement() { return this.nativeNode.nodeType == Node.ELEMENT_NODE ? this.nativeNode : null; } /** * The element tag name, if it is an element. */ get name() { const context = getLContext(this.nativeNode); const lView = context ? context.lView : null; if (lView !== null) { const tData = lView[TVIEW].data; const tNode = tData[context.nodeIndex]; return tNode.value; } else { return this.nativeNode.nodeName; } } /** * Gets a map of property names to property values for an element. * * This map includes: * - Regular property bindings (e.g. `[id]="id"`) * - Host property bindings (e.g. `host: { '[id]': "id" }`) * - Interpolated property bindings (e.g. `id="{{ value }}") * * It does not include: * - input property bindings (e.g. `[myCustomInput]="value"`) * - attribute bindings (e.g. `[attr.role]="menu"`) */ get properties() { const context = getLContext(this.nativeNode); const lView = context ? context.lView : null; if (lView === null) { return {}; } const tData = lView[TVIEW].data; const tNode = tData[context.nodeIndex]; const properties = {}; // Collect properties from the DOM. copyDomProperties(this.nativeElement, properties); // Collect properties from the bindings. This is needed for animation renderer which has // synthetic properties which don't get reflected into the DOM. collectPropertyBindings(properties, tNode, lView, tData); return properties; } /** * A map of attribute names to attribute values for an element. */ get attributes() { const attributes = {}; const element = this.nativeElement; if (!element) { return attributes; } const context = getLContext(element); const lView = context ? context.lView : null; if (lView === null) { return {}; } const tNodeAttrs = lView[TVIEW].data[context.nodeIndex].attrs; const lowercaseTNodeAttrs = []; // For debug nodes we take the element's attribute directly from the DOM since it allows us // to account for ones that weren't set via bindings (e.g. ViewEngine keeps track of the ones // that are set through `Renderer2`). The problem is that the browser will lowercase all names, // however since we have the attributes already on the TNode, we can preserve the case by going // through them once, adding them to the `attributes` map and putting their lower-cased name // into an array. Afterwards when we're going through the native DOM attributes, we can check // whether we haven't run into an attribute already through the TNode. if (tNodeAttrs) { let i = 0; while (i < tNodeAttrs.length) { const attrName = tNodeAttrs[i]; // Stop as soon as we hit a marker. We only care about the regular attributes. Everything // else will be handled below when we read the final attributes off the DOM. if (typeof attrName !== 'string') break; const attrValue = tNodeAttrs[i + 1]; attributes[attrName] = attrValue; lowercaseTNodeAttrs.push(attrName.toLowerCase()); i += 2; } } for (const attr of element.attributes) { // Make sure that we don't assign the same attribute both in its // case-sensitive form and the lower-cased one from the browser. if (!lowercaseTNodeAttrs.includes(attr.name)) { attributes[attr.name] = attr.value; } } return attributes; } /** * The inline styles of the DOM element. * * Will be `null` if there is no `style` property on the underlying DOM element. * * @see [ElementCSSInlineStyle](https://developer.mozilla.org/en-US/docs/Web/API/ElementCSSInlineStyle/style) */ get styles() { if (this.nativeElement && this.nativeElement.style) { return this.nativeElement.style; } return {}; } /** * A map containing the class names on the element as keys. * * This map is derived from the `className` property of the DOM element. * * Note: The values of this object will always be `true`. The class key will not appear in the KV * object if it does not exist on the element. * * @see [Element.className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) */ get classes() { const result = {}; const element = this.nativeElement; // SVG elements return an `SVGAnimatedString` instead of a plain string for the `className`. const className = element.className; const classes = typeof className !== 'string' ? className.baseVal.split(' ') : className.split(' '); classes.forEach(value => result[value] = true); return result; } /** * The `childNodes` of the DOM element as a `DebugNode` array. * * @see [Node.childNodes](https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes) */ get childNodes() { const childNodes = this.nativeNode.childNodes; const children = []; for (let i = 0; i < childNodes.length; i++) { const element = childNodes[i]; children.push(getDebugNode(element)); } return children; } /** * The immediate `DebugElement` children. Walk the tree by descending through `children`. */ get children() { const nativeElement = this.nativeElement; if (!nativeElement) return []; const childNodes = nativeElement.children; const children = []; for (let i = 0; i < childNodes.length; i++) { const element = childNodes[i]; children.push(getDebugNode(element)); } return children; } /** * @returns the first `DebugElement` that matches the predicate at any depth in the subtree. */ query(predicate) { const results = this.queryAll(predicate); return results[0] || null; } /** * @returns All `DebugElement` matches for the predicate at any depth in the subtree. */ queryAll(predicate) { const matches = []; _queryAll(this, predicate, matches, true); return matches; } /** * @returns All `DebugNode` matches for the predicate at any depth in the subtree. */ queryAllNodes(predicate) { const matches = []; _queryAll(this, predicate, matches, false); return matches; } /** * Triggers the event by its name if there is a corresponding listener in the element's * `listeners` collection. * * If the event lacks a listener or there's some other problem, consider * calling `nativeElement.dispatchEvent(eventObject)`. * * @param eventName The name of the event to trigger * @param eventObj The _event object_ expected by the handler * * @see [Testing components scenarios](guide/testing-components-scenarios#trigger-event-handler) */ triggerEventHandler(eventName, eventObj) { const node = this.nativeNode; const invokedListeners = []; this.listeners.forEach(listener => { if (listener.name === eventName) { const callback = listener.callback; callback.call(node, eventObj); invokedListeners.push(callback); } }); // We need to check whether `eventListeners` exists, because it's something // that Zone.js only adds to `EventTarget` in browser environments. if (typeof node.eventListeners === 'function') { // Note that in Ivy we wrap event listeners with a call to `event.preventDefault` in some // cases. We use '__ngUnwrap__' as a special token that gives us access to the actual event // listener. node.eventListeners(eventName).forEach(listener => { // In order to ensure that we can detect the special __ngUnwrap__ token described above, we // use `toString` on the listener and see if it contains the token. We use this approach to // ensure that it still worked with compiled code since it cannot remove or rename string // literals. We also considered using a special function name (i.e. if(listener.name === // special)) but that was more cumbersome and we were also concerned the compiled code could // strip the name, turning the condition in to ("" === "") and always returning true. if (listener.toString().indexOf('__ngUnwrap__') !== -1) { const unwrappedListener = listener('__ngUnwrap__'); return invokedListeners.indexOf(unwrappedListener) === -1 && unwrappedListener.call(node, eventObj); } }); } } } function copyDomProperties(element, properties) { if (element) { // Skip own properties (as those are patched) let obj = Object.getPrototypeOf(element); const NodePrototype = Node.prototype; while (obj !== null && obj !== NodePrototype) { const descriptors = Object.getOwnPropertyDescriptors(obj); for (let key in descriptors) { if (!key.startsWith('__') && !key.startsWith('on')) { // don't include properties starting with `__` and `on`. // `__` are patched values which should not be included. // `on` are listeners which also should not be included. const value = element[key]; if (isPrimitiveValue(value)) { properties[key] = value; } } } obj = Object.getPrototypeOf(obj); } } } function isPrimitiveValue(value) { return typeof value === 'string' || typeof value === 'boolean' || typeof value === 'number' || value === null; } function _queryAll(parentElement, predicate, matches, elementsOnly) { const context = getLContext(parentElement.nativeNode); const lView = context ? context.lView : null; if (lView !== null) { const parentTNode = lView[TVIEW].data[context.nodeIndex]; _queryNodeChildren(parentTNode, lView, predicate, matches, elementsOnly, parentElement.nativeNode); } else { // If the context is null, then `parentElement` was either created with Renderer2 or native DOM // APIs. _queryNativeNodeDescendants(parentElement.nativeNode, predicate, matches, elementsOnly); } } /** * Recursively match the current TNode against the predicate, and goes on with the next ones. * * @param tNode the current TNode * @param lView the LView of this TNode * @param predicate the predicate to match * @param matches the list of positive matches * @param elementsOnly whether only elements should be searched * @param rootNativeNode the root native node on which predicate should not be matched */ function _queryNodeChildren(tNode, lView, predicate, matches, elementsOnly, rootNativeNode) { ngDevMode && assertTNodeForLView(tNode, lView); const nativeNode = getNativeByTNodeOrNull(tNode, lView); // For each type of TNode, specific logic is executed. if (tNode.type & (3 /* TNodeType.AnyRNode */ | 8 /* TNodeType.ElementContainer */)) { // Case 1: the TNode is an element // The native node has to be checked. _addQueryMatch(nativeNode, predicate, matches, elementsOnly, rootNativeNode); if (isComponentHost(tNode)) { // If the element is the host of a component, then all nodes in its view have to be processed. // Note: the component's content (tNode.child) will be processed from the insertion points. const componentView = getComponentLViewByIndex(tNode.index, lView); if (componentView && componentView[TVIEW].firstChild) { _queryNodeChildren(componentView[TVIEW].firstChild, componentView, predicate, matches, elementsOnly, rootNativeNode); } } else { if (tNode.child) { // Otherwise, its children have to be processed. _queryNodeChildren(tNode.child, lView, predicate, matches, elementsOnly, rootNativeNode); } // We also have to query the DOM directly in order to catch elements inserted through // Renderer2. Note that this is __not__ optimal, because we're walking similar trees multiple // times. ViewEngine could do it more efficiently, because all the insertions go through // Renderer2, however that's not the case in Ivy. This approach is being used because: // 1. Matching the ViewEngine behavior would mean potentially introducing a dependency // from `Renderer2` to Ivy which could bring Ivy code into ViewEngine. // 2. It allows us to capture nodes that were inserted directly via the DOM. nativeNode && _queryNativeNodeDescendants(nativeNode, predicate, matches, elementsOnly); } // In all cases, if a dynamic container exists for this node, each view inside it has to be // processed. const nodeOrContainer = lView[tNode.index]; if (isLContainer(nodeOrContainer)) { _queryNodeChildrenInContainer(nodeOrContainer, predicate, matches, elementsOnly, rootNativeNode); } } else if (tNode.type & 4 /* TNodeType.Container */) { // Case 2: the TNode is a container // The native node has to be checked. const lContainer = lView[tNode.index]; _addQueryMatch(lContainer[NATIVE], predicate, matches, elementsOnly, rootNativeNode); // Each view inside the container has to be processed. _queryNodeChildrenInContainer(lContainer, predicate, matches, elementsOnly, rootNativeNode); } else if (tNode.type & 16 /* TNodeType.Projection */) { // Case 3: the TNode is a projection insertion point (i.e. a <ng-content>). // The nodes projected at this location all need to be processed. const componentView = lView[DECLARATION_COMPONENT_VIEW]; const componentHost = componentView[T_HOST]; const head = componentHost.projection[tNode.projection]; if (Array.isArray(head)) { for (let nativeNode of head) { _addQueryMatch(nativeNode, predicate, matches, elementsOnly, rootNativeNode); } } else if (head) { const nextLView = componentView[PARENT]; const nextTNode = nextLView[TVIEW].data[head.index]; _queryNodeChildren(nextTNode, nextLView, predicate, matches, elementsOnly, rootNativeNode); } } else if (tNode.child) { // Case 4: the TNode is a view. _queryNodeChildren(tNode.child, lView, predicate, matches, elementsOnly, rootNativeNode); } // We don't want to go to the next sibling of the root node. if (rootNativeNode !== nativeNode) { // To determine the next node to be processed, we need to use the next or the projectionNext // link, depending on whether the current node has been projected. const nextTNode = tNode.flags & 2 /* TNodeFlags.isProjected */ ? tNode.projectionNext : tNode.next; if (nextTNode) { _queryNodeChildren(nextTNode, lView, predicate, matches, elementsOnly, rootNativeNode); } } } /** * Process all TNodes in a given container. * * @param lContainer the container to be processed * @param predicate the predicate to match * @param matches the list of positive matches * @param elementsOnly whether only elements should be searched * @param rootNativeNode the root native node on which predicate should not be matched */ function _queryNodeChildrenInContainer(lContainer, predicate, matches, elementsOnly, rootNativeNode) { for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) { const childView = lContainer[i]; const firstChild = childView[TVIEW].firstChild; if (firstChild) { _queryNodeChildren(firstChild, childView, predicate, matches, elementsOnly, rootNativeNode); } } } /** * Match the current native node against the predicate. * * @param nativeNode the current native node * @param predicate the predicate to match * @param matches the list of positive matches * @param elementsOnly whether only elements should be searched * @param rootNativeNode the root native node on which predicate should not be matched */ function _addQueryMatch(nativeNode, predicate, matches, elementsOnly, rootNativeNode) { if (rootNativeNode !== nativeNode) { const debugNode = getDebugNode(nativeNode); if (!debugNode) { return; } // Type of the "predicate and "matches" array are set based on the value of // the "elementsOnly" parameter. TypeScript is not able to properly infer these // types with generics, so we manually cast the parameters accordingly. if (elementsOnly && debugNode instanceof DebugElement && predicate(debugNode) && matches.indexOf(debugNode) === -1) { matches.push(debugNode); } else if (!elementsOnly && predicate(debugNode) && matches.indexOf(debugNode) === -1) { matches.push(debugNode); } } } /** * Match all the descendants of a DOM node against a predicate. * * @param nativeNode the current native node * @param predicate the predicate to match * @param matches the list where matches are stored * @param elementsOnly whether only elements should be searched */ function _queryNativeNodeDescendants(parentNode, predicate, matches, elementsOnly) { const nodes = parentNode.childNodes; const length = nodes.length; for (let i = 0; i < length; i++) { const node = nodes[i]; const debugNode = getDebugNode(node); if (debugNode) { if (elementsOnly && debugNode instanceof DebugElement && predicate(debugNode) && matches.indexOf(debugNode) === -1) { matches.push(debugNode); } else if (!elementsOnly && predicate(debugNode) && matches.indexOf(debugNode) === -1) { matches.push(debugNode); } _queryNativeNodeDescendants(node, predicate, matches, elementsOnly); } } } /** * Iterates through the property bindings for a given node and generates * a map of property names to values. This map only contains property bindings * defined in templates, not in host bindings. */ function collectPropertyBindings(properties, tNode, lView, tData) { let bindingIndexes = tNode.propertyBindings; if (bindingIndexes !== null) { for (let i = 0; i < bindingIndexes.length; i++) { const bindingIndex = bindingIndexes[i]; const propMetadata = tData[bindingIndex]; const metadataParts = propMetadata.split(INTERPOLATION_DELIMITER); const propertyName = metadataParts[0]; if (metadataParts.length > 1) { let value = metadataParts[1]; for (let j = 1; j < metadataParts.length - 1; j++) { value += renderStringify(lView[bindingIndex + j - 1]) + metadataParts[j + 1]; } properties[propertyName] = value; } else { properties[propertyName] = lView[bindingIndex]; } } } } // Need to keep the nodes in a global Map so that multiple angular apps are supported. const _nativeNodeToDebugNode = new Map(); const NG_DEBUG_PROPERTY = '__ng_debug__'; /** * @publicApi */ function getDebugNode(nativeNode) { if (nativeNode instanceof Node) { if (!nativeNode.hasOwnProperty(NG_DEBUG_PROPERTY)) { nativeNode[NG_DEBUG_PROPERTY] = nativeNode.nodeType == Node.ELEMENT_NODE ? new DebugElement(nativeNode) : new DebugNode(nativeNode); } return nativeNode[NG_DEBUG_PROPERTY]; } return null; } // TODO: cleanup all references to this function and remove it. function getDebugNodeR2(_nativeNode) { return null; } function getAllDebugNodes() { return Array.from(_nativeNodeToDebugNode.values()); } function indexDebugNode(node) { _nativeNodeToDebugNode.set(node.nativeNode, node); } function removeDebugNodeFromIndex(node) { _nativeNodeToDebugNode.delete(node.nativeNode); } class DefaultIterableDifferFactory { constructor() {} supports(obj) { return isListLikeIterable(obj); } create(trackByFn) { return new DefaultIterableDiffer(trackByFn); } } const trackByIdentity = (index, item) => item; /** * @deprecated v4.0.0 - Should not be part of public API. * @publicApi */ class DefaultIterableDiffer { constructor(trackByFn) { this.length = 0; // Keeps track of the used records at any point in time (during & across `_check()` calls) this._linkedRecords = null; // Keeps track of the removed records at any point in time during `_check()` calls. this._unlinkedRecords = null; this._previousItHead = null; this._itHead = null; this._itTail = null; this._additionsHead = null; this._additionsTail = null; this._movesHead = null; this._movesTail = null; this._removalsHead = null; this._removalsTail = null; // Keeps track of records where custom track by is the same, but item identity has changed this._identityChangesHead = null; this._identityChangesTail = null; this._trackByFn = trackByFn || trackByIdentity; } forEachItem(fn) { let record; for (record = this._itHead; record !== null; record = record._next) { fn(record); } } forEachOperation(fn) { let nextIt = this._itHead; let nextRemove = this._removalsHead; let addRemoveOffset = 0; let moveOffsets = null; while (nextIt || nextRemove) { // Figure out which is the next record to process // Order: remove, add, move const record = !nextRemove || nextIt && nextIt.currentIndex < getPreviousIndex(nextRemove, addRemoveOffset, moveOffsets) ? nextIt : nextRemove; const adjPreviousIndex = getPreviousIndex(record, addRemoveOffset, moveOffsets); const currentIndex = record.currentIndex; // consume the item, and adjust the addRemoveOffset and update moveDistance if necessary if (record === nextRemove) { addRemoveOffset--; nextRemove = nextRemove._nextRemoved; } else { nextIt = nextIt._next; if (record.previousIndex == null) { addRemoveOffset++; } else { // INVARIANT: currentIndex < previousIndex if (!moveOffsets) moveOffsets = []; const localMovePreviousIndex = adjPreviousIndex - addRemoveOffset; const localCurrentIndex = currentIndex - addRemoveOffset; if (localMovePreviousIndex != localCurrentIndex) { for (let i = 0; i < localMovePreviousIndex; i++) { const offset = i < moveOffsets.length ? moveOffsets[i] : moveOffsets[i] = 0; const index = offset + i; if (localCurrentIndex <= index && index < localMovePreviousIndex) { moveOffsets[i] = offset + 1; } } const previousIndex = record.previousIndex; moveOffsets[previousIndex] = localCurrentIndex - localMovePreviousIndex; } } } if (adjPreviousIndex !== currentIndex) { fn(record, adjPreviousIndex, currentIndex); } } } forEachPreviousItem(fn) { let record; for (record = this._previousItHead; record !== null; record = record._nextPrevious) { fn(record); } } forEachAddedItem(fn) { let record; for (record = this._additionsHead; record !== null; record = record._nextAdded) { fn(record); } } forEachMovedItem(fn) { let record; for (record = this._movesHead; record !== null; record = record._nextMoved) { fn(record); } } forEachRemovedItem(fn) { let record; for (record = this._removalsHead; record !== null; record = record._nextRemoved) { fn(record); } } forEachIdentityChange(fn) { let record; for (record = this._identityChangesHead; record !== null; record = record._nextIdentityChange) { fn(record); } } diff(collection) { if (collection == null) collection = []; if (!isListLikeIterable(collection)) { throw new RuntimeError(900 /* RuntimeErrorCode.INVALID_DIFFER_INPUT */, ngDevMode && `Error trying to diff '${stringify(collection)}'. Only arrays and iterables are allowed`); } if (this.check(collection)) { return this; } else { return null; } } onDestroy() {} check(collection) { this._reset(); let record = this._itHead; let mayBeDirty = false; let index; let item; let itemTrackBy; if (Array.isArray(collection)) { this.length = collection.length; for (let index = 0; index < this.length; index++) { item = collection[index]; itemTrackBy = this._trackByFn(index, item); if (record === null || !Object.is(record.trackById, itemTrackBy)) { record = this._mismatch(record, item, itemTrackBy, index); mayBeDirty = true; } else { if (mayBeDirty) { // TODO(misko): can we limit this to duplicates only? record = this._verifyReinsertion(record, item, itemTrackBy, index); } if (!Object.is(record.item, item)) this._addIdentityChange(record, item); } record = record._next; } } else { index = 0; iterateListLike(collection, item => { itemTrackBy = this._trackByFn(index, item); if (record === null || !Object.is(record.trackById, itemTrackBy)) { record = this._mismatch(record, item, itemTrackBy, index); mayBeDirty = true; } else { if (mayBeDirty) { // TODO(misko): can we limit this to duplicates only? record = this._verifyReinsertion(record, item, itemTrackBy, index); } if (!Object.is(record.item, item)) this._addIdentityChange(record, item); } record = record._next; index++; }); this.length = index; } this._truncate(record); this.collection = collection; return this.isDirty; } /* CollectionChanges is considered dirty if it has any additions, moves, removals, or identity * changes. */ get isDirty() { return this._additionsHead !== null || this._movesHead !== null || this._removalsHead !== null || this._identityChangesHead !== null; } /** * Reset the state of the change objects to show no changes. This means set previousKey to * currentKey, and clear all of the queues (additions, moves, removals). * Set the previousIndexes of moved and added items to their currentIndexes * Reset the list of additions, moves and removals * * @internal */ _reset() { if (this.isDirty) { let record; for (record = this._previousItHead = this._itHead; record !== null; record = record._next) { record._nextPrevious = record._next; } for (record = this._additionsHead; record !== null; record = record._nextAdded) { record.previousIndex = record.currentIndex; } this._additionsHead = this._additionsTail = null; for (record = this._movesHead; record !== null; record = record._nextMoved) { record.previousIndex = record.currentIndex; } this._movesHead = this._movesTail = null; this._removalsHead = this._removalsTail = null; this._identityChangesHead = this._identityChangesTail = null; // TODO(vicb): when assert gets supported // assert(!this.isDirty); } } /** * This is the core function which handles differences between collections. * * - `record` is the record which we saw at this position last time. If null then it is a new * item. * - `item` is the current item in the collection * - `index` is the position of the item in the collection * * @internal */ _mismatch(record, item, itemTrackBy, index) { // The previous record after which we will append the current one. let previousRecord; if (record === null) { previousRecord = this._itTail; } else { previousRecord = record._prev; // Remove the record from the collection since we know it does not match the item. this._remove(record); } // See if we have evicted the item, which used to be at some anterior position of _itHead list. record = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null); if (record !== null) { // It is an item which we have evicted earlier: reinsert it back into the list. // But first we need to check if identity changed, so we can update in view if necessary. if (!Object.is(record.item, item)) this._addIdentityChange(record, item); this._reinsertAfter(record, previousRecord, index); } else { // Attempt to see if the item is at some posterior position of _itHead list. record = this._linkedRecords === null ? null : this._linkedRecords.get(itemTrackBy, index); if (record !== null) { // We have the item in _itHead at/after `index` position. We need to move it forward in the // collection. // But first we need to check if identity changed, so we can update in view if necessary. if (!Object.is(record.item, item)) this._addIdentityChange(record, item); this._moveAfter(record, previousRecord, index); } else { // It is a new item: add it. record = this._addAfter(new IterableChangeRecord_(item, itemTrackBy), previousRecord, index); } } return record; } /** * This check is only needed if an array contains duplicates. (Short circuit of nothing dirty) * * Use case: `[a, a]` => `[b, a, a]` * * If we did not have this check then the insertion of `b` would: * 1) evict first `a` * 2) insert `b` at `0` index. * 3) leave `a` at index `1` as is. <-- this is wrong! * 3) reinsert `a` at index 2. <-- this is wrong! * * The correct behavior is: * 1) evict first `a` * 2) insert `b` at `0` index. * 3) reinsert `a` at index 1. * 3) move `a` at from `1` to `2`. * * * Double check that we have not evicted a duplicate item. We need to check if the item type may * have already been removed: * The insertion of b will evict the first 'a'. If we don't reinsert it now it will be reinserted * at the end. Which will show up as the two 'a's switching position. This is incorrect, since a * better way to think of it is as insert of 'b' rather then switch 'a' with 'b' and then add 'a' * at the end. * * @internal */ _verifyReinsertion(record, item, itemTrackBy, index) { let reinsertRecord = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null); if (reinsertRecord !== null) { record = this._reinsertAfter(reinsertRecord, record._prev, index); } else if (record.currentIndex != index) { record.currentIndex = index; this._addToMoves(record, index); } return record; } /** * Get rid of any excess {@link IterableChangeRecord_}s from the previous collection * * - `record` The first excess {@link IterableChangeRecord_}. * * @internal */ _truncate(record) { // Anything after that needs to be removed; while (record !== null) { const nextRecord = record._next; this._addToRemovals(this._unlink(record)); record = nextRecord; } if (this._unlinkedRecords !== null) { this._unlinkedRecords.clear(); } if (this._additionsTail !== null) { this._additionsTail._nextAdded = null; } if (this._movesTail !== null) { this._movesTail._nextMoved = null; } if (this._itTail !== null) { this._itTail._next = null; } if (this._removalsTail !== null) { this._removalsTail._nextRemoved = null; } if (this._identityChangesTail !== null) { this._identityChangesTail._nextIdentityChange = null; } } /** @internal */ _reinsertAfter(record, prevRecord, index) { if (this._unlinkedRecords !== null) { this._unlinkedRecords.remove(record); } const prev = record._prevRemoved; const next = record._nextRemoved; if (prev === null) { this._removalsHead = next; } else { prev._nextRemoved = next; } if (next === null) { this._removalsTail = prev; } else { next._prevRemoved = prev; } this._insertAfter(record, prevRecord, index); this._addToMoves(record, index); return record; } /** @internal */ _moveAfter(record, prevRecord, index) { this._unlink(record); this._insertAfter(record, prevRecord, index); this._addToMoves(record, index); return record; } /** @internal */ _addAfter(record, prevRecord, index) { this._insertAfter(record, prevRecord, index); if (this._additionsTail === null) { // TODO(vicb): // assert(this._additionsHead === null); this._additionsTail = this._additionsHead = record; } else { // TODO(vicb): // assert(_additionsTail._nextAdded === null); // assert(record._nextAdded === null); this._additionsTail = this._additionsTail._nextAdded = record; } return record; } /** @internal */ _insertAfter(record, prevRecord, index) { // TODO(vicb): // assert(record != prevRecord); // assert(record._next === null); // assert(record._prev === null); const next = prevRecord === null ? this._itHead : prevRecord._next; // TODO(vicb): // assert(next != record); // assert(prevRecord != record); record._next = next; record._prev = prevRecord; if (next === null) { this._itTail = record; } else { next._prev = record; } if (prevRecord === null) { this._itHead = record; } else { prevRecord._next = record; } if (this._linkedRecords === null) { this._linkedRecords = new _DuplicateMap(); } this._linkedRecords.put(record); record.currentIndex = index; return record; } /** @internal */ _remove(record) { return this._addToRemovals(this._unlink(record)); } /** @internal */ _unlink(record) { if (this._linkedRecords !== null) { this._linkedRecords.remove(record); } const prev = record._prev; const next = record._next; // TODO(vicb): // assert((record._prev = null) === null); // assert((record._next = null) === null); if (prev === null) { this._itHead = next; } else { prev._next = next; } if (next === null) { this._itTail = prev; } else { next._prev = prev; } return record; } /** @internal */ _addToMoves(record, toIndex) { // TODO(vicb): // assert(record._nextMoved === null); if (record.previousIndex === toIndex) { return record; } if (this._movesTail === null) { // TODO(vicb): // assert(_movesHead === null); this._movesTail = this._movesHead = record; } else { // TODO(vicb): // assert(_movesTail._nextMoved === null); this._movesTail = this._movesTail._nextMoved = record; } return record; } _addToRemovals(record) { if (this._unlinkedRecords === null) { this._unlinkedRecords = new _DuplicateMap(); } this._unlinkedRecords.put(record); record.currentIndex = null; record._nextRemoved = null; if (this._removalsTail === null) { // TODO(vicb): // assert(_removalsHead === null); this._removalsTail = this._removalsHead = record; record._prevRemoved = null; } else { // TODO(vicb): // assert(_removalsTail._nextRemoved === null); // assert(record._nextRemoved === null); record._prevRemoved = this._removalsTail; this._removalsTail = this._removalsTail._nextRemoved = record; } return record; } /** @internal */ _addIdentityChange(record, item) { record.item = item; if (this._identityChangesTail === null) { this._identityChangesTail = this._identityChangesHead = record; } else { this._identityChangesTail = this._identityChangesTail._nextIdentityChange = record; } return record; } } class IterableChangeRecord_ { constructor(item, trackById) { this.item = item; this.trackById = trackById; this.currentIndex = null; this.previousIndex = null; /** @internal */ this._nextPrevious = null; /** @internal */ this._prev = null; /** @internal */ this._next = null; /** @internal */ this._prevDup = null; /** @internal */ this._nextDup = null; /** @internal */ this._prevRemoved = null; /** @internal */ this._nextRemoved = null; /** @internal */ this._nextAdded = null; /** @internal */ this._nextMoved = null; /** @internal */ this._nextIdentityChange = null; } } // A linked list of IterableChangeRecords with the same IterableChangeRecord_.item class _DuplicateItemRecordList { constructor() { /** @internal */ this._head = null; /** @internal */ this._tail = null; } /** * Append the record to the list of duplicates. * * Note: by design all records in the list of duplicates hold the same value in record.item. */ add(record) { if (this._head === null) { this._head = this._tail = record; record._nextDup = null; record._prevDup = null; } else { // TODO(vicb): // assert(record.item == _head.item || // record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN); this._tail._nextDup = record; record._prevDup = this._tail; record._nextDup = null; this._tail = record; } } // Returns a IterableChangeRecord_ having IterableChangeRecord_.trackById == trackById and // IterableChangeRecord_.currentIndex >= atOrAfterIndex get(trackById, atOrAfterIndex) { let record; for (record = this._head; record !== null; record = record._nextDup) { if ((atOrAfterIndex === null || atOrAfterIndex <= record.currentIndex) && Object.is(record.trackById, trackById)) { return record; } } return null; } /** * Remove one {@link IterableChangeRecord_} from the list of duplicates. * * Returns whether the list of duplicates is empty. */ remove(record) { // TODO(vicb): // assert(() { // // verify that the record being removed is in the list. // for (IterableChangeRecord_ cursor = _head; cursor != null; cursor = cursor._nextDup) { // if (identical(cursor, record)) return true; // } // return false; //}); const prev = record._prevDup; const next = record._nextDup; if (prev === null) { this._head = next; } else { prev._nextDup = next; } if (next === null) { this._tail = prev; } else { next._prevDup = prev; } return this._head === null; } } class _DuplicateMap { constructor() { this.map = new Map(); } put(record) { const key = record.trackById; let duplicates = this.map.get(key); if (!duplicates) { duplicates = new _DuplicateItemRecordList(); this.map.set(key, duplicates); } duplicates.add(record); } /** * Retrieve the `value` using key. Because the IterableChangeRecord_ value may be one which we * have already iterated over, we use the `atOrAfterIndex` to pretend it is not there. * * Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we * have any more `a`s needs to return the second `a`. */ get(trackById, atOrAfterIndex) { const key = trackById; const recordList = this.map.get(key); return recordList ? recordList.get(trackById, atOrAfterIndex) : null; } /** * Removes a {@link IterableChangeRecord_} from the list of duplicates. * * The list of duplicates also is removed from the map if it gets empty. */ remove(record) { const key = record.trackById; const recordList = this.map.get(key); // Remove the list of duplicates when it gets empty if (recordList.remove(record)) { this.map.delete(key); } return record; } get isEmpty() { return this.map.size === 0; } clear() { this.map.clear(); } } function getPreviousIndex(item, addRemoveOffset, moveOffsets) { const previousIndex = item.previousIndex; if (previousIndex === null) return previousIndex; let moveOffset = 0; if (moveOffsets && previousIndex < moveOffsets.length) { moveOffset = moveOffsets[previousIndex]; } return previousIndex + addRemoveOffset + moveOffset; } class DefaultKeyValueDifferFactory { constructor() {} supports(obj) { return obj instanceof Map || isJsObject(obj); } create() { return new DefaultKeyValueDiffer(); } } class DefaultKeyValueDiffer { constructor() { this._records = new Map(); this._mapHead = null; // _appendAfter is used in the check loop this._appendAfter = null; this._previousMapHead = null; this._changesHead = null; this._changesTail = null; this._additionsHead = null; this._additionsTail = null; this._removalsHead = null; this._removalsTail = null; } get isDirty() { return this._additionsHead !== null || this._changesHead !== null || this._removalsHead !== null; } forEachItem(fn) { let record; for (record = this._mapHead; record !== null; record = record._next) { fn(record); } } forEachPreviousItem(fn) { let record; for (record = this._previousMapHead; record !== null; record = record._nextPrevious) { fn(record); } } forEachChangedItem(fn) { let record; for (record = this._changesHead; record !== null; record = record._nextChanged) { fn(record); } } forEachAddedItem(fn) { let record; for (record = this._additionsHead; record !== null; record = record._nextAdded) { fn(record); } } forEachRemovedItem(fn) { let record; for (record = this._removalsHead; record !== null; record = record._nextRemoved) { fn(record); } } diff(map) { if (!map) { map = new Map(); } else if (!(map instanceof Map || isJsObject(map))) { throw new RuntimeError(900 /* RuntimeErrorCode.INVALID_DIFFER_INPUT */, ngDevMode && `Error trying to diff '${stringify(map)}'. Only maps and objects are allowed`); } return this.check(map) ? this : null; } onDestroy() {} /** * Check the current state of the map vs the previous. * The algorithm is optimised for when the keys do no change. */ check(map) { this._reset(); let insertBefore = this._mapHead; this._appendAfter = null; this._forEach(map, (value, key) => { if (insertBefore && insertBefore.key === key) { this._maybeAddToChanges(insertBefore, value); this._appendAfter = insertBefore; insertBefore = insertBefore._next; } else { const record = this._getOrCreateRecordForKey(key, value); insertBefore = this._insertBeforeOrAppend(insertBefore, record); } }); // Items remaining at the end of the list have been deleted if (insertBefore) { if (insertBefore._prev) { insertBefore._prev._next = null; } this._removalsHead = insertBefore; for (let record = insertBefore; record !== null; record = record._nextRemoved) { if (record === this._mapHead) { this._mapHead = null; } this._records.delete(record.key); record._nextRemoved = record._next; record.previousValue = record.currentValue; record.currentValue = null; record._prev = null; record._next = null; } } // Make sure tails have no next records from previous runs if (this._changesTail) this._changesTail._nextChanged = null; if (this._additionsTail) this._additionsTail._nextAdded = null; return this.isDirty; } /** * Inserts a record before `before` or append at the end of the list when `before` is null. * * Notes: * - This method appends at `this._appendAfter`, * - This method updates `this._appendAfter`, * - The return value is the new value for the insertion pointer. */ _insertBeforeOrAppend(before, record) { if (before) { const prev = before._prev; record._next = before; record._prev = prev; before._prev = record; if (prev) { prev._next = record; } if (before === this._mapHead) { this._mapHead = record; } this._appendAfter = before; return before; } if (this._appendAfter) { this._appendAfter._next = record; record._prev = this._appendAfter; } else { this._mapHead = record; } this._appendAfter = record; return null; } _getOrCreateRecordForKey(key, value) { if (this._records.has(key)) { const record = this._records.get(key); this._maybeAddToChanges(record, value); const prev = record._prev; const next = record._next; if (prev) { prev._next = next; } if (next) { next._prev = prev; } record._next = null; record._prev = null; return record; } const record = new KeyValueChangeRecord_(key); this._records.set(key, record); record.currentValue = value; this._addToAdditions(record); return record; } /** @internal */ _reset() { if (this.isDirty) { let record; // let `_previousMapHead` contain the state of the map before the changes this._previousMapHead = this._mapHead; for (record = this._previousMapHead; record !== null; record = record._next) { record._nextPrevious = record._next; } // Update `record.previousValue` with the value of the item before the changes // We need to update all changed items (that's those which have been added and changed) for (record = this._changesHead; record !== null; record = record._nextChanged) { record.previousValue = record.currentValue; } for (record = this._additionsHead; record != null; record = record._nextAdded) { record.previousValue = record.currentValue; } this._changesHead = this._changesTail = null; this._additionsHead = this._additionsTail = null; this._removalsHead = null; } } // Add the record or a given key to the list of changes only when the value has actually changed _maybeAddToChanges(record, newValue) { if (!Object.is(newValue, record.currentValue)) { record.previousValue = record.currentValue; record.currentValue = newValue; this._addToChanges(record); } } _addToAdditions(record) { if (this._additionsHead === null) { this._additionsHead = this._additionsTail = record; } else { this._additionsTail._nextAdded = record; this._additionsTail = record; } } _addToChanges(record) { if (this._changesHead === null) { this._changesHead = this._changesTail = record; } else { this._changesTail._nextChanged = record; this._changesTail = record; } } /** @internal */ _forEach(obj, fn) { if (obj instanceof Map) { obj.forEach(fn); } else { Object.keys(obj).forEach(k => fn(obj[k], k)); } } } class KeyValueChangeRecord_ { constructor(key) { this.key = key; this.previousValue = null; this.currentValue = null; /** @internal */ this._nextPrevious = null; /** @internal */ this._next = null; /** @internal */ this._prev = null; /** @internal */ this._nextAdded = null; /** @internal */ this._nextRemoved = null; /** @internal */ this._nextChanged = null; } } function defaultIterableDiffersFactory() { return new IterableDiffers([new DefaultIterableDifferFactory()]); } /** * A repository of different iterable diffing strategies used by NgFor, NgClass, and others. * * @publicApi */ class IterableDiffers { constructor(factories) { this.factories = factories; } static create(factories, parent) { if (parent != null) { const copied = parent.factories.slice(); factories = factories.concat(copied); } return new IterableDiffers(factories); } /** * Takes an array of {@link IterableDifferFactory} and returns a provider used to extend the * inherited {@link IterableDiffers} instance with the provided factories and return a new * {@link IterableDiffers} instance. * * @usageNotes * ### Example * * The following example shows how to extend an existing list of factories, * which will only be applied to the injector for this component and its children. * This step is all that's required to make a new {@link IterableDiffer} available. * * ``` * @Component({ * viewProviders: [ * IterableDiffers.extend([new ImmutableListDiffer()]) * ] * }) * ``` */ static extend(factories) { return { provide: IterableDiffers, useFactory: parent => { // if parent is null, it means that we are in the root injector and we have just overridden // the default injection mechanism for IterableDiffers, in such a case just assume // `defaultIterableDiffersFactory`. return IterableDiffers.create(factories, parent || defaultIterableDiffersFactory()); }, // Dependency technically isn't optional, but we can provide a better error message this way. deps: [[IterableDiffers, new SkipSelf(), new Optional()]] }; } find(iterable) { const factory = this.factories.find(f => f.supports(iterable)); if (factory != null) { return factory; } else { throw new RuntimeError(901 /* RuntimeErrorCode.NO_SUPPORTING_DIFFER_FACTORY */, ngDevMode && `Cannot find a differ supporting object '${iterable}' of type '${getTypeNameForDebugging(iterable)}'`); } } } /** @nocollapse */ IterableDiffers.ɵprov = ɵɵdefineInjectable({ token: IterableDiffers, providedIn: 'root', factory: defaultIterableDiffersFactory }); function getTypeNameForDebugging(type) { return type['name'] || typeof type; } function defaultKeyValueDiffersFactory() { return new KeyValueDiffers([new DefaultKeyValueDifferFactory()]); } /** * A repository of different Map diffing strategies used by NgClass, NgStyle, and others. * * @publicApi */ class KeyValueDiffers { constructor(factories) { this.factories = factories; } static create(factories, parent) { if (parent) { const copied = parent.factories.slice(); factories = factories.concat(copied); } return new KeyValueDiffers(factories); } /** * Takes an array of {@link KeyValueDifferFactory} and returns a provider used to extend the * inherited {@link KeyValueDiffers} instance with the provided factories and return a new * {@link KeyValueDiffers} instance. * * @usageNotes * ### Example * * The following example shows how to extend an existing list of factories, * which will only be applied to the injector for this component and its children. * This step is all that's required to make a new {@link KeyValueDiffer} available. * * ``` * @Component({ * viewProviders: [ * KeyValueDiffers.extend([new ImmutableMapDiffer()]) * ] * }) * ``` */ static extend(factories) { return { provide: KeyValueDiffers, useFactory: parent => { // if parent is null, it means that we are in the root injector and we have just overridden // the default injection mechanism for KeyValueDiffers, in such a case just assume // `defaultKeyValueDiffersFactory`. return KeyValueDiffers.create(factories, parent || defaultKeyValueDiffersFactory()); }, // Dependency technically isn't optional, but we can provide a better error message this way. deps: [[KeyValueDiffers, new SkipSelf(), new Optional()]] }; } find(kv) { const factory = this.factories.find(f => f.supports(kv)); if (factory) { return factory; } throw new RuntimeError(901 /* RuntimeErrorCode.NO_SUPPORTING_DIFFER_FACTORY */, ngDevMode && `Cannot find a differ supporting object '${kv}'`); } } /** @nocollapse */ KeyValueDiffers.ɵprov = ɵɵdefineInjectable({ token: KeyValueDiffers, providedIn: 'root', factory: defaultKeyValueDiffersFactory }); /** * Structural diffing for `Object`s and `Map`s. */ const keyValDiff = [new DefaultKeyValueDifferFactory()]; /** * Structural diffing for `Iterable` types such as `Array`s. */ const iterableDiff = [new DefaultIterableDifferFactory()]; const defaultIterableDiffers = new IterableDiffers(iterableDiff); const defaultKeyValueDiffers = new KeyValueDiffers(keyValDiff); /** * @module * @description * Change detection enables data binding in Angular. */ /** * This platform has to be included in any other platform * * @publicApi */ const platformCore = createPlatformFactory(null, 'core', []); /** * Re-exported by `BrowserModule`, which is included automatically in the root * `AppModule` when you create a new app with the CLI `new` command. Eagerly injects * `ApplicationRef` to instantiate it. * * @publicApi */ class ApplicationModule { // Inject ApplicationRef to make it eager... constructor(appRef) {} } ApplicationModule.ɵfac = function ApplicationModule_Factory(t) { return new (t || ApplicationModule)(ɵɵinject(ApplicationRef)); }; ApplicationModule.ɵmod = /*@__PURE__*/ɵɵdefineNgModule({ type: ApplicationModule }); ApplicationModule.ɵinj = /*@__PURE__*/ɵɵdefineInjector({}); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ApplicationModule, [{ type: NgModule }], function () { return [{ type: ApplicationRef }]; }, null); })(); /** Coerces a value (typically a string) to a boolean. */ function coerceToBoolean(value) { return typeof value === 'boolean' ? value : value != null && value !== 'false'; } /** * Compiles a partial directive declaration object into a full directive definition object. * * @codeGenApi */ function ɵɵngDeclareDirective(decl) { const compiler = getCompilerFacade({ usage: 1 /* JitCompilerUsage.PartialDeclaration */, kind: 'directive', type: decl.type }); return compiler.compileDirectiveDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵfac.js`, decl); } /** * Evaluates the class metadata declaration. * * @codeGenApi */ function ɵɵngDeclareClassMetadata(decl) { setClassMetadata(decl.type, decl.decorators, decl.ctorParameters ?? null, decl.propDecorators ?? null); } /** * Compiles a partial component declaration object into a full component definition object. * * @codeGenApi */ function ɵɵngDeclareComponent(decl) { const compiler = getCompilerFacade({ usage: 1 /* JitCompilerUsage.PartialDeclaration */, kind: 'component', type: decl.type }); return compiler.compileComponentDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵcmp.js`, decl); } /** * Compiles a partial pipe declaration object into a full pipe definition object. * * @codeGenApi */ function ɵɵngDeclareFactory(decl) { const compiler = getCompilerFacade({ usage: 1 /* JitCompilerUsage.PartialDeclaration */, kind: getFactoryKind(decl.target), type: decl.type }); return compiler.compileFactoryDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵfac.js`, decl); } function getFactoryKind(target) { switch (target) { case FactoryTarget.Directive: return 'directive'; case FactoryTarget.Component: return 'component'; case FactoryTarget.Injectable: return 'injectable'; case FactoryTarget.Pipe: return 'pipe'; case FactoryTarget.NgModule: return 'NgModule'; } } /** * Compiles a partial injectable declaration object into a full injectable definition object. * * @codeGenApi */ function ɵɵngDeclareInjectable(decl) { const compiler = getCompilerFacade({ usage: 1 /* JitCompilerUsage.PartialDeclaration */, kind: 'injectable', type: decl.type }); return compiler.compileInjectableDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵprov.js`, decl); } /** * Compiles a partial injector declaration object into a full injector definition object. * * @codeGenApi */ function ɵɵngDeclareInjector(decl) { const compiler = getCompilerFacade({ usage: 1 /* JitCompilerUsage.PartialDeclaration */, kind: 'NgModule', type: decl.type }); return compiler.compileInjectorDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵinj.js`, decl); } /** * Compiles a partial NgModule declaration object into a full NgModule definition object. * * @codeGenApi */ function ɵɵngDeclareNgModule(decl) { const compiler = getCompilerFacade({ usage: 1 /* JitCompilerUsage.PartialDeclaration */, kind: 'NgModule', type: decl.type }); return compiler.compileNgModuleDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵmod.js`, decl); } /** * Compiles a partial pipe declaration object into a full pipe definition object. * * @codeGenApi */ function ɵɵngDeclarePipe(decl) { const compiler = getCompilerFacade({ usage: 1 /* JitCompilerUsage.PartialDeclaration */, kind: 'pipe', type: decl.type }); return compiler.compilePipeDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵpipe.js`, decl); } // clang-format off // clang-format on /** * Creates a `ComponentRef` instance based on provided component type and a set of options. * * @usageNotes * * The example below demonstrates how the `createComponent` function can be used * to create an instance of a ComponentRef dynamically and attach it to an ApplicationRef, * so that it gets included into change detection cycles. * * Note: the example uses standalone components, but the function can also be used for * non-standalone components (declared in an NgModule) as well. * * ```typescript * @Component({ * standalone: true, * template: `Hello {{ name }}!` * }) * class HelloComponent { * name = 'Angular'; * } * * @Component({ * standalone: true, * template: `<div id="hello-component-host"></div>` * }) * class RootComponent {} * * // Bootstrap an application. * const applicationRef = await bootstrapApplication(RootComponent); * * // Locate a DOM node that would be used as a host. * const host = document.getElementById('hello-component-host'); * * // Get an `EnvironmentInjector` instance from the `ApplicationRef`. * const environmentInjector = applicationRef.injector; * * // We can now create a `ComponentRef` instance. * const componentRef = createComponent(HelloComponent, {host, environmentInjector}); * * // Last step is to register the newly created ref using the `ApplicationRef` instance * // to include the component view into change detection cycles. * applicationRef.attachView(componentRef.hostView); * ``` * * @param component Component class reference. * @param options Set of options to use: * * `environmentInjector`: An `EnvironmentInjector` instance to be used for the component, see * additional info about it at https://angular.io/guide/standalone-components#environment-injectors. * * `hostElement` (optional): A DOM node that should act as a host node for the component. If not * provided, Angular creates one based on the tag name used in the component selector (and falls * back to using `div` if selector doesn't have tag name info). * * `elementInjector` (optional): An `ElementInjector` instance, see additional info about it at * https://angular.io/guide/hierarchical-dependency-injection#elementinjector. * * `projectableNodes` (optional): A list of DOM nodes that should be projected through * [`<ng-content>`](api/core/ng-content) of the new component instance. * @returns ComponentRef instance that represents a given Component. * * @publicApi */ function createComponent(component, options) { ngDevMode && assertComponentDef(component); const componentDef = getComponentDef(component); const elementInjector = options.elementInjector || getNullInjector(); const factory = new ComponentFactory(componentDef); return factory.create(elementInjector, options.projectableNodes, options.hostElement, options.environmentInjector); } /** * Creates an object that allows to retrieve component metadata. * * @usageNotes * * The example below demonstrates how to use the function and how the fields * of the returned object map to the component metadata. * * ```typescript * @Component({ * standalone: true, * selector: 'foo-component', * template: ` * <ng-content></ng-content> * <ng-content select="content-selector-a"></ng-content> * `, * }) * class FooComponent { * @Input('inputName') inputPropName: string; * @Output('outputName') outputPropName = new EventEmitter<void>(); * } * * const mirror = reflectComponentType(FooComponent); * expect(mirror.type).toBe(FooComponent); * expect(mirror.selector).toBe('foo-component'); * expect(mirror.isStandalone).toBe(true); * expect(mirror.inputs).toEqual([{propName: 'inputName', templateName: 'inputPropName'}]); * expect(mirror.outputs).toEqual([{propName: 'outputName', templateName: 'outputPropName'}]); * expect(mirror.ngContentSelectors).toEqual([ * '*', // first `<ng-content>` in a template, the selector defaults to `*` * 'content-selector-a' // second `<ng-content>` in a template * ]); * ``` * * @param component Component class reference. * @returns An object that allows to retrieve component metadata. * * @publicApi */ function reflectComponentType(component) { const componentDef = getComponentDef(component); if (!componentDef) return null; const factory = new ComponentFactory(componentDef); return { get selector() { return factory.selector; }, get type() { return factory.componentType; }, get inputs() { return factory.inputs; }, get outputs() { return factory.outputs; }, get ngContentSelectors() { return factory.ngContentSelectors; }, get isStandalone() { return componentDef.standalone; } }; } /** * @module * @description * Entry point from which you should import all public core APIs. */ if (typeof ngDevMode !== 'undefined' && ngDevMode) { // This helper is to give a reasonable error message to people upgrading to v9 that have not yet // installed `@angular/localize` in their app. // tslint:disable-next-line: no-toplevel-property-access _global.$localize = _global.$localize || function () { throw new Error('It looks like your application or one of its dependencies is using i18n.\n' + 'Angular 9 introduced a global `$localize()` function that needs to be loaded.\n' + 'Please run `ng add @angular/localize` from the Angular CLI.\n' + '(For non-CLI projects, add `import \'@angular/localize/init\';` to your `polyfills.ts` file.\n' + 'For server-side rendering applications add the import to your `main.server.ts` file.)'); }; } /** * @module * @description * Entry point for all public APIs of this package. */ // This file only reexports content of the `src` folder. Keep it that way. /* This file is not used to build this module. It is only used during editing * by the TypeScript language service and during build for verification. `ngc` * replaces this file with production index.ts when it rewrites private symbol * names. */ /** * Generated bundle index. Do not edit. */ /***/ }), /***/ 2508: /*!********************************************************!*\ !*** ./node_modules/@angular/forms/fesm2020/forms.mjs ***! \********************************************************/ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "AbstractControl": () => (/* binding */ AbstractControl), /* harmony export */ "AbstractControlDirective": () => (/* binding */ AbstractControlDirective), /* harmony export */ "AbstractFormGroupDirective": () => (/* binding */ AbstractFormGroupDirective), /* harmony export */ "COMPOSITION_BUFFER_MODE": () => (/* binding */ COMPOSITION_BUFFER_MODE), /* harmony export */ "CheckboxControlValueAccessor": () => (/* binding */ CheckboxControlValueAccessor), /* harmony export */ "CheckboxRequiredValidator": () => (/* binding */ CheckboxRequiredValidator), /* harmony export */ "ControlContainer": () => (/* binding */ ControlContainer), /* harmony export */ "DefaultValueAccessor": () => (/* binding */ DefaultValueAccessor), /* harmony export */ "EmailValidator": () => (/* binding */ EmailValidator), /* harmony export */ "FormArray": () => (/* binding */ FormArray), /* harmony export */ "FormArrayName": () => (/* binding */ FormArrayName), /* harmony export */ "FormBuilder": () => (/* binding */ FormBuilder), /* harmony export */ "FormControl": () => (/* binding */ FormControl), /* harmony export */ "FormControlDirective": () => (/* binding */ FormControlDirective), /* harmony export */ "FormControlName": () => (/* binding */ FormControlName), /* harmony export */ "FormGroup": () => (/* binding */ FormGroup), /* harmony export */ "FormGroupDirective": () => (/* binding */ FormGroupDirective), /* harmony export */ "FormGroupName": () => (/* binding */ FormGroupName), /* harmony export */ "FormRecord": () => (/* binding */ FormRecord), /* harmony export */ "FormsModule": () => (/* binding */ FormsModule), /* harmony export */ "MaxLengthValidator": () => (/* binding */ MaxLengthValidator), /* harmony export */ "MaxValidator": () => (/* binding */ MaxValidator), /* harmony export */ "MinLengthValidator": () => (/* binding */ MinLengthValidator), /* harmony export */ "MinValidator": () => (/* binding */ MinValidator), /* harmony export */ "NG_ASYNC_VALIDATORS": () => (/* binding */ NG_ASYNC_VALIDATORS), /* harmony export */ "NG_VALIDATORS": () => (/* binding */ NG_VALIDATORS), /* harmony export */ "NG_VALUE_ACCESSOR": () => (/* binding */ NG_VALUE_ACCESSOR), /* harmony export */ "NgControl": () => (/* binding */ NgControl), /* harmony export */ "NgControlStatus": () => (/* binding */ NgControlStatus), /* harmony export */ "NgControlStatusGroup": () => (/* binding */ NgControlStatusGroup), /* harmony export */ "NgForm": () => (/* binding */ NgForm), /* harmony export */ "NgModel": () => (/* binding */ NgModel), /* harmony export */ "NgModelGroup": () => (/* binding */ NgModelGroup), /* harmony export */ "NgSelectOption": () => (/* binding */ NgSelectOption), /* harmony export */ "NonNullableFormBuilder": () => (/* binding */ NonNullableFormBuilder), /* harmony export */ "NumberValueAccessor": () => (/* binding */ NumberValueAccessor), /* harmony export */ "PatternValidator": () => (/* binding */ PatternValidator), /* harmony export */ "RadioControlValueAccessor": () => (/* binding */ RadioControlValueAccessor), /* harmony export */ "RangeValueAccessor": () => (/* binding */ RangeValueAccessor), /* harmony export */ "ReactiveFormsModule": () => (/* binding */ ReactiveFormsModule), /* harmony export */ "RequiredValidator": () => (/* binding */ RequiredValidator), /* harmony export */ "SelectControlValueAccessor": () => (/* binding */ SelectControlValueAccessor), /* harmony export */ "SelectMultipleControlValueAccessor": () => (/* binding */ SelectMultipleControlValueAccessor), /* harmony export */ "UntypedFormArray": () => (/* binding */ UntypedFormArray), /* harmony export */ "UntypedFormBuilder": () => (/* binding */ UntypedFormBuilder), /* harmony export */ "UntypedFormControl": () => (/* binding */ UntypedFormControl), /* harmony export */ "UntypedFormGroup": () => (/* binding */ UntypedFormGroup), /* harmony export */ "VERSION": () => (/* binding */ VERSION), /* harmony export */ "Validators": () => (/* binding */ Validators), /* harmony export */ "isFormArray": () => (/* binding */ isFormArray), /* harmony export */ "isFormControl": () => (/* binding */ isFormControl), /* harmony export */ "isFormGroup": () => (/* binding */ isFormGroup), /* harmony export */ "isFormRecord": () => (/* binding */ isFormRecord), /* harmony export */ "ɵInternalFormsSharedModule": () => (/* binding */ ɵInternalFormsSharedModule), /* harmony export */ "ɵNgNoValidate": () => (/* binding */ ɵNgNoValidate), /* harmony export */ "ɵNgSelectMultipleOption": () => (/* binding */ ɵNgSelectMultipleOption) /* harmony export */ }); /* harmony import */ var _angular_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @angular/core */ 2560); /* harmony import */ var _angular_common__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @angular/common */ 4666); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! rxjs */ 9346); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! rxjs */ 1640); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! rxjs/operators */ 635); /** * @license Angular v15.2.9 * (c) 2010-2022 Google LLC. https://angular.io/ * License: MIT */ /** * Base class for all ControlValueAccessor classes defined in Forms package. * Contains common logic and utility functions. * * Note: this is an *internal-only* class and should not be extended or used directly in * applications code. */ class BaseControlValueAccessor { constructor(_renderer, _elementRef) { this._renderer = _renderer; this._elementRef = _elementRef; /** * The registered callback function called when a change or input event occurs on the input * element. * @nodoc */ this.onChange = _ => {}; /** * The registered callback function called when a blur event occurs on the input element. * @nodoc */ this.onTouched = () => {}; } /** * Helper method that sets a property on a target element using the current Renderer * implementation. * @nodoc */ setProperty(key, value) { this._renderer.setProperty(this._elementRef.nativeElement, key, value); } /** * Registers a function called when the control is touched. * @nodoc */ registerOnTouched(fn) { this.onTouched = fn; } /** * Registers a function called when the control value changes. * @nodoc */ registerOnChange(fn) { this.onChange = fn; } /** * Sets the "disabled" property on the range input element. * @nodoc */ setDisabledState(isDisabled) { this.setProperty('disabled', isDisabled); } } BaseControlValueAccessor.ɵfac = function BaseControlValueAccessor_Factory(t) { return new (t || BaseControlValueAccessor)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef)); }; BaseControlValueAccessor.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: BaseControlValueAccessor }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](BaseControlValueAccessor, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2 }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef }]; }, null); })(); /** * Base class for all built-in ControlValueAccessor classes (except DefaultValueAccessor, which is * used in case no other CVAs can be found). We use this class to distinguish between default CVA, * built-in CVAs and custom CVAs, so that Forms logic can recognize built-in CVAs and treat custom * ones with higher priority (when both built-in and custom CVAs are present). * * Note: this is an *internal-only* class and should not be extended or used directly in * applications code. */ class BuiltInControlValueAccessor extends BaseControlValueAccessor {} BuiltInControlValueAccessor.ɵfac = /* @__PURE__ */function () { let ɵBuiltInControlValueAccessor_BaseFactory; return function BuiltInControlValueAccessor_Factory(t) { return (ɵBuiltInControlValueAccessor_BaseFactory || (ɵBuiltInControlValueAccessor_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](BuiltInControlValueAccessor)))(t || BuiltInControlValueAccessor); }; }(); BuiltInControlValueAccessor.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: BuiltInControlValueAccessor, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](BuiltInControlValueAccessor, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive }], null, null); })(); /** * Used to provide a `ControlValueAccessor` for form controls. * * See `DefaultValueAccessor` for how to implement one. * * @publicApi */ const NG_VALUE_ACCESSOR = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('NgValueAccessor'); const CHECKBOX_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => CheckboxControlValueAccessor), multi: true }; /** * @description * A `ControlValueAccessor` for writing a value and listening to changes on a checkbox input * element. * * @usageNotes * * ### Using a checkbox with a reactive form. * * The following example shows how to use a checkbox with a reactive form. * * ```ts * const rememberLoginControl = new FormControl(); * ``` * * ``` * <input type="checkbox" [formControl]="rememberLoginControl"> * ``` * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class CheckboxControlValueAccessor extends BuiltInControlValueAccessor { /** * Sets the "checked" property on the input element. * @nodoc */ writeValue(value) { this.setProperty('checked', value); } } CheckboxControlValueAccessor.ɵfac = /* @__PURE__ */function () { let ɵCheckboxControlValueAccessor_BaseFactory; return function CheckboxControlValueAccessor_Factory(t) { return (ɵCheckboxControlValueAccessor_BaseFactory || (ɵCheckboxControlValueAccessor_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](CheckboxControlValueAccessor)))(t || CheckboxControlValueAccessor); }; }(); CheckboxControlValueAccessor.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: CheckboxControlValueAccessor, selectors: [["input", "type", "checkbox", "formControlName", ""], ["input", "type", "checkbox", "formControl", ""], ["input", "type", "checkbox", "ngModel", ""]], hostBindings: function CheckboxControlValueAccessor_HostBindings(rf, ctx) { if (rf & 1) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵlistener"]("change", function CheckboxControlValueAccessor_change_HostBindingHandler($event) { return ctx.onChange($event.target.checked); })("blur", function CheckboxControlValueAccessor_blur_HostBindingHandler() { return ctx.onTouched(); }); } }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([CHECKBOX_VALUE_ACCESSOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](CheckboxControlValueAccessor, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]', host: { '(change)': 'onChange($event.target.checked)', '(blur)': 'onTouched()' }, providers: [CHECKBOX_VALUE_ACCESSOR] }] }], null, null); })(); const DEFAULT_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => DefaultValueAccessor), multi: true }; /** * We must check whether the agent is Android because composition events * behave differently between iOS and Android. */ function _isAndroid() { const userAgent = (0,_angular_common__WEBPACK_IMPORTED_MODULE_1__["ɵgetDOM"])() ? (0,_angular_common__WEBPACK_IMPORTED_MODULE_1__["ɵgetDOM"])().getUserAgent() : ''; return /android (\d+)/.test(userAgent.toLowerCase()); } /** * @description * Provide this token to control if form directives buffer IME input until * the "compositionend" event occurs. * @publicApi */ const COMPOSITION_BUFFER_MODE = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('CompositionEventMode'); /** * The default `ControlValueAccessor` for writing a value and listening to changes on input * elements. The accessor is used by the `FormControlDirective`, `FormControlName`, and * `NgModel` directives. * * {@searchKeywords ngDefaultControl} * * @usageNotes * * ### Using the default value accessor * * The following example shows how to use an input element that activates the default value accessor * (in this case, a text field). * * ```ts * const firstNameControl = new FormControl(); * ``` * * ``` * <input type="text" [formControl]="firstNameControl"> * ``` * * This value accessor is used by default for `<input type="text">` and `<textarea>` elements, but * you could also use it for custom components that have similar behavior and do not require special * processing. In order to attach the default value accessor to a custom element, add the * `ngDefaultControl` attribute as shown below. * * ``` * <custom-input-component ngDefaultControl [(ngModel)]="value"></custom-input-component> * ``` * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class DefaultValueAccessor extends BaseControlValueAccessor { constructor(renderer, elementRef, _compositionMode) { super(renderer, elementRef); this._compositionMode = _compositionMode; /** Whether the user is creating a composition string (IME events). */ this._composing = false; if (this._compositionMode == null) { this._compositionMode = !_isAndroid(); } } /** * Sets the "value" property on the input element. * @nodoc */ writeValue(value) { const normalizedValue = value == null ? '' : value; this.setProperty('value', normalizedValue); } /** @internal */ _handleInput(value) { if (!this._compositionMode || this._compositionMode && !this._composing) { this.onChange(value); } } /** @internal */ _compositionStart() { this._composing = true; } /** @internal */ _compositionEnd(value) { this._composing = false; this._compositionMode && this.onChange(value); } } DefaultValueAccessor.ɵfac = function DefaultValueAccessor_Factory(t) { return new (t || DefaultValueAccessor)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](COMPOSITION_BUFFER_MODE, 8)); }; DefaultValueAccessor.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: DefaultValueAccessor, selectors: [["input", "formControlName", "", 3, "type", "checkbox"], ["textarea", "formControlName", ""], ["input", "formControl", "", 3, "type", "checkbox"], ["textarea", "formControl", ""], ["input", "ngModel", "", 3, "type", "checkbox"], ["textarea", "ngModel", ""], ["", "ngDefaultControl", ""]], hostBindings: function DefaultValueAccessor_HostBindings(rf, ctx) { if (rf & 1) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵlistener"]("input", function DefaultValueAccessor_input_HostBindingHandler($event) { return ctx._handleInput($event.target.value); })("blur", function DefaultValueAccessor_blur_HostBindingHandler() { return ctx.onTouched(); })("compositionstart", function DefaultValueAccessor_compositionstart_HostBindingHandler() { return ctx._compositionStart(); })("compositionend", function DefaultValueAccessor_compositionend_HostBindingHandler($event) { return ctx._compositionEnd($event.target.value); }); } }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([DEFAULT_VALUE_ACCESSOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](DefaultValueAccessor, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]', // TODO: vsavkin replace the above selector with the one below it once // https://github.com/angular/angular/issues/3011 is implemented // selector: '[ngModel],[formControl],[formControlName]', host: { '(input)': '$any(this)._handleInput($event.target.value)', '(blur)': 'onTouched()', '(compositionstart)': '$any(this)._compositionStart()', '(compositionend)': '$any(this)._compositionEnd($event.target.value)' }, providers: [DEFAULT_VALUE_ACCESSOR] }] }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2 }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [COMPOSITION_BUFFER_MODE] }] }]; }, null); })(); const NG_DEV_MODE$1 = typeof ngDevMode === 'undefined' || !!ngDevMode; function isEmptyInputValue(value) { /** * Check if the object is a string or array before evaluating the length attribute. * This avoids falsely rejecting objects that contain a custom length attribute. * For example, the object {id: 1, length: 0, width: 0} should not be returned as empty. */ return value == null || (typeof value === 'string' || Array.isArray(value)) && value.length === 0; } function hasValidLength(value) { // non-strict comparison is intentional, to check for both `null` and `undefined` values return value != null && typeof value.length === 'number'; } /** * @description * An `InjectionToken` for registering additional synchronous validators used with * `AbstractControl`s. * * @see `NG_ASYNC_VALIDATORS` * * @usageNotes * * ### Providing a custom validator * * The following example registers a custom validator directive. Adding the validator to the * existing collection of validators requires the `multi: true` option. * * ```typescript * @Directive({ * selector: '[customValidator]', * providers: [{provide: NG_VALIDATORS, useExisting: CustomValidatorDirective, multi: true}] * }) * class CustomValidatorDirective implements Validator { * validate(control: AbstractControl): ValidationErrors | null { * return { 'custom': true }; * } * } * ``` * * @publicApi */ const NG_VALIDATORS = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('NgValidators'); /** * @description * An `InjectionToken` for registering additional asynchronous validators used with * `AbstractControl`s. * * @see `NG_VALIDATORS` * * @usageNotes * * ### Provide a custom async validator directive * * The following example implements the `AsyncValidator` interface to create an * async validator directive with a custom error key. * * ```typescript * @Directive({ * selector: '[customAsyncValidator]', * providers: [{provide: NG_ASYNC_VALIDATORS, useExisting: CustomAsyncValidatorDirective, multi: * true}] * }) * class CustomAsyncValidatorDirective implements AsyncValidator { * validate(control: AbstractControl): Promise<ValidationErrors|null> { * return Promise.resolve({'custom': true}); * } * } * ``` * * @publicApi */ const NG_ASYNC_VALIDATORS = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('NgAsyncValidators'); /** * A regular expression that matches valid e-mail addresses. * * At a high level, this regexp matches e-mail addresses of the format `local-part@tld`, where: * - `local-part` consists of one or more of the allowed characters (alphanumeric and some * punctuation symbols). * - `local-part` cannot begin or end with a period (`.`). * - `local-part` cannot be longer than 64 characters. * - `tld` consists of one or more `labels` separated by periods (`.`). For example `localhost` or * `foo.com`. * - A `label` consists of one or more of the allowed characters (alphanumeric, dashes (`-`) and * periods (`.`)). * - A `label` cannot begin or end with a dash (`-`) or a period (`.`). * - A `label` cannot be longer than 63 characters. * - The whole address cannot be longer than 254 characters. * * ## Implementation background * * This regexp was ported over from AngularJS (see there for git history): * https://github.com/angular/angular.js/blob/c133ef836/src/ng/directive/input.js#L27 * It is based on the * [WHATWG version](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address) with * some enhancements to incorporate more RFC rules (such as rules related to domain names and the * lengths of different parts of the address). The main differences from the WHATWG version are: * - Disallow `local-part` to begin or end with a period (`.`). * - Disallow `local-part` length to exceed 64 characters. * - Disallow total address length to exceed 254 characters. * * See [this commit](https://github.com/angular/angular.js/commit/f3f5cf72e) for more details. */ const EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; /** * @description * Provides a set of built-in validators that can be used by form controls. * * A validator is a function that processes a `FormControl` or collection of * controls and returns an error map or null. A null map means that validation has passed. * * @see [Form Validation](/guide/form-validation) * * @publicApi */ class Validators { /** * @description * Validator that requires the control's value to be greater than or equal to the provided number. * * @usageNotes * * ### Validate against a minimum of 3 * * ```typescript * const control = new FormControl(2, Validators.min(3)); * * console.log(control.errors); // {min: {min: 3, actual: 2}} * ``` * * @returns A validator function that returns an error map with the * `min` property if the validation check fails, otherwise `null`. * * @see `updateValueAndValidity()` * */ static min(min) { return minValidator(min); } /** * @description * Validator that requires the control's value to be less than or equal to the provided number. * * @usageNotes * * ### Validate against a maximum of 15 * * ```typescript * const control = new FormControl(16, Validators.max(15)); * * console.log(control.errors); // {max: {max: 15, actual: 16}} * ``` * * @returns A validator function that returns an error map with the * `max` property if the validation check fails, otherwise `null`. * * @see `updateValueAndValidity()` * */ static max(max) { return maxValidator(max); } /** * @description * Validator that requires the control have a non-empty value. * * @usageNotes * * ### Validate that the field is non-empty * * ```typescript * const control = new FormControl('', Validators.required); * * console.log(control.errors); // {required: true} * ``` * * @returns An error map with the `required` property * if the validation check fails, otherwise `null`. * * @see `updateValueAndValidity()` * */ static required(control) { return requiredValidator(control); } /** * @description * Validator that requires the control's value be true. This validator is commonly * used for required checkboxes. * * @usageNotes * * ### Validate that the field value is true * * ```typescript * const control = new FormControl('some value', Validators.requiredTrue); * * console.log(control.errors); // {required: true} * ``` * * @returns An error map that contains the `required` property * set to `true` if the validation check fails, otherwise `null`. * * @see `updateValueAndValidity()` * */ static requiredTrue(control) { return requiredTrueValidator(control); } /** * @description * Validator that requires the control's value pass an email validation test. * * Tests the value using a [regular * expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) * pattern suitable for common use cases. The pattern is based on the definition of a valid email * address in the [WHATWG HTML * specification](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address) with * some enhancements to incorporate more RFC rules (such as rules related to domain names and the * lengths of different parts of the address). * * The differences from the WHATWG version include: * - Disallow `local-part` (the part before the `@` symbol) to begin or end with a period (`.`). * - Disallow `local-part` to be longer than 64 characters. * - Disallow the whole address to be longer than 254 characters. * * If this pattern does not satisfy your business needs, you can use `Validators.pattern()` to * validate the value against a different pattern. * * @usageNotes * * ### Validate that the field matches a valid email pattern * * ```typescript * const control = new FormControl('bad@', Validators.email); * * console.log(control.errors); // {email: true} * ``` * * @returns An error map with the `email` property * if the validation check fails, otherwise `null`. * * @see `updateValueAndValidity()` * */ static email(control) { return emailValidator(control); } /** * @description * Validator that requires the length of the control's value to be greater than or equal * to the provided minimum length. This validator is also provided by default if you use the * the HTML5 `minlength` attribute. Note that the `minLength` validator is intended to be used * only for types that have a numeric `length` property, such as strings or arrays. The * `minLength` validator logic is also not invoked for values when their `length` property is 0 * (for example in case of an empty string or an empty array), to support optional controls. You * can use the standard `required` validator if empty values should not be considered valid. * * @usageNotes * * ### Validate that the field has a minimum of 3 characters * * ```typescript * const control = new FormControl('ng', Validators.minLength(3)); * * console.log(control.errors); // {minlength: {requiredLength: 3, actualLength: 2}} * ``` * * ```html * <input minlength="5"> * ``` * * @returns A validator function that returns an error map with the * `minlength` property if the validation check fails, otherwise `null`. * * @see `updateValueAndValidity()` * */ static minLength(minLength) { return minLengthValidator(minLength); } /** * @description * Validator that requires the length of the control's value to be less than or equal * to the provided maximum length. This validator is also provided by default if you use the * the HTML5 `maxlength` attribute. Note that the `maxLength` validator is intended to be used * only for types that have a numeric `length` property, such as strings or arrays. * * @usageNotes * * ### Validate that the field has maximum of 5 characters * * ```typescript * const control = new FormControl('Angular', Validators.maxLength(5)); * * console.log(control.errors); // {maxlength: {requiredLength: 5, actualLength: 7}} * ``` * * ```html * <input maxlength="5"> * ``` * * @returns A validator function that returns an error map with the * `maxlength` property if the validation check fails, otherwise `null`. * * @see `updateValueAndValidity()` * */ static maxLength(maxLength) { return maxLengthValidator(maxLength); } /** * @description * Validator that requires the control's value to match a regex pattern. This validator is also * provided by default if you use the HTML5 `pattern` attribute. * * @usageNotes * * ### Validate that the field only contains letters or spaces * * ```typescript * const control = new FormControl('1', Validators.pattern('[a-zA-Z ]*')); * * console.log(control.errors); // {pattern: {requiredPattern: '^[a-zA-Z ]*$', actualValue: '1'}} * ``` * * ```html * <input pattern="[a-zA-Z ]*"> * ``` * * ### Pattern matching with the global or sticky flag * * `RegExp` objects created with the `g` or `y` flags that are passed into `Validators.pattern` * can produce different results on the same input when validations are run consecutively. This is * due to how the behavior of `RegExp.prototype.test` is * specified in [ECMA-262](https://tc39.es/ecma262/#sec-regexpbuiltinexec) * (`RegExp` preserves the index of the last match when the global or sticky flag is used). * Due to this behavior, it is recommended that when using * `Validators.pattern` you **do not** pass in a `RegExp` object with either the global or sticky * flag enabled. * * ```typescript * // Not recommended (since the `g` flag is used) * const controlOne = new FormControl('1', Validators.pattern(/foo/g)); * * // Good * const controlTwo = new FormControl('1', Validators.pattern(/foo/)); * ``` * * @param pattern A regular expression to be used as is to test the values, or a string. * If a string is passed, the `^` character is prepended and the `$` character is * appended to the provided string (if not already present), and the resulting regular * expression is used to test the values. * * @returns A validator function that returns an error map with the * `pattern` property if the validation check fails, otherwise `null`. * * @see `updateValueAndValidity()` * */ static pattern(pattern) { return patternValidator(pattern); } /** * @description * Validator that performs no operation. * * @see `updateValueAndValidity()` * */ static nullValidator(control) { return nullValidator(control); } static compose(validators) { return compose(validators); } /** * @description * Compose multiple async validators into a single function that returns the union * of the individual error objects for the provided control. * * @returns A validator function that returns an error map with the * merged error objects of the async validators if the validation check fails, otherwise `null`. * * @see `updateValueAndValidity()` * */ static composeAsync(validators) { return composeAsync(validators); } } /** * Validator that requires the control's value to be greater than or equal to the provided number. * See `Validators.min` for additional information. */ function minValidator(min) { return control => { if (isEmptyInputValue(control.value) || isEmptyInputValue(min)) { return null; // don't validate empty values to allow optional controls } const value = parseFloat(control.value); // Controls with NaN values after parsing should be treated as not having a // minimum, per the HTML forms spec: https://www.w3.org/TR/html5/forms.html#attr-input-min return !isNaN(value) && value < min ? { 'min': { 'min': min, 'actual': control.value } } : null; }; } /** * Validator that requires the control's value to be less than or equal to the provided number. * See `Validators.max` for additional information. */ function maxValidator(max) { return control => { if (isEmptyInputValue(control.value) || isEmptyInputValue(max)) { return null; // don't validate empty values to allow optional controls } const value = parseFloat(control.value); // Controls with NaN values after parsing should be treated as not having a // maximum, per the HTML forms spec: https://www.w3.org/TR/html5/forms.html#attr-input-max return !isNaN(value) && value > max ? { 'max': { 'max': max, 'actual': control.value } } : null; }; } /** * Validator that requires the control have a non-empty value. * See `Validators.required` for additional information. */ function requiredValidator(control) { return isEmptyInputValue(control.value) ? { 'required': true } : null; } /** * Validator that requires the control's value be true. This validator is commonly * used for required checkboxes. * See `Validators.requiredTrue` for additional information. */ function requiredTrueValidator(control) { return control.value === true ? null : { 'required': true }; } /** * Validator that requires the control's value pass an email validation test. * See `Validators.email` for additional information. */ function emailValidator(control) { if (isEmptyInputValue(control.value)) { return null; // don't validate empty values to allow optional controls } return EMAIL_REGEXP.test(control.value) ? null : { 'email': true }; } /** * Validator that requires the length of the control's value to be greater than or equal * to the provided minimum length. See `Validators.minLength` for additional information. */ function minLengthValidator(minLength) { return control => { if (isEmptyInputValue(control.value) || !hasValidLength(control.value)) { // don't validate empty values to allow optional controls // don't validate values without `length` property return null; } return control.value.length < minLength ? { 'minlength': { 'requiredLength': minLength, 'actualLength': control.value.length } } : null; }; } /** * Validator that requires the length of the control's value to be less than or equal * to the provided maximum length. See `Validators.maxLength` for additional information. */ function maxLengthValidator(maxLength) { return control => { return hasValidLength(control.value) && control.value.length > maxLength ? { 'maxlength': { 'requiredLength': maxLength, 'actualLength': control.value.length } } : null; }; } /** * Validator that requires the control's value to match a regex pattern. * See `Validators.pattern` for additional information. */ function patternValidator(pattern) { if (!pattern) return nullValidator; let regex; let regexStr; if (typeof pattern === 'string') { regexStr = ''; if (pattern.charAt(0) !== '^') regexStr += '^'; regexStr += pattern; if (pattern.charAt(pattern.length - 1) !== '$') regexStr += '$'; regex = new RegExp(regexStr); } else { regexStr = pattern.toString(); regex = pattern; } return control => { if (isEmptyInputValue(control.value)) { return null; // don't validate empty values to allow optional controls } const value = control.value; return regex.test(value) ? null : { 'pattern': { 'requiredPattern': regexStr, 'actualValue': value } }; }; } /** * Function that has `ValidatorFn` shape, but performs no operation. */ function nullValidator(control) { return null; } function isPresent(o) { return o != null; } function toObservable(value) { const obs = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵisPromise"])(value) ? (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.from)(value) : value; if (NG_DEV_MODE$1 && !(0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵisObservable"])(obs)) { let errorMessage = `Expected async validator to return Promise or Observable.`; // A synchronous validator will return object or null. if (typeof value === 'object') { errorMessage += ' Are you using a synchronous validator where an async validator is expected?'; } throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](-1101 /* RuntimeErrorCode.WRONG_VALIDATOR_RETURN_TYPE */, errorMessage); } return obs; } function mergeErrors(arrayOfErrors) { let res = {}; arrayOfErrors.forEach(errors => { res = errors != null ? { ...res, ...errors } : res; }); return Object.keys(res).length === 0 ? null : res; } function executeValidators(control, validators) { return validators.map(validator => validator(control)); } function isValidatorFn(validator) { return !validator.validate; } /** * Given the list of validators that may contain both functions as well as classes, return the list * of validator functions (convert validator classes into validator functions). This is needed to * have consistent structure in validators list before composing them. * * @param validators The set of validators that may contain validators both in plain function form * as well as represented as a validator class. */ function normalizeValidators(validators) { return validators.map(validator => { return isValidatorFn(validator) ? validator : c => validator.validate(c); }); } /** * Merges synchronous validators into a single validator function. * See `Validators.compose` for additional information. */ function compose(validators) { if (!validators) return null; const presentValidators = validators.filter(isPresent); if (presentValidators.length == 0) return null; return function (control) { return mergeErrors(executeValidators(control, presentValidators)); }; } /** * Accepts a list of validators of different possible shapes (`Validator` and `ValidatorFn`), * normalizes the list (converts everything to `ValidatorFn`) and merges them into a single * validator function. */ function composeValidators(validators) { return validators != null ? compose(normalizeValidators(validators)) : null; } /** * Merges asynchronous validators into a single validator function. * See `Validators.composeAsync` for additional information. */ function composeAsync(validators) { if (!validators) return null; const presentValidators = validators.filter(isPresent); if (presentValidators.length == 0) return null; return function (control) { const observables = executeValidators(control, presentValidators).map(toObservable); return (0,rxjs__WEBPACK_IMPORTED_MODULE_3__.forkJoin)(observables).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(mergeErrors)); }; } /** * Accepts a list of async validators of different possible shapes (`AsyncValidator` and * `AsyncValidatorFn`), normalizes the list (converts everything to `AsyncValidatorFn`) and merges * them into a single validator function. */ function composeAsyncValidators(validators) { return validators != null ? composeAsync(normalizeValidators(validators)) : null; } /** * Merges raw control validators with a given directive validator and returns the combined list of * validators as an array. */ function mergeValidators(controlValidators, dirValidator) { if (controlValidators === null) return [dirValidator]; return Array.isArray(controlValidators) ? [...controlValidators, dirValidator] : [controlValidators, dirValidator]; } /** * Retrieves the list of raw synchronous validators attached to a given control. */ function getControlValidators(control) { return control._rawValidators; } /** * Retrieves the list of raw asynchronous validators attached to a given control. */ function getControlAsyncValidators(control) { return control._rawAsyncValidators; } /** * Accepts a singleton validator, an array, or null, and returns an array type with the provided * validators. * * @param validators A validator, validators, or null. * @returns A validators array. */ function makeValidatorsArray(validators) { if (!validators) return []; return Array.isArray(validators) ? validators : [validators]; } /** * Determines whether a validator or validators array has a given validator. * * @param validators The validator or validators to compare against. * @param validator The validator to check. * @returns Whether the validator is present. */ function hasValidator(validators, validator) { return Array.isArray(validators) ? validators.includes(validator) : validators === validator; } /** * Combines two arrays of validators into one. If duplicates are provided, only one will be added. * * @param validators The new validators. * @param currentValidators The base array of current validators. * @returns An array of validators. */ function addValidators(validators, currentValidators) { const current = makeValidatorsArray(currentValidators); const validatorsToAdd = makeValidatorsArray(validators); validatorsToAdd.forEach(v => { // Note: if there are duplicate entries in the new validators array, // only the first one would be added to the current list of validators. // Duplicate ones would be ignored since `hasValidator` would detect // the presence of a validator function and we update the current list in place. if (!hasValidator(current, v)) { current.push(v); } }); return current; } function removeValidators(validators, currentValidators) { return makeValidatorsArray(currentValidators).filter(v => !hasValidator(validators, v)); } /** * @description * Base class for control directives. * * This class is only used internally in the `ReactiveFormsModule` and the `FormsModule`. * * @publicApi */ class AbstractControlDirective { constructor() { /** * Set of synchronous validators as they were provided while calling `setValidators` function. * @internal */ this._rawValidators = []; /** * Set of asynchronous validators as they were provided while calling `setAsyncValidators` * function. * @internal */ this._rawAsyncValidators = []; /* * The set of callbacks to be invoked when directive instance is being destroyed. */ this._onDestroyCallbacks = []; } /** * @description * Reports the value of the control if it is present, otherwise null. */ get value() { return this.control ? this.control.value : null; } /** * @description * Reports whether the control is valid. A control is considered valid if no * validation errors exist with the current value. * If the control is not present, null is returned. */ get valid() { return this.control ? this.control.valid : null; } /** * @description * Reports whether the control is invalid, meaning that an error exists in the input value. * If the control is not present, null is returned. */ get invalid() { return this.control ? this.control.invalid : null; } /** * @description * Reports whether a control is pending, meaning that that async validation is occurring and * errors are not yet available for the input value. If the control is not present, null is * returned. */ get pending() { return this.control ? this.control.pending : null; } /** * @description * Reports whether the control is disabled, meaning that the control is disabled * in the UI and is exempt from validation checks and excluded from aggregate * values of ancestor controls. If the control is not present, null is returned. */ get disabled() { return this.control ? this.control.disabled : null; } /** * @description * Reports whether the control is enabled, meaning that the control is included in ancestor * calculations of validity or value. If the control is not present, null is returned. */ get enabled() { return this.control ? this.control.enabled : null; } /** * @description * Reports the control's validation errors. If the control is not present, null is returned. */ get errors() { return this.control ? this.control.errors : null; } /** * @description * Reports whether the control is pristine, meaning that the user has not yet changed * the value in the UI. If the control is not present, null is returned. */ get pristine() { return this.control ? this.control.pristine : null; } /** * @description * Reports whether the control is dirty, meaning that the user has changed * the value in the UI. If the control is not present, null is returned. */ get dirty() { return this.control ? this.control.dirty : null; } /** * @description * Reports whether the control is touched, meaning that the user has triggered * a `blur` event on it. If the control is not present, null is returned. */ get touched() { return this.control ? this.control.touched : null; } /** * @description * Reports the validation status of the control. Possible values include: * 'VALID', 'INVALID', 'DISABLED', and 'PENDING'. * If the control is not present, null is returned. */ get status() { return this.control ? this.control.status : null; } /** * @description * Reports whether the control is untouched, meaning that the user has not yet triggered * a `blur` event on it. If the control is not present, null is returned. */ get untouched() { return this.control ? this.control.untouched : null; } /** * @description * Returns a multicasting observable that emits a validation status whenever it is * calculated for the control. If the control is not present, null is returned. */ get statusChanges() { return this.control ? this.control.statusChanges : null; } /** * @description * Returns a multicasting observable of value changes for the control that emits every time the * value of the control changes in the UI or programmatically. * If the control is not present, null is returned. */ get valueChanges() { return this.control ? this.control.valueChanges : null; } /** * @description * Returns an array that represents the path from the top-level form to this control. * Each index is the string name of the control on that level. */ get path() { return null; } /** * Sets synchronous validators for this directive. * @internal */ _setValidators(validators) { this._rawValidators = validators || []; this._composedValidatorFn = composeValidators(this._rawValidators); } /** * Sets asynchronous validators for this directive. * @internal */ _setAsyncValidators(validators) { this._rawAsyncValidators = validators || []; this._composedAsyncValidatorFn = composeAsyncValidators(this._rawAsyncValidators); } /** * @description * Synchronous validator function composed of all the synchronous validators registered with this * directive. */ get validator() { return this._composedValidatorFn || null; } /** * @description * Asynchronous validator function composed of all the asynchronous validators registered with * this directive. */ get asyncValidator() { return this._composedAsyncValidatorFn || null; } /** * Internal function to register callbacks that should be invoked * when directive instance is being destroyed. * @internal */ _registerOnDestroy(fn) { this._onDestroyCallbacks.push(fn); } /** * Internal function to invoke all registered "on destroy" callbacks. * Note: calling this function also clears the list of callbacks. * @internal */ _invokeOnDestroyCallbacks() { this._onDestroyCallbacks.forEach(fn => fn()); this._onDestroyCallbacks = []; } /** * @description * Resets the control with the provided value if the control is present. */ reset(value = undefined) { if (this.control) this.control.reset(value); } /** * @description * Reports whether the control with the given path has the error specified. * * @param errorCode The code of the error to check * @param path A list of control names that designates how to move from the current control * to the control that should be queried for errors. * * @usageNotes * For example, for the following `FormGroup`: * * ``` * form = new FormGroup({ * address: new FormGroup({ street: new FormControl() }) * }); * ``` * * The path to the 'street' control from the root form would be 'address' -> 'street'. * * It can be provided to this method in one of two formats: * * 1. An array of string control names, e.g. `['address', 'street']` * 1. A period-delimited list of control names in one string, e.g. `'address.street'` * * If no path is given, this method checks for the error on the current control. * * @returns whether the given error is present in the control at the given path. * * If the control is not present, false is returned. */ hasError(errorCode, path) { return this.control ? this.control.hasError(errorCode, path) : false; } /** * @description * Reports error data for the control with the given path. * * @param errorCode The code of the error to check * @param path A list of control names that designates how to move from the current control * to the control that should be queried for errors. * * @usageNotes * For example, for the following `FormGroup`: * * ``` * form = new FormGroup({ * address: new FormGroup({ street: new FormControl() }) * }); * ``` * * The path to the 'street' control from the root form would be 'address' -> 'street'. * * It can be provided to this method in one of two formats: * * 1. An array of string control names, e.g. `['address', 'street']` * 1. A period-delimited list of control names in one string, e.g. `'address.street'` * * @returns error data for that particular error. If the control or error is not present, * null is returned. */ getError(errorCode, path) { return this.control ? this.control.getError(errorCode, path) : null; } } /** * @description * A base class for directives that contain multiple registered instances of `NgControl`. * Only used by the forms module. * * @publicApi */ class ControlContainer extends AbstractControlDirective { /** * @description * The top-level form directive for the control. */ get formDirective() { return null; } /** * @description * The path to this group. */ get path() { return null; } } /** * @description * A base class that all `FormControl`-based directives extend. It binds a `FormControl` * object to a DOM element. * * @publicApi */ class NgControl extends AbstractControlDirective { constructor() { super(...arguments); /** * @description * The parent form for the control. * * @internal */ this._parent = null; /** * @description * The name for the control */ this.name = null; /** * @description * The value accessor for the control */ this.valueAccessor = null; } } // DO NOT REFACTOR! // Each status is represented by a separate function to make sure that // advanced Closure Compiler optimizations related to property renaming // can work correctly. class AbstractControlStatus { constructor(cd) { this._cd = cd; } get isTouched() { return !!this._cd?.control?.touched; } get isUntouched() { return !!this._cd?.control?.untouched; } get isPristine() { return !!this._cd?.control?.pristine; } get isDirty() { return !!this._cd?.control?.dirty; } get isValid() { return !!this._cd?.control?.valid; } get isInvalid() { return !!this._cd?.control?.invalid; } get isPending() { return !!this._cd?.control?.pending; } get isSubmitted() { // We check for the `submitted` field from `NgForm` and `FormGroupDirective` classes, but // we avoid instanceof checks to prevent non-tree-shakable references to those types. return !!this._cd?.submitted; } } const ngControlStatusHost = { '[class.ng-untouched]': 'isUntouched', '[class.ng-touched]': 'isTouched', '[class.ng-pristine]': 'isPristine', '[class.ng-dirty]': 'isDirty', '[class.ng-valid]': 'isValid', '[class.ng-invalid]': 'isInvalid', '[class.ng-pending]': 'isPending' }; const ngGroupStatusHost = { ...ngControlStatusHost, '[class.ng-submitted]': 'isSubmitted' }; /** * @description * Directive automatically applied to Angular form controls that sets CSS classes * based on control status. * * @usageNotes * * ### CSS classes applied * * The following classes are applied as the properties become true: * * * ng-valid * * ng-invalid * * ng-pending * * ng-pristine * * ng-dirty * * ng-untouched * * ng-touched * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class NgControlStatus extends AbstractControlStatus { constructor(cd) { super(cd); } } NgControlStatus.ɵfac = function NgControlStatus_Factory(t) { return new (t || NgControlStatus)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NgControl, 2)); }; NgControlStatus.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgControlStatus, selectors: [["", "formControlName", ""], ["", "ngModel", ""], ["", "formControl", ""]], hostVars: 14, hostBindings: function NgControlStatus_HostBindings(rf, ctx) { if (rf & 2) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵclassProp"]("ng-untouched", ctx.isUntouched)("ng-touched", ctx.isTouched)("ng-pristine", ctx.isPristine)("ng-dirty", ctx.isDirty)("ng-valid", ctx.isValid)("ng-invalid", ctx.isInvalid)("ng-pending", ctx.isPending); } }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgControlStatus, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[formControlName],[ngModel],[formControl]', host: ngControlStatusHost }] }], function () { return [{ type: NgControl, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }] }]; }, null); })(); /** * @description * Directive automatically applied to Angular form groups that sets CSS classes * based on control status (valid/invalid/dirty/etc). On groups, this includes the additional * class ng-submitted. * * @see `NgControlStatus` * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class NgControlStatusGroup extends AbstractControlStatus { constructor(cd) { super(cd); } } NgControlStatusGroup.ɵfac = function NgControlStatusGroup_Factory(t) { return new (t || NgControlStatusGroup)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](ControlContainer, 10)); }; NgControlStatusGroup.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgControlStatusGroup, selectors: [["", "formGroupName", ""], ["", "formArrayName", ""], ["", "ngModelGroup", ""], ["", "formGroup", ""], ["form", 3, "ngNoForm", ""], ["", "ngForm", ""]], hostVars: 16, hostBindings: function NgControlStatusGroup_HostBindings(rf, ctx) { if (rf & 2) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵclassProp"]("ng-untouched", ctx.isUntouched)("ng-touched", ctx.isTouched)("ng-pristine", ctx.isPristine)("ng-dirty", ctx.isDirty)("ng-valid", ctx.isValid)("ng-invalid", ctx.isInvalid)("ng-pending", ctx.isPending)("ng-submitted", ctx.isSubmitted); } }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgControlStatusGroup, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]', host: ngGroupStatusHost }] }], function () { return [{ type: ControlContainer, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }] }]; }, null); })(); const formControlNameExample = ` <div [formGroup]="myGroup"> <input formControlName="firstName"> </div> In your class: this.myGroup = new FormGroup({ firstName: new FormControl() });`; const formGroupNameExample = ` <div [formGroup]="myGroup"> <div formGroupName="person"> <input formControlName="firstName"> </div> </div> In your class: this.myGroup = new FormGroup({ person: new FormGroup({ firstName: new FormControl() }) });`; const formArrayNameExample = ` <div [formGroup]="myGroup"> <div formArrayName="cities"> <div *ngFor="let city of cityArray.controls; index as i"> <input [formControlName]="i"> </div> </div> </div> In your class: this.cityArray = new FormArray([new FormControl('SF')]); this.myGroup = new FormGroup({ cities: this.cityArray });`; const ngModelGroupExample = ` <form> <div ngModelGroup="person"> <input [(ngModel)]="person.name" name="firstName"> </div> </form>`; const ngModelWithFormGroupExample = ` <div [formGroup]="myGroup"> <input formControlName="firstName"> <input [(ngModel)]="showMoreControls" [ngModelOptions]="{standalone: true}"> </div> `; function controlParentException() { return new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1050 /* RuntimeErrorCode.FORM_CONTROL_NAME_MISSING_PARENT */, `formControlName must be used with a parent formGroup directive. You'll want to add a formGroup directive and pass it an existing FormGroup instance (you can create one in your class). Example: ${formControlNameExample}`); } function ngModelGroupException() { return new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1051 /* RuntimeErrorCode.FORM_CONTROL_NAME_INSIDE_MODEL_GROUP */, `formControlName cannot be used with an ngModelGroup parent. It is only compatible with parents that also have a "form" prefix: formGroupName, formArrayName, or formGroup. Option 1: Update the parent to be formGroupName (reactive form strategy) ${formGroupNameExample} Option 2: Use ngModel instead of formControlName (template-driven strategy) ${ngModelGroupExample}`); } function missingFormException() { return new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1052 /* RuntimeErrorCode.FORM_GROUP_MISSING_INSTANCE */, `formGroup expects a FormGroup instance. Please pass one in. Example: ${formControlNameExample}`); } function groupParentException() { return new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1053 /* RuntimeErrorCode.FORM_GROUP_NAME_MISSING_PARENT */, `formGroupName must be used with a parent formGroup directive. You'll want to add a formGroup directive and pass it an existing FormGroup instance (you can create one in your class). Example: ${formGroupNameExample}`); } function arrayParentException() { return new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1054 /* RuntimeErrorCode.FORM_ARRAY_NAME_MISSING_PARENT */, `formArrayName must be used with a parent formGroup directive. You'll want to add a formGroup directive and pass it an existing FormGroup instance (you can create one in your class). Example: ${formArrayNameExample}`); } const disabledAttrWarning = ` It looks like you're using the disabled attribute with a reactive form directive. If you set disabled to true when you set up this control in your component class, the disabled attribute will actually be set in the DOM for you. We recommend using this approach to avoid 'changed after checked' errors. Example: // Specify the \`disabled\` property at control creation time: form = new FormGroup({ first: new FormControl({value: 'Nancy', disabled: true}, Validators.required), last: new FormControl('Drew', Validators.required) }); // Controls can also be enabled/disabled after creation: form.get('first')?.enable(); form.get('last')?.disable(); `; const asyncValidatorsDroppedWithOptsWarning = ` It looks like you're constructing using a FormControl with both an options argument and an async validators argument. Mixing these arguments will cause your async validators to be dropped. You should either put all your validators in the options object, or in separate validators arguments. For example: // Using validators arguments fc = new FormControl(42, Validators.required, myAsyncValidator); // Using AbstractControlOptions fc = new FormControl(42, {validators: Validators.required, asyncValidators: myAV}); // Do NOT mix them: async validators will be dropped! fc = new FormControl(42, {validators: Validators.required}, /* Oops! */ myAsyncValidator); `; function ngModelWarning(directiveName) { return ` It looks like you're using ngModel on the same form field as ${directiveName}. Support for using the ngModel input property and ngModelChange event with reactive form directives has been deprecated in Angular v6 and will be removed in a future version of Angular. For more information on this, see our API docs here: https://angular.io/api/forms/${directiveName === 'formControl' ? 'FormControlDirective' : 'FormControlName'}#use-with-ngmodel `; } function describeKey(isFormGroup, key) { return isFormGroup ? `with name: '${key}'` : `at index: ${key}`; } function noControlsError(isFormGroup) { return ` There are no form controls registered with this ${isFormGroup ? 'group' : 'array'} yet. If you're using ngModel, you may want to check next tick (e.g. use setTimeout). `; } function missingControlError(isFormGroup, key) { return `Cannot find form control ${describeKey(isFormGroup, key)}`; } function missingControlValueError(isFormGroup, key) { return `Must supply a value for form control ${describeKey(isFormGroup, key)}`; } const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode; /** * Reports that a control is valid, meaning that no errors exist in the input value. * * @see `status` */ const VALID = 'VALID'; /** * Reports that a control is invalid, meaning that an error exists in the input value. * * @see `status` */ const INVALID = 'INVALID'; /** * Reports that a control is pending, meaning that that async validation is occurring and * errors are not yet available for the input value. * * @see `markAsPending` * @see `status` */ const PENDING = 'PENDING'; /** * Reports that a control is disabled, meaning that the control is exempt from ancestor * calculations of validity or value. * * @see `markAsDisabled` * @see `status` */ const DISABLED = 'DISABLED'; /** * Gets validators from either an options object or given validators. */ function pickValidators(validatorOrOpts) { return (isOptionsObj(validatorOrOpts) ? validatorOrOpts.validators : validatorOrOpts) || null; } /** * Creates validator function by combining provided validators. */ function coerceToValidator(validator) { return Array.isArray(validator) ? composeValidators(validator) : validator || null; } /** * Gets async validators from either an options object or given validators. */ function pickAsyncValidators(asyncValidator, validatorOrOpts) { if (typeof ngDevMode === 'undefined' || ngDevMode) { if (isOptionsObj(validatorOrOpts) && asyncValidator) { console.warn(asyncValidatorsDroppedWithOptsWarning); } } return (isOptionsObj(validatorOrOpts) ? validatorOrOpts.asyncValidators : asyncValidator) || null; } /** * Creates async validator function by combining provided async validators. */ function coerceToAsyncValidator(asyncValidator) { return Array.isArray(asyncValidator) ? composeAsyncValidators(asyncValidator) : asyncValidator || null; } function isOptionsObj(validatorOrOpts) { return validatorOrOpts != null && !Array.isArray(validatorOrOpts) && typeof validatorOrOpts === 'object'; } function assertControlPresent(parent, isGroup, key) { const controls = parent.controls; const collection = isGroup ? Object.keys(controls) : controls; if (!collection.length) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1000 /* RuntimeErrorCode.NO_CONTROLS */, NG_DEV_MODE ? noControlsError(isGroup) : ''); } if (!controls[key]) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1001 /* RuntimeErrorCode.MISSING_CONTROL */, NG_DEV_MODE ? missingControlError(isGroup, key) : ''); } } function assertAllValuesPresent(control, isGroup, value) { control._forEachChild((_, key) => { if (value[key] === undefined) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1002 /* RuntimeErrorCode.MISSING_CONTROL_VALUE */, NG_DEV_MODE ? missingControlValueError(isGroup, key) : ''); } }); } // clang-format on /** * This is the base class for `FormControl`, `FormGroup`, and `FormArray`. * * It provides some of the shared behavior that all controls and groups of controls have, like * running validators, calculating status, and resetting state. It also defines the properties * that are shared between all sub-classes, like `value`, `valid`, and `dirty`. It shouldn't be * instantiated directly. * * The first type parameter TValue represents the value type of the control (`control.value`). * The optional type parameter TRawValue represents the raw value type (`control.getRawValue()`). * * @see [Forms Guide](/guide/forms) * @see [Reactive Forms Guide](/guide/reactive-forms) * @see [Dynamic Forms Guide](/guide/dynamic-form) * * @publicApi */ class AbstractControl { /** * Initialize the AbstractControl instance. * * @param validators The function or array of functions that is used to determine the validity of * this control synchronously. * @param asyncValidators The function or array of functions that is used to determine validity of * this control asynchronously. */ constructor(validators, asyncValidators) { /** @internal */ this._pendingDirty = false; /** * Indicates that a control has its own pending asynchronous validation in progress. * * @internal */ this._hasOwnPendingAsyncValidator = false; /** @internal */ this._pendingTouched = false; /** @internal */ this._onCollectionChange = () => {}; this._parent = null; /** * A control is `pristine` if the user has not yet changed * the value in the UI. * * @returns True if the user has not yet changed the value in the UI; compare `dirty`. * Programmatic changes to a control's value do not mark it dirty. */ this.pristine = true; /** * True if the control is marked as `touched`. * * A control is marked `touched` once the user has triggered * a `blur` event on it. */ this.touched = false; /** @internal */ this._onDisabledChange = []; this._assignValidators(validators); this._assignAsyncValidators(asyncValidators); } /** * Returns the function that is used to determine the validity of this control synchronously. * If multiple validators have been added, this will be a single composed function. * See `Validators.compose()` for additional information. */ get validator() { return this._composedValidatorFn; } set validator(validatorFn) { this._rawValidators = this._composedValidatorFn = validatorFn; } /** * Returns the function that is used to determine the validity of this control asynchronously. * If multiple validators have been added, this will be a single composed function. * See `Validators.compose()` for additional information. */ get asyncValidator() { return this._composedAsyncValidatorFn; } set asyncValidator(asyncValidatorFn) { this._rawAsyncValidators = this._composedAsyncValidatorFn = asyncValidatorFn; } /** * The parent control. */ get parent() { return this._parent; } /** * A control is `valid` when its `status` is `VALID`. * * @see {@link AbstractControl.status} * * @returns True if the control has passed all of its validation tests, * false otherwise. */ get valid() { return this.status === VALID; } /** * A control is `invalid` when its `status` is `INVALID`. * * @see {@link AbstractControl.status} * * @returns True if this control has failed one or more of its validation checks, * false otherwise. */ get invalid() { return this.status === INVALID; } /** * A control is `pending` when its `status` is `PENDING`. * * @see {@link AbstractControl.status} * * @returns True if this control is in the process of conducting a validation check, * false otherwise. */ get pending() { return this.status == PENDING; } /** * A control is `disabled` when its `status` is `DISABLED`. * * Disabled controls are exempt from validation checks and * are not included in the aggregate value of their ancestor * controls. * * @see {@link AbstractControl.status} * * @returns True if the control is disabled, false otherwise. */ get disabled() { return this.status === DISABLED; } /** * A control is `enabled` as long as its `status` is not `DISABLED`. * * @returns True if the control has any status other than 'DISABLED', * false if the status is 'DISABLED'. * * @see {@link AbstractControl.status} * */ get enabled() { return this.status !== DISABLED; } /** * A control is `dirty` if the user has changed the value * in the UI. * * @returns True if the user has changed the value of this control in the UI; compare `pristine`. * Programmatic changes to a control's value do not mark it dirty. */ get dirty() { return !this.pristine; } /** * True if the control has not been marked as touched * * A control is `untouched` if the user has not yet triggered * a `blur` event on it. */ get untouched() { return !this.touched; } /** * Reports the update strategy of the `AbstractControl` (meaning * the event on which the control updates itself). * Possible values: `'change'` | `'blur'` | `'submit'` * Default value: `'change'` */ get updateOn() { return this._updateOn ? this._updateOn : this.parent ? this.parent.updateOn : 'change'; } /** * Sets the synchronous validators that are active on this control. Calling * this overwrites any existing synchronous validators. * * When you add or remove a validator at run time, you must call * `updateValueAndValidity()` for the new validation to take effect. * * If you want to add a new validator without affecting existing ones, consider * using `addValidators()` method instead. */ setValidators(validators) { this._assignValidators(validators); } /** * Sets the asynchronous validators that are active on this control. Calling this * overwrites any existing asynchronous validators. * * When you add or remove a validator at run time, you must call * `updateValueAndValidity()` for the new validation to take effect. * * If you want to add a new validator without affecting existing ones, consider * using `addAsyncValidators()` method instead. */ setAsyncValidators(validators) { this._assignAsyncValidators(validators); } /** * Add a synchronous validator or validators to this control, without affecting other validators. * * When you add or remove a validator at run time, you must call * `updateValueAndValidity()` for the new validation to take effect. * * Adding a validator that already exists will have no effect. If duplicate validator functions * are present in the `validators` array, only the first instance would be added to a form * control. * * @param validators The new validator function or functions to add to this control. */ addValidators(validators) { this.setValidators(addValidators(validators, this._rawValidators)); } /** * Add an asynchronous validator or validators to this control, without affecting other * validators. * * When you add or remove a validator at run time, you must call * `updateValueAndValidity()` for the new validation to take effect. * * Adding a validator that already exists will have no effect. * * @param validators The new asynchronous validator function or functions to add to this control. */ addAsyncValidators(validators) { this.setAsyncValidators(addValidators(validators, this._rawAsyncValidators)); } /** * Remove a synchronous validator from this control, without affecting other validators. * Validators are compared by function reference; you must pass a reference to the exact same * validator function as the one that was originally set. If a provided validator is not found, * it is ignored. * * @usageNotes * * ### Reference to a ValidatorFn * * ``` * // Reference to the RequiredValidator * const ctrl = new FormControl<string | null>('', Validators.required); * ctrl.removeValidators(Validators.required); * * // Reference to anonymous function inside MinValidator * const minValidator = Validators.min(3); * const ctrl = new FormControl<string | null>('', minValidator); * expect(ctrl.hasValidator(minValidator)).toEqual(true) * expect(ctrl.hasValidator(Validators.min(3))).toEqual(false) * * ctrl.removeValidators(minValidator); * ``` * * When you add or remove a validator at run time, you must call * `updateValueAndValidity()` for the new validation to take effect. * * @param validators The validator or validators to remove. */ removeValidators(validators) { this.setValidators(removeValidators(validators, this._rawValidators)); } /** * Remove an asynchronous validator from this control, without affecting other validators. * Validators are compared by function reference; you must pass a reference to the exact same * validator function as the one that was originally set. If a provided validator is not found, it * is ignored. * * When you add or remove a validator at run time, you must call * `updateValueAndValidity()` for the new validation to take effect. * * @param validators The asynchronous validator or validators to remove. */ removeAsyncValidators(validators) { this.setAsyncValidators(removeValidators(validators, this._rawAsyncValidators)); } /** * Check whether a synchronous validator function is present on this control. The provided * validator must be a reference to the exact same function that was provided. * * @usageNotes * * ### Reference to a ValidatorFn * * ``` * // Reference to the RequiredValidator * const ctrl = new FormControl<number | null>(0, Validators.required); * expect(ctrl.hasValidator(Validators.required)).toEqual(true) * * // Reference to anonymous function inside MinValidator * const minValidator = Validators.min(3); * const ctrl = new FormControl<number | null>(0, minValidator); * expect(ctrl.hasValidator(minValidator)).toEqual(true) * expect(ctrl.hasValidator(Validators.min(3))).toEqual(false) * ``` * * @param validator The validator to check for presence. Compared by function reference. * @returns Whether the provided validator was found on this control. */ hasValidator(validator) { return hasValidator(this._rawValidators, validator); } /** * Check whether an asynchronous validator function is present on this control. The provided * validator must be a reference to the exact same function that was provided. * * @param validator The asynchronous validator to check for presence. Compared by function * reference. * @returns Whether the provided asynchronous validator was found on this control. */ hasAsyncValidator(validator) { return hasValidator(this._rawAsyncValidators, validator); } /** * Empties out the synchronous validator list. * * When you add or remove a validator at run time, you must call * `updateValueAndValidity()` for the new validation to take effect. * */ clearValidators() { this.validator = null; } /** * Empties out the async validator list. * * When you add or remove a validator at run time, you must call * `updateValueAndValidity()` for the new validation to take effect. * */ clearAsyncValidators() { this.asyncValidator = null; } /** * Marks the control as `touched`. A control is touched by focus and * blur events that do not change the value. * * @see `markAsUntouched()` * @see `markAsDirty()` * @see `markAsPristine()` * * @param opts Configuration options that determine how the control propagates changes * and emits events after marking is applied. * * `onlySelf`: When true, mark only this control. When false or not supplied, * marks all direct ancestors. Default is false. */ markAsTouched(opts = {}) { this.touched = true; if (this._parent && !opts.onlySelf) { this._parent.markAsTouched(opts); } } /** * Marks the control and all its descendant controls as `touched`. * @see `markAsTouched()` */ markAllAsTouched() { this.markAsTouched({ onlySelf: true }); this._forEachChild(control => control.markAllAsTouched()); } /** * Marks the control as `untouched`. * * If the control has any children, also marks all children as `untouched` * and recalculates the `touched` status of all parent controls. * * @see `markAsTouched()` * @see `markAsDirty()` * @see `markAsPristine()` * * @param opts Configuration options that determine how the control propagates changes * and emits events after the marking is applied. * * `onlySelf`: When true, mark only this control. When false or not supplied, * marks all direct ancestors. Default is false. */ markAsUntouched(opts = {}) { this.touched = false; this._pendingTouched = false; this._forEachChild(control => { control.markAsUntouched({ onlySelf: true }); }); if (this._parent && !opts.onlySelf) { this._parent._updateTouched(opts); } } /** * Marks the control as `dirty`. A control becomes dirty when * the control's value is changed through the UI; compare `markAsTouched`. * * @see `markAsTouched()` * @see `markAsUntouched()` * @see `markAsPristine()` * * @param opts Configuration options that determine how the control propagates changes * and emits events after marking is applied. * * `onlySelf`: When true, mark only this control. When false or not supplied, * marks all direct ancestors. Default is false. */ markAsDirty(opts = {}) { this.pristine = false; if (this._parent && !opts.onlySelf) { this._parent.markAsDirty(opts); } } /** * Marks the control as `pristine`. * * If the control has any children, marks all children as `pristine`, * and recalculates the `pristine` status of all parent * controls. * * @see `markAsTouched()` * @see `markAsUntouched()` * @see `markAsDirty()` * * @param opts Configuration options that determine how the control emits events after * marking is applied. * * `onlySelf`: When true, mark only this control. When false or not supplied, * marks all direct ancestors. Default is false. */ markAsPristine(opts = {}) { this.pristine = true; this._pendingDirty = false; this._forEachChild(control => { control.markAsPristine({ onlySelf: true }); }); if (this._parent && !opts.onlySelf) { this._parent._updatePristine(opts); } } /** * Marks the control as `pending`. * * A control is pending while the control performs async validation. * * @see {@link AbstractControl.status} * * @param opts Configuration options that determine how the control propagates changes and * emits events after marking is applied. * * `onlySelf`: When true, mark only this control. When false or not supplied, * marks all direct ancestors. Default is false. * * `emitEvent`: When true or not supplied (the default), the `statusChanges` * observable emits an event with the latest status the control is marked pending. * When false, no events are emitted. * */ markAsPending(opts = {}) { this.status = PENDING; if (opts.emitEvent !== false) { this.statusChanges.emit(this.status); } if (this._parent && !opts.onlySelf) { this._parent.markAsPending(opts); } } /** * Disables the control. This means the control is exempt from validation checks and * excluded from the aggregate value of any parent. Its status is `DISABLED`. * * If the control has children, all children are also disabled. * * @see {@link AbstractControl.status} * * @param opts Configuration options that determine how the control propagates * changes and emits events after the control is disabled. * * `onlySelf`: When true, mark only this control. When false or not supplied, * marks all direct ancestors. Default is false. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` * observables emit events with the latest status and value when the control is disabled. * When false, no events are emitted. */ disable(opts = {}) { // If parent has been marked artificially dirty we don't want to re-calculate the // parent's dirtiness based on the children. const skipPristineCheck = this._parentMarkedDirty(opts.onlySelf); this.status = DISABLED; this.errors = null; this._forEachChild(control => { control.disable({ ...opts, onlySelf: true }); }); this._updateValue(); if (opts.emitEvent !== false) { this.valueChanges.emit(this.value); this.statusChanges.emit(this.status); } this._updateAncestors({ ...opts, skipPristineCheck }); this._onDisabledChange.forEach(changeFn => changeFn(true)); } /** * Enables the control. This means the control is included in validation checks and * the aggregate value of its parent. Its status recalculates based on its value and * its validators. * * By default, if the control has children, all children are enabled. * * @see {@link AbstractControl.status} * * @param opts Configure options that control how the control propagates changes and * emits events when marked as untouched * * `onlySelf`: When true, mark only this control. When false or not supplied, * marks all direct ancestors. Default is false. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` * observables emit events with the latest status and value when the control is enabled. * When false, no events are emitted. */ enable(opts = {}) { // If parent has been marked artificially dirty we don't want to re-calculate the // parent's dirtiness based on the children. const skipPristineCheck = this._parentMarkedDirty(opts.onlySelf); this.status = VALID; this._forEachChild(control => { control.enable({ ...opts, onlySelf: true }); }); this.updateValueAndValidity({ onlySelf: true, emitEvent: opts.emitEvent }); this._updateAncestors({ ...opts, skipPristineCheck }); this._onDisabledChange.forEach(changeFn => changeFn(false)); } _updateAncestors(opts) { if (this._parent && !opts.onlySelf) { this._parent.updateValueAndValidity(opts); if (!opts.skipPristineCheck) { this._parent._updatePristine(); } this._parent._updateTouched(); } } /** * Sets the parent of the control * * @param parent The new parent. */ setParent(parent) { this._parent = parent; } /** * The raw value of this control. For most control implementations, the raw value will include * disabled children. */ getRawValue() { return this.value; } /** * Recalculates the value and validation status of the control. * * By default, it also updates the value and validity of its ancestors. * * @param opts Configuration options determine how the control propagates changes and emits events * after updates and validity checks are applied. * * `onlySelf`: When true, only update this control. When false or not supplied, * update all direct ancestors. Default is false. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` * observables emit events with the latest status and value when the control is updated. * When false, no events are emitted. */ updateValueAndValidity(opts = {}) { this._setInitialStatus(); this._updateValue(); if (this.enabled) { this._cancelExistingSubscription(); this.errors = this._runValidator(); this.status = this._calculateStatus(); if (this.status === VALID || this.status === PENDING) { this._runAsyncValidator(opts.emitEvent); } } if (opts.emitEvent !== false) { this.valueChanges.emit(this.value); this.statusChanges.emit(this.status); } if (this._parent && !opts.onlySelf) { this._parent.updateValueAndValidity(opts); } } /** @internal */ _updateTreeValidity(opts = { emitEvent: true }) { this._forEachChild(ctrl => ctrl._updateTreeValidity(opts)); this.updateValueAndValidity({ onlySelf: true, emitEvent: opts.emitEvent }); } _setInitialStatus() { this.status = this._allControlsDisabled() ? DISABLED : VALID; } _runValidator() { return this.validator ? this.validator(this) : null; } _runAsyncValidator(emitEvent) { if (this.asyncValidator) { this.status = PENDING; this._hasOwnPendingAsyncValidator = true; const obs = toObservable(this.asyncValidator(this)); this._asyncValidationSubscription = obs.subscribe(errors => { this._hasOwnPendingAsyncValidator = false; // This will trigger the recalculation of the validation status, which depends on // the state of the asynchronous validation (whether it is in progress or not). So, it is // necessary that we have updated the `_hasOwnPendingAsyncValidator` boolean flag first. this.setErrors(errors, { emitEvent }); }); } } _cancelExistingSubscription() { if (this._asyncValidationSubscription) { this._asyncValidationSubscription.unsubscribe(); this._hasOwnPendingAsyncValidator = false; } } /** * Sets errors on a form control when running validations manually, rather than automatically. * * Calling `setErrors` also updates the validity of the parent control. * * @param opts Configuration options that determine how the control propagates * changes and emits events after the control errors are set. * * `emitEvent`: When true or not supplied (the default), the `statusChanges` * observable emits an event after the errors are set. * * @usageNotes * * ### Manually set the errors for a control * * ``` * const login = new FormControl('someLogin'); * login.setErrors({ * notUnique: true * }); * * expect(login.valid).toEqual(false); * expect(login.errors).toEqual({ notUnique: true }); * * login.setValue('someOtherLogin'); * * expect(login.valid).toEqual(true); * ``` */ setErrors(errors, opts = {}) { this.errors = errors; this._updateControlsErrors(opts.emitEvent !== false); } /** * Retrieves a child control given the control's name or path. * * @param path A dot-delimited string or array of string/number values that define the path to the * control. If a string is provided, passing it as a string literal will result in improved type * information. Likewise, if an array is provided, passing it `as const` will cause improved type * information to be available. * * @usageNotes * ### Retrieve a nested control * * For example, to get a `name` control nested within a `person` sub-group: * * * `this.form.get('person.name');` * * -OR- * * * `this.form.get(['person', 'name'] as const);` // `as const` gives improved typings * * ### Retrieve a control in a FormArray * * When accessing an element inside a FormArray, you can use an element index. * For example, to get a `price` control from the first element in an `items` array you can use: * * * `this.form.get('items.0.price');` * * -OR- * * * `this.form.get(['items', 0, 'price']);` */ get(path) { let currPath = path; if (currPath == null) return null; if (!Array.isArray(currPath)) currPath = currPath.split('.'); if (currPath.length === 0) return null; return currPath.reduce((control, name) => control && control._find(name), this); } /** * @description * Reports error data for the control with the given path. * * @param errorCode The code of the error to check * @param path A list of control names that designates how to move from the current control * to the control that should be queried for errors. * * @usageNotes * For example, for the following `FormGroup`: * * ``` * form = new FormGroup({ * address: new FormGroup({ street: new FormControl() }) * }); * ``` * * The path to the 'street' control from the root form would be 'address' -> 'street'. * * It can be provided to this method in one of two formats: * * 1. An array of string control names, e.g. `['address', 'street']` * 1. A period-delimited list of control names in one string, e.g. `'address.street'` * * @returns error data for that particular error. If the control or error is not present, * null is returned. */ getError(errorCode, path) { const control = path ? this.get(path) : this; return control && control.errors ? control.errors[errorCode] : null; } /** * @description * Reports whether the control with the given path has the error specified. * * @param errorCode The code of the error to check * @param path A list of control names that designates how to move from the current control * to the control that should be queried for errors. * * @usageNotes * For example, for the following `FormGroup`: * * ``` * form = new FormGroup({ * address: new FormGroup({ street: new FormControl() }) * }); * ``` * * The path to the 'street' control from the root form would be 'address' -> 'street'. * * It can be provided to this method in one of two formats: * * 1. An array of string control names, e.g. `['address', 'street']` * 1. A period-delimited list of control names in one string, e.g. `'address.street'` * * If no path is given, this method checks for the error on the current control. * * @returns whether the given error is present in the control at the given path. * * If the control is not present, false is returned. */ hasError(errorCode, path) { return !!this.getError(errorCode, path); } /** * Retrieves the top-level ancestor of this control. */ get root() { let x = this; while (x._parent) { x = x._parent; } return x; } /** @internal */ _updateControlsErrors(emitEvent) { this.status = this._calculateStatus(); if (emitEvent) { this.statusChanges.emit(this.status); } if (this._parent) { this._parent._updateControlsErrors(emitEvent); } } /** @internal */ _initObservables() { this.valueChanges = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.EventEmitter(); this.statusChanges = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.EventEmitter(); } _calculateStatus() { if (this._allControlsDisabled()) return DISABLED; if (this.errors) return INVALID; if (this._hasOwnPendingAsyncValidator || this._anyControlsHaveStatus(PENDING)) return PENDING; if (this._anyControlsHaveStatus(INVALID)) return INVALID; return VALID; } /** @internal */ _anyControlsHaveStatus(status) { return this._anyControls(control => control.status === status); } /** @internal */ _anyControlsDirty() { return this._anyControls(control => control.dirty); } /** @internal */ _anyControlsTouched() { return this._anyControls(control => control.touched); } /** @internal */ _updatePristine(opts = {}) { this.pristine = !this._anyControlsDirty(); if (this._parent && !opts.onlySelf) { this._parent._updatePristine(opts); } } /** @internal */ _updateTouched(opts = {}) { this.touched = this._anyControlsTouched(); if (this._parent && !opts.onlySelf) { this._parent._updateTouched(opts); } } /** @internal */ _registerOnCollectionChange(fn) { this._onCollectionChange = fn; } /** @internal */ _setUpdateStrategy(opts) { if (isOptionsObj(opts) && opts.updateOn != null) { this._updateOn = opts.updateOn; } } /** * Check to see if parent has been marked artificially dirty. * * @internal */ _parentMarkedDirty(onlySelf) { const parentDirty = this._parent && this._parent.dirty; return !onlySelf && !!parentDirty && !this._parent._anyControlsDirty(); } /** @internal */ _find(name) { return null; } /** * Internal implementation of the `setValidators` method. Needs to be separated out into a * different method, because it is called in the constructor and it can break cases where * a control is extended. */ _assignValidators(validators) { this._rawValidators = Array.isArray(validators) ? validators.slice() : validators; this._composedValidatorFn = coerceToValidator(this._rawValidators); } /** * Internal implementation of the `setAsyncValidators` method. Needs to be separated out into a * different method, because it is called in the constructor and it can break cases where * a control is extended. */ _assignAsyncValidators(validators) { this._rawAsyncValidators = Array.isArray(validators) ? validators.slice() : validators; this._composedAsyncValidatorFn = coerceToAsyncValidator(this._rawAsyncValidators); } } /** * Tracks the value and validity state of a group of `FormControl` instances. * * A `FormGroup` aggregates the values of each child `FormControl` into one object, * with each control name as the key. It calculates its status by reducing the status values * of its children. For example, if one of the controls in a group is invalid, the entire * group becomes invalid. * * `FormGroup` is one of the four fundamental building blocks used to define forms in Angular, * along with `FormControl`, `FormArray`, and `FormRecord`. * * When instantiating a `FormGroup`, pass in a collection of child controls as the first * argument. The key for each child registers the name for the control. * * `FormGroup` is intended for use cases where the keys are known ahead of time. * If you need to dynamically add and remove controls, use {@link FormRecord} instead. * * `FormGroup` accepts an optional type parameter `TControl`, which is an object type with inner * control types as values. * * @usageNotes * * ### Create a form group with 2 controls * * ``` * const form = new FormGroup({ * first: new FormControl('Nancy', Validators.minLength(2)), * last: new FormControl('Drew'), * }); * * console.log(form.value); // {first: 'Nancy', last; 'Drew'} * console.log(form.status); // 'VALID' * ``` * * ### The type argument, and optional controls * * `FormGroup` accepts one generic argument, which is an object containing its inner controls. * This type will usually be inferred automatically, but you can always specify it explicitly if you * wish. * * If you have controls that are optional (i.e. they can be removed, you can use the `?` in the * type): * * ``` * const form = new FormGroup<{ * first: FormControl<string|null>, * middle?: FormControl<string|null>, // Middle name is optional. * last: FormControl<string|null>, * }>({ * first: new FormControl('Nancy'), * last: new FormControl('Drew'), * }); * ``` * * ### Create a form group with a group-level validator * * You include group-level validators as the second arg, or group-level async * validators as the third arg. These come in handy when you want to perform validation * that considers the value of more than one child control. * * ``` * const form = new FormGroup({ * password: new FormControl('', Validators.minLength(2)), * passwordConfirm: new FormControl('', Validators.minLength(2)), * }, passwordMatchValidator); * * * function passwordMatchValidator(g: FormGroup) { * return g.get('password').value === g.get('passwordConfirm').value * ? null : {'mismatch': true}; * } * ``` * * Like `FormControl` instances, you choose to pass in * validators and async validators as part of an options object. * * ``` * const form = new FormGroup({ * password: new FormControl('') * passwordConfirm: new FormControl('') * }, { validators: passwordMatchValidator, asyncValidators: otherValidator }); * ``` * * ### Set the updateOn property for all controls in a form group * * The options object is used to set a default value for each child * control's `updateOn` property. If you set `updateOn` to `'blur'` at the * group level, all child controls default to 'blur', unless the child * has explicitly specified a different `updateOn` value. * * ```ts * const c = new FormGroup({ * one: new FormControl() * }, { updateOn: 'blur' }); * ``` * * ### Using a FormGroup with optional controls * * It is possible to have optional controls in a FormGroup. An optional control can be removed later * using `removeControl`, and can be omitted when calling `reset`. Optional controls must be * declared optional in the group's type. * * ```ts * const c = new FormGroup<{one?: FormControl<string>}>({ * one: new FormControl('') * }); * ``` * * Notice that `c.value.one` has type `string|null|undefined`. This is because calling `c.reset({})` * without providing the optional key `one` will cause it to become `null`. * * @publicApi */ class FormGroup extends AbstractControl { /** * Creates a new `FormGroup` instance. * * @param controls A collection of child controls. The key for each child is the name * under which it is registered. * * @param validatorOrOpts A synchronous validator function, or an array of * such functions, or an `AbstractControlOptions` object that contains validation functions * and a validation trigger. * * @param asyncValidator A single async validator or array of async validator functions * */ constructor(controls, validatorOrOpts, asyncValidator) { super(pickValidators(validatorOrOpts), pickAsyncValidators(asyncValidator, validatorOrOpts)); this.controls = controls; this._initObservables(); this._setUpdateStrategy(validatorOrOpts); this._setUpControls(); this.updateValueAndValidity({ onlySelf: true, // If `asyncValidator` is present, it will trigger control status change from `PENDING` to // `VALID` or `INVALID`. The status should be broadcasted via the `statusChanges` observable, // so we set `emitEvent` to `true` to allow that during the control creation process. emitEvent: !!this.asyncValidator }); } registerControl(name, control) { if (this.controls[name]) return this.controls[name]; this.controls[name] = control; control.setParent(this); control._registerOnCollectionChange(this._onCollectionChange); return control; } addControl(name, control, options = {}) { this.registerControl(name, control); this.updateValueAndValidity({ emitEvent: options.emitEvent }); this._onCollectionChange(); } /** * Remove a control from this group. In a strongly-typed group, required controls cannot be * removed. * * This method also updates the value and validity of the control. * * @param name The control name to remove from the collection * @param options Specifies whether this FormGroup instance should emit events after a * control is removed. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` observables emit events with the latest status and value when the control is * removed. When false, no events are emitted. */ removeControl(name, options = {}) { if (this.controls[name]) this.controls[name]._registerOnCollectionChange(() => {}); delete this.controls[name]; this.updateValueAndValidity({ emitEvent: options.emitEvent }); this._onCollectionChange(); } setControl(name, control, options = {}) { if (this.controls[name]) this.controls[name]._registerOnCollectionChange(() => {}); delete this.controls[name]; if (control) this.registerControl(name, control); this.updateValueAndValidity({ emitEvent: options.emitEvent }); this._onCollectionChange(); } contains(controlName) { return this.controls.hasOwnProperty(controlName) && this.controls[controlName].enabled; } /** * Sets the value of the `FormGroup`. It accepts an object that matches * the structure of the group, with control names as keys. * * @usageNotes * ### Set the complete value for the form group * * ``` * const form = new FormGroup({ * first: new FormControl(), * last: new FormControl() * }); * * console.log(form.value); // {first: null, last: null} * * form.setValue({first: 'Nancy', last: 'Drew'}); * console.log(form.value); // {first: 'Nancy', last: 'Drew'} * ``` * * @throws When strict checks fail, such as setting the value of a control * that doesn't exist or if you exclude a value of a control that does exist. * * @param value The new value for the control that matches the structure of the group. * @param options Configuration options that determine how the control propagates changes * and emits events after the value changes. * The configuration options are passed to the {@link AbstractControl#updateValueAndValidity * updateValueAndValidity} method. * * * `onlySelf`: When true, each change only affects this control, and not its parent. Default is * false. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` * observables emit events with the latest status and value when the control value is updated. * When false, no events are emitted. */ setValue(value, options = {}) { assertAllValuesPresent(this, true, value); Object.keys(value).forEach(name => { assertControlPresent(this, true, name); this.controls[name].setValue(value[name], { onlySelf: true, emitEvent: options.emitEvent }); }); this.updateValueAndValidity(options); } /** * Patches the value of the `FormGroup`. It accepts an object with control * names as keys, and does its best to match the values to the correct controls * in the group. * * It accepts both super-sets and sub-sets of the group without throwing an error. * * @usageNotes * ### Patch the value for a form group * * ``` * const form = new FormGroup({ * first: new FormControl(), * last: new FormControl() * }); * console.log(form.value); // {first: null, last: null} * * form.patchValue({first: 'Nancy'}); * console.log(form.value); // {first: 'Nancy', last: null} * ``` * * @param value The object that matches the structure of the group. * @param options Configuration options that determine how the control propagates changes and * emits events after the value is patched. * * `onlySelf`: When true, each change only affects this control and not its parent. Default is * true. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` observables emit events with the latest status and value when the control value * is updated. When false, no events are emitted. The configuration options are passed to * the {@link AbstractControl#updateValueAndValidity updateValueAndValidity} method. */ patchValue(value, options = {}) { // Even though the `value` argument type doesn't allow `null` and `undefined` values, the // `patchValue` can be called recursively and inner data structures might have these values, so // we just ignore such cases when a field containing FormGroup instance receives `null` or // `undefined` as a value. if (value == null /* both `null` and `undefined` */) return; Object.keys(value).forEach(name => { // The compiler cannot see through the uninstantiated conditional type of `this.controls`, so // `as any` is required. const control = this.controls[name]; if (control) { control.patchValue( /* Guaranteed to be present, due to the outer forEach. */value[name], { onlySelf: true, emitEvent: options.emitEvent }); } }); this.updateValueAndValidity(options); } /** * Resets the `FormGroup`, marks all descendants `pristine` and `untouched` and sets * the value of all descendants to their default values, or null if no defaults were provided. * * You reset to a specific form state by passing in a map of states * that matches the structure of your form, with control names as keys. The state * is a standalone value or a form state object with both a value and a disabled * status. * * @param value Resets the control with an initial value, * or an object that defines the initial value and disabled state. * * @param options Configuration options that determine how the control propagates changes * and emits events when the group is reset. * * `onlySelf`: When true, each change only affects this control, and not its parent. Default is * false. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` * observables emit events with the latest status and value when the control is reset. * When false, no events are emitted. * The configuration options are passed to the {@link AbstractControl#updateValueAndValidity * updateValueAndValidity} method. * * @usageNotes * * ### Reset the form group values * * ```ts * const form = new FormGroup({ * first: new FormControl('first name'), * last: new FormControl('last name') * }); * * console.log(form.value); // {first: 'first name', last: 'last name'} * * form.reset({ first: 'name', last: 'last name' }); * * console.log(form.value); // {first: 'name', last: 'last name'} * ``` * * ### Reset the form group values and disabled status * * ``` * const form = new FormGroup({ * first: new FormControl('first name'), * last: new FormControl('last name') * }); * * form.reset({ * first: {value: 'name', disabled: true}, * last: 'last' * }); * * console.log(form.value); // {last: 'last'} * console.log(form.get('first').status); // 'DISABLED' * ``` */ reset(value = {}, options = {}) { this._forEachChild((control, name) => { control.reset(value[name], { onlySelf: true, emitEvent: options.emitEvent }); }); this._updatePristine(options); this._updateTouched(options); this.updateValueAndValidity(options); } /** * The aggregate value of the `FormGroup`, including any disabled controls. * * Retrieves all values regardless of disabled status. */ getRawValue() { return this._reduceChildren({}, (acc, control, name) => { acc[name] = control.getRawValue(); return acc; }); } /** @internal */ _syncPendingControls() { let subtreeUpdated = this._reduceChildren(false, (updated, child) => { return child._syncPendingControls() ? true : updated; }); if (subtreeUpdated) this.updateValueAndValidity({ onlySelf: true }); return subtreeUpdated; } /** @internal */ _forEachChild(cb) { Object.keys(this.controls).forEach(key => { // The list of controls can change (for ex. controls might be removed) while the loop // is running (as a result of invoking Forms API in `valueChanges` subscription), so we // have to null check before invoking the callback. const control = this.controls[key]; control && cb(control, key); }); } /** @internal */ _setUpControls() { this._forEachChild(control => { control.setParent(this); control._registerOnCollectionChange(this._onCollectionChange); }); } /** @internal */ _updateValue() { this.value = this._reduceValue(); } /** @internal */ _anyControls(condition) { for (const [controlName, control] of Object.entries(this.controls)) { if (this.contains(controlName) && condition(control)) { return true; } } return false; } /** @internal */ _reduceValue() { let acc = {}; return this._reduceChildren(acc, (acc, control, name) => { if (control.enabled || this.disabled) { acc[name] = control.value; } return acc; }); } /** @internal */ _reduceChildren(initValue, fn) { let res = initValue; this._forEachChild((control, name) => { res = fn(res, control, name); }); return res; } /** @internal */ _allControlsDisabled() { for (const controlName of Object.keys(this.controls)) { if (this.controls[controlName].enabled) { return false; } } return Object.keys(this.controls).length > 0 || this.disabled; } /** @internal */ _find(name) { return this.controls.hasOwnProperty(name) ? this.controls[name] : null; } } const UntypedFormGroup = FormGroup; /** * @description * Asserts that the given control is an instance of `FormGroup` * * @publicApi */ const isFormGroup = control => control instanceof FormGroup; /** * Tracks the value and validity state of a collection of `FormControl` instances, each of which has * the same value type. * * `FormRecord` is very similar to {@link FormGroup}, except it can be used with a dynamic keys, * with controls added and removed as needed. * * `FormRecord` accepts one generic argument, which describes the type of the controls it contains. * * @usageNotes * * ``` * let numbers = new FormRecord({bill: new FormControl('415-123-456')}); * numbers.addControl('bob', new FormControl('415-234-567')); * numbers.removeControl('bill'); * ``` * * @publicApi */ class FormRecord extends FormGroup {} /** * @description * Asserts that the given control is an instance of `FormRecord` * * @publicApi */ const isFormRecord = control => control instanceof FormRecord; /** * Token to provide to allow SetDisabledState to always be called when a CVA is added, regardless of * whether the control is disabled or enabled. * * @see `FormsModule.withConfig` */ const CALL_SET_DISABLED_STATE = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('CallSetDisabledState', { providedIn: 'root', factory: () => setDisabledStateDefault }); /** * Whether to use the fixed setDisabledState behavior by default. */ const setDisabledStateDefault = 'always'; function controlPath(name, parent) { return [...parent.path, name]; } /** * Links a Form control and a Form directive by setting up callbacks (such as `onChange`) on both * instances. This function is typically invoked when form directive is being initialized. * * @param control Form control instance that should be linked. * @param dir Directive that should be linked with a given control. */ function setUpControl(control, dir, callSetDisabledState = setDisabledStateDefault) { if (typeof ngDevMode === 'undefined' || ngDevMode) { if (!control) _throwError(dir, 'Cannot find control with'); if (!dir.valueAccessor) _throwMissingValueAccessorError(dir); } setUpValidators(control, dir); dir.valueAccessor.writeValue(control.value); // The legacy behavior only calls the CVA's `setDisabledState` if the control is disabled. // If the `callSetDisabledState` option is set to `always`, then this bug is fixed and // the method is always called. if (control.disabled || callSetDisabledState === 'always') { dir.valueAccessor.setDisabledState?.(control.disabled); } setUpViewChangePipeline(control, dir); setUpModelChangePipeline(control, dir); setUpBlurPipeline(control, dir); setUpDisabledChangeHandler(control, dir); } /** * Reverts configuration performed by the `setUpControl` control function. * Effectively disconnects form control with a given form directive. * This function is typically invoked when corresponding form directive is being destroyed. * * @param control Form control which should be cleaned up. * @param dir Directive that should be disconnected from a given control. * @param validateControlPresenceOnChange Flag that indicates whether onChange handler should * contain asserts to verify that it's not called once directive is destroyed. We need this flag * to avoid potentially breaking changes caused by better control cleanup introduced in #39235. */ function cleanUpControl(control, dir, validateControlPresenceOnChange = true) { const noop = () => { if (validateControlPresenceOnChange && (typeof ngDevMode === 'undefined' || ngDevMode)) { _noControlError(dir); } }; // The `valueAccessor` field is typically defined on FromControl and FormControlName directive // instances and there is a logic in `selectValueAccessor` function that throws if it's not the // case. We still check the presence of `valueAccessor` before invoking its methods to make sure // that cleanup works correctly if app code or tests are setup to ignore the error thrown from // `selectValueAccessor`. See https://github.com/angular/angular/issues/40521. if (dir.valueAccessor) { dir.valueAccessor.registerOnChange(noop); dir.valueAccessor.registerOnTouched(noop); } cleanUpValidators(control, dir); if (control) { dir._invokeOnDestroyCallbacks(); control._registerOnCollectionChange(() => {}); } } function registerOnValidatorChange(validators, onChange) { validators.forEach(validator => { if (validator.registerOnValidatorChange) validator.registerOnValidatorChange(onChange); }); } /** * Sets up disabled change handler function on a given form control if ControlValueAccessor * associated with a given directive instance supports the `setDisabledState` call. * * @param control Form control where disabled change handler should be setup. * @param dir Corresponding directive instance associated with this control. */ function setUpDisabledChangeHandler(control, dir) { if (dir.valueAccessor.setDisabledState) { const onDisabledChange = isDisabled => { dir.valueAccessor.setDisabledState(isDisabled); }; control.registerOnDisabledChange(onDisabledChange); // Register a callback function to cleanup disabled change handler // from a control instance when a directive is destroyed. dir._registerOnDestroy(() => { control._unregisterOnDisabledChange(onDisabledChange); }); } } /** * Sets up sync and async directive validators on provided form control. * This function merges validators from the directive into the validators of the control. * * @param control Form control where directive validators should be setup. * @param dir Directive instance that contains validators to be setup. */ function setUpValidators(control, dir) { const validators = getControlValidators(control); if (dir.validator !== null) { control.setValidators(mergeValidators(validators, dir.validator)); } else if (typeof validators === 'function') { // If sync validators are represented by a single validator function, we force the // `Validators.compose` call to happen by executing the `setValidators` function with // an array that contains that function. We need this to avoid possible discrepancies in // validators behavior, so sync validators are always processed by the `Validators.compose`. // Note: we should consider moving this logic inside the `setValidators` function itself, so we // have consistent behavior on AbstractControl API level. The same applies to the async // validators logic below. control.setValidators([validators]); } const asyncValidators = getControlAsyncValidators(control); if (dir.asyncValidator !== null) { control.setAsyncValidators(mergeValidators(asyncValidators, dir.asyncValidator)); } else if (typeof asyncValidators === 'function') { control.setAsyncValidators([asyncValidators]); } // Re-run validation when validator binding changes, e.g. minlength=3 -> minlength=4 const onValidatorChange = () => control.updateValueAndValidity(); registerOnValidatorChange(dir._rawValidators, onValidatorChange); registerOnValidatorChange(dir._rawAsyncValidators, onValidatorChange); } /** * Cleans up sync and async directive validators on provided form control. * This function reverts the setup performed by the `setUpValidators` function, i.e. * removes directive-specific validators from a given control instance. * * @param control Form control from where directive validators should be removed. * @param dir Directive instance that contains validators to be removed. * @returns true if a control was updated as a result of this action. */ function cleanUpValidators(control, dir) { let isControlUpdated = false; if (control !== null) { if (dir.validator !== null) { const validators = getControlValidators(control); if (Array.isArray(validators) && validators.length > 0) { // Filter out directive validator function. const updatedValidators = validators.filter(validator => validator !== dir.validator); if (updatedValidators.length !== validators.length) { isControlUpdated = true; control.setValidators(updatedValidators); } } } if (dir.asyncValidator !== null) { const asyncValidators = getControlAsyncValidators(control); if (Array.isArray(asyncValidators) && asyncValidators.length > 0) { // Filter out directive async validator function. const updatedAsyncValidators = asyncValidators.filter(asyncValidator => asyncValidator !== dir.asyncValidator); if (updatedAsyncValidators.length !== asyncValidators.length) { isControlUpdated = true; control.setAsyncValidators(updatedAsyncValidators); } } } } // Clear onValidatorChange callbacks by providing a noop function. const noop = () => {}; registerOnValidatorChange(dir._rawValidators, noop); registerOnValidatorChange(dir._rawAsyncValidators, noop); return isControlUpdated; } function setUpViewChangePipeline(control, dir) { dir.valueAccessor.registerOnChange(newValue => { control._pendingValue = newValue; control._pendingChange = true; control._pendingDirty = true; if (control.updateOn === 'change') updateControl(control, dir); }); } function setUpBlurPipeline(control, dir) { dir.valueAccessor.registerOnTouched(() => { control._pendingTouched = true; if (control.updateOn === 'blur' && control._pendingChange) updateControl(control, dir); if (control.updateOn !== 'submit') control.markAsTouched(); }); } function updateControl(control, dir) { if (control._pendingDirty) control.markAsDirty(); control.setValue(control._pendingValue, { emitModelToViewChange: false }); dir.viewToModelUpdate(control._pendingValue); control._pendingChange = false; } function setUpModelChangePipeline(control, dir) { const onChange = (newValue, emitModelEvent) => { // control -> view dir.valueAccessor.writeValue(newValue); // control -> ngModel if (emitModelEvent) dir.viewToModelUpdate(newValue); }; control.registerOnChange(onChange); // Register a callback function to cleanup onChange handler // from a control instance when a directive is destroyed. dir._registerOnDestroy(() => { control._unregisterOnChange(onChange); }); } /** * Links a FormGroup or FormArray instance and corresponding Form directive by setting up validators * present in the view. * * @param control FormGroup or FormArray instance that should be linked. * @param dir Directive that provides view validators. */ function setUpFormContainer(control, dir) { if (control == null && (typeof ngDevMode === 'undefined' || ngDevMode)) _throwError(dir, 'Cannot find control with'); setUpValidators(control, dir); } /** * Reverts the setup performed by the `setUpFormContainer` function. * * @param control FormGroup or FormArray instance that should be cleaned up. * @param dir Directive that provided view validators. * @returns true if a control was updated as a result of this action. */ function cleanUpFormContainer(control, dir) { return cleanUpValidators(control, dir); } function _noControlError(dir) { return _throwError(dir, 'There is no FormControl instance attached to form control element with'); } function _throwError(dir, message) { const messageEnd = _describeControlLocation(dir); throw new Error(`${message} ${messageEnd}`); } function _describeControlLocation(dir) { const path = dir.path; if (path && path.length > 1) return `path: '${path.join(' -> ')}'`; if (path?.[0]) return `name: '${path}'`; return 'unspecified name attribute'; } function _throwMissingValueAccessorError(dir) { const loc = _describeControlLocation(dir); throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](-1203 /* RuntimeErrorCode.NG_MISSING_VALUE_ACCESSOR */, `No value accessor for form control ${loc}.`); } function _throwInvalidValueAccessorError(dir) { const loc = _describeControlLocation(dir); throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1200 /* RuntimeErrorCode.NG_VALUE_ACCESSOR_NOT_PROVIDED */, `Value accessor was not provided as an array for form control with ${loc}. ` + `Check that the \`NG_VALUE_ACCESSOR\` token is configured as a \`multi: true\` provider.`); } function isPropertyUpdated(changes, viewModel) { if (!changes.hasOwnProperty('model')) return false; const change = changes['model']; if (change.isFirstChange()) return true; return !Object.is(viewModel, change.currentValue); } function isBuiltInAccessor(valueAccessor) { // Check if a given value accessor is an instance of a class that directly extends // `BuiltInControlValueAccessor` one. return Object.getPrototypeOf(valueAccessor.constructor) === BuiltInControlValueAccessor; } function syncPendingControls(form, directives) { form._syncPendingControls(); directives.forEach(dir => { const control = dir.control; if (control.updateOn === 'submit' && control._pendingChange) { dir.viewToModelUpdate(control._pendingValue); control._pendingChange = false; } }); } // TODO: vsavkin remove it once https://github.com/angular/angular/issues/3011 is implemented function selectValueAccessor(dir, valueAccessors) { if (!valueAccessors) return null; if (!Array.isArray(valueAccessors) && (typeof ngDevMode === 'undefined' || ngDevMode)) _throwInvalidValueAccessorError(dir); let defaultAccessor = undefined; let builtinAccessor = undefined; let customAccessor = undefined; valueAccessors.forEach(v => { if (v.constructor === DefaultValueAccessor) { defaultAccessor = v; } else if (isBuiltInAccessor(v)) { if (builtinAccessor && (typeof ngDevMode === 'undefined' || ngDevMode)) _throwError(dir, 'More than one built-in value accessor matches form control with'); builtinAccessor = v; } else { if (customAccessor && (typeof ngDevMode === 'undefined' || ngDevMode)) _throwError(dir, 'More than one custom value accessor matches form control with'); customAccessor = v; } }); if (customAccessor) return customAccessor; if (builtinAccessor) return builtinAccessor; if (defaultAccessor) return defaultAccessor; if (typeof ngDevMode === 'undefined' || ngDevMode) { _throwError(dir, 'No valid value accessor for form control with'); } return null; } function removeListItem$1(list, el) { const index = list.indexOf(el); if (index > -1) list.splice(index, 1); } // TODO(kara): remove after deprecation period function _ngModelWarning(name, type, instance, warningConfig) { if (warningConfig === 'never') return; if ((warningConfig === null || warningConfig === 'once') && !type._ngModelWarningSentOnce || warningConfig === 'always' && !instance._ngModelWarningSent) { console.warn(ngModelWarning(name)); type._ngModelWarningSentOnce = true; instance._ngModelWarningSent = true; } } const formDirectiveProvider$1 = { provide: ControlContainer, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => NgForm) }; const resolvedPromise$1 = (() => Promise.resolve())(); /** * @description * Creates a top-level `FormGroup` instance and binds it to a form * to track aggregate form value and validation status. * * As soon as you import the `FormsModule`, this directive becomes active by default on * all `<form>` tags. You don't need to add a special selector. * * You optionally export the directive into a local template variable using `ngForm` as the key * (ex: `#myForm="ngForm"`). This is optional, but useful. Many properties from the underlying * `FormGroup` instance are duplicated on the directive itself, so a reference to it * gives you access to the aggregate value and validity status of the form, as well as * user interaction properties like `dirty` and `touched`. * * To register child controls with the form, use `NgModel` with a `name` * attribute. You may use `NgModelGroup` to create sub-groups within the form. * * If necessary, listen to the directive's `ngSubmit` event to be notified when the user has * triggered a form submission. The `ngSubmit` event emits the original form * submission event. * * In template driven forms, all `<form>` tags are automatically tagged as `NgForm`. * To import the `FormsModule` but skip its usage in some forms, * for example, to use native HTML5 validation, add the `ngNoForm` and the `<form>` * tags won't create an `NgForm` directive. In reactive forms, using `ngNoForm` is * unnecessary because the `<form>` tags are inert. In that case, you would * refrain from using the `formGroup` directive. * * @usageNotes * * ### Listening for form submission * * The following example shows how to capture the form values from the "ngSubmit" event. * * {@example forms/ts/simpleForm/simple_form_example.ts region='Component'} * * ### Setting the update options * * The following example shows you how to change the "updateOn" option from its default using * ngFormOptions. * * ```html * <form [ngFormOptions]="{updateOn: 'blur'}"> * <input name="one" ngModel> <!-- this ngModel will update on blur --> * </form> * ``` * * ### Native DOM validation UI * * In order to prevent the native DOM form validation UI from interfering with Angular's form * validation, Angular automatically adds the `novalidate` attribute on any `<form>` whenever * `FormModule` or `ReactiveFormModule` are imported into the application. * If you want to explicitly enable native DOM validation UI with Angular forms, you can add the * `ngNativeValidate` attribute to the `<form>` element: * * ```html * <form ngNativeValidate> * ... * </form> * ``` * * @ngModule FormsModule * @publicApi */ class NgForm extends ControlContainer { constructor(validators, asyncValidators, callSetDisabledState) { super(); this.callSetDisabledState = callSetDisabledState; /** * @description * Returns whether the form submission has been triggered. */ this.submitted = false; this._directives = new Set(); /** * @description * Event emitter for the "ngSubmit" event */ this.ngSubmit = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.EventEmitter(); this.form = new FormGroup({}, composeValidators(validators), composeAsyncValidators(asyncValidators)); } /** @nodoc */ ngAfterViewInit() { this._setUpdateStrategy(); } /** * @description * The directive instance. */ get formDirective() { return this; } /** * @description * The internal `FormGroup` instance. */ get control() { return this.form; } /** * @description * Returns an array representing the path to this group. Because this directive * always lives at the top level of a form, it is always an empty array. */ get path() { return []; } /** * @description * Returns a map of the controls in this group. */ get controls() { return this.form.controls; } /** * @description * Method that sets up the control directive in this group, re-calculates its value * and validity, and adds the instance to the internal list of directives. * * @param dir The `NgModel` directive instance. */ addControl(dir) { resolvedPromise$1.then(() => { const container = this._findContainer(dir.path); dir.control = container.registerControl(dir.name, dir.control); setUpControl(dir.control, dir, this.callSetDisabledState); dir.control.updateValueAndValidity({ emitEvent: false }); this._directives.add(dir); }); } /** * @description * Retrieves the `FormControl` instance from the provided `NgModel` directive. * * @param dir The `NgModel` directive instance. */ getControl(dir) { return this.form.get(dir.path); } /** * @description * Removes the `NgModel` instance from the internal list of directives * * @param dir The `NgModel` directive instance. */ removeControl(dir) { resolvedPromise$1.then(() => { const container = this._findContainer(dir.path); if (container) { container.removeControl(dir.name); } this._directives.delete(dir); }); } /** * @description * Adds a new `NgModelGroup` directive instance to the form. * * @param dir The `NgModelGroup` directive instance. */ addFormGroup(dir) { resolvedPromise$1.then(() => { const container = this._findContainer(dir.path); const group = new FormGroup({}); setUpFormContainer(group, dir); container.registerControl(dir.name, group); group.updateValueAndValidity({ emitEvent: false }); }); } /** * @description * Removes the `NgModelGroup` directive instance from the form. * * @param dir The `NgModelGroup` directive instance. */ removeFormGroup(dir) { resolvedPromise$1.then(() => { const container = this._findContainer(dir.path); if (container) { container.removeControl(dir.name); } }); } /** * @description * Retrieves the `FormGroup` for a provided `NgModelGroup` directive instance * * @param dir The `NgModelGroup` directive instance. */ getFormGroup(dir) { return this.form.get(dir.path); } /** * Sets the new value for the provided `NgControl` directive. * * @param dir The `NgControl` directive instance. * @param value The new value for the directive's control. */ updateModel(dir, value) { resolvedPromise$1.then(() => { const ctrl = this.form.get(dir.path); ctrl.setValue(value); }); } /** * @description * Sets the value for this `FormGroup`. * * @param value The new value */ setValue(value) { this.control.setValue(value); } /** * @description * Method called when the "submit" event is triggered on the form. * Triggers the `ngSubmit` emitter to emit the "submit" event as its payload. * * @param $event The "submit" event object */ onSubmit($event) { this.submitted = true; syncPendingControls(this.form, this._directives); this.ngSubmit.emit($event); // Forms with `method="dialog"` have some special behavior // that won't reload the page and that shouldn't be prevented. return $event?.target?.method === 'dialog'; } /** * @description * Method called when the "reset" event is triggered on the form. */ onReset() { this.resetForm(); } /** * @description * Resets the form to an initial value and resets its submitted status. * * @param value The new value for the form. */ resetForm(value = undefined) { this.form.reset(value); this.submitted = false; } _setUpdateStrategy() { if (this.options && this.options.updateOn != null) { this.form._updateOn = this.options.updateOn; } } _findContainer(path) { path.pop(); return path.length ? this.form.get(path) : this.form; } } NgForm.ɵfac = function NgForm_Factory(t) { return new (t || NgForm)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_VALIDATORS, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_ASYNC_VALIDATORS, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](CALL_SET_DISABLED_STATE, 8)); }; NgForm.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgForm, selectors: [["form", 3, "ngNoForm", "", 3, "formGroup", ""], ["ng-form"], ["", "ngForm", ""]], hostBindings: function NgForm_HostBindings(rf, ctx) { if (rf & 1) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵlistener"]("submit", function NgForm_submit_HostBindingHandler($event) { return ctx.onSubmit($event); })("reset", function NgForm_reset_HostBindingHandler() { return ctx.onReset(); }); } }, inputs: { options: ["ngFormOptions", "options"] }, outputs: { ngSubmit: "ngSubmit" }, exportAs: ["ngForm"], features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([formDirectiveProvider$1]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgForm, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]', providers: [formDirectiveProvider$1], host: { '(submit)': 'onSubmit($event)', '(reset)': 'onReset()' }, outputs: ['ngSubmit'], exportAs: 'ngForm' }] }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_VALIDATORS] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_ASYNC_VALIDATORS] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [CALL_SET_DISABLED_STATE] }] }]; }, { options: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['ngFormOptions'] }] }); })(); function removeListItem(list, el) { const index = list.indexOf(el); if (index > -1) list.splice(index, 1); } function isFormControlState(formState) { return typeof formState === 'object' && formState !== null && Object.keys(formState).length === 2 && 'value' in formState && 'disabled' in formState; } const FormControl = class FormControl extends AbstractControl { constructor( // formState and defaultValue will only be null if T is nullable formState = null, validatorOrOpts, asyncValidator) { super(pickValidators(validatorOrOpts), pickAsyncValidators(asyncValidator, validatorOrOpts)); /** @publicApi */ this.defaultValue = null; /** @internal */ this._onChange = []; /** @internal */ this._pendingChange = false; this._applyFormState(formState); this._setUpdateStrategy(validatorOrOpts); this._initObservables(); this.updateValueAndValidity({ onlySelf: true, // If `asyncValidator` is present, it will trigger control status change from `PENDING` to // `VALID` or `INVALID`. // The status should be broadcasted via the `statusChanges` observable, so we set // `emitEvent` to `true` to allow that during the control creation process. emitEvent: !!this.asyncValidator }); if (isOptionsObj(validatorOrOpts) && (validatorOrOpts.nonNullable || validatorOrOpts.initialValueIsDefault)) { if (isFormControlState(formState)) { this.defaultValue = formState.value; } else { this.defaultValue = formState; } } } setValue(value, options = {}) { this.value = this._pendingValue = value; if (this._onChange.length && options.emitModelToViewChange !== false) { this._onChange.forEach(changeFn => changeFn(this.value, options.emitViewToModelChange !== false)); } this.updateValueAndValidity(options); } patchValue(value, options = {}) { this.setValue(value, options); } reset(formState = this.defaultValue, options = {}) { this._applyFormState(formState); this.markAsPristine(options); this.markAsUntouched(options); this.setValue(this.value, options); this._pendingChange = false; } /** @internal */ _updateValue() {} /** @internal */ _anyControls(condition) { return false; } /** @internal */ _allControlsDisabled() { return this.disabled; } registerOnChange(fn) { this._onChange.push(fn); } /** @internal */ _unregisterOnChange(fn) { removeListItem(this._onChange, fn); } registerOnDisabledChange(fn) { this._onDisabledChange.push(fn); } /** @internal */ _unregisterOnDisabledChange(fn) { removeListItem(this._onDisabledChange, fn); } /** @internal */ _forEachChild(cb) {} /** @internal */ _syncPendingControls() { if (this.updateOn === 'submit') { if (this._pendingDirty) this.markAsDirty(); if (this._pendingTouched) this.markAsTouched(); if (this._pendingChange) { this.setValue(this._pendingValue, { onlySelf: true, emitModelToViewChange: false }); return true; } } return false; } _applyFormState(formState) { if (isFormControlState(formState)) { this.value = this._pendingValue = formState.value; formState.disabled ? this.disable({ onlySelf: true, emitEvent: false }) : this.enable({ onlySelf: true, emitEvent: false }); } else { this.value = this._pendingValue = formState; } } }; const UntypedFormControl = FormControl; /** * @description * Asserts that the given control is an instance of `FormControl` * * @publicApi */ const isFormControl = control => control instanceof FormControl; /** * @description * A base class for code shared between the `NgModelGroup` and `FormGroupName` directives. * * @publicApi */ class AbstractFormGroupDirective extends ControlContainer { /** @nodoc */ ngOnInit() { this._checkParentType(); // Register the group with its parent group. this.formDirective.addFormGroup(this); } /** @nodoc */ ngOnDestroy() { if (this.formDirective) { // Remove the group from its parent group. this.formDirective.removeFormGroup(this); } } /** * @description * The `FormGroup` bound to this directive. */ get control() { return this.formDirective.getFormGroup(this); } /** * @description * The path to this group from the top-level directive. */ get path() { return controlPath(this.name == null ? this.name : this.name.toString(), this._parent); } /** * @description * The top-level directive for this group if present, otherwise null. */ get formDirective() { return this._parent ? this._parent.formDirective : null; } /** @internal */ _checkParentType() {} } AbstractFormGroupDirective.ɵfac = /* @__PURE__ */function () { let ɵAbstractFormGroupDirective_BaseFactory; return function AbstractFormGroupDirective_Factory(t) { return (ɵAbstractFormGroupDirective_BaseFactory || (ɵAbstractFormGroupDirective_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](AbstractFormGroupDirective)))(t || AbstractFormGroupDirective); }; }(); AbstractFormGroupDirective.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: AbstractFormGroupDirective, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](AbstractFormGroupDirective, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive }], null, null); })(); function modelParentException() { return new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1350 /* RuntimeErrorCode.NGMODEL_IN_FORM_GROUP */, ` ngModel cannot be used to register form controls with a parent formGroup directive. Try using formGroup's partner directive "formControlName" instead. Example: ${formControlNameExample} Or, if you'd like to avoid registering this form control, indicate that it's standalone in ngModelOptions: Example: ${ngModelWithFormGroupExample}`); } function formGroupNameException() { return new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1351 /* RuntimeErrorCode.NGMODEL_IN_FORM_GROUP_NAME */, ` ngModel cannot be used to register form controls with a parent formGroupName or formArrayName directive. Option 1: Use formControlName instead of ngModel (reactive strategy): ${formGroupNameExample} Option 2: Update ngModel's parent be ngModelGroup (template-driven strategy): ${ngModelGroupExample}`); } function missingNameException() { return new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1352 /* RuntimeErrorCode.NGMODEL_WITHOUT_NAME */, `If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions. Example 1: <input [(ngModel)]="person.firstName" name="first"> Example 2: <input [(ngModel)]="person.firstName" [ngModelOptions]="{standalone: true}">`); } function modelGroupParentException() { return new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1353 /* RuntimeErrorCode.NGMODELGROUP_IN_FORM_GROUP */, ` ngModelGroup cannot be used with a parent formGroup directive. Option 1: Use formGroupName instead of ngModelGroup (reactive strategy): ${formGroupNameExample} Option 2: Use a regular form tag instead of the formGroup directive (template-driven strategy): ${ngModelGroupExample}`); } const modelGroupProvider = { provide: ControlContainer, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => NgModelGroup) }; /** * @description * Creates and binds a `FormGroup` instance to a DOM element. * * This directive can only be used as a child of `NgForm` (within `<form>` tags). * * Use this directive to validate a sub-group of your form separately from the * rest of your form, or if some values in your domain model make more sense * to consume together in a nested object. * * Provide a name for the sub-group and it will become the key * for the sub-group in the form's full value. If you need direct access, export the directive into * a local template variable using `ngModelGroup` (ex: `#myGroup="ngModelGroup"`). * * @usageNotes * * ### Consuming controls in a grouping * * The following example shows you how to combine controls together in a sub-group * of the form. * * {@example forms/ts/ngModelGroup/ng_model_group_example.ts region='Component'} * * @ngModule FormsModule * @publicApi */ class NgModelGroup extends AbstractFormGroupDirective { constructor(parent, validators, asyncValidators) { super(); this._parent = parent; this._setValidators(validators); this._setAsyncValidators(asyncValidators); } /** @internal */ _checkParentType() { if (!(this._parent instanceof NgModelGroup) && !(this._parent instanceof NgForm) && (typeof ngDevMode === 'undefined' || ngDevMode)) { throw modelGroupParentException(); } } } NgModelGroup.ɵfac = function NgModelGroup_Factory(t) { return new (t || NgModelGroup)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](ControlContainer, 5), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_VALIDATORS, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_ASYNC_VALIDATORS, 10)); }; NgModelGroup.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgModelGroup, selectors: [["", "ngModelGroup", ""]], inputs: { name: ["ngModelGroup", "name"] }, exportAs: ["ngModelGroup"], features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([modelGroupProvider]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgModelGroup, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[ngModelGroup]', providers: [modelGroupProvider], exportAs: 'ngModelGroup' }] }], function () { return [{ type: ControlContainer, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Host }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.SkipSelf }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_VALIDATORS] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_ASYNC_VALIDATORS] }] }]; }, { name: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['ngModelGroup'] }] }); })(); const formControlBinding$1 = { provide: NgControl, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => NgModel) }; /** * `ngModel` forces an additional change detection run when its inputs change: * E.g.: * ``` * <div>{{myModel.valid}}</div> * <input [(ngModel)]="myValue" #myModel="ngModel"> * ``` * I.e. `ngModel` can export itself on the element and then be used in the template. * Normally, this would result in expressions before the `input` that use the exported directive * to have an old value as they have been * dirty checked before. As this is a very common case for `ngModel`, we added this second change * detection run. * * Notes: * - this is just one extra run no matter how many `ngModel`s have been changed. * - this is a general problem when using `exportAs` for directives! */ const resolvedPromise = (() => Promise.resolve())(); /** * @description * Creates a `FormControl` instance from a domain model and binds it * to a form control element. * * The `FormControl` instance tracks the value, user interaction, and * validation status of the control and keeps the view synced with the model. If used * within a parent form, the directive also registers itself with the form as a child * control. * * This directive is used by itself or as part of a larger form. Use the * `ngModel` selector to activate it. * * It accepts a domain model as an optional `Input`. If you have a one-way binding * to `ngModel` with `[]` syntax, changing the domain model's value in the component * class sets the value in the view. If you have a two-way binding with `[()]` syntax * (also known as 'banana-in-a-box syntax'), the value in the UI always syncs back to * the domain model in your class. * * To inspect the properties of the associated `FormControl` (like the validity state), * export the directive into a local template variable using `ngModel` as the key (ex: * `#myVar="ngModel"`). You can then access the control using the directive's `control` property. * However, the most commonly used properties (like `valid` and `dirty`) also exist on the control * for direct access. See a full list of properties directly available in * `AbstractControlDirective`. * * @see `RadioControlValueAccessor` * @see `SelectControlValueAccessor` * * @usageNotes * * ### Using ngModel on a standalone control * * The following examples show a simple standalone control using `ngModel`: * * {@example forms/ts/simpleNgModel/simple_ng_model_example.ts region='Component'} * * When using the `ngModel` within `<form>` tags, you'll also need to supply a `name` attribute * so that the control can be registered with the parent form under that name. * * In the context of a parent form, it's often unnecessary to include one-way or two-way binding, * as the parent form syncs the value for you. You access its properties by exporting it into a * local template variable using `ngForm` such as (`#f="ngForm"`). Use the variable where * needed on form submission. * * If you do need to populate initial values into your form, using a one-way binding for * `ngModel` tends to be sufficient as long as you use the exported form's value rather * than the domain model's value on submit. * * ### Using ngModel within a form * * The following example shows controls using `ngModel` within a form: * * {@example forms/ts/simpleForm/simple_form_example.ts region='Component'} * * ### Using a standalone ngModel within a group * * The following example shows you how to use a standalone ngModel control * within a form. This controls the display of the form, but doesn't contain form data. * * ```html * <form> * <input name="login" ngModel placeholder="Login"> * <input type="checkbox" ngModel [ngModelOptions]="{standalone: true}"> Show more options? * </form> * <!-- form value: {login: ''} --> * ``` * * ### Setting the ngModel `name` attribute through options * * The following example shows you an alternate way to set the name attribute. Here, * an attribute identified as name is used within a custom form control component. To still be able * to specify the NgModel's name, you must specify it using the `ngModelOptions` input instead. * * ```html * <form> * <my-custom-form-control name="Nancy" ngModel [ngModelOptions]="{name: 'user'}"> * </my-custom-form-control> * </form> * <!-- form value: {user: ''} --> * ``` * * @ngModule FormsModule * @publicApi */ class NgModel extends NgControl { constructor(parent, validators, asyncValidators, valueAccessors, _changeDetectorRef, callSetDisabledState) { super(); this._changeDetectorRef = _changeDetectorRef; this.callSetDisabledState = callSetDisabledState; this.control = new FormControl(); /** @internal */ this._registered = false; /** * @description * Event emitter for producing the `ngModelChange` event after * the view model updates. */ this.update = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.EventEmitter(); this._parent = parent; this._setValidators(validators); this._setAsyncValidators(asyncValidators); this.valueAccessor = selectValueAccessor(this, valueAccessors); } /** @nodoc */ ngOnChanges(changes) { this._checkForErrors(); if (!this._registered || 'name' in changes) { if (this._registered) { this._checkName(); if (this.formDirective) { // We can't call `formDirective.removeControl(this)`, because the `name` has already been // changed. We also can't reset the name temporarily since the logic in `removeControl` // is inside a promise and it won't run immediately. We work around it by giving it an // object with the same shape instead. const oldName = changes['name'].previousValue; this.formDirective.removeControl({ name: oldName, path: this._getPath(oldName) }); } } this._setUpControl(); } if ('isDisabled' in changes) { this._updateDisabled(changes); } if (isPropertyUpdated(changes, this.viewModel)) { this._updateValue(this.model); this.viewModel = this.model; } } /** @nodoc */ ngOnDestroy() { this.formDirective && this.formDirective.removeControl(this); } /** * @description * Returns an array that represents the path from the top-level form to this control. * Each index is the string name of the control on that level. */ get path() { return this._getPath(this.name); } /** * @description * The top-level directive for this control if present, otherwise null. */ get formDirective() { return this._parent ? this._parent.formDirective : null; } /** * @description * Sets the new value for the view model and emits an `ngModelChange` event. * * @param newValue The new value emitted by `ngModelChange`. */ viewToModelUpdate(newValue) { this.viewModel = newValue; this.update.emit(newValue); } _setUpControl() { this._setUpdateStrategy(); this._isStandalone() ? this._setUpStandalone() : this.formDirective.addControl(this); this._registered = true; } _setUpdateStrategy() { if (this.options && this.options.updateOn != null) { this.control._updateOn = this.options.updateOn; } } _isStandalone() { return !this._parent || !!(this.options && this.options.standalone); } _setUpStandalone() { setUpControl(this.control, this, this.callSetDisabledState); this.control.updateValueAndValidity({ emitEvent: false }); } _checkForErrors() { if (!this._isStandalone()) { this._checkParentType(); } this._checkName(); } _checkParentType() { if (typeof ngDevMode === 'undefined' || ngDevMode) { if (!(this._parent instanceof NgModelGroup) && this._parent instanceof AbstractFormGroupDirective) { throw formGroupNameException(); } else if (!(this._parent instanceof NgModelGroup) && !(this._parent instanceof NgForm)) { throw modelParentException(); } } } _checkName() { if (this.options && this.options.name) this.name = this.options.name; if (!this._isStandalone() && !this.name && (typeof ngDevMode === 'undefined' || ngDevMode)) { throw missingNameException(); } } _updateValue(value) { resolvedPromise.then(() => { this.control.setValue(value, { emitViewToModelChange: false }); this._changeDetectorRef?.markForCheck(); }); } _updateDisabled(changes) { const disabledValue = changes['isDisabled'].currentValue; // checking for 0 to avoid breaking change const isDisabled = disabledValue !== 0 && (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵcoerceToBoolean"])(disabledValue); resolvedPromise.then(() => { if (isDisabled && !this.control.disabled) { this.control.disable(); } else if (!isDisabled && this.control.disabled) { this.control.enable(); } this._changeDetectorRef?.markForCheck(); }); } _getPath(controlName) { return this._parent ? controlPath(controlName, this._parent) : [controlName]; } } NgModel.ɵfac = function NgModel_Factory(t) { return new (t || NgModel)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](ControlContainer, 9), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_VALIDATORS, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_ASYNC_VALIDATORS, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_VALUE_ACCESSOR, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ChangeDetectorRef, 8), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](CALL_SET_DISABLED_STATE, 8)); }; NgModel.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgModel, selectors: [["", "ngModel", "", 3, "formControlName", "", 3, "formControl", ""]], inputs: { name: "name", isDisabled: ["disabled", "isDisabled"], model: ["ngModel", "model"], options: ["ngModelOptions", "options"] }, outputs: { update: "ngModelChange" }, exportAs: ["ngModel"], features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([formControlBinding$1]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"], _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵNgOnChangesFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgModel, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[ngModel]:not([formControlName]):not([formControl])', providers: [formControlBinding$1], exportAs: 'ngModel' }] }], function () { return [{ type: ControlContainer, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Host }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_VALIDATORS] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_ASYNC_VALIDATORS] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_VALUE_ACCESSOR] }] }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ChangeDetectorRef, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.ChangeDetectorRef] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [CALL_SET_DISABLED_STATE] }] }]; }, { name: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], isDisabled: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['disabled'] }], model: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['ngModel'] }], options: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['ngModelOptions'] }], update: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Output, args: ['ngModelChange'] }] }); })(); /** * @description * * Adds `novalidate` attribute to all forms by default. * * `novalidate` is used to disable browser's native form validation. * * If you want to use native validation with Angular forms, just add `ngNativeValidate` attribute: * * ``` * <form ngNativeValidate></form> * ``` * * @publicApi * @ngModule ReactiveFormsModule * @ngModule FormsModule */ class ɵNgNoValidate {} ɵNgNoValidate.ɵfac = function ɵNgNoValidate_Factory(t) { return new (t || ɵNgNoValidate)(); }; ɵNgNoValidate.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: ɵNgNoValidate, selectors: [["form", 3, "ngNoForm", "", 3, "ngNativeValidate", ""]], hostAttrs: ["novalidate", ""] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](ɵNgNoValidate, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'form:not([ngNoForm]):not([ngNativeValidate])', host: { 'novalidate': '' } }] }], null, null); })(); const NUMBER_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => NumberValueAccessor), multi: true }; /** * @description * The `ControlValueAccessor` for writing a number value and listening to number input changes. * The value accessor is used by the `FormControlDirective`, `FormControlName`, and `NgModel` * directives. * * @usageNotes * * ### Using a number input with a reactive form. * * The following example shows how to use a number input with a reactive form. * * ```ts * const totalCountControl = new FormControl(); * ``` * * ``` * <input type="number" [formControl]="totalCountControl"> * ``` * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class NumberValueAccessor extends BuiltInControlValueAccessor { /** * Sets the "value" property on the input element. * @nodoc */ writeValue(value) { // The value needs to be normalized for IE9, otherwise it is set to 'null' when null const normalizedValue = value == null ? '' : value; this.setProperty('value', normalizedValue); } /** * Registers a function called when the control value changes. * @nodoc */ registerOnChange(fn) { this.onChange = value => { fn(value == '' ? null : parseFloat(value)); }; } } NumberValueAccessor.ɵfac = /* @__PURE__ */function () { let ɵNumberValueAccessor_BaseFactory; return function NumberValueAccessor_Factory(t) { return (ɵNumberValueAccessor_BaseFactory || (ɵNumberValueAccessor_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](NumberValueAccessor)))(t || NumberValueAccessor); }; }(); NumberValueAccessor.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NumberValueAccessor, selectors: [["input", "type", "number", "formControlName", ""], ["input", "type", "number", "formControl", ""], ["input", "type", "number", "ngModel", ""]], hostBindings: function NumberValueAccessor_HostBindings(rf, ctx) { if (rf & 1) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵlistener"]("input", function NumberValueAccessor_input_HostBindingHandler($event) { return ctx.onChange($event.target.value); })("blur", function NumberValueAccessor_blur_HostBindingHandler() { return ctx.onTouched(); }); } }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([NUMBER_VALUE_ACCESSOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NumberValueAccessor, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]', host: { '(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()' }, providers: [NUMBER_VALUE_ACCESSOR] }] }], null, null); })(); const RADIO_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => RadioControlValueAccessor), multi: true }; function throwNameError() { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1202 /* RuntimeErrorCode.NAME_AND_FORM_CONTROL_NAME_MUST_MATCH */, ` If you define both a name and a formControlName attribute on your radio button, their values must match. Ex: <input type="radio" formControlName="food" name="food"> `); } /** * Internal-only NgModule that works as a host for the `RadioControlRegistry` tree-shakable * provider. Note: the `InternalFormsSharedModule` can not be used here directly, since it's * declared *after* the `RadioControlRegistry` class and the `providedIn` doesn't support * `forwardRef` logic. */ class RadioControlRegistryModule {} RadioControlRegistryModule.ɵfac = function RadioControlRegistryModule_Factory(t) { return new (t || RadioControlRegistryModule)(); }; RadioControlRegistryModule.ɵmod = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineNgModule"]({ type: RadioControlRegistryModule }); RadioControlRegistryModule.ɵinj = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjector"]({}); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](RadioControlRegistryModule, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.NgModule }], null, null); })(); /** * @description * Class used by Angular to track radio buttons. For internal use only. */ class RadioControlRegistry { constructor() { this._accessors = []; } /** * @description * Adds a control to the internal registry. For internal use only. */ add(control, accessor) { this._accessors.push([control, accessor]); } /** * @description * Removes a control from the internal registry. For internal use only. */ remove(accessor) { for (let i = this._accessors.length - 1; i >= 0; --i) { if (this._accessors[i][1] === accessor) { this._accessors.splice(i, 1); return; } } } /** * @description * Selects a radio button. For internal use only. */ select(accessor) { this._accessors.forEach(c => { if (this._isSameGroup(c, accessor) && c[1] !== accessor) { c[1].fireUncheck(accessor.value); } }); } _isSameGroup(controlPair, accessor) { if (!controlPair[0].control) return false; return controlPair[0]._parent === accessor._control._parent && controlPair[1].name === accessor.name; } } RadioControlRegistry.ɵfac = function RadioControlRegistry_Factory(t) { return new (t || RadioControlRegistry)(); }; RadioControlRegistry.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: RadioControlRegistry, factory: RadioControlRegistry.ɵfac, providedIn: RadioControlRegistryModule }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](RadioControlRegistry, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: RadioControlRegistryModule }] }], null, null); })(); /** * @description * The `ControlValueAccessor` for writing radio control values and listening to radio control * changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and * `NgModel` directives. * * @usageNotes * * ### Using radio buttons with reactive form directives * * The follow example shows how to use radio buttons in a reactive form. When using radio buttons in * a reactive form, radio buttons in the same group should have the same `formControlName`. * Providing a `name` attribute is optional. * * {@example forms/ts/reactiveRadioButtons/reactive_radio_button_example.ts region='Reactive'} * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class RadioControlValueAccessor extends BuiltInControlValueAccessor { constructor(renderer, elementRef, _registry, _injector) { super(renderer, elementRef); this._registry = _registry; this._injector = _injector; this.setDisabledStateFired = false; /** * The registered callback function called when a change event occurs on the input element. * Note: we declare `onChange` here (also used as host listener) as a function with no arguments * to override the `onChange` function (which expects 1 argument) in the parent * `BaseControlValueAccessor` class. * @nodoc */ this.onChange = () => {}; this.callSetDisabledState = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(CALL_SET_DISABLED_STATE, { optional: true }) ?? setDisabledStateDefault; } /** @nodoc */ ngOnInit() { this._control = this._injector.get(NgControl); this._checkName(); this._registry.add(this._control, this); } /** @nodoc */ ngOnDestroy() { this._registry.remove(this); } /** * Sets the "checked" property value on the radio input element. * @nodoc */ writeValue(value) { this._state = value === this.value; this.setProperty('checked', this._state); } /** * Registers a function called when the control value changes. * @nodoc */ registerOnChange(fn) { this._fn = fn; this.onChange = () => { fn(this.value); this._registry.select(this); }; } /** @nodoc */ setDisabledState(isDisabled) { /** * `setDisabledState` is supposed to be called whenever the disabled state of a control changes, * including upon control creation. However, a longstanding bug caused the method to not fire * when an *enabled* control was attached. This bug was fixed in v15 in #47576. * * This had a side effect: previously, it was possible to instantiate a reactive form control * with `[attr.disabled]=true`, even though the the corresponding control was enabled in the * model. This resulted in a mismatch between the model and the DOM. Now, because * `setDisabledState` is always called, the value in the DOM will be immediately overwritten * with the "correct" enabled value. * * However, the fix also created an exceptional case: radio buttons. Because Reactive Forms * models the entire group of radio buttons as a single `FormControl`, there is no way to * control the disabled state for individual radios, so they can no longer be configured as * disabled. Thus, we keep the old behavior for radio buttons, so that `[attr.disabled]` * continues to work. Specifically, we drop the first call to `setDisabledState` if `disabled` * is `false`, and we are not in legacy mode. */ if (this.setDisabledStateFired || isDisabled || this.callSetDisabledState === 'whenDisabledForLegacyCode') { this.setProperty('disabled', isDisabled); } this.setDisabledStateFired = true; } /** * Sets the "value" on the radio input element and unchecks it. * * @param value */ fireUncheck(value) { this.writeValue(value); } _checkName() { if (this.name && this.formControlName && this.name !== this.formControlName && (typeof ngDevMode === 'undefined' || ngDevMode)) { throwNameError(); } if (!this.name && this.formControlName) this.name = this.formControlName; } } RadioControlValueAccessor.ɵfac = function RadioControlValueAccessor_Factory(t) { return new (t || RadioControlValueAccessor)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](RadioControlRegistry), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.Injector)); }; RadioControlValueAccessor.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: RadioControlValueAccessor, selectors: [["input", "type", "radio", "formControlName", ""], ["input", "type", "radio", "formControl", ""], ["input", "type", "radio", "ngModel", ""]], hostBindings: function RadioControlValueAccessor_HostBindings(rf, ctx) { if (rf & 1) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵlistener"]("change", function RadioControlValueAccessor_change_HostBindingHandler() { return ctx.onChange(); })("blur", function RadioControlValueAccessor_blur_HostBindingHandler() { return ctx.onTouched(); }); } }, inputs: { name: "name", formControlName: "formControlName", value: "value" }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([RADIO_VALUE_ACCESSOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](RadioControlValueAccessor, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]', host: { '(change)': 'onChange()', '(blur)': 'onTouched()' }, providers: [RADIO_VALUE_ACCESSOR] }] }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2 }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef }, { type: RadioControlRegistry }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injector }]; }, { name: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], formControlName: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], value: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); const RANGE_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => RangeValueAccessor), multi: true }; /** * @description * The `ControlValueAccessor` for writing a range value and listening to range input changes. * The value accessor is used by the `FormControlDirective`, `FormControlName`, and `NgModel` * directives. * * @usageNotes * * ### Using a range input with a reactive form * * The following example shows how to use a range input with a reactive form. * * ```ts * const ageControl = new FormControl(); * ``` * * ``` * <input type="range" [formControl]="ageControl"> * ``` * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class RangeValueAccessor extends BuiltInControlValueAccessor { /** * Sets the "value" property on the input element. * @nodoc */ writeValue(value) { this.setProperty('value', parseFloat(value)); } /** * Registers a function called when the control value changes. * @nodoc */ registerOnChange(fn) { this.onChange = value => { fn(value == '' ? null : parseFloat(value)); }; } } RangeValueAccessor.ɵfac = /* @__PURE__ */function () { let ɵRangeValueAccessor_BaseFactory; return function RangeValueAccessor_Factory(t) { return (ɵRangeValueAccessor_BaseFactory || (ɵRangeValueAccessor_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](RangeValueAccessor)))(t || RangeValueAccessor); }; }(); RangeValueAccessor.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: RangeValueAccessor, selectors: [["input", "type", "range", "formControlName", ""], ["input", "type", "range", "formControl", ""], ["input", "type", "range", "ngModel", ""]], hostBindings: function RangeValueAccessor_HostBindings(rf, ctx) { if (rf & 1) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵlistener"]("change", function RangeValueAccessor_change_HostBindingHandler($event) { return ctx.onChange($event.target.value); })("input", function RangeValueAccessor_input_HostBindingHandler($event) { return ctx.onChange($event.target.value); })("blur", function RangeValueAccessor_blur_HostBindingHandler() { return ctx.onTouched(); }); } }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([RANGE_VALUE_ACCESSOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](RangeValueAccessor, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]', host: { '(change)': 'onChange($event.target.value)', '(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()' }, providers: [RANGE_VALUE_ACCESSOR] }] }], null, null); })(); /** * Token to provide to turn off the ngModel warning on formControl and formControlName. */ const NG_MODEL_WITH_FORM_CONTROL_WARNING = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('NgModelWithFormControlWarning'); const formControlBinding = { provide: NgControl, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => FormControlDirective) }; /** * @description * Synchronizes a standalone `FormControl` instance to a form control element. * * Note that support for using the `ngModel` input property and `ngModelChange` event with reactive * form directives was deprecated in Angular v6 and is scheduled for removal in * a future version of Angular. * For details, see [Deprecated features](guide/deprecations#ngmodel-with-reactive-forms). * * @see [Reactive Forms Guide](guide/reactive-forms) * @see `FormControl` * @see `AbstractControl` * * @usageNotes * * The following example shows how to register a standalone control and set its value. * * {@example forms/ts/simpleFormControl/simple_form_control_example.ts region='Component'} * * @ngModule ReactiveFormsModule * @publicApi */ class FormControlDirective extends NgControl { /** * @description * Triggers a warning in dev mode that this input should not be used with reactive forms. */ set isDisabled(isDisabled) { if (typeof ngDevMode === 'undefined' || ngDevMode) { console.warn(disabledAttrWarning); } } constructor(validators, asyncValidators, valueAccessors, _ngModelWarningConfig, callSetDisabledState) { super(); this._ngModelWarningConfig = _ngModelWarningConfig; this.callSetDisabledState = callSetDisabledState; /** @deprecated as of v6 */ this.update = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.EventEmitter(); /** * @description * Instance property used to track whether an ngModel warning has been sent out for this * particular `FormControlDirective` instance. Used to support warning config of "always". * * @internal */ this._ngModelWarningSent = false; this._setValidators(validators); this._setAsyncValidators(asyncValidators); this.valueAccessor = selectValueAccessor(this, valueAccessors); } /** @nodoc */ ngOnChanges(changes) { if (this._isControlChanged(changes)) { const previousForm = changes['form'].previousValue; if (previousForm) { cleanUpControl(previousForm, this, /* validateControlPresenceOnChange */false); } setUpControl(this.form, this, this.callSetDisabledState); this.form.updateValueAndValidity({ emitEvent: false }); } if (isPropertyUpdated(changes, this.viewModel)) { if (typeof ngDevMode === 'undefined' || ngDevMode) { _ngModelWarning('formControl', FormControlDirective, this, this._ngModelWarningConfig); } this.form.setValue(this.model); this.viewModel = this.model; } } /** @nodoc */ ngOnDestroy() { if (this.form) { cleanUpControl(this.form, this, /* validateControlPresenceOnChange */false); } } /** * @description * Returns an array that represents the path from the top-level form to this control. * Each index is the string name of the control on that level. */ get path() { return []; } /** * @description * The `FormControl` bound to this directive. */ get control() { return this.form; } /** * @description * Sets the new value for the view model and emits an `ngModelChange` event. * * @param newValue The new value for the view model. */ viewToModelUpdate(newValue) { this.viewModel = newValue; this.update.emit(newValue); } _isControlChanged(changes) { return changes.hasOwnProperty('form'); } } /** * @description * Static property used to track whether any ngModel warnings have been sent across * all instances of FormControlDirective. Used to support warning config of "once". * * @internal */ FormControlDirective._ngModelWarningSentOnce = false; FormControlDirective.ɵfac = function FormControlDirective_Factory(t) { return new (t || FormControlDirective)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_VALIDATORS, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_ASYNC_VALIDATORS, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_VALUE_ACCESSOR, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_MODEL_WITH_FORM_CONTROL_WARNING, 8), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](CALL_SET_DISABLED_STATE, 8)); }; FormControlDirective.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: FormControlDirective, selectors: [["", "formControl", ""]], inputs: { form: ["formControl", "form"], isDisabled: ["disabled", "isDisabled"], model: ["ngModel", "model"] }, outputs: { update: "ngModelChange" }, exportAs: ["ngForm"], features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([formControlBinding]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"], _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵNgOnChangesFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](FormControlDirective, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[formControl]', providers: [formControlBinding], exportAs: 'ngForm' }] }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_VALIDATORS] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_ASYNC_VALIDATORS] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_VALUE_ACCESSOR] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_MODEL_WITH_FORM_CONTROL_WARNING] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [CALL_SET_DISABLED_STATE] }] }]; }, { form: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['formControl'] }], isDisabled: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['disabled'] }], model: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['ngModel'] }], update: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Output, args: ['ngModelChange'] }] }); })(); const formDirectiveProvider = { provide: ControlContainer, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => FormGroupDirective) }; /** * @description * * Binds an existing `FormGroup` or `FormRecord` to a DOM element. * * This directive accepts an existing `FormGroup` instance. It will then use this * `FormGroup` instance to match any child `FormControl`, `FormGroup`/`FormRecord`, * and `FormArray` instances to child `FormControlName`, `FormGroupName`, * and `FormArrayName` directives. * * @see [Reactive Forms Guide](guide/reactive-forms) * @see `AbstractControl` * * @usageNotes * ### Register Form Group * * The following example registers a `FormGroup` with first name and last name controls, * and listens for the *ngSubmit* event when the button is clicked. * * {@example forms/ts/simpleFormGroup/simple_form_group_example.ts region='Component'} * * @ngModule ReactiveFormsModule * @publicApi */ class FormGroupDirective extends ControlContainer { constructor(validators, asyncValidators, callSetDisabledState) { super(); this.callSetDisabledState = callSetDisabledState; /** * @description * Reports whether the form submission has been triggered. */ this.submitted = false; /** * Callback that should be invoked when controls in FormGroup or FormArray collection change * (added or removed). This callback triggers corresponding DOM updates. */ this._onCollectionChange = () => this._updateDomValue(); /** * @description * Tracks the list of added `FormControlName` instances */ this.directives = []; /** * @description * Tracks the `FormGroup` bound to this directive. */ this.form = null; /** * @description * Emits an event when the form submission has been triggered. */ this.ngSubmit = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.EventEmitter(); this._setValidators(validators); this._setAsyncValidators(asyncValidators); } /** @nodoc */ ngOnChanges(changes) { this._checkFormPresent(); if (changes.hasOwnProperty('form')) { this._updateValidators(); this._updateDomValue(); this._updateRegistrations(); this._oldForm = this.form; } } /** @nodoc */ ngOnDestroy() { if (this.form) { cleanUpValidators(this.form, this); // Currently the `onCollectionChange` callback is rewritten each time the // `_registerOnCollectionChange` function is invoked. The implication is that cleanup should // happen *only* when the `onCollectionChange` callback was set by this directive instance. // Otherwise it might cause overriding a callback of some other directive instances. We should // consider updating this logic later to make it similar to how `onChange` callbacks are // handled, see https://github.com/angular/angular/issues/39732 for additional info. if (this.form._onCollectionChange === this._onCollectionChange) { this.form._registerOnCollectionChange(() => {}); } } } /** * @description * Returns this directive's instance. */ get formDirective() { return this; } /** * @description * Returns the `FormGroup` bound to this directive. */ get control() { return this.form; } /** * @description * Returns an array representing the path to this group. Because this directive * always lives at the top level of a form, it always an empty array. */ get path() { return []; } /** * @description * Method that sets up the control directive in this group, re-calculates its value * and validity, and adds the instance to the internal list of directives. * * @param dir The `FormControlName` directive instance. */ addControl(dir) { const ctrl = this.form.get(dir.path); setUpControl(ctrl, dir, this.callSetDisabledState); ctrl.updateValueAndValidity({ emitEvent: false }); this.directives.push(dir); return ctrl; } /** * @description * Retrieves the `FormControl` instance from the provided `FormControlName` directive * * @param dir The `FormControlName` directive instance. */ getControl(dir) { return this.form.get(dir.path); } /** * @description * Removes the `FormControlName` instance from the internal list of directives * * @param dir The `FormControlName` directive instance. */ removeControl(dir) { cleanUpControl(dir.control || null, dir, /* validateControlPresenceOnChange */false); removeListItem$1(this.directives, dir); } /** * Adds a new `FormGroupName` directive instance to the form. * * @param dir The `FormGroupName` directive instance. */ addFormGroup(dir) { this._setUpFormContainer(dir); } /** * Performs the necessary cleanup when a `FormGroupName` directive instance is removed from the * view. * * @param dir The `FormGroupName` directive instance. */ removeFormGroup(dir) { this._cleanUpFormContainer(dir); } /** * @description * Retrieves the `FormGroup` for a provided `FormGroupName` directive instance * * @param dir The `FormGroupName` directive instance. */ getFormGroup(dir) { return this.form.get(dir.path); } /** * Performs the necessary setup when a `FormArrayName` directive instance is added to the view. * * @param dir The `FormArrayName` directive instance. */ addFormArray(dir) { this._setUpFormContainer(dir); } /** * Performs the necessary cleanup when a `FormArrayName` directive instance is removed from the * view. * * @param dir The `FormArrayName` directive instance. */ removeFormArray(dir) { this._cleanUpFormContainer(dir); } /** * @description * Retrieves the `FormArray` for a provided `FormArrayName` directive instance. * * @param dir The `FormArrayName` directive instance. */ getFormArray(dir) { return this.form.get(dir.path); } /** * Sets the new value for the provided `FormControlName` directive. * * @param dir The `FormControlName` directive instance. * @param value The new value for the directive's control. */ updateModel(dir, value) { const ctrl = this.form.get(dir.path); ctrl.setValue(value); } /** * @description * Method called with the "submit" event is triggered on the form. * Triggers the `ngSubmit` emitter to emit the "submit" event as its payload. * * @param $event The "submit" event object */ onSubmit($event) { this.submitted = true; syncPendingControls(this.form, this.directives); this.ngSubmit.emit($event); // Forms with `method="dialog"` have some special behavior that won't reload the page and that // shouldn't be prevented. Note that we need to null check the `event` and the `target`, because // some internal apps call this method directly with the wrong arguments. return $event?.target?.method === 'dialog'; } /** * @description * Method called when the "reset" event is triggered on the form. */ onReset() { this.resetForm(); } /** * @description * Resets the form to an initial value and resets its submitted status. * * @param value The new value for the form. */ resetForm(value = undefined) { this.form.reset(value); this.submitted = false; } /** @internal */ _updateDomValue() { this.directives.forEach(dir => { const oldCtrl = dir.control; const newCtrl = this.form.get(dir.path); if (oldCtrl !== newCtrl) { // Note: the value of the `dir.control` may not be defined, for example when it's a first // `FormControl` that is added to a `FormGroup` instance (via `addControl` call). cleanUpControl(oldCtrl || null, dir); // Check whether new control at the same location inside the corresponding `FormGroup` is an // instance of `FormControl` and perform control setup only if that's the case. // Note: we don't need to clear the list of directives (`this.directives`) here, it would be // taken care of in the `removeControl` method invoked when corresponding `formControlName` // directive instance is being removed (invoked from `FormControlName.ngOnDestroy`). if (isFormControl(newCtrl)) { setUpControl(newCtrl, dir, this.callSetDisabledState); dir.control = newCtrl; } } }); this.form._updateTreeValidity({ emitEvent: false }); } _setUpFormContainer(dir) { const ctrl = this.form.get(dir.path); setUpFormContainer(ctrl, dir); // NOTE: this operation looks unnecessary in case no new validators were added in // `setUpFormContainer` call. Consider updating this code to match the logic in // `_cleanUpFormContainer` function. ctrl.updateValueAndValidity({ emitEvent: false }); } _cleanUpFormContainer(dir) { if (this.form) { const ctrl = this.form.get(dir.path); if (ctrl) { const isControlUpdated = cleanUpFormContainer(ctrl, dir); if (isControlUpdated) { // Run validity check only in case a control was updated (i.e. view validators were // removed) as removing view validators might cause validity to change. ctrl.updateValueAndValidity({ emitEvent: false }); } } } } _updateRegistrations() { this.form._registerOnCollectionChange(this._onCollectionChange); if (this._oldForm) { this._oldForm._registerOnCollectionChange(() => {}); } } _updateValidators() { setUpValidators(this.form, this); if (this._oldForm) { cleanUpValidators(this._oldForm, this); } } _checkFormPresent() { if (!this.form && (typeof ngDevMode === 'undefined' || ngDevMode)) { throw missingFormException(); } } } FormGroupDirective.ɵfac = function FormGroupDirective_Factory(t) { return new (t || FormGroupDirective)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_VALIDATORS, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_ASYNC_VALIDATORS, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](CALL_SET_DISABLED_STATE, 8)); }; FormGroupDirective.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: FormGroupDirective, selectors: [["", "formGroup", ""]], hostBindings: function FormGroupDirective_HostBindings(rf, ctx) { if (rf & 1) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵlistener"]("submit", function FormGroupDirective_submit_HostBindingHandler($event) { return ctx.onSubmit($event); })("reset", function FormGroupDirective_reset_HostBindingHandler() { return ctx.onReset(); }); } }, inputs: { form: ["formGroup", "form"] }, outputs: { ngSubmit: "ngSubmit" }, exportAs: ["ngForm"], features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([formDirectiveProvider]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"], _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵNgOnChangesFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](FormGroupDirective, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[formGroup]', providers: [formDirectiveProvider], host: { '(submit)': 'onSubmit($event)', '(reset)': 'onReset()' }, exportAs: 'ngForm' }] }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_VALIDATORS] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_ASYNC_VALIDATORS] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [CALL_SET_DISABLED_STATE] }] }]; }, { form: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['formGroup'] }], ngSubmit: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Output }] }); })(); const formGroupNameProvider = { provide: ControlContainer, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => FormGroupName) }; /** * @description * * Syncs a nested `FormGroup` or `FormRecord` to a DOM element. * * This directive can only be used with a parent `FormGroupDirective`. * * It accepts the string name of the nested `FormGroup` or `FormRecord` to link, and * looks for a `FormGroup` or `FormRecord` registered with that name in the parent * `FormGroup` instance you passed into `FormGroupDirective`. * * Use nested form groups to validate a sub-group of a * form separately from the rest or to group the values of certain * controls into their own nested object. * * @see [Reactive Forms Guide](guide/reactive-forms) * * @usageNotes * * ### Access the group by name * * The following example uses the `AbstractControl.get` method to access the * associated `FormGroup` * * ```ts * this.form.get('name'); * ``` * * ### Access individual controls in the group * * The following example uses the `AbstractControl.get` method to access * individual controls within the group using dot syntax. * * ```ts * this.form.get('name.first'); * ``` * * ### Register a nested `FormGroup`. * * The following example registers a nested *name* `FormGroup` within an existing `FormGroup`, * and provides methods to retrieve the nested `FormGroup` and individual controls. * * {@example forms/ts/nestedFormGroup/nested_form_group_example.ts region='Component'} * * @ngModule ReactiveFormsModule * @publicApi */ class FormGroupName extends AbstractFormGroupDirective { constructor(parent, validators, asyncValidators) { super(); this._parent = parent; this._setValidators(validators); this._setAsyncValidators(asyncValidators); } /** @internal */ _checkParentType() { if (_hasInvalidParent(this._parent) && (typeof ngDevMode === 'undefined' || ngDevMode)) { throw groupParentException(); } } } FormGroupName.ɵfac = function FormGroupName_Factory(t) { return new (t || FormGroupName)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](ControlContainer, 13), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_VALIDATORS, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_ASYNC_VALIDATORS, 10)); }; FormGroupName.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: FormGroupName, selectors: [["", "formGroupName", ""]], inputs: { name: ["formGroupName", "name"] }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([formGroupNameProvider]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](FormGroupName, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[formGroupName]', providers: [formGroupNameProvider] }] }], function () { return [{ type: ControlContainer, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Host }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.SkipSelf }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_VALIDATORS] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_ASYNC_VALIDATORS] }] }]; }, { name: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['formGroupName'] }] }); })(); const formArrayNameProvider = { provide: ControlContainer, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => FormArrayName) }; /** * @description * * Syncs a nested `FormArray` to a DOM element. * * This directive is designed to be used with a parent `FormGroupDirective` (selector: * `[formGroup]`). * * It accepts the string name of the nested `FormArray` you want to link, and * will look for a `FormArray` registered with that name in the parent * `FormGroup` instance you passed into `FormGroupDirective`. * * @see [Reactive Forms Guide](guide/reactive-forms) * @see `AbstractControl` * * @usageNotes * * ### Example * * {@example forms/ts/nestedFormArray/nested_form_array_example.ts region='Component'} * * @ngModule ReactiveFormsModule * @publicApi */ class FormArrayName extends ControlContainer { constructor(parent, validators, asyncValidators) { super(); this._parent = parent; this._setValidators(validators); this._setAsyncValidators(asyncValidators); } /** * A lifecycle method called when the directive's inputs are initialized. For internal use only. * @throws If the directive does not have a valid parent. * @nodoc */ ngOnInit() { this._checkParentType(); this.formDirective.addFormArray(this); } /** * A lifecycle method called before the directive's instance is destroyed. For internal use only. * @nodoc */ ngOnDestroy() { if (this.formDirective) { this.formDirective.removeFormArray(this); } } /** * @description * The `FormArray` bound to this directive. */ get control() { return this.formDirective.getFormArray(this); } /** * @description * The top-level directive for this group if present, otherwise null. */ get formDirective() { return this._parent ? this._parent.formDirective : null; } /** * @description * Returns an array that represents the path from the top-level form to this control. * Each index is the string name of the control on that level. */ get path() { return controlPath(this.name == null ? this.name : this.name.toString(), this._parent); } _checkParentType() { if (_hasInvalidParent(this._parent) && (typeof ngDevMode === 'undefined' || ngDevMode)) { throw arrayParentException(); } } } FormArrayName.ɵfac = function FormArrayName_Factory(t) { return new (t || FormArrayName)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](ControlContainer, 13), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_VALIDATORS, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_ASYNC_VALIDATORS, 10)); }; FormArrayName.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: FormArrayName, selectors: [["", "formArrayName", ""]], inputs: { name: ["formArrayName", "name"] }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([formArrayNameProvider]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](FormArrayName, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[formArrayName]', providers: [formArrayNameProvider] }] }], function () { return [{ type: ControlContainer, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Host }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.SkipSelf }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_VALIDATORS] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_ASYNC_VALIDATORS] }] }]; }, { name: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['formArrayName'] }] }); })(); function _hasInvalidParent(parent) { return !(parent instanceof FormGroupName) && !(parent instanceof FormGroupDirective) && !(parent instanceof FormArrayName); } const controlNameBinding = { provide: NgControl, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => FormControlName) }; /** * @description * Syncs a `FormControl` in an existing `FormGroup` to a form control * element by name. * * @see [Reactive Forms Guide](guide/reactive-forms) * @see `FormControl` * @see `AbstractControl` * * @usageNotes * * ### Register `FormControl` within a group * * The following example shows how to register multiple form controls within a form group * and set their value. * * {@example forms/ts/simpleFormGroup/simple_form_group_example.ts region='Component'} * * To see `formControlName` examples with different form control types, see: * * * Radio buttons: `RadioControlValueAccessor` * * Selects: `SelectControlValueAccessor` * * ### Use with ngModel is deprecated * * Support for using the `ngModel` input property and `ngModelChange` event with reactive * form directives has been deprecated in Angular v6 and is scheduled for removal in * a future version of Angular. * * For details, see [Deprecated features](guide/deprecations#ngmodel-with-reactive-forms). * * @ngModule ReactiveFormsModule * @publicApi */ class FormControlName extends NgControl { /** * @description * Triggers a warning in dev mode that this input should not be used with reactive forms. */ set isDisabled(isDisabled) { if (typeof ngDevMode === 'undefined' || ngDevMode) { console.warn(disabledAttrWarning); } } constructor(parent, validators, asyncValidators, valueAccessors, _ngModelWarningConfig) { super(); this._ngModelWarningConfig = _ngModelWarningConfig; this._added = false; /** @deprecated as of v6 */ this.update = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.EventEmitter(); /** * @description * Instance property used to track whether an ngModel warning has been sent out for this * particular FormControlName instance. Used to support warning config of "always". * * @internal */ this._ngModelWarningSent = false; this._parent = parent; this._setValidators(validators); this._setAsyncValidators(asyncValidators); this.valueAccessor = selectValueAccessor(this, valueAccessors); } /** @nodoc */ ngOnChanges(changes) { if (!this._added) this._setUpControl(); if (isPropertyUpdated(changes, this.viewModel)) { if (typeof ngDevMode === 'undefined' || ngDevMode) { _ngModelWarning('formControlName', FormControlName, this, this._ngModelWarningConfig); } this.viewModel = this.model; this.formDirective.updateModel(this, this.model); } } /** @nodoc */ ngOnDestroy() { if (this.formDirective) { this.formDirective.removeControl(this); } } /** * @description * Sets the new value for the view model and emits an `ngModelChange` event. * * @param newValue The new value for the view model. */ viewToModelUpdate(newValue) { this.viewModel = newValue; this.update.emit(newValue); } /** * @description * Returns an array that represents the path from the top-level form to this control. * Each index is the string name of the control on that level. */ get path() { return controlPath(this.name == null ? this.name : this.name.toString(), this._parent); } /** * @description * The top-level directive for this group if present, otherwise null. */ get formDirective() { return this._parent ? this._parent.formDirective : null; } _checkParentType() { if (typeof ngDevMode === 'undefined' || ngDevMode) { if (!(this._parent instanceof FormGroupName) && this._parent instanceof AbstractFormGroupDirective) { throw ngModelGroupException(); } else if (!(this._parent instanceof FormGroupName) && !(this._parent instanceof FormGroupDirective) && !(this._parent instanceof FormArrayName)) { throw controlParentException(); } } } _setUpControl() { this._checkParentType(); this.control = this.formDirective.addControl(this); this._added = true; } } /** * @description * Static property used to track whether any ngModel warnings have been sent across * all instances of FormControlName. Used to support warning config of "once". * * @internal */ FormControlName._ngModelWarningSentOnce = false; FormControlName.ɵfac = function FormControlName_Factory(t) { return new (t || FormControlName)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](ControlContainer, 13), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_VALIDATORS, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_ASYNC_VALIDATORS, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_VALUE_ACCESSOR, 10), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](NG_MODEL_WITH_FORM_CONTROL_WARNING, 8)); }; FormControlName.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: FormControlName, selectors: [["", "formControlName", ""]], inputs: { name: ["formControlName", "name"], isDisabled: ["disabled", "isDisabled"], model: ["ngModel", "model"] }, outputs: { update: "ngModelChange" }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([controlNameBinding]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"], _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵNgOnChangesFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](FormControlName, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[formControlName]', providers: [controlNameBinding] }] }], function () { return [{ type: ControlContainer, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Host }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.SkipSelf }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_VALIDATORS] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_ASYNC_VALIDATORS] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Self }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_VALUE_ACCESSOR] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [NG_MODEL_WITH_FORM_CONTROL_WARNING] }] }]; }, { name: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['formControlName'] }], isDisabled: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['disabled'] }], model: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['ngModel'] }], update: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Output, args: ['ngModelChange'] }] }); })(); const SELECT_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => SelectControlValueAccessor), multi: true }; function _buildValueString$1(id, value) { if (id == null) return `${value}`; if (value && typeof value === 'object') value = 'Object'; return `${id}: ${value}`.slice(0, 50); } function _extractId$1(valueString) { return valueString.split(':')[0]; } /** * @description * The `ControlValueAccessor` for writing select control values and listening to select control * changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and * `NgModel` directives. * * @usageNotes * * ### Using select controls in a reactive form * * The following examples show how to use a select control in a reactive form. * * {@example forms/ts/reactiveSelectControl/reactive_select_control_example.ts region='Component'} * * ### Using select controls in a template-driven form * * To use a select in a template-driven form, simply add an `ngModel` and a `name` * attribute to the main `<select>` tag. * * {@example forms/ts/selectControl/select_control_example.ts region='Component'} * * ### Customizing option selection * * Angular uses object identity to select option. It's possible for the identities of items * to change while the data does not. This can happen, for example, if the items are produced * from an RPC to the server, and that RPC is re-run. Even if the data hasn't changed, the * second response will produce objects with different identities. * * To customize the default option comparison algorithm, `<select>` supports `compareWith` input. * `compareWith` takes a **function** which has two arguments: `option1` and `option2`. * If `compareWith` is given, Angular selects option by the return value of the function. * * ```ts * const selectedCountriesControl = new FormControl(); * ``` * * ``` * <select [compareWith]="compareFn" [formControl]="selectedCountriesControl"> * <option *ngFor="let country of countries" [ngValue]="country"> * {{country.name}} * </option> * </select> * * compareFn(c1: Country, c2: Country): boolean { * return c1 && c2 ? c1.id === c2.id : c1 === c2; * } * ``` * * **Note:** We listen to the 'change' event because 'input' events aren't fired * for selects in IE, see: * https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event#browser_compatibility * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class SelectControlValueAccessor extends BuiltInControlValueAccessor { constructor() { super(...arguments); /** @internal */ this._optionMap = new Map(); /** @internal */ this._idCounter = 0; this._compareWith = Object.is; } /** * @description * Tracks the option comparison algorithm for tracking identities when * checking for changes. */ set compareWith(fn) { if (typeof fn !== 'function' && (typeof ngDevMode === 'undefined' || ngDevMode)) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1201 /* RuntimeErrorCode.COMPAREWITH_NOT_A_FN */, `compareWith must be a function, but received ${JSON.stringify(fn)}`); } this._compareWith = fn; } /** * Sets the "value" property on the select element. * @nodoc */ writeValue(value) { this.value = value; const id = this._getOptionId(value); const valueString = _buildValueString$1(id, value); this.setProperty('value', valueString); } /** * Registers a function called when the control value changes. * @nodoc */ registerOnChange(fn) { this.onChange = valueString => { this.value = this._getOptionValue(valueString); fn(this.value); }; } /** @internal */ _registerOption() { return (this._idCounter++).toString(); } /** @internal */ _getOptionId(value) { for (const id of Array.from(this._optionMap.keys())) { if (this._compareWith(this._optionMap.get(id), value)) return id; } return null; } /** @internal */ _getOptionValue(valueString) { const id = _extractId$1(valueString); return this._optionMap.has(id) ? this._optionMap.get(id) : valueString; } } SelectControlValueAccessor.ɵfac = /* @__PURE__ */function () { let ɵSelectControlValueAccessor_BaseFactory; return function SelectControlValueAccessor_Factory(t) { return (ɵSelectControlValueAccessor_BaseFactory || (ɵSelectControlValueAccessor_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](SelectControlValueAccessor)))(t || SelectControlValueAccessor); }; }(); SelectControlValueAccessor.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: SelectControlValueAccessor, selectors: [["select", "formControlName", "", 3, "multiple", ""], ["select", "formControl", "", 3, "multiple", ""], ["select", "ngModel", "", 3, "multiple", ""]], hostBindings: function SelectControlValueAccessor_HostBindings(rf, ctx) { if (rf & 1) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵlistener"]("change", function SelectControlValueAccessor_change_HostBindingHandler($event) { return ctx.onChange($event.target.value); })("blur", function SelectControlValueAccessor_blur_HostBindingHandler() { return ctx.onTouched(); }); } }, inputs: { compareWith: "compareWith" }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([SELECT_VALUE_ACCESSOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](SelectControlValueAccessor, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]', host: { '(change)': 'onChange($event.target.value)', '(blur)': 'onTouched()' }, providers: [SELECT_VALUE_ACCESSOR] }] }], null, { compareWith: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /** * @description * Marks `<option>` as dynamic, so Angular can be notified when options change. * * @see `SelectControlValueAccessor` * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class NgSelectOption { constructor(_element, _renderer, _select) { this._element = _element; this._renderer = _renderer; this._select = _select; if (this._select) this.id = this._select._registerOption(); } /** * @description * Tracks the value bound to the option element. Unlike the value binding, * ngValue supports binding to objects. */ set ngValue(value) { if (this._select == null) return; this._select._optionMap.set(this.id, value); this._setElementValue(_buildValueString$1(this.id, value)); this._select.writeValue(this._select.value); } /** * @description * Tracks simple string values bound to the option element. * For objects, use the `ngValue` input binding. */ set value(value) { this._setElementValue(value); if (this._select) this._select.writeValue(this._select.value); } /** @internal */ _setElementValue(value) { this._renderer.setProperty(this._element.nativeElement, 'value', value); } /** @nodoc */ ngOnDestroy() { if (this._select) { this._select._optionMap.delete(this.id); this._select.writeValue(this._select.value); } } } NgSelectOption.ɵfac = function NgSelectOption_Factory(t) { return new (t || NgSelectOption)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](SelectControlValueAccessor, 9)); }; NgSelectOption.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: NgSelectOption, selectors: [["option"]], inputs: { ngValue: "ngValue", value: "value" } }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NgSelectOption, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'option' }] }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2 }, { type: SelectControlValueAccessor, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Host }] }]; }, { ngValue: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['ngValue'] }], value: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['value'] }] }); })(); const SELECT_MULTIPLE_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => SelectMultipleControlValueAccessor), multi: true }; function _buildValueString(id, value) { if (id == null) return `${value}`; if (typeof value === 'string') value = `'${value}'`; if (value && typeof value === 'object') value = 'Object'; return `${id}: ${value}`.slice(0, 50); } function _extractId(valueString) { return valueString.split(':')[0]; } /** Mock interface for HTMLCollection */ class HTMLCollection {} /** * @description * The `ControlValueAccessor` for writing multi-select control values and listening to multi-select * control changes. The value accessor is used by the `FormControlDirective`, `FormControlName`, and * `NgModel` directives. * * @see `SelectControlValueAccessor` * * @usageNotes * * ### Using a multi-select control * * The follow example shows you how to use a multi-select control with a reactive form. * * ```ts * const countryControl = new FormControl(); * ``` * * ``` * <select multiple name="countries" [formControl]="countryControl"> * <option *ngFor="let country of countries" [ngValue]="country"> * {{ country.name }} * </option> * </select> * ``` * * ### Customizing option selection * * To customize the default option comparison algorithm, `<select>` supports `compareWith` input. * See the `SelectControlValueAccessor` for usage. * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class SelectMultipleControlValueAccessor extends BuiltInControlValueAccessor { constructor() { super(...arguments); /** @internal */ this._optionMap = new Map(); /** @internal */ this._idCounter = 0; this._compareWith = Object.is; } /** * @description * Tracks the option comparison algorithm for tracking identities when * checking for changes. */ set compareWith(fn) { if (typeof fn !== 'function' && (typeof ngDevMode === 'undefined' || ngDevMode)) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](1201 /* RuntimeErrorCode.COMPAREWITH_NOT_A_FN */, `compareWith must be a function, but received ${JSON.stringify(fn)}`); } this._compareWith = fn; } /** * Sets the "value" property on one or of more of the select's options. * @nodoc */ writeValue(value) { this.value = value; let optionSelectedStateSetter; if (Array.isArray(value)) { // convert values to ids const ids = value.map(v => this._getOptionId(v)); optionSelectedStateSetter = (opt, o) => { opt._setSelected(ids.indexOf(o.toString()) > -1); }; } else { optionSelectedStateSetter = (opt, o) => { opt._setSelected(false); }; } this._optionMap.forEach(optionSelectedStateSetter); } /** * Registers a function called when the control value changes * and writes an array of the selected options. * @nodoc */ registerOnChange(fn) { this.onChange = element => { const selected = []; const selectedOptions = element.selectedOptions; if (selectedOptions !== undefined) { const options = selectedOptions; for (let i = 0; i < options.length; i++) { const opt = options[i]; const val = this._getOptionValue(opt.value); selected.push(val); } } // Degrade to use `options` when `selectedOptions` property is not available. // Note: the `selectedOptions` is available in all supported browsers, but the Domino lib // doesn't have it currently, see https://github.com/fgnass/domino/issues/177. else { const options = element.options; for (let i = 0; i < options.length; i++) { const opt = options[i]; if (opt.selected) { const val = this._getOptionValue(opt.value); selected.push(val); } } } this.value = selected; fn(selected); }; } /** @internal */ _registerOption(value) { const id = (this._idCounter++).toString(); this._optionMap.set(id, value); return id; } /** @internal */ _getOptionId(value) { for (const id of Array.from(this._optionMap.keys())) { if (this._compareWith(this._optionMap.get(id)._value, value)) return id; } return null; } /** @internal */ _getOptionValue(valueString) { const id = _extractId(valueString); return this._optionMap.has(id) ? this._optionMap.get(id)._value : valueString; } } SelectMultipleControlValueAccessor.ɵfac = /* @__PURE__ */function () { let ɵSelectMultipleControlValueAccessor_BaseFactory; return function SelectMultipleControlValueAccessor_Factory(t) { return (ɵSelectMultipleControlValueAccessor_BaseFactory || (ɵSelectMultipleControlValueAccessor_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](SelectMultipleControlValueAccessor)))(t || SelectMultipleControlValueAccessor); }; }(); SelectMultipleControlValueAccessor.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: SelectMultipleControlValueAccessor, selectors: [["select", "multiple", "", "formControlName", ""], ["select", "multiple", "", "formControl", ""], ["select", "multiple", "", "ngModel", ""]], hostBindings: function SelectMultipleControlValueAccessor_HostBindings(rf, ctx) { if (rf & 1) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵlistener"]("change", function SelectMultipleControlValueAccessor_change_HostBindingHandler($event) { return ctx.onChange($event.target); })("blur", function SelectMultipleControlValueAccessor_blur_HostBindingHandler() { return ctx.onTouched(); }); } }, inputs: { compareWith: "compareWith" }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([SELECT_MULTIPLE_VALUE_ACCESSOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](SelectMultipleControlValueAccessor, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'select[multiple][formControlName],select[multiple][formControl],select[multiple][ngModel]', host: { '(change)': 'onChange($event.target)', '(blur)': 'onTouched()' }, providers: [SELECT_MULTIPLE_VALUE_ACCESSOR] }] }], null, { compareWith: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /** * @description * Marks `<option>` as dynamic, so Angular can be notified when options change. * * @see `SelectMultipleControlValueAccessor` * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class ɵNgSelectMultipleOption { constructor(_element, _renderer, _select) { this._element = _element; this._renderer = _renderer; this._select = _select; if (this._select) { this.id = this._select._registerOption(this); } } /** * @description * Tracks the value bound to the option element. Unlike the value binding, * ngValue supports binding to objects. */ set ngValue(value) { if (this._select == null) return; this._value = value; this._setElementValue(_buildValueString(this.id, value)); this._select.writeValue(this._select.value); } /** * @description * Tracks simple string values bound to the option element. * For objects, use the `ngValue` input binding. */ set value(value) { if (this._select) { this._value = value; this._setElementValue(_buildValueString(this.id, value)); this._select.writeValue(this._select.value); } else { this._setElementValue(value); } } /** @internal */ _setElementValue(value) { this._renderer.setProperty(this._element.nativeElement, 'value', value); } /** @internal */ _setSelected(selected) { this._renderer.setProperty(this._element.nativeElement, 'selected', selected); } /** @nodoc */ ngOnDestroy() { if (this._select) { this._select._optionMap.delete(this.id); this._select.writeValue(this._select.value); } } } ɵNgSelectMultipleOption.ɵfac = function ɵNgSelectMultipleOption_Factory(t) { return new (t || ɵNgSelectMultipleOption)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](SelectMultipleControlValueAccessor, 9)); }; ɵNgSelectMultipleOption.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: ɵNgSelectMultipleOption, selectors: [["option"]], inputs: { ngValue: "ngValue", value: "value" } }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](ɵNgSelectMultipleOption, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'option' }] }], function () { return [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2 }, { type: SelectMultipleControlValueAccessor, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Host }] }]; }, { ngValue: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['ngValue'] }], value: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input, args: ['value'] }] }); })(); /** * Method that updates string to integer if not already a number * * @param value The value to convert to integer. * @returns value of parameter converted to number or integer. */ function toInteger(value) { return typeof value === 'number' ? value : parseInt(value, 10); } /** * Method that ensures that provided value is a float (and converts it to float if needed). * * @param value The value to convert to float. * @returns value of parameter converted to number or float. */ function toFloat(value) { return typeof value === 'number' ? value : parseFloat(value); } /** * A base class for Validator-based Directives. The class contains common logic shared across such * Directives. * * For internal use only, this class is not intended for use outside of the Forms package. */ class AbstractValidatorDirective { constructor() { this._validator = nullValidator; } /** @nodoc */ ngOnChanges(changes) { if (this.inputName in changes) { const input = this.normalizeInput(changes[this.inputName].currentValue); this._enabled = this.enabled(input); this._validator = this._enabled ? this.createValidator(input) : nullValidator; if (this._onChange) { this._onChange(); } } } /** @nodoc */ validate(control) { return this._validator(control); } /** @nodoc */ registerOnValidatorChange(fn) { this._onChange = fn; } /** * @description * Determines whether this validator should be active or not based on an input. * Base class implementation checks whether an input is defined (if the value is different from * `null` and `undefined`). Validator classes that extend this base class can override this * function with the logic specific to a particular validator directive. */ enabled(input) { return input != null /* both `null` and `undefined` */; } } AbstractValidatorDirective.ɵfac = function AbstractValidatorDirective_Factory(t) { return new (t || AbstractValidatorDirective)(); }; AbstractValidatorDirective.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: AbstractValidatorDirective, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵNgOnChangesFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](AbstractValidatorDirective, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive }], null, null); })(); /** * @description * Provider which adds `MaxValidator` to the `NG_VALIDATORS` multi-provider list. */ const MAX_VALIDATOR = { provide: NG_VALIDATORS, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => MaxValidator), multi: true }; /** * A directive which installs the {@link MaxValidator} for any `formControlName`, * `formControl`, or control with `ngModel` that also has a `max` attribute. * * @see [Form Validation](guide/form-validation) * * @usageNotes * * ### Adding a max validator * * The following example shows how to add a max validator to an input attached to an * ngModel binding. * * ```html * <input type="number" ngModel max="4"> * ``` * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class MaxValidator extends AbstractValidatorDirective { constructor() { super(...arguments); /** @internal */ this.inputName = 'max'; /** @internal */ this.normalizeInput = input => toFloat(input); /** @internal */ this.createValidator = max => maxValidator(max); } } MaxValidator.ɵfac = /* @__PURE__ */function () { let ɵMaxValidator_BaseFactory; return function MaxValidator_Factory(t) { return (ɵMaxValidator_BaseFactory || (ɵMaxValidator_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](MaxValidator)))(t || MaxValidator); }; }(); MaxValidator.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: MaxValidator, selectors: [["input", "type", "number", "max", "", "formControlName", ""], ["input", "type", "number", "max", "", "formControl", ""], ["input", "type", "number", "max", "", "ngModel", ""]], hostVars: 1, hostBindings: function MaxValidator_HostBindings(rf, ctx) { if (rf & 2) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵattribute"]("max", ctx._enabled ? ctx.max : null); } }, inputs: { max: "max" }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([MAX_VALIDATOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](MaxValidator, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]', providers: [MAX_VALIDATOR], host: { '[attr.max]': '_enabled ? max : null' } }] }], null, { max: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /** * @description * Provider which adds `MinValidator` to the `NG_VALIDATORS` multi-provider list. */ const MIN_VALIDATOR = { provide: NG_VALIDATORS, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => MinValidator), multi: true }; /** * A directive which installs the {@link MinValidator} for any `formControlName`, * `formControl`, or control with `ngModel` that also has a `min` attribute. * * @see [Form Validation](guide/form-validation) * * @usageNotes * * ### Adding a min validator * * The following example shows how to add a min validator to an input attached to an * ngModel binding. * * ```html * <input type="number" ngModel min="4"> * ``` * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class MinValidator extends AbstractValidatorDirective { constructor() { super(...arguments); /** @internal */ this.inputName = 'min'; /** @internal */ this.normalizeInput = input => toFloat(input); /** @internal */ this.createValidator = min => minValidator(min); } } MinValidator.ɵfac = /* @__PURE__ */function () { let ɵMinValidator_BaseFactory; return function MinValidator_Factory(t) { return (ɵMinValidator_BaseFactory || (ɵMinValidator_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](MinValidator)))(t || MinValidator); }; }(); MinValidator.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: MinValidator, selectors: [["input", "type", "number", "min", "", "formControlName", ""], ["input", "type", "number", "min", "", "formControl", ""], ["input", "type", "number", "min", "", "ngModel", ""]], hostVars: 1, hostBindings: function MinValidator_HostBindings(rf, ctx) { if (rf & 2) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵattribute"]("min", ctx._enabled ? ctx.min : null); } }, inputs: { min: "min" }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([MIN_VALIDATOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](MinValidator, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]', providers: [MIN_VALIDATOR], host: { '[attr.min]': '_enabled ? min : null' } }] }], null, { min: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /** * @description * Provider which adds `RequiredValidator` to the `NG_VALIDATORS` multi-provider list. */ const REQUIRED_VALIDATOR = { provide: NG_VALIDATORS, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => RequiredValidator), multi: true }; /** * @description * Provider which adds `CheckboxRequiredValidator` to the `NG_VALIDATORS` multi-provider list. */ const CHECKBOX_REQUIRED_VALIDATOR = { provide: NG_VALIDATORS, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => CheckboxRequiredValidator), multi: true }; /** * @description * A directive that adds the `required` validator to any controls marked with the * `required` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list. * * @see [Form Validation](guide/form-validation) * * @usageNotes * * ### Adding a required validator using template-driven forms * * ``` * <input name="fullName" ngModel required> * ``` * * @ngModule FormsModule * @ngModule ReactiveFormsModule * @publicApi */ class RequiredValidator extends AbstractValidatorDirective { constructor() { super(...arguments); /** @internal */ this.inputName = 'required'; /** @internal */ this.normalizeInput = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵcoerceToBoolean"]; /** @internal */ this.createValidator = input => requiredValidator; } /** @nodoc */ enabled(input) { return input; } } RequiredValidator.ɵfac = /* @__PURE__ */function () { let ɵRequiredValidator_BaseFactory; return function RequiredValidator_Factory(t) { return (ɵRequiredValidator_BaseFactory || (ɵRequiredValidator_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](RequiredValidator)))(t || RequiredValidator); }; }(); RequiredValidator.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: RequiredValidator, selectors: [["", "required", "", "formControlName", "", 3, "type", "checkbox"], ["", "required", "", "formControl", "", 3, "type", "checkbox"], ["", "required", "", "ngModel", "", 3, "type", "checkbox"]], hostVars: 1, hostBindings: function RequiredValidator_HostBindings(rf, ctx) { if (rf & 2) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵattribute"]("required", ctx._enabled ? "" : null); } }, inputs: { required: "required" }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([REQUIRED_VALIDATOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](RequiredValidator, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: ':not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]', providers: [REQUIRED_VALIDATOR], host: { '[attr.required]': '_enabled ? "" : null' } }] }], null, { required: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /** * A Directive that adds the `required` validator to checkbox controls marked with the * `required` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list. * * @see [Form Validation](guide/form-validation) * * @usageNotes * * ### Adding a required checkbox validator using template-driven forms * * The following example shows how to add a checkbox required validator to an input attached to an * ngModel binding. * * ``` * <input type="checkbox" name="active" ngModel required> * ``` * * @publicApi * @ngModule FormsModule * @ngModule ReactiveFormsModule */ class CheckboxRequiredValidator extends RequiredValidator { constructor() { super(...arguments); /** @internal */ this.createValidator = input => requiredTrueValidator; } } CheckboxRequiredValidator.ɵfac = /* @__PURE__ */function () { let ɵCheckboxRequiredValidator_BaseFactory; return function CheckboxRequiredValidator_Factory(t) { return (ɵCheckboxRequiredValidator_BaseFactory || (ɵCheckboxRequiredValidator_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](CheckboxRequiredValidator)))(t || CheckboxRequiredValidator); }; }(); CheckboxRequiredValidator.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: CheckboxRequiredValidator, selectors: [["input", "type", "checkbox", "required", "", "formControlName", ""], ["input", "type", "checkbox", "required", "", "formControl", ""], ["input", "type", "checkbox", "required", "", "ngModel", ""]], hostVars: 1, hostBindings: function CheckboxRequiredValidator_HostBindings(rf, ctx) { if (rf & 2) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵattribute"]("required", ctx._enabled ? "" : null); } }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([CHECKBOX_REQUIRED_VALIDATOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](CheckboxRequiredValidator, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'input[type=checkbox][required][formControlName],input[type=checkbox][required][formControl],input[type=checkbox][required][ngModel]', providers: [CHECKBOX_REQUIRED_VALIDATOR], host: { '[attr.required]': '_enabled ? "" : null' } }] }], null, null); })(); /** * @description * Provider which adds `EmailValidator` to the `NG_VALIDATORS` multi-provider list. */ const EMAIL_VALIDATOR = { provide: NG_VALIDATORS, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => EmailValidator), multi: true }; /** * A directive that adds the `email` validator to controls marked with the * `email` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list. * * The email validation is based on the WHATWG HTML specification with some enhancements to * incorporate more RFC rules. More information can be found on the [Validators.email * page](api/forms/Validators#email). * * @see [Form Validation](guide/form-validation) * * @usageNotes * * ### Adding an email validator * * The following example shows how to add an email validator to an input attached to an ngModel * binding. * * ``` * <input type="email" name="email" ngModel email> * <input type="email" name="email" ngModel email="true"> * <input type="email" name="email" ngModel [email]="true"> * ``` * * @publicApi * @ngModule FormsModule * @ngModule ReactiveFormsModule */ class EmailValidator extends AbstractValidatorDirective { constructor() { super(...arguments); /** @internal */ this.inputName = 'email'; /** @internal */ this.normalizeInput = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵcoerceToBoolean"]; /** @internal */ this.createValidator = input => emailValidator; } /** @nodoc */ enabled(input) { return input; } } EmailValidator.ɵfac = /* @__PURE__ */function () { let ɵEmailValidator_BaseFactory; return function EmailValidator_Factory(t) { return (ɵEmailValidator_BaseFactory || (ɵEmailValidator_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](EmailValidator)))(t || EmailValidator); }; }(); EmailValidator.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: EmailValidator, selectors: [["", "email", "", "formControlName", ""], ["", "email", "", "formControl", ""], ["", "email", "", "ngModel", ""]], inputs: { email: "email" }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([EMAIL_VALIDATOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](EmailValidator, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[email][formControlName],[email][formControl],[email][ngModel]', providers: [EMAIL_VALIDATOR] }] }], null, { email: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /** * @description * Provider which adds `MinLengthValidator` to the `NG_VALIDATORS` multi-provider list. */ const MIN_LENGTH_VALIDATOR = { provide: NG_VALIDATORS, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => MinLengthValidator), multi: true }; /** * A directive that adds minimum length validation to controls marked with the * `minlength` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list. * * @see [Form Validation](guide/form-validation) * * @usageNotes * * ### Adding a minimum length validator * * The following example shows how to add a minimum length validator to an input attached to an * ngModel binding. * * ```html * <input name="firstName" ngModel minlength="4"> * ``` * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class MinLengthValidator extends AbstractValidatorDirective { constructor() { super(...arguments); /** @internal */ this.inputName = 'minlength'; /** @internal */ this.normalizeInput = input => toInteger(input); /** @internal */ this.createValidator = minlength => minLengthValidator(minlength); } } MinLengthValidator.ɵfac = /* @__PURE__ */function () { let ɵMinLengthValidator_BaseFactory; return function MinLengthValidator_Factory(t) { return (ɵMinLengthValidator_BaseFactory || (ɵMinLengthValidator_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](MinLengthValidator)))(t || MinLengthValidator); }; }(); MinLengthValidator.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: MinLengthValidator, selectors: [["", "minlength", "", "formControlName", ""], ["", "minlength", "", "formControl", ""], ["", "minlength", "", "ngModel", ""]], hostVars: 1, hostBindings: function MinLengthValidator_HostBindings(rf, ctx) { if (rf & 2) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵattribute"]("minlength", ctx._enabled ? ctx.minlength : null); } }, inputs: { minlength: "minlength" }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([MIN_LENGTH_VALIDATOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](MinLengthValidator, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[minlength][formControlName],[minlength][formControl],[minlength][ngModel]', providers: [MIN_LENGTH_VALIDATOR], host: { '[attr.minlength]': '_enabled ? minlength : null' } }] }], null, { minlength: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /** * @description * Provider which adds `MaxLengthValidator` to the `NG_VALIDATORS` multi-provider list. */ const MAX_LENGTH_VALIDATOR = { provide: NG_VALIDATORS, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => MaxLengthValidator), multi: true }; /** * A directive that adds max length validation to controls marked with the * `maxlength` attribute. The directive is provided with the `NG_VALIDATORS` multi-provider list. * * @see [Form Validation](guide/form-validation) * * @usageNotes * * ### Adding a maximum length validator * * The following example shows how to add a maximum length validator to an input attached to an * ngModel binding. * * ```html * <input name="firstName" ngModel maxlength="25"> * ``` * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class MaxLengthValidator extends AbstractValidatorDirective { constructor() { super(...arguments); /** @internal */ this.inputName = 'maxlength'; /** @internal */ this.normalizeInput = input => toInteger(input); /** @internal */ this.createValidator = maxlength => maxLengthValidator(maxlength); } } MaxLengthValidator.ɵfac = /* @__PURE__ */function () { let ɵMaxLengthValidator_BaseFactory; return function MaxLengthValidator_Factory(t) { return (ɵMaxLengthValidator_BaseFactory || (ɵMaxLengthValidator_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](MaxLengthValidator)))(t || MaxLengthValidator); }; }(); MaxLengthValidator.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: MaxLengthValidator, selectors: [["", "maxlength", "", "formControlName", ""], ["", "maxlength", "", "formControl", ""], ["", "maxlength", "", "ngModel", ""]], hostVars: 1, hostBindings: function MaxLengthValidator_HostBindings(rf, ctx) { if (rf & 2) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵattribute"]("maxlength", ctx._enabled ? ctx.maxlength : null); } }, inputs: { maxlength: "maxlength" }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([MAX_LENGTH_VALIDATOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](MaxLengthValidator, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]', providers: [MAX_LENGTH_VALIDATOR], host: { '[attr.maxlength]': '_enabled ? maxlength : null' } }] }], null, { maxlength: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /** * @description * Provider which adds `PatternValidator` to the `NG_VALIDATORS` multi-provider list. */ const PATTERN_VALIDATOR = { provide: NG_VALIDATORS, useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(() => PatternValidator), multi: true }; /** * @description * A directive that adds regex pattern validation to controls marked with the * `pattern` attribute. The regex must match the entire control value. * The directive is provided with the `NG_VALIDATORS` multi-provider list. * * @see [Form Validation](guide/form-validation) * * @usageNotes * * ### Adding a pattern validator * * The following example shows how to add a pattern validator to an input attached to an * ngModel binding. * * ```html * <input name="firstName" ngModel pattern="[a-zA-Z ]*"> * ``` * * @ngModule ReactiveFormsModule * @ngModule FormsModule * @publicApi */ class PatternValidator extends AbstractValidatorDirective { constructor() { super(...arguments); /** @internal */ this.inputName = 'pattern'; /** @internal */ this.normalizeInput = input => input; /** @internal */ this.createValidator = input => patternValidator(input); } } PatternValidator.ɵfac = /* @__PURE__ */function () { let ɵPatternValidator_BaseFactory; return function PatternValidator_Factory(t) { return (ɵPatternValidator_BaseFactory || (ɵPatternValidator_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](PatternValidator)))(t || PatternValidator); }; }(); PatternValidator.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: PatternValidator, selectors: [["", "pattern", "", "formControlName", ""], ["", "pattern", "", "formControl", ""], ["", "pattern", "", "ngModel", ""]], hostVars: 1, hostBindings: function PatternValidator_HostBindings(rf, ctx) { if (rf & 2) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵattribute"]("pattern", ctx._enabled ? ctx.pattern : null); } }, inputs: { pattern: "pattern" }, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵProvidersFeature"]([PATTERN_VALIDATOR]), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵInheritDefinitionFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](PatternValidator, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[pattern][formControlName],[pattern][formControl],[pattern][ngModel]', providers: [PATTERN_VALIDATOR], host: { '[attr.pattern]': '_enabled ? pattern : null' } }] }], null, { pattern: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); const SHARED_FORM_DIRECTIVES = [ɵNgNoValidate, NgSelectOption, ɵNgSelectMultipleOption, DefaultValueAccessor, NumberValueAccessor, RangeValueAccessor, CheckboxControlValueAccessor, SelectControlValueAccessor, SelectMultipleControlValueAccessor, RadioControlValueAccessor, NgControlStatus, NgControlStatusGroup, RequiredValidator, MinLengthValidator, MaxLengthValidator, PatternValidator, CheckboxRequiredValidator, EmailValidator, MinValidator, MaxValidator]; const TEMPLATE_DRIVEN_DIRECTIVES = [NgModel, NgModelGroup, NgForm]; const REACTIVE_DRIVEN_DIRECTIVES = [FormControlDirective, FormGroupDirective, FormControlName, FormGroupName, FormArrayName]; /** * Internal module used for sharing directives between FormsModule and ReactiveFormsModule */ class ɵInternalFormsSharedModule {} ɵInternalFormsSharedModule.ɵfac = function ɵInternalFormsSharedModule_Factory(t) { return new (t || ɵInternalFormsSharedModule)(); }; ɵInternalFormsSharedModule.ɵmod = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineNgModule"]({ type: ɵInternalFormsSharedModule }); ɵInternalFormsSharedModule.ɵinj = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjector"]({ imports: [RadioControlRegistryModule] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](ɵInternalFormsSharedModule, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.NgModule, args: [{ declarations: SHARED_FORM_DIRECTIVES, imports: [RadioControlRegistryModule], exports: SHARED_FORM_DIRECTIVES }] }], null, null); })(); /** * Tracks the value and validity state of an array of `FormControl`, * `FormGroup` or `FormArray` instances. * * A `FormArray` aggregates the values of each child `FormControl` into an array. * It calculates its status by reducing the status values of its children. For example, if one of * the controls in a `FormArray` is invalid, the entire array becomes invalid. * * `FormArray` accepts one generic argument, which is the type of the controls inside. * If you need a heterogenous array, use {@link UntypedFormArray}. * * `FormArray` is one of the four fundamental building blocks used to define forms in Angular, * along with `FormControl`, `FormGroup`, and `FormRecord`. * * @usageNotes * * ### Create an array of form controls * * ``` * const arr = new FormArray([ * new FormControl('Nancy', Validators.minLength(2)), * new FormControl('Drew'), * ]); * * console.log(arr.value); // ['Nancy', 'Drew'] * console.log(arr.status); // 'VALID' * ``` * * ### Create a form array with array-level validators * * You include array-level validators and async validators. These come in handy * when you want to perform validation that considers the value of more than one child * control. * * The two types of validators are passed in separately as the second and third arg * respectively, or together as part of an options object. * * ``` * const arr = new FormArray([ * new FormControl('Nancy'), * new FormControl('Drew') * ], {validators: myValidator, asyncValidators: myAsyncValidator}); * ``` * * ### Set the updateOn property for all controls in a form array * * The options object is used to set a default value for each child * control's `updateOn` property. If you set `updateOn` to `'blur'` at the * array level, all child controls default to 'blur', unless the child * has explicitly specified a different `updateOn` value. * * ```ts * const arr = new FormArray([ * new FormControl() * ], {updateOn: 'blur'}); * ``` * * ### Adding or removing controls from a form array * * To change the controls in the array, use the `push`, `insert`, `removeAt` or `clear` methods * in `FormArray` itself. These methods ensure the controls are properly tracked in the * form's hierarchy. Do not modify the array of `AbstractControl`s used to instantiate * the `FormArray` directly, as that result in strange and unexpected behavior such * as broken change detection. * * @publicApi */ class FormArray extends AbstractControl { /** * Creates a new `FormArray` instance. * * @param controls An array of child controls. Each child control is given an index * where it is registered. * * @param validatorOrOpts A synchronous validator function, or an array of * such functions, or an `AbstractControlOptions` object that contains validation functions * and a validation trigger. * * @param asyncValidator A single async validator or array of async validator functions * */ constructor(controls, validatorOrOpts, asyncValidator) { super(pickValidators(validatorOrOpts), pickAsyncValidators(asyncValidator, validatorOrOpts)); this.controls = controls; this._initObservables(); this._setUpdateStrategy(validatorOrOpts); this._setUpControls(); this.updateValueAndValidity({ onlySelf: true, // If `asyncValidator` is present, it will trigger control status change from `PENDING` to // `VALID` or `INVALID`. // The status should be broadcasted via the `statusChanges` observable, so we set `emitEvent` // to `true` to allow that during the control creation process. emitEvent: !!this.asyncValidator }); } /** * Get the `AbstractControl` at the given `index` in the array. * * @param index Index in the array to retrieve the control. If `index` is negative, it will wrap * around from the back, and if index is greatly negative (less than `-length`), the result is * undefined. This behavior is the same as `Array.at(index)`. */ at(index) { return this.controls[this._adjustIndex(index)]; } /** * Insert a new `AbstractControl` at the end of the array. * * @param control Form control to be inserted * @param options Specifies whether this FormArray instance should emit events after a new * control is added. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` observables emit events with the latest status and value when the control is * inserted. When false, no events are emitted. */ push(control, options = {}) { this.controls.push(control); this._registerControl(control); this.updateValueAndValidity({ emitEvent: options.emitEvent }); this._onCollectionChange(); } /** * Insert a new `AbstractControl` at the given `index` in the array. * * @param index Index in the array to insert the control. If `index` is negative, wraps around * from the back. If `index` is greatly negative (less than `-length`), prepends to the array. * This behavior is the same as `Array.splice(index, 0, control)`. * @param control Form control to be inserted * @param options Specifies whether this FormArray instance should emit events after a new * control is inserted. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` observables emit events with the latest status and value when the control is * inserted. When false, no events are emitted. */ insert(index, control, options = {}) { this.controls.splice(index, 0, control); this._registerControl(control); this.updateValueAndValidity({ emitEvent: options.emitEvent }); } /** * Remove the control at the given `index` in the array. * * @param index Index in the array to remove the control. If `index` is negative, wraps around * from the back. If `index` is greatly negative (less than `-length`), removes the first * element. This behavior is the same as `Array.splice(index, 1)`. * @param options Specifies whether this FormArray instance should emit events after a * control is removed. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` observables emit events with the latest status and value when the control is * removed. When false, no events are emitted. */ removeAt(index, options = {}) { // Adjust the index, then clamp it at no less than 0 to prevent undesired underflows. let adjustedIndex = this._adjustIndex(index); if (adjustedIndex < 0) adjustedIndex = 0; if (this.controls[adjustedIndex]) this.controls[adjustedIndex]._registerOnCollectionChange(() => {}); this.controls.splice(adjustedIndex, 1); this.updateValueAndValidity({ emitEvent: options.emitEvent }); } /** * Replace an existing control. * * @param index Index in the array to replace the control. If `index` is negative, wraps around * from the back. If `index` is greatly negative (less than `-length`), replaces the first * element. This behavior is the same as `Array.splice(index, 1, control)`. * @param control The `AbstractControl` control to replace the existing control * @param options Specifies whether this FormArray instance should emit events after an * existing control is replaced with a new one. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` observables emit events with the latest status and value when the control is * replaced with a new one. When false, no events are emitted. */ setControl(index, control, options = {}) { // Adjust the index, then clamp it at no less than 0 to prevent undesired underflows. let adjustedIndex = this._adjustIndex(index); if (adjustedIndex < 0) adjustedIndex = 0; if (this.controls[adjustedIndex]) this.controls[adjustedIndex]._registerOnCollectionChange(() => {}); this.controls.splice(adjustedIndex, 1); if (control) { this.controls.splice(adjustedIndex, 0, control); this._registerControl(control); } this.updateValueAndValidity({ emitEvent: options.emitEvent }); this._onCollectionChange(); } /** * Length of the control array. */ get length() { return this.controls.length; } /** * Sets the value of the `FormArray`. It accepts an array that matches * the structure of the control. * * This method performs strict checks, and throws an error if you try * to set the value of a control that doesn't exist or if you exclude the * value of a control. * * @usageNotes * ### Set the values for the controls in the form array * * ``` * const arr = new FormArray([ * new FormControl(), * new FormControl() * ]); * console.log(arr.value); // [null, null] * * arr.setValue(['Nancy', 'Drew']); * console.log(arr.value); // ['Nancy', 'Drew'] * ``` * * @param value Array of values for the controls * @param options Configure options that determine how the control propagates changes and * emits events after the value changes * * * `onlySelf`: When true, each change only affects this control, and not its parent. Default * is false. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` * observables emit events with the latest status and value when the control value is updated. * When false, no events are emitted. * The configuration options are passed to the {@link AbstractControl#updateValueAndValidity * updateValueAndValidity} method. */ setValue(value, options = {}) { assertAllValuesPresent(this, false, value); value.forEach((newValue, index) => { assertControlPresent(this, false, index); this.at(index).setValue(newValue, { onlySelf: true, emitEvent: options.emitEvent }); }); this.updateValueAndValidity(options); } /** * Patches the value of the `FormArray`. It accepts an array that matches the * structure of the control, and does its best to match the values to the correct * controls in the group. * * It accepts both super-sets and sub-sets of the array without throwing an error. * * @usageNotes * ### Patch the values for controls in a form array * * ``` * const arr = new FormArray([ * new FormControl(), * new FormControl() * ]); * console.log(arr.value); // [null, null] * * arr.patchValue(['Nancy']); * console.log(arr.value); // ['Nancy', null] * ``` * * @param value Array of latest values for the controls * @param options Configure options that determine how the control propagates changes and * emits events after the value changes * * * `onlySelf`: When true, each change only affects this control, and not its parent. Default * is false. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` observables emit events with the latest status and value when the control * value is updated. When false, no events are emitted. The configuration options are passed to * the {@link AbstractControl#updateValueAndValidity updateValueAndValidity} method. */ patchValue(value, options = {}) { // Even though the `value` argument type doesn't allow `null` and `undefined` values, the // `patchValue` can be called recursively and inner data structures might have these values, // so we just ignore such cases when a field containing FormArray instance receives `null` or // `undefined` as a value. if (value == null /* both `null` and `undefined` */) return; value.forEach((newValue, index) => { if (this.at(index)) { this.at(index).patchValue(newValue, { onlySelf: true, emitEvent: options.emitEvent }); } }); this.updateValueAndValidity(options); } /** * Resets the `FormArray` and all descendants are marked `pristine` and `untouched`, and the * value of all descendants to null or null maps. * * You reset to a specific form state by passing in an array of states * that matches the structure of the control. The state is a standalone value * or a form state object with both a value and a disabled status. * * @usageNotes * ### Reset the values in a form array * * ```ts * const arr = new FormArray([ * new FormControl(), * new FormControl() * ]); * arr.reset(['name', 'last name']); * * console.log(arr.value); // ['name', 'last name'] * ``` * * ### Reset the values in a form array and the disabled status for the first control * * ``` * arr.reset([ * {value: 'name', disabled: true}, * 'last' * ]); * * console.log(arr.value); // ['last'] * console.log(arr.at(0).status); // 'DISABLED' * ``` * * @param value Array of values for the controls * @param options Configure options that determine how the control propagates changes and * emits events after the value changes * * * `onlySelf`: When true, each change only affects this control, and not its parent. Default * is false. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` * observables emit events with the latest status and value when the control is reset. * When false, no events are emitted. * The configuration options are passed to the {@link AbstractControl#updateValueAndValidity * updateValueAndValidity} method. */ reset(value = [], options = {}) { this._forEachChild((control, index) => { control.reset(value[index], { onlySelf: true, emitEvent: options.emitEvent }); }); this._updatePristine(options); this._updateTouched(options); this.updateValueAndValidity(options); } /** * The aggregate value of the array, including any disabled controls. * * Reports all values regardless of disabled status. */ getRawValue() { return this.controls.map(control => control.getRawValue()); } /** * Remove all controls in the `FormArray`. * * @param options Specifies whether this FormArray instance should emit events after all * controls are removed. * * `emitEvent`: When true or not supplied (the default), both the `statusChanges` and * `valueChanges` observables emit events with the latest status and value when all controls * in this FormArray instance are removed. When false, no events are emitted. * * @usageNotes * ### Remove all elements from a FormArray * * ```ts * const arr = new FormArray([ * new FormControl(), * new FormControl() * ]); * console.log(arr.length); // 2 * * arr.clear(); * console.log(arr.length); // 0 * ``` * * It's a simpler and more efficient alternative to removing all elements one by one: * * ```ts * const arr = new FormArray([ * new FormControl(), * new FormControl() * ]); * * while (arr.length) { * arr.removeAt(0); * } * ``` */ clear(options = {}) { if (this.controls.length < 1) return; this._forEachChild(control => control._registerOnCollectionChange(() => {})); this.controls.splice(0); this.updateValueAndValidity({ emitEvent: options.emitEvent }); } /** * Adjusts a negative index by summing it with the length of the array. For very negative * indices, the result may remain negative. * @internal */ _adjustIndex(index) { return index < 0 ? index + this.length : index; } /** @internal */ _syncPendingControls() { let subtreeUpdated = this.controls.reduce((updated, child) => { return child._syncPendingControls() ? true : updated; }, false); if (subtreeUpdated) this.updateValueAndValidity({ onlySelf: true }); return subtreeUpdated; } /** @internal */ _forEachChild(cb) { this.controls.forEach((control, index) => { cb(control, index); }); } /** @internal */ _updateValue() { this.value = this.controls.filter(control => control.enabled || this.disabled).map(control => control.value); } /** @internal */ _anyControls(condition) { return this.controls.some(control => control.enabled && condition(control)); } /** @internal */ _setUpControls() { this._forEachChild(control => this._registerControl(control)); } /** @internal */ _allControlsDisabled() { for (const control of this.controls) { if (control.enabled) return false; } return this.controls.length > 0 || this.disabled; } _registerControl(control) { control.setParent(this); control._registerOnCollectionChange(this._onCollectionChange); } /** @internal */ _find(name) { return this.at(name) ?? null; } } const UntypedFormArray = FormArray; /** * @description * Asserts that the given control is an instance of `FormArray` * * @publicApi */ const isFormArray = control => control instanceof FormArray; function isAbstractControlOptions(options) { return !!options && (options.asyncValidators !== undefined || options.validators !== undefined || options.updateOn !== undefined); } // clang-format on /** * @description * Creates an `AbstractControl` from a user-specified configuration. * * The `FormBuilder` provides syntactic sugar that shortens creating instances of a * `FormControl`, `FormGroup`, or `FormArray`. It reduces the amount of boilerplate needed to * build complex forms. * * @see [Reactive Forms Guide](guide/reactive-forms) * * @publicApi */ class FormBuilder { constructor() { this.useNonNullable = false; } /** * @description * Returns a FormBuilder in which automatically constructed `FormControl` elements * have `{nonNullable: true}` and are non-nullable. * * **Constructing non-nullable controls** * * When constructing a control, it will be non-nullable, and will reset to its initial value. * * ```ts * let nnfb = new FormBuilder().nonNullable; * let name = nnfb.control('Alex'); // FormControl<string> * name.reset(); * console.log(name); // 'Alex' * ``` * * **Constructing non-nullable groups or arrays** * * When constructing a group or array, all automatically created inner controls will be * non-nullable, and will reset to their initial values. * * ```ts * let nnfb = new FormBuilder().nonNullable; * let name = nnfb.group({who: 'Alex'}); // FormGroup<{who: FormControl<string>}> * name.reset(); * console.log(name); // {who: 'Alex'} * ``` * **Constructing *nullable* fields on groups or arrays** * * It is still possible to have a nullable field. In particular, any `FormControl` which is * *already* constructed will not be altered. For example: * * ```ts * let nnfb = new FormBuilder().nonNullable; * // FormGroup<{who: FormControl<string|null>}> * let name = nnfb.group({who: new FormControl('Alex')}); * name.reset(); console.log(name); // {who: null} * ``` * * Because the inner control is constructed explicitly by the caller, the builder has * no control over how it is created, and cannot exclude the `null`. */ get nonNullable() { const nnfb = new FormBuilder(); nnfb.useNonNullable = true; return nnfb; } group(controls, options = null) { const reducedControls = this._reduceControls(controls); let newOptions = {}; if (isAbstractControlOptions(options)) { // `options` are `AbstractControlOptions` newOptions = options; } else if (options !== null) { // `options` are legacy form group options newOptions.validators = options.validator; newOptions.asyncValidators = options.asyncValidator; } return new FormGroup(reducedControls, newOptions); } /** * @description * Constructs a new `FormRecord` instance. Accepts a single generic argument, which is an object * containing all the keys and corresponding inner control types. * * @param controls A collection of child controls. The key for each child is the name * under which it is registered. * * @param options Configuration options object for the `FormRecord`. The object should have the * `AbstractControlOptions` type and might contain the following fields: * * `validators`: A synchronous validator function, or an array of validator functions. * * `asyncValidators`: A single async validator or array of async validator functions. * * `updateOn`: The event upon which the control should be updated (options: 'change' | 'blur' * | submit'). */ record(controls, options = null) { const reducedControls = this._reduceControls(controls); // Cast to `any` because the inferred types are not as specific as Element. return new FormRecord(reducedControls, options); } /** * @description * Constructs a new `FormControl` with the given state, validators and options. Sets * `{nonNullable: true}` in the options to get a non-nullable control. Otherwise, the * control will be nullable. Accepts a single generic argument, which is the type of the * control's value. * * @param formState Initializes the control with an initial state value, or * with an object that contains both a value and a disabled status. * * @param validatorOrOpts A synchronous validator function, or an array of * such functions, or a `FormControlOptions` object that contains * validation functions and a validation trigger. * * @param asyncValidator A single async validator or array of async validator * functions. * * @usageNotes * * ### Initialize a control as disabled * * The following example returns a control with an initial value in a disabled state. * * <code-example path="forms/ts/formBuilder/form_builder_example.ts" region="disabled-control"> * </code-example> */ control(formState, validatorOrOpts, asyncValidator) { let newOptions = {}; if (!this.useNonNullable) { return new FormControl(formState, validatorOrOpts, asyncValidator); } if (isAbstractControlOptions(validatorOrOpts)) { // If the second argument is options, then they are copied. newOptions = validatorOrOpts; } else { // If the other arguments are validators, they are copied into an options object. newOptions.validators = validatorOrOpts; newOptions.asyncValidators = asyncValidator; } return new FormControl(formState, { ...newOptions, nonNullable: true }); } /** * Constructs a new `FormArray` from the given array of configurations, * validators and options. Accepts a single generic argument, which is the type of each control * inside the array. * * @param controls An array of child controls or control configs. Each child control is given an * index when it is registered. * * @param validatorOrOpts A synchronous validator function, or an array of such functions, or an * `AbstractControlOptions` object that contains * validation functions and a validation trigger. * * @param asyncValidator A single async validator or array of async validator functions. */ array(controls, validatorOrOpts, asyncValidator) { const createdControls = controls.map(c => this._createControl(c)); // Cast to `any` because the inferred types are not as specific as Element. return new FormArray(createdControls, validatorOrOpts, asyncValidator); } /** @internal */ _reduceControls(controls) { const createdControls = {}; Object.keys(controls).forEach(controlName => { createdControls[controlName] = this._createControl(controls[controlName]); }); return createdControls; } /** @internal */ _createControl(controls) { if (controls instanceof FormControl) { return controls; } else if (controls instanceof AbstractControl) { // A control; just return it return controls; } else if (Array.isArray(controls)) { // ControlConfig Tuple const value = controls[0]; const validator = controls.length > 1 ? controls[1] : null; const asyncValidator = controls.length > 2 ? controls[2] : null; return this.control(value, validator, asyncValidator); } else { // T or FormControlState<T> return this.control(controls); } } } FormBuilder.ɵfac = function FormBuilder_Factory(t) { return new (t || FormBuilder)(); }; FormBuilder.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: FormBuilder, factory: FormBuilder.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](FormBuilder, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], null, null); })(); /** * @description * `NonNullableFormBuilder` is similar to {@link FormBuilder}, but automatically constructed * {@link FormControl} elements have `{nonNullable: true}` and are non-nullable. * * @publicApi */ class NonNullableFormBuilder {} NonNullableFormBuilder.ɵfac = function NonNullableFormBuilder_Factory(t) { return new (t || NonNullableFormBuilder)(); }; NonNullableFormBuilder.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: NonNullableFormBuilder, factory: function () { return (() => (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(FormBuilder).nonNullable)(); }, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NonNullableFormBuilder, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root', useFactory: () => (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(FormBuilder).nonNullable }] }], null, null); })(); /** * UntypedFormBuilder is the same as `FormBuilder`, but it provides untyped controls. */ class UntypedFormBuilder extends FormBuilder { group(controlsConfig, options = null) { return super.group(controlsConfig, options); } /** * Like `FormBuilder#control`, except the resulting control is untyped. */ control(formState, validatorOrOpts, asyncValidator) { return super.control(formState, validatorOrOpts, asyncValidator); } /** * Like `FormBuilder#array`, except the resulting array is untyped. */ array(controlsConfig, validatorOrOpts, asyncValidator) { return super.array(controlsConfig, validatorOrOpts, asyncValidator); } } UntypedFormBuilder.ɵfac = /* @__PURE__ */function () { let ɵUntypedFormBuilder_BaseFactory; return function UntypedFormBuilder_Factory(t) { return (ɵUntypedFormBuilder_BaseFactory || (ɵUntypedFormBuilder_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](UntypedFormBuilder)))(t || UntypedFormBuilder); }; }(); UntypedFormBuilder.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: UntypedFormBuilder, factory: UntypedFormBuilder.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](UntypedFormBuilder, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], null, null); })(); /** * @module * @description * Entry point for all public APIs of the forms package. */ /** * @publicApi */ const VERSION = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.Version('15.2.9'); /** * Exports the required providers and directives for template-driven forms, * making them available for import by NgModules that import this module. * * Providers associated with this module: * * `RadioControlRegistry` * * @see [Forms Overview](/guide/forms-overview) * @see [Template-driven Forms Guide](/guide/forms) * * @publicApi */ class FormsModule { /** * @description * Provides options for configuring the forms module. * * @param opts An object of configuration options * * `callSetDisabledState` Configures whether to `always` call `setDisabledState`, which is more * correct, or to only call it `whenDisabled`, which is the legacy behavior. */ static withConfig(opts) { return { ngModule: FormsModule, providers: [{ provide: CALL_SET_DISABLED_STATE, useValue: opts.callSetDisabledState ?? setDisabledStateDefault }] }; } } FormsModule.ɵfac = function FormsModule_Factory(t) { return new (t || FormsModule)(); }; FormsModule.ɵmod = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineNgModule"]({ type: FormsModule }); FormsModule.ɵinj = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjector"]({ imports: [ɵInternalFormsSharedModule] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](FormsModule, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.NgModule, args: [{ declarations: TEMPLATE_DRIVEN_DIRECTIVES, exports: [ɵInternalFormsSharedModule, TEMPLATE_DRIVEN_DIRECTIVES] }] }], null, null); })(); /** * Exports the required infrastructure and directives for reactive forms, * making them available for import by NgModules that import this module. * * Providers associated with this module: * * `FormBuilder` * * `RadioControlRegistry` * * @see [Forms Overview](guide/forms-overview) * @see [Reactive Forms Guide](guide/reactive-forms) * * @publicApi */ class ReactiveFormsModule { /** * @description * Provides options for configuring the reactive forms module. * * @param opts An object of configuration options * * `warnOnNgModelWithFormControl` Configures when to emit a warning when an `ngModel` * binding is used with reactive form directives. * * `callSetDisabledState` Configures whether to `always` call `setDisabledState`, which is more * correct, or to only call it `whenDisabled`, which is the legacy behavior. */ static withConfig(opts) { return { ngModule: ReactiveFormsModule, providers: [{ provide: NG_MODEL_WITH_FORM_CONTROL_WARNING, useValue: opts.warnOnNgModelWithFormControl ?? 'always' }, { provide: CALL_SET_DISABLED_STATE, useValue: opts.callSetDisabledState ?? setDisabledStateDefault }] }; } } ReactiveFormsModule.ɵfac = function ReactiveFormsModule_Factory(t) { return new (t || ReactiveFormsModule)(); }; ReactiveFormsModule.ɵmod = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineNgModule"]({ type: ReactiveFormsModule }); ReactiveFormsModule.ɵinj = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjector"]({ imports: [ɵInternalFormsSharedModule] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](ReactiveFormsModule, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.NgModule, args: [{ declarations: [REACTIVE_DRIVEN_DIRECTIVES], exports: [ɵInternalFormsSharedModule, REACTIVE_DRIVEN_DIRECTIVES] }] }], null, null); })(); /** * @module * @description * This module is used for handling user input, by defining and building a `FormGroup` that * consists of `FormControl` objects, and mapping them onto the DOM. `FormControl` * objects can then be used to read information from the form DOM elements. * * Forms providers are not included in default providers; you must import these providers * explicitly. */ /** * @module * @description * Entry point for all public APIs of this package. */ // This file only reexports content of the `src` folder. Keep it that way. // This file is not used to build this module. It is only used during editing /** * Generated bundle index. Do not edit. */ /***/ }), /***/ 4497: /*!******************************************************************************!*\ !*** ./node_modules/@angular/platform-browser/fesm2020/platform-browser.mjs ***! \******************************************************************************/ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "BrowserModule": () => (/* binding */ BrowserModule), /* harmony export */ "BrowserTransferStateModule": () => (/* binding */ BrowserTransferStateModule), /* harmony export */ "By": () => (/* binding */ By), /* harmony export */ "DomSanitizer": () => (/* binding */ DomSanitizer), /* harmony export */ "EVENT_MANAGER_PLUGINS": () => (/* binding */ EVENT_MANAGER_PLUGINS), /* harmony export */ "EventManager": () => (/* binding */ EventManager), /* harmony export */ "HAMMER_GESTURE_CONFIG": () => (/* binding */ HAMMER_GESTURE_CONFIG), /* harmony export */ "HAMMER_LOADER": () => (/* binding */ HAMMER_LOADER), /* harmony export */ "HammerGestureConfig": () => (/* binding */ HammerGestureConfig), /* harmony export */ "HammerModule": () => (/* binding */ HammerModule), /* harmony export */ "Meta": () => (/* binding */ Meta), /* harmony export */ "REMOVE_STYLES_ON_COMPONENT_DESTROY": () => (/* binding */ REMOVE_STYLES_ON_COMPONENT_DESTROY), /* harmony export */ "Title": () => (/* binding */ Title), /* harmony export */ "TransferState": () => (/* binding */ TransferState), /* harmony export */ "VERSION": () => (/* binding */ VERSION), /* harmony export */ "bootstrapApplication": () => (/* binding */ bootstrapApplication), /* harmony export */ "createApplication": () => (/* binding */ createApplication), /* harmony export */ "disableDebugTools": () => (/* binding */ disableDebugTools), /* harmony export */ "enableDebugTools": () => (/* binding */ enableDebugTools), /* harmony export */ "makeStateKey": () => (/* binding */ makeStateKey), /* harmony export */ "platformBrowser": () => (/* binding */ platformBrowser), /* harmony export */ "provideProtractorTestingSupport": () => (/* binding */ provideProtractorTestingSupport), /* harmony export */ "ɵBrowserDomAdapter": () => (/* binding */ BrowserDomAdapter), /* harmony export */ "ɵBrowserGetTestability": () => (/* binding */ BrowserGetTestability), /* harmony export */ "ɵDomEventsPlugin": () => (/* binding */ DomEventsPlugin), /* harmony export */ "ɵDomRendererFactory2": () => (/* binding */ DomRendererFactory2), /* harmony export */ "ɵDomSanitizerImpl": () => (/* binding */ DomSanitizerImpl), /* harmony export */ "ɵDomSharedStylesHost": () => (/* binding */ DomSharedStylesHost), /* harmony export */ "ɵHammerGesturesPlugin": () => (/* binding */ HammerGesturesPlugin), /* harmony export */ "ɵINTERNAL_BROWSER_PLATFORM_PROVIDERS": () => (/* binding */ INTERNAL_BROWSER_PLATFORM_PROVIDERS), /* harmony export */ "ɵKeyEventsPlugin": () => (/* binding */ KeyEventsPlugin), /* harmony export */ "ɵNAMESPACE_URIS": () => (/* binding */ NAMESPACE_URIS), /* harmony export */ "ɵSharedStylesHost": () => (/* binding */ SharedStylesHost), /* harmony export */ "ɵTRANSITION_ID": () => (/* binding */ TRANSITION_ID), /* harmony export */ "ɵescapeHtml": () => (/* binding */ escapeHtml), /* harmony export */ "ɵflattenStyles": () => (/* binding */ flattenStyles), /* harmony export */ "ɵgetDOM": () => (/* reexport safe */ _angular_common__WEBPACK_IMPORTED_MODULE_0__["ɵgetDOM"]), /* harmony export */ "ɵinitDomAdapter": () => (/* binding */ initDomAdapter), /* harmony export */ "ɵshimContentAttribute": () => (/* binding */ shimContentAttribute), /* harmony export */ "ɵshimHostAttribute": () => (/* binding */ shimHostAttribute) /* harmony export */ }); /* harmony import */ var _angular_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @angular/common */ 4666); /* harmony import */ var _angular_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @angular/core */ 2560); /** * @license Angular v15.2.9 * (c) 2010-2022 Google LLC. https://angular.io/ * License: MIT */ /** * Provides DOM operations in any browser environment. * * @security Tread carefully! Interacting with the DOM directly is dangerous and * can introduce XSS risks. */ class GenericBrowserDomAdapter extends _angular_common__WEBPACK_IMPORTED_MODULE_0__["ɵDomAdapter"] { constructor() { super(...arguments); this.supportsDOMEvents = true; } } /** * A `DomAdapter` powered by full browser DOM APIs. * * @security Tread carefully! Interacting with the DOM directly is dangerous and * can introduce XSS risks. */ /* tslint:disable:requireParameterType no-console */ class BrowserDomAdapter extends GenericBrowserDomAdapter { static makeCurrent() { (0,_angular_common__WEBPACK_IMPORTED_MODULE_0__["ɵsetRootDomAdapter"])(new BrowserDomAdapter()); } onAndCancel(el, evt, listener) { el.addEventListener(evt, listener, false); // Needed to follow Dart's subscription semantic, until fix of // https://code.google.com/p/dart/issues/detail?id=17406 return () => { el.removeEventListener(evt, listener, false); }; } dispatchEvent(el, evt) { el.dispatchEvent(evt); } remove(node) { if (node.parentNode) { node.parentNode.removeChild(node); } } createElement(tagName, doc) { doc = doc || this.getDefaultDocument(); return doc.createElement(tagName); } createHtmlDocument() { return document.implementation.createHTMLDocument('fakeTitle'); } getDefaultDocument() { return document; } isElementNode(node) { return node.nodeType === Node.ELEMENT_NODE; } isShadowRoot(node) { return node instanceof DocumentFragment; } /** @deprecated No longer being used in Ivy code. To be removed in version 14. */ getGlobalEventTarget(doc, target) { if (target === 'window') { return window; } if (target === 'document') { return doc; } if (target === 'body') { return doc.body; } return null; } getBaseHref(doc) { const href = getBaseElementHref(); return href == null ? null : relativePath(href); } resetBaseElement() { baseElement = null; } getUserAgent() { return window.navigator.userAgent; } getCookie(name) { return (0,_angular_common__WEBPACK_IMPORTED_MODULE_0__["ɵparseCookieValue"])(document.cookie, name); } } let baseElement = null; function getBaseElementHref() { baseElement = baseElement || document.querySelector('base'); return baseElement ? baseElement.getAttribute('href') : null; } // based on urlUtils.js in AngularJS 1 let urlParsingNode; function relativePath(url) { urlParsingNode = urlParsingNode || document.createElement('a'); urlParsingNode.setAttribute('href', url); const pathName = urlParsingNode.pathname; return pathName.charAt(0) === '/' ? pathName : `/${pathName}`; } /** * An id that identifies a particular application being bootstrapped, that should * match across the client/server boundary. */ const TRANSITION_ID = new _angular_core__WEBPACK_IMPORTED_MODULE_1__.InjectionToken('TRANSITION_ID'); function appInitializerFactory(transitionId, document, injector) { return () => { // Wait for all application initializers to be completed before removing the styles set by // the server. injector.get(_angular_core__WEBPACK_IMPORTED_MODULE_1__.ApplicationInitStatus).donePromise.then(() => { const dom = (0,_angular_common__WEBPACK_IMPORTED_MODULE_0__["ɵgetDOM"])(); const styles = document.querySelectorAll(`style[ng-transition="${transitionId}"]`); for (let i = 0; i < styles.length; i++) { dom.remove(styles[i]); } }); }; } const SERVER_TRANSITION_PROVIDERS = [{ provide: _angular_core__WEBPACK_IMPORTED_MODULE_1__.APP_INITIALIZER, useFactory: appInitializerFactory, deps: [TRANSITION_ID, _angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT, _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injector], multi: true }]; class BrowserGetTestability { addToWindow(registry) { _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵglobal"].getAngularTestability = (elem, findInAncestors = true) => { const testability = registry.findTestabilityInTree(elem, findInAncestors); if (testability == null) { throw new Error('Could not find testability for element.'); } return testability; }; _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵglobal"].getAllAngularTestabilities = () => registry.getAllTestabilities(); _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵglobal"].getAllAngularRootElements = () => registry.getAllRootElements(); const whenAllStable = (callback /** TODO #9100 */) => { const testabilities = _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵglobal"].getAllAngularTestabilities(); let count = testabilities.length; let didWork = false; const decrement = function (didWork_ /** TODO #9100 */) { didWork = didWork || didWork_; count--; if (count == 0) { callback(didWork); } }; testabilities.forEach(function (testability /** TODO #9100 */) { testability.whenStable(decrement); }); }; if (!_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵglobal"].frameworkStabilizers) { _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵglobal"].frameworkStabilizers = []; } _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵglobal"].frameworkStabilizers.push(whenAllStable); } findTestabilityInTree(registry, elem, findInAncestors) { if (elem == null) { return null; } const t = registry.getTestability(elem); if (t != null) { return t; } else if (!findInAncestors) { return null; } if ((0,_angular_common__WEBPACK_IMPORTED_MODULE_0__["ɵgetDOM"])().isShadowRoot(elem)) { return this.findTestabilityInTree(registry, elem.host, true); } return this.findTestabilityInTree(registry, elem.parentElement, true); } } /** * A factory for `HttpXhrBackend` that uses the `XMLHttpRequest` browser API. */ class BrowserXhr { build() { return new XMLHttpRequest(); } } BrowserXhr.ɵfac = function BrowserXhr_Factory(t) { return new (t || BrowserXhr)(); }; BrowserXhr.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjectable"]({ token: BrowserXhr, factory: BrowserXhr.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](BrowserXhr, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injectable }], null, null); })(); /** * The injection token for the event-manager plug-in service. * * @publicApi */ const EVENT_MANAGER_PLUGINS = new _angular_core__WEBPACK_IMPORTED_MODULE_1__.InjectionToken('EventManagerPlugins'); /** * An injectable service that provides event management for Angular * through a browser plug-in. * * @publicApi */ class EventManager { /** * Initializes an instance of the event-manager service. */ constructor(plugins, _zone) { this._zone = _zone; this._eventNameToPlugin = new Map(); plugins.forEach(plugin => { plugin.manager = this; }); this._plugins = plugins.slice().reverse(); } /** * Registers a handler for a specific element and event. * * @param element The HTML element to receive event notifications. * @param eventName The name of the event to listen for. * @param handler A function to call when the notification occurs. Receives the * event object as an argument. * @returns A callback function that can be used to remove the handler. */ addEventListener(element, eventName, handler) { const plugin = this._findPluginFor(eventName); return plugin.addEventListener(element, eventName, handler); } /** * Registers a global handler for an event in a target view. * * @param target A target for global event notifications. One of "window", "document", or "body". * @param eventName The name of the event to listen for. * @param handler A function to call when the notification occurs. Receives the * event object as an argument. * @returns A callback function that can be used to remove the handler. * @deprecated No longer being used in Ivy code. To be removed in version 14. */ addGlobalEventListener(target, eventName, handler) { const plugin = this._findPluginFor(eventName); return plugin.addGlobalEventListener(target, eventName, handler); } /** * Retrieves the compilation zone in which event listeners are registered. */ getZone() { return this._zone; } /** @internal */ _findPluginFor(eventName) { const plugin = this._eventNameToPlugin.get(eventName); if (plugin) { return plugin; } const plugins = this._plugins; for (let i = 0; i < plugins.length; i++) { const plugin = plugins[i]; if (plugin.supports(eventName)) { this._eventNameToPlugin.set(eventName, plugin); return plugin; } } throw new Error(`No event manager plugin found for event ${eventName}`); } } EventManager.ɵfac = function EventManager_Factory(t) { return new (t || EventManager)(_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](EVENT_MANAGER_PLUGINS), _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](_angular_core__WEBPACK_IMPORTED_MODULE_1__.NgZone)); }; EventManager.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjectable"]({ token: EventManager, factory: EventManager.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](EventManager, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injectable }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Inject, args: [EVENT_MANAGER_PLUGINS] }] }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.NgZone }]; }, null); })(); class EventManagerPlugin { constructor(_doc) { this._doc = _doc; } addGlobalEventListener(element, eventName, handler) { const target = (0,_angular_common__WEBPACK_IMPORTED_MODULE_0__["ɵgetDOM"])().getGlobalEventTarget(this._doc, element); if (!target) { throw new Error(`Unsupported event target ${target} for event ${eventName}`); } return this.addEventListener(target, eventName, handler); } } class SharedStylesHost { constructor() { this.usageCount = new Map(); } addStyles(styles) { for (const style of styles) { const usageCount = this.changeUsageCount(style, 1); if (usageCount === 1) { this.onStyleAdded(style); } } } removeStyles(styles) { for (const style of styles) { const usageCount = this.changeUsageCount(style, -1); if (usageCount === 0) { this.onStyleRemoved(style); } } } onStyleRemoved(style) {} onStyleAdded(style) {} getAllStyles() { return this.usageCount.keys(); } changeUsageCount(style, delta) { const map = this.usageCount; let usage = map.get(style) ?? 0; usage += delta; if (usage > 0) { map.set(style, usage); } else { map.delete(style); } return usage; } ngOnDestroy() { for (const style of this.getAllStyles()) { this.onStyleRemoved(style); } this.usageCount.clear(); } } SharedStylesHost.ɵfac = function SharedStylesHost_Factory(t) { return new (t || SharedStylesHost)(); }; SharedStylesHost.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjectable"]({ token: SharedStylesHost, factory: SharedStylesHost.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](SharedStylesHost, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injectable }], null, null); })(); class DomSharedStylesHost extends SharedStylesHost { constructor(doc) { super(); this.doc = doc; // Maps all registered host nodes to a list of style nodes that have been added to the host node. this.styleRef = new Map(); this.hostNodes = new Set(); this.resetHostNodes(); } onStyleAdded(style) { for (const host of this.hostNodes) { this.addStyleToHost(host, style); } } onStyleRemoved(style) { const styleRef = this.styleRef; const styleElements = styleRef.get(style); styleElements?.forEach(e => e.remove()); styleRef.delete(style); } ngOnDestroy() { super.ngOnDestroy(); this.styleRef.clear(); this.resetHostNodes(); } addHost(hostNode) { this.hostNodes.add(hostNode); for (const style of this.getAllStyles()) { this.addStyleToHost(hostNode, style); } } removeHost(hostNode) { this.hostNodes.delete(hostNode); } addStyleToHost(host, style) { const styleEl = this.doc.createElement('style'); styleEl.textContent = style; host.appendChild(styleEl); const styleElRef = this.styleRef.get(style); if (styleElRef) { styleElRef.push(styleEl); } else { this.styleRef.set(style, [styleEl]); } } resetHostNodes() { const hostNodes = this.hostNodes; hostNodes.clear(); // Re-add the head element back since this is the default host. hostNodes.add(this.doc.head); } } DomSharedStylesHost.ɵfac = function DomSharedStylesHost_Factory(t) { return new (t || DomSharedStylesHost)(_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT)); }; DomSharedStylesHost.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjectable"]({ token: DomSharedStylesHost, factory: DomSharedStylesHost.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](DomSharedStylesHost, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injectable }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Inject, args: [_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT] }] }]; }, null); })(); const NAMESPACE_URIS = { 'svg': 'http://www.w3.org/2000/svg', 'xhtml': 'http://www.w3.org/1999/xhtml', 'xlink': 'http://www.w3.org/1999/xlink', 'xml': 'http://www.w3.org/XML/1998/namespace', 'xmlns': 'http://www.w3.org/2000/xmlns/', 'math': 'http://www.w3.org/1998/MathML/' }; const COMPONENT_REGEX = /%COMP%/g; const NG_DEV_MODE$1 = typeof ngDevMode === 'undefined' || !!ngDevMode; const COMPONENT_VARIABLE = '%COMP%'; const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`; const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`; /** * The default value for the `REMOVE_STYLES_ON_COMPONENT_DESTROY` DI token. */ const REMOVE_STYLES_ON_COMPONENT_DESTROY_DEFAULT = false; /** * A [DI token](guide/glossary#di-token "DI token definition") that indicates whether styles * of destroyed components should be removed from DOM. * * By default, the value is set to `false`. This will be changed in the next major version. * @publicApi */ const REMOVE_STYLES_ON_COMPONENT_DESTROY = new _angular_core__WEBPACK_IMPORTED_MODULE_1__.InjectionToken('RemoveStylesOnCompDestory', { providedIn: 'root', factory: () => REMOVE_STYLES_ON_COMPONENT_DESTROY_DEFAULT }); function shimContentAttribute(componentShortId) { return CONTENT_ATTR.replace(COMPONENT_REGEX, componentShortId); } function shimHostAttribute(componentShortId) { return HOST_ATTR.replace(COMPONENT_REGEX, componentShortId); } function flattenStyles(compId, styles) { // Cannot use `Infinity` as depth as `infinity` is not a number literal in TypeScript. // See: https://github.com/microsoft/TypeScript/issues/32277 return styles.flat(100).map(s => s.replace(COMPONENT_REGEX, compId)); } function decoratePreventDefault(eventHandler) { // `DebugNode.triggerEventHandler` needs to know if the listener was created with // decoratePreventDefault or is a listener added outside the Angular context so it can handle the // two differently. In the first case, the special '__ngUnwrap__' token is passed to the unwrap // the listener (see below). return event => { // Ivy uses '__ngUnwrap__' as a special token that allows us to unwrap the function // so that it can be invoked programmatically by `DebugNode.triggerEventHandler`. The debug_node // can inspect the listener toString contents for the existence of this special token. Because // the token is a string literal, it is ensured to not be modified by compiled code. if (event === '__ngUnwrap__') { return eventHandler; } const allowDefaultBehavior = eventHandler(event); if (allowDefaultBehavior === false) { // TODO(tbosch): move preventDefault into event plugins... event.preventDefault(); event.returnValue = false; } return undefined; }; } class DomRendererFactory2 { constructor(eventManager, sharedStylesHost, appId, removeStylesOnCompDestory) { this.eventManager = eventManager; this.sharedStylesHost = sharedStylesHost; this.appId = appId; this.removeStylesOnCompDestory = removeStylesOnCompDestory; this.rendererByCompId = new Map(); this.defaultRenderer = new DefaultDomRenderer2(eventManager); } createRenderer(element, type) { if (!element || !type) { return this.defaultRenderer; } const renderer = this.getOrCreateRenderer(element, type); // Renderers have different logic due to different encapsulation behaviours. // Ex: for emulated, an attribute is added to the element. if (renderer instanceof EmulatedEncapsulationDomRenderer2) { renderer.applyToHost(element); } else if (renderer instanceof NoneEncapsulationDomRenderer) { renderer.applyStyles(); } return renderer; } getOrCreateRenderer(element, type) { const rendererByCompId = this.rendererByCompId; let renderer = rendererByCompId.get(type.id); if (!renderer) { const eventManager = this.eventManager; const sharedStylesHost = this.sharedStylesHost; const removeStylesOnCompDestory = this.removeStylesOnCompDestory; switch (type.encapsulation) { case _angular_core__WEBPACK_IMPORTED_MODULE_1__.ViewEncapsulation.Emulated: renderer = new EmulatedEncapsulationDomRenderer2(eventManager, sharedStylesHost, type, this.appId, removeStylesOnCompDestory); break; case _angular_core__WEBPACK_IMPORTED_MODULE_1__.ViewEncapsulation.ShadowDom: return new ShadowDomRenderer(eventManager, sharedStylesHost, element, type); default: renderer = new NoneEncapsulationDomRenderer(eventManager, sharedStylesHost, type, removeStylesOnCompDestory); break; } renderer.onDestroy = () => rendererByCompId.delete(type.id); rendererByCompId.set(type.id, renderer); } return renderer; } ngOnDestroy() { this.rendererByCompId.clear(); } begin() {} end() {} } DomRendererFactory2.ɵfac = function DomRendererFactory2_Factory(t) { return new (t || DomRendererFactory2)(_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](EventManager), _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](DomSharedStylesHost), _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](_angular_core__WEBPACK_IMPORTED_MODULE_1__.APP_ID), _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](REMOVE_STYLES_ON_COMPONENT_DESTROY)); }; DomRendererFactory2.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjectable"]({ token: DomRendererFactory2, factory: DomRendererFactory2.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](DomRendererFactory2, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injectable }], function () { return [{ type: EventManager }, { type: DomSharedStylesHost }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Inject, args: [_angular_core__WEBPACK_IMPORTED_MODULE_1__.APP_ID] }] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Inject, args: [REMOVE_STYLES_ON_COMPONENT_DESTROY] }] }]; }, null); })(); class DefaultDomRenderer2 { constructor(eventManager) { this.eventManager = eventManager; this.data = Object.create(null); this.destroyNode = null; } destroy() {} createElement(name, namespace) { if (namespace) { // TODO: `|| namespace` was added in // https://github.com/angular/angular/commit/2b9cc8503d48173492c29f5a271b61126104fbdb to // support how Ivy passed around the namespace URI rather than short name at the time. It did // not, however extend the support to other parts of the system (setAttribute, setAttribute, // and the ServerRenderer). We should decide what exactly the semantics for dealing with // namespaces should be and make it consistent. // Related issues: // https://github.com/angular/angular/issues/44028 // https://github.com/angular/angular/issues/44883 return document.createElementNS(NAMESPACE_URIS[namespace] || namespace, name); } return document.createElement(name); } createComment(value) { return document.createComment(value); } createText(value) { return document.createTextNode(value); } appendChild(parent, newChild) { const targetParent = isTemplateNode(parent) ? parent.content : parent; targetParent.appendChild(newChild); } insertBefore(parent, newChild, refChild) { if (parent) { const targetParent = isTemplateNode(parent) ? parent.content : parent; targetParent.insertBefore(newChild, refChild); } } removeChild(parent, oldChild) { if (parent) { parent.removeChild(oldChild); } } selectRootElement(selectorOrNode, preserveContent) { let el = typeof selectorOrNode === 'string' ? document.querySelector(selectorOrNode) : selectorOrNode; if (!el) { throw new Error(`The selector "${selectorOrNode}" did not match any elements`); } if (!preserveContent) { el.textContent = ''; } return el; } parentNode(node) { return node.parentNode; } nextSibling(node) { return node.nextSibling; } setAttribute(el, name, value, namespace) { if (namespace) { name = namespace + ':' + name; const namespaceUri = NAMESPACE_URIS[namespace]; if (namespaceUri) { el.setAttributeNS(namespaceUri, name, value); } else { el.setAttribute(name, value); } } else { el.setAttribute(name, value); } } removeAttribute(el, name, namespace) { if (namespace) { const namespaceUri = NAMESPACE_URIS[namespace]; if (namespaceUri) { el.removeAttributeNS(namespaceUri, name); } else { el.removeAttribute(`${namespace}:${name}`); } } else { el.removeAttribute(name); } } addClass(el, name) { el.classList.add(name); } removeClass(el, name) { el.classList.remove(name); } setStyle(el, style, value, flags) { if (flags & (_angular_core__WEBPACK_IMPORTED_MODULE_1__.RendererStyleFlags2.DashCase | _angular_core__WEBPACK_IMPORTED_MODULE_1__.RendererStyleFlags2.Important)) { el.style.setProperty(style, value, flags & _angular_core__WEBPACK_IMPORTED_MODULE_1__.RendererStyleFlags2.Important ? 'important' : ''); } else { el.style[style] = value; } } removeStyle(el, style, flags) { if (flags & _angular_core__WEBPACK_IMPORTED_MODULE_1__.RendererStyleFlags2.DashCase) { // removeProperty has no effect when used on camelCased properties. el.style.removeProperty(style); } else { el.style[style] = ''; } } setProperty(el, name, value) { NG_DEV_MODE$1 && checkNoSyntheticProp(name, 'property'); el[name] = value; } setValue(node, value) { node.nodeValue = value; } listen(target, event, callback) { NG_DEV_MODE$1 && checkNoSyntheticProp(event, 'listener'); if (typeof target === 'string') { return this.eventManager.addGlobalEventListener(target, event, decoratePreventDefault(callback)); } return this.eventManager.addEventListener(target, event, decoratePreventDefault(callback)); } } const AT_CHARCODE = (() => '@'.charCodeAt(0))(); function checkNoSyntheticProp(name, nameKind) { if (name.charCodeAt(0) === AT_CHARCODE) { throw new Error(`Unexpected synthetic ${nameKind} ${name} found. Please make sure that: - Either \`BrowserAnimationsModule\` or \`NoopAnimationsModule\` are imported in your application. - There is corresponding configuration for the animation named \`${name}\` defined in the \`animations\` field of the \`@Component\` decorator (see https://angular.io/api/core/Component#animations).`); } } function isTemplateNode(node) { return node.tagName === 'TEMPLATE' && node.content !== undefined; } class ShadowDomRenderer extends DefaultDomRenderer2 { constructor(eventManager, sharedStylesHost, hostEl, component) { super(eventManager); this.sharedStylesHost = sharedStylesHost; this.hostEl = hostEl; this.shadowRoot = hostEl.attachShadow({ mode: 'open' }); this.sharedStylesHost.addHost(this.shadowRoot); const styles = flattenStyles(component.id, component.styles); for (const style of styles) { const styleEl = document.createElement('style'); styleEl.textContent = style; this.shadowRoot.appendChild(styleEl); } } nodeOrShadowRoot(node) { return node === this.hostEl ? this.shadowRoot : node; } appendChild(parent, newChild) { return super.appendChild(this.nodeOrShadowRoot(parent), newChild); } insertBefore(parent, newChild, refChild) { return super.insertBefore(this.nodeOrShadowRoot(parent), newChild, refChild); } removeChild(parent, oldChild) { return super.removeChild(this.nodeOrShadowRoot(parent), oldChild); } parentNode(node) { return this.nodeOrShadowRoot(super.parentNode(this.nodeOrShadowRoot(node))); } destroy() { this.sharedStylesHost.removeHost(this.shadowRoot); } } class NoneEncapsulationDomRenderer extends DefaultDomRenderer2 { constructor(eventManager, sharedStylesHost, component, removeStylesOnCompDestory, compId = component.id) { super(eventManager); this.sharedStylesHost = sharedStylesHost; this.removeStylesOnCompDestory = removeStylesOnCompDestory; this.rendererUsageCount = 0; this.styles = flattenStyles(compId, component.styles); } applyStyles() { this.sharedStylesHost.addStyles(this.styles); this.rendererUsageCount++; } destroy() { if (!this.removeStylesOnCompDestory) { return; } this.sharedStylesHost.removeStyles(this.styles); this.rendererUsageCount--; if (this.rendererUsageCount === 0) { this.onDestroy?.(); } } } class EmulatedEncapsulationDomRenderer2 extends NoneEncapsulationDomRenderer { constructor(eventManager, sharedStylesHost, component, appId, removeStylesOnCompDestory) { const compId = appId + '-' + component.id; super(eventManager, sharedStylesHost, component, removeStylesOnCompDestory, compId); this.contentAttr = shimContentAttribute(compId); this.hostAttr = shimHostAttribute(compId); } applyToHost(element) { this.applyStyles(); this.setAttribute(element, this.hostAttr, ''); } createElement(parent, name) { const el = super.createElement(parent, name); super.setAttribute(el, this.contentAttr, ''); return el; } } class DomEventsPlugin extends EventManagerPlugin { constructor(doc) { super(doc); } // This plugin should come last in the list of plugins, because it accepts all // events. supports(eventName) { return true; } addEventListener(element, eventName, handler) { element.addEventListener(eventName, handler, false); return () => this.removeEventListener(element, eventName, handler); } removeEventListener(target, eventName, callback) { return target.removeEventListener(eventName, callback); } } DomEventsPlugin.ɵfac = function DomEventsPlugin_Factory(t) { return new (t || DomEventsPlugin)(_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT)); }; DomEventsPlugin.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjectable"]({ token: DomEventsPlugin, factory: DomEventsPlugin.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](DomEventsPlugin, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injectable }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Inject, args: [_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT] }] }]; }, null); })(); /** * Defines supported modifiers for key events. */ const MODIFIER_KEYS = ['alt', 'control', 'meta', 'shift']; // The following values are here for cross-browser compatibility and to match the W3C standard // cf https://www.w3.org/TR/DOM-Level-3-Events-key/ const _keyMap = { '\b': 'Backspace', '\t': 'Tab', '\x7F': 'Delete', '\x1B': 'Escape', 'Del': 'Delete', 'Esc': 'Escape', 'Left': 'ArrowLeft', 'Right': 'ArrowRight', 'Up': 'ArrowUp', 'Down': 'ArrowDown', 'Menu': 'ContextMenu', 'Scroll': 'ScrollLock', 'Win': 'OS' }; /** * Retrieves modifiers from key-event objects. */ const MODIFIER_KEY_GETTERS = { 'alt': event => event.altKey, 'control': event => event.ctrlKey, 'meta': event => event.metaKey, 'shift': event => event.shiftKey }; /** * @publicApi * A browser plug-in that provides support for handling of key events in Angular. */ class KeyEventsPlugin extends EventManagerPlugin { /** * Initializes an instance of the browser plug-in. * @param doc The document in which key events will be detected. */ constructor(doc) { super(doc); } /** * Reports whether a named key event is supported. * @param eventName The event name to query. * @return True if the named key event is supported. */ supports(eventName) { return KeyEventsPlugin.parseEventName(eventName) != null; } /** * Registers a handler for a specific element and key event. * @param element The HTML element to receive event notifications. * @param eventName The name of the key event to listen for. * @param handler A function to call when the notification occurs. Receives the * event object as an argument. * @returns The key event that was registered. */ addEventListener(element, eventName, handler) { const parsedEvent = KeyEventsPlugin.parseEventName(eventName); const outsideHandler = KeyEventsPlugin.eventCallback(parsedEvent['fullKey'], handler, this.manager.getZone()); return this.manager.getZone().runOutsideAngular(() => { return (0,_angular_common__WEBPACK_IMPORTED_MODULE_0__["ɵgetDOM"])().onAndCancel(element, parsedEvent['domEventName'], outsideHandler); }); } /** * Parses the user provided full keyboard event definition and normalizes it for * later internal use. It ensures the string is all lowercase, converts special * characters to a standard spelling, and orders all the values consistently. * * @param eventName The name of the key event to listen for. * @returns an object with the full, normalized string, and the dom event name * or null in the case when the event doesn't match a keyboard event. */ static parseEventName(eventName) { const parts = eventName.toLowerCase().split('.'); const domEventName = parts.shift(); if (parts.length === 0 || !(domEventName === 'keydown' || domEventName === 'keyup')) { return null; } const key = KeyEventsPlugin._normalizeKey(parts.pop()); let fullKey = ''; let codeIX = parts.indexOf('code'); if (codeIX > -1) { parts.splice(codeIX, 1); fullKey = 'code.'; } MODIFIER_KEYS.forEach(modifierName => { const index = parts.indexOf(modifierName); if (index > -1) { parts.splice(index, 1); fullKey += modifierName + '.'; } }); fullKey += key; if (parts.length != 0 || key.length === 0) { // returning null instead of throwing to let another plugin process the event return null; } // NOTE: Please don't rewrite this as so, as it will break JSCompiler property renaming. // The code must remain in the `result['domEventName']` form. // return {domEventName, fullKey}; const result = {}; result['domEventName'] = domEventName; result['fullKey'] = fullKey; return result; } /** * Determines whether the actual keys pressed match the configured key code string. * The `fullKeyCode` event is normalized in the `parseEventName` method when the * event is attached to the DOM during the `addEventListener` call. This is unseen * by the end user and is normalized for internal consistency and parsing. * * @param event The keyboard event. * @param fullKeyCode The normalized user defined expected key event string * @returns boolean. */ static matchEventFullKeyCode(event, fullKeyCode) { let keycode = _keyMap[event.key] || event.key; let key = ''; if (fullKeyCode.indexOf('code.') > -1) { keycode = event.code; key = 'code.'; } // the keycode could be unidentified so we have to check here if (keycode == null || !keycode) return false; keycode = keycode.toLowerCase(); if (keycode === ' ') { keycode = 'space'; // for readability } else if (keycode === '.') { keycode = 'dot'; // because '.' is used as a separator in event names } MODIFIER_KEYS.forEach(modifierName => { if (modifierName !== keycode) { const modifierGetter = MODIFIER_KEY_GETTERS[modifierName]; if (modifierGetter(event)) { key += modifierName + '.'; } } }); key += keycode; return key === fullKeyCode; } /** * Configures a handler callback for a key event. * @param fullKey The event name that combines all simultaneous keystrokes. * @param handler The function that responds to the key event. * @param zone The zone in which the event occurred. * @returns A callback function. */ static eventCallback(fullKey, handler, zone) { return event => { if (KeyEventsPlugin.matchEventFullKeyCode(event, fullKey)) { zone.runGuarded(() => handler(event)); } }; } /** @internal */ static _normalizeKey(keyName) { // TODO: switch to a Map if the mapping grows too much switch (keyName) { case 'esc': return 'escape'; default: return keyName; } } } KeyEventsPlugin.ɵfac = function KeyEventsPlugin_Factory(t) { return new (t || KeyEventsPlugin)(_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT)); }; KeyEventsPlugin.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjectable"]({ token: KeyEventsPlugin, factory: KeyEventsPlugin.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](KeyEventsPlugin, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injectable }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Inject, args: [_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT] }] }]; }, null); })(); const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode; /** * Bootstraps an instance of an Angular application and renders a standalone component as the * application's root component. More information about standalone components can be found in [this * guide](guide/standalone-components). * * @usageNotes * The root component passed into this function *must* be a standalone one (should have the * `standalone: true` flag in the `@Component` decorator config). * * ```typescript * @Component({ * standalone: true, * template: 'Hello world!' * }) * class RootComponent {} * * const appRef: ApplicationRef = await bootstrapApplication(RootComponent); * ``` * * You can add the list of providers that should be available in the application injector by * specifying the `providers` field in an object passed as the second argument: * * ```typescript * await bootstrapApplication(RootComponent, { * providers: [ * {provide: BACKEND_URL, useValue: 'https://yourdomain.com/api'} * ] * }); * ``` * * The `importProvidersFrom` helper method can be used to collect all providers from any * existing NgModule (and transitively from all NgModules that it imports): * * ```typescript * await bootstrapApplication(RootComponent, { * providers: [ * importProvidersFrom(SomeNgModule) * ] * }); * ``` * * Note: the `bootstrapApplication` method doesn't include [Testability](api/core/Testability) by * default. You can add [Testability](api/core/Testability) by getting the list of necessary * providers using `provideProtractorTestingSupport()` function and adding them into the `providers` * array, for example: * * ```typescript * import {provideProtractorTestingSupport} from '@angular/platform-browser'; * * await bootstrapApplication(RootComponent, {providers: [provideProtractorTestingSupport()]}); * ``` * * @param rootComponent A reference to a standalone component that should be rendered. * @param options Extra configuration for the bootstrap operation, see `ApplicationConfig` for * additional info. * @returns A promise that returns an `ApplicationRef` instance once resolved. * * @publicApi */ function bootstrapApplication(rootComponent, options) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵinternalCreateApplication"])({ rootComponent, ...createProvidersConfig(options) }); } /** * Create an instance of an Angular application without bootstrapping any components. This is useful * for the situation where one wants to decouple application environment creation (a platform and * associated injectors) from rendering components on a screen. Components can be subsequently * bootstrapped on the returned `ApplicationRef`. * * @param options Extra configuration for the application environment, see `ApplicationConfig` for * additional info. * @returns A promise that returns an `ApplicationRef` instance once resolved. * * @publicApi */ function createApplication(options) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵinternalCreateApplication"])(createProvidersConfig(options)); } function createProvidersConfig(options) { return { appProviders: [...BROWSER_MODULE_PROVIDERS, ...(options?.providers ?? [])], platformProviders: INTERNAL_BROWSER_PLATFORM_PROVIDERS }; } /** * Returns a set of providers required to setup [Testability](api/core/Testability) for an * application bootstrapped using the `bootstrapApplication` function. The set of providers is * needed to support testing an application with Protractor (which relies on the Testability APIs * to be present). * * @returns An array of providers required to setup Testability for an application and make it * available for testing using Protractor. * * @publicApi */ function provideProtractorTestingSupport() { // Return a copy to prevent changes to the original array in case any in-place // alterations are performed to the `provideProtractorTestingSupport` call results in app code. return [...TESTABILITY_PROVIDERS]; } function initDomAdapter() { BrowserDomAdapter.makeCurrent(); } function errorHandler() { return new _angular_core__WEBPACK_IMPORTED_MODULE_1__.ErrorHandler(); } function _document() { // Tell ivy about the global document (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetDocument"])(document); return document; } const INTERNAL_BROWSER_PLATFORM_PROVIDERS = [{ provide: _angular_core__WEBPACK_IMPORTED_MODULE_1__.PLATFORM_ID, useValue: _angular_common__WEBPACK_IMPORTED_MODULE_0__["ɵPLATFORM_BROWSER_ID"] }, { provide: _angular_core__WEBPACK_IMPORTED_MODULE_1__.PLATFORM_INITIALIZER, useValue: initDomAdapter, multi: true }, { provide: _angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT, useFactory: _document, deps: [] }]; /** * A factory function that returns a `PlatformRef` instance associated with browser service * providers. * * @publicApi */ const platformBrowser = (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__.createPlatformFactory)(_angular_core__WEBPACK_IMPORTED_MODULE_1__.platformCore, 'browser', INTERNAL_BROWSER_PLATFORM_PROVIDERS); /** * Internal marker to signal whether providers from the `BrowserModule` are already present in DI. * This is needed to avoid loading `BrowserModule` providers twice. We can't rely on the * `BrowserModule` presence itself, since the standalone-based bootstrap just imports * `BrowserModule` providers without referencing the module itself. */ const BROWSER_MODULE_PROVIDERS_MARKER = new _angular_core__WEBPACK_IMPORTED_MODULE_1__.InjectionToken(NG_DEV_MODE ? 'BrowserModule Providers Marker' : ''); const TESTABILITY_PROVIDERS = [{ provide: _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵTESTABILITY_GETTER"], useClass: BrowserGetTestability, deps: [] }, { provide: _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵTESTABILITY"], useClass: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Testability, deps: [_angular_core__WEBPACK_IMPORTED_MODULE_1__.NgZone, _angular_core__WEBPACK_IMPORTED_MODULE_1__.TestabilityRegistry, _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵTESTABILITY_GETTER"]] }, { provide: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Testability, useClass: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Testability, deps: [_angular_core__WEBPACK_IMPORTED_MODULE_1__.NgZone, _angular_core__WEBPACK_IMPORTED_MODULE_1__.TestabilityRegistry, _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵTESTABILITY_GETTER"]] }]; const BROWSER_MODULE_PROVIDERS = [{ provide: _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵINJECTOR_SCOPE"], useValue: 'root' }, { provide: _angular_core__WEBPACK_IMPORTED_MODULE_1__.ErrorHandler, useFactory: errorHandler, deps: [] }, { provide: EVENT_MANAGER_PLUGINS, useClass: DomEventsPlugin, multi: true, deps: [_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT, _angular_core__WEBPACK_IMPORTED_MODULE_1__.NgZone, _angular_core__WEBPACK_IMPORTED_MODULE_1__.PLATFORM_ID] }, { provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true, deps: [_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT] }, { provide: DomRendererFactory2, useClass: DomRendererFactory2, deps: [EventManager, DomSharedStylesHost, _angular_core__WEBPACK_IMPORTED_MODULE_1__.APP_ID, REMOVE_STYLES_ON_COMPONENT_DESTROY] }, { provide: _angular_core__WEBPACK_IMPORTED_MODULE_1__.RendererFactory2, useExisting: DomRendererFactory2 }, { provide: SharedStylesHost, useExisting: DomSharedStylesHost }, { provide: DomSharedStylesHost, useClass: DomSharedStylesHost, deps: [_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT] }, { provide: EventManager, useClass: EventManager, deps: [EVENT_MANAGER_PLUGINS, _angular_core__WEBPACK_IMPORTED_MODULE_1__.NgZone] }, { provide: _angular_common__WEBPACK_IMPORTED_MODULE_0__.XhrFactory, useClass: BrowserXhr, deps: [] }, NG_DEV_MODE ? { provide: BROWSER_MODULE_PROVIDERS_MARKER, useValue: true } : []]; /** * Exports required infrastructure for all Angular apps. * Included by default in all Angular apps created with the CLI * `new` command. * Re-exports `CommonModule` and `ApplicationModule`, making their * exports and providers available to all apps. * * @publicApi */ class BrowserModule { constructor(providersAlreadyPresent) { if (NG_DEV_MODE && providersAlreadyPresent) { throw new Error(`Providers from the \`BrowserModule\` have already been loaded. If you need access ` + `to common directives such as NgIf and NgFor, import the \`CommonModule\` instead.`); } } /** * Configures a browser-based app to transition from a server-rendered app, if * one is present on the page. * * @param params An object containing an identifier for the app to transition. * The ID must match between the client and server versions of the app. * @returns The reconfigured `BrowserModule` to import into the app's root `AppModule`. */ static withServerTransition(params) { return { ngModule: BrowserModule, providers: [{ provide: _angular_core__WEBPACK_IMPORTED_MODULE_1__.APP_ID, useValue: params.appId }, { provide: TRANSITION_ID, useExisting: _angular_core__WEBPACK_IMPORTED_MODULE_1__.APP_ID }, SERVER_TRANSITION_PROVIDERS] }; } } BrowserModule.ɵfac = function BrowserModule_Factory(t) { return new (t || BrowserModule)(_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](BROWSER_MODULE_PROVIDERS_MARKER, 12)); }; BrowserModule.ɵmod = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineNgModule"]({ type: BrowserModule }); BrowserModule.ɵinj = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjector"]({ providers: [...BROWSER_MODULE_PROVIDERS, ...TESTABILITY_PROVIDERS], imports: [_angular_common__WEBPACK_IMPORTED_MODULE_0__.CommonModule, _angular_core__WEBPACK_IMPORTED_MODULE_1__.ApplicationModule] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](BrowserModule, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.NgModule, args: [{ providers: [...BROWSER_MODULE_PROVIDERS, ...TESTABILITY_PROVIDERS], exports: [_angular_common__WEBPACK_IMPORTED_MODULE_0__.CommonModule, _angular_core__WEBPACK_IMPORTED_MODULE_1__.ApplicationModule] }] }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.SkipSelf }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Inject, args: [BROWSER_MODULE_PROVIDERS_MARKER] }] }]; }, null); })(); /** * Factory to create a `Meta` service instance for the current DOM document. */ function createMeta() { return new Meta((0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"])(_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT)); } /** * A service for managing HTML `<meta>` tags. * * Properties of the `MetaDefinition` object match the attributes of the * HTML `<meta>` tag. These tags define document metadata that is important for * things like configuring a Content Security Policy, defining browser compatibility * and security settings, setting HTTP Headers, defining rich content for social sharing, * and Search Engine Optimization (SEO). * * To identify specific `<meta>` tags in a document, use an attribute selection * string in the format `"tag_attribute='value string'"`. * For example, an `attrSelector` value of `"name='description'"` matches a tag * whose `name` attribute has the value `"description"`. * Selectors are used with the `querySelector()` Document method, * in the format `meta[{attrSelector}]`. * * @see [HTML meta tag](https://developer.mozilla.org/docs/Web/HTML/Element/meta) * @see [Document.querySelector()](https://developer.mozilla.org/docs/Web/API/Document/querySelector) * * * @publicApi */ class Meta { constructor(_doc) { this._doc = _doc; this._dom = (0,_angular_common__WEBPACK_IMPORTED_MODULE_0__["ɵgetDOM"])(); } /** * Retrieves or creates a specific `<meta>` tag element in the current HTML document. * In searching for an existing tag, Angular attempts to match the `name` or `property` attribute * values in the provided tag definition, and verifies that all other attribute values are equal. * If an existing element is found, it is returned and is not modified in any way. * @param tag The definition of a `<meta>` element to match or create. * @param forceCreation True to create a new element without checking whether one already exists. * @returns The existing element with the same attributes and values if found, * the new element if no match is found, or `null` if the tag parameter is not defined. */ addTag(tag, forceCreation = false) { if (!tag) return null; return this._getOrCreateElement(tag, forceCreation); } /** * Retrieves or creates a set of `<meta>` tag elements in the current HTML document. * In searching for an existing tag, Angular attempts to match the `name` or `property` attribute * values in the provided tag definition, and verifies that all other attribute values are equal. * @param tags An array of tag definitions to match or create. * @param forceCreation True to create new elements without checking whether they already exist. * @returns The matching elements if found, or the new elements. */ addTags(tags, forceCreation = false) { if (!tags) return []; return tags.reduce((result, tag) => { if (tag) { result.push(this._getOrCreateElement(tag, forceCreation)); } return result; }, []); } /** * Retrieves a `<meta>` tag element in the current HTML document. * @param attrSelector The tag attribute and value to match against, in the format * `"tag_attribute='value string'"`. * @returns The matching element, if any. */ getTag(attrSelector) { if (!attrSelector) return null; return this._doc.querySelector(`meta[${attrSelector}]`) || null; } /** * Retrieves a set of `<meta>` tag elements in the current HTML document. * @param attrSelector The tag attribute and value to match against, in the format * `"tag_attribute='value string'"`. * @returns The matching elements, if any. */ getTags(attrSelector) { if (!attrSelector) return []; const list /*NodeList*/ = this._doc.querySelectorAll(`meta[${attrSelector}]`); return list ? [].slice.call(list) : []; } /** * Modifies an existing `<meta>` tag element in the current HTML document. * @param tag The tag description with which to replace the existing tag content. * @param selector A tag attribute and value to match against, to identify * an existing tag. A string in the format `"tag_attribute=`value string`"`. * If not supplied, matches a tag with the same `name` or `property` attribute value as the * replacement tag. * @return The modified element. */ updateTag(tag, selector) { if (!tag) return null; selector = selector || this._parseSelector(tag); const meta = this.getTag(selector); if (meta) { return this._setMetaElementAttributes(tag, meta); } return this._getOrCreateElement(tag, true); } /** * Removes an existing `<meta>` tag element from the current HTML document. * @param attrSelector A tag attribute and value to match against, to identify * an existing tag. A string in the format `"tag_attribute=`value string`"`. */ removeTag(attrSelector) { this.removeTagElement(this.getTag(attrSelector)); } /** * Removes an existing `<meta>` tag element from the current HTML document. * @param meta The tag definition to match against to identify an existing tag. */ removeTagElement(meta) { if (meta) { this._dom.remove(meta); } } _getOrCreateElement(meta, forceCreation = false) { if (!forceCreation) { const selector = this._parseSelector(meta); // It's allowed to have multiple elements with the same name so it's not enough to // just check that element with the same name already present on the page. We also need to // check if element has tag attributes const elem = this.getTags(selector).filter(elem => this._containsAttributes(meta, elem))[0]; if (elem !== undefined) return elem; } const element = this._dom.createElement('meta'); this._setMetaElementAttributes(meta, element); const head = this._doc.getElementsByTagName('head')[0]; head.appendChild(element); return element; } _setMetaElementAttributes(tag, el) { Object.keys(tag).forEach(prop => el.setAttribute(this._getMetaKeyMap(prop), tag[prop])); return el; } _parseSelector(tag) { const attr = tag.name ? 'name' : 'property'; return `${attr}="${tag[attr]}"`; } _containsAttributes(tag, elem) { return Object.keys(tag).every(key => elem.getAttribute(this._getMetaKeyMap(key)) === tag[key]); } _getMetaKeyMap(prop) { return META_KEYS_MAP[prop] || prop; } } Meta.ɵfac = function Meta_Factory(t) { return new (t || Meta)(_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT)); }; Meta.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjectable"]({ token: Meta, factory: function Meta_Factory(t) { let r = null; if (t) { r = new t(); } else { r = createMeta(); } return r; }, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](Meta, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injectable, args: [{ providedIn: 'root', useFactory: createMeta, deps: [] }] }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Inject, args: [_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT] }] }]; }, null); })(); /** * Mapping for MetaDefinition properties with their correct meta attribute names */ const META_KEYS_MAP = { httpEquiv: 'http-equiv' }; /** * Factory to create Title service. */ function createTitle() { return new Title((0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"])(_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT)); } /** * A service that can be used to get and set the title of a current HTML document. * * Since an Angular application can't be bootstrapped on the entire HTML document (`<html>` tag) * it is not possible to bind to the `text` property of the `HTMLTitleElement` elements * (representing the `<title>` tag). Instead, this service can be used to set and get the current * title value. * * @publicApi */ class Title { constructor(_doc) { this._doc = _doc; } /** * Get the title of the current HTML document. */ getTitle() { return this._doc.title; } /** * Set the title of the current HTML document. * @param newTitle */ setTitle(newTitle) { this._doc.title = newTitle || ''; } } Title.ɵfac = function Title_Factory(t) { return new (t || Title)(_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT)); }; Title.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjectable"]({ token: Title, factory: function Title_Factory(t) { let r = null; if (t) { r = new t(); } else { r = createTitle(); } return r; }, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](Title, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injectable, args: [{ providedIn: 'root', useFactory: createTitle, deps: [] }] }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Inject, args: [_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT] }] }]; }, null); })(); /** * Exports the value under a given `name` in the global property `ng`. For example `ng.probe` if * `name` is `'probe'`. * @param name Name under which it will be exported. Keep in mind this will be a property of the * global `ng` object. * @param value The value to export. */ function exportNgVar(name, value) { if (typeof COMPILED === 'undefined' || !COMPILED) { // Note: we can't export `ng` when using closure enhanced optimization as: // - closure declares globals itself for minified names, which sometimes clobber our `ng` global // - we can't declare a closure extern as the namespace `ng` is already used within Google // for typings for angularJS (via `goog.provide('ng....')`). const ng = _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵglobal"].ng = _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵglobal"].ng || {}; ng[name] = value; } } const win = typeof window !== 'undefined' && window || {}; class ChangeDetectionPerfRecord { constructor(msPerTick, numTicks) { this.msPerTick = msPerTick; this.numTicks = numTicks; } } /** * Entry point for all Angular profiling-related debug tools. This object * corresponds to the `ng.profiler` in the dev console. */ class AngularProfiler { constructor(ref) { this.appRef = ref.injector.get(_angular_core__WEBPACK_IMPORTED_MODULE_1__.ApplicationRef); } // tslint:disable:no-console /** * Exercises change detection in a loop and then prints the average amount of * time in milliseconds how long a single round of change detection takes for * the current state of the UI. It runs a minimum of 5 rounds for a minimum * of 500 milliseconds. * * Optionally, a user may pass a `config` parameter containing a map of * options. Supported options are: * * `record` (boolean) - causes the profiler to record a CPU profile while * it exercises the change detector. Example: * * ``` * ng.profiler.timeChangeDetection({record: true}) * ``` */ timeChangeDetection(config) { const record = config && config['record']; const profileName = 'Change Detection'; // Profiler is not available in Android browsers without dev tools opened const isProfilerAvailable = win.console.profile != null; if (record && isProfilerAvailable) { win.console.profile(profileName); } const start = performanceNow(); let numTicks = 0; while (numTicks < 5 || performanceNow() - start < 500) { this.appRef.tick(); numTicks++; } const end = performanceNow(); if (record && isProfilerAvailable) { win.console.profileEnd(profileName); } const msPerTick = (end - start) / numTicks; win.console.log(`ran ${numTicks} change detection cycles`); win.console.log(`${msPerTick.toFixed(2)} ms per check`); return new ChangeDetectionPerfRecord(msPerTick, numTicks); } } function performanceNow() { return win.performance && win.performance.now ? win.performance.now() : new Date().getTime(); } const PROFILER_GLOBAL_NAME = 'profiler'; /** * Enabled Angular debug tools that are accessible via your browser's * developer console. * * Usage: * * 1. Open developer console (e.g. in Chrome Ctrl + Shift + j) * 1. Type `ng.` (usually the console will show auto-complete suggestion) * 1. Try the change detection profiler `ng.profiler.timeChangeDetection()` * then hit Enter. * * @publicApi */ function enableDebugTools(ref) { exportNgVar(PROFILER_GLOBAL_NAME, new AngularProfiler(ref)); return ref; } /** * Disables Angular tools. * * @publicApi */ function disableDebugTools() { exportNgVar(PROFILER_GLOBAL_NAME, null); } function escapeHtml(text) { const escapedText = { '&': '&a;', '"': '&q;', '\'': '&s;', '<': '&l;', '>': '&g;' }; return text.replace(/[&"'<>]/g, s => escapedText[s]); } function unescapeHtml(text) { const unescapedText = { '&a;': '&', '&q;': '"', '&s;': '\'', '&l;': '<', '&g;': '>' }; return text.replace(/&[^;]+;/g, s => unescapedText[s]); } /** * Create a `StateKey<T>` that can be used to store value of type T with `TransferState`. * * Example: * * ``` * const COUNTER_KEY = makeStateKey<number>('counter'); * let value = 10; * * transferState.set(COUNTER_KEY, value); * ``` * * @publicApi */ function makeStateKey(key) { return key; } /** * A key value store that is transferred from the application on the server side to the application * on the client side. * * The `TransferState` is available as an injectable token. * On the client, just inject this token using DI and use it, it will be lazily initialized. * On the server it's already included if `renderApplication` function is used. Otherwise, import * the `ServerTransferStateModule` module to make the `TransferState` available. * * The values in the store are serialized/deserialized using JSON.stringify/JSON.parse. So only * boolean, number, string, null and non-class objects will be serialized and deserialized in a * non-lossy manner. * * @publicApi */ class TransferState { constructor() { this.store = {}; this.onSerializeCallbacks = {}; this.store = retrieveTransferredState((0,_angular_core__WEBPACK_IMPORTED_MODULE_1__.inject)(_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT), (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_1__.APP_ID)); } /** * Get the value corresponding to a key. Return `defaultValue` if key is not found. */ get(key, defaultValue) { return this.store[key] !== undefined ? this.store[key] : defaultValue; } /** * Set the value corresponding to a key. */ set(key, value) { this.store[key] = value; } /** * Remove a key from the store. */ remove(key) { delete this.store[key]; } /** * Test whether a key exists in the store. */ hasKey(key) { return this.store.hasOwnProperty(key); } /** * Indicates whether the state is empty. */ get isEmpty() { return Object.keys(this.store).length === 0; } /** * Register a callback to provide the value for a key when `toJson` is called. */ onSerialize(key, callback) { this.onSerializeCallbacks[key] = callback; } /** * Serialize the current state of the store to JSON. */ toJson() { // Call the onSerialize callbacks and put those values into the store. for (const key in this.onSerializeCallbacks) { if (this.onSerializeCallbacks.hasOwnProperty(key)) { try { this.store[key] = this.onSerializeCallbacks[key](); } catch (e) { console.warn('Exception in onSerialize callback: ', e); } } } return JSON.stringify(this.store); } } TransferState.ɵfac = function TransferState_Factory(t) { return new (t || TransferState)(); }; TransferState.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjectable"]({ token: TransferState, factory: TransferState.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](TransferState, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injectable, args: [{ providedIn: 'root' }] }], function () { return []; }, null); })(); function retrieveTransferredState(doc, appId) { // Locate the script tag with the JSON data transferred from the server. // The id of the script tag is set to the Angular appId + 'state'. const script = doc.getElementById(appId + '-state'); let initialState = {}; if (script && script.textContent) { try { // Avoid using any here as it triggers lint errors in google3 (any is not allowed). initialState = JSON.parse(unescapeHtml(script.textContent)); } catch (e) { console.warn('Exception while restoring TransferState for app ' + appId, e); } } return initialState; } /** * NgModule to install on the client side while using the `TransferState` to transfer state from * server to client. * * @publicApi * @deprecated no longer needed, you can inject the `TransferState` in an app without providing * this module. */ class BrowserTransferStateModule {} BrowserTransferStateModule.ɵfac = function BrowserTransferStateModule_Factory(t) { return new (t || BrowserTransferStateModule)(); }; BrowserTransferStateModule.ɵmod = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineNgModule"]({ type: BrowserTransferStateModule }); BrowserTransferStateModule.ɵinj = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjector"]({}); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](BrowserTransferStateModule, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.NgModule, args: [{}] }], null, null); })(); /** * Predicates for use with {@link DebugElement}'s query functions. * * @publicApi */ class By { /** * Match all nodes. * * @usageNotes * ### Example * * {@example platform-browser/dom/debug/ts/by/by.ts region='by_all'} */ static all() { return () => true; } /** * Match elements by the given CSS selector. * * @usageNotes * ### Example * * {@example platform-browser/dom/debug/ts/by/by.ts region='by_css'} */ static css(selector) { return debugElement => { return debugElement.nativeElement != null ? elementMatches(debugElement.nativeElement, selector) : false; }; } /** * Match nodes that have the given directive present. * * @usageNotes * ### Example * * {@example platform-browser/dom/debug/ts/by/by.ts region='by_directive'} */ static directive(type) { return debugNode => debugNode.providerTokens.indexOf(type) !== -1; } } function elementMatches(n, selector) { if ((0,_angular_common__WEBPACK_IMPORTED_MODULE_0__["ɵgetDOM"])().isElementNode(n)) { return n.matches && n.matches(selector) || n.msMatchesSelector && n.msMatchesSelector(selector) || n.webkitMatchesSelector && n.webkitMatchesSelector(selector); } return false; } /** * Supported HammerJS recognizer event names. */ const EVENT_NAMES = { // pan 'pan': true, 'panstart': true, 'panmove': true, 'panend': true, 'pancancel': true, 'panleft': true, 'panright': true, 'panup': true, 'pandown': true, // pinch 'pinch': true, 'pinchstart': true, 'pinchmove': true, 'pinchend': true, 'pinchcancel': true, 'pinchin': true, 'pinchout': true, // press 'press': true, 'pressup': true, // rotate 'rotate': true, 'rotatestart': true, 'rotatemove': true, 'rotateend': true, 'rotatecancel': true, // swipe 'swipe': true, 'swipeleft': true, 'swiperight': true, 'swipeup': true, 'swipedown': true, // tap 'tap': true, 'doubletap': true }; /** * DI token for providing [HammerJS](https://hammerjs.github.io/) support to Angular. * @see `HammerGestureConfig` * * @ngModule HammerModule * @publicApi */ const HAMMER_GESTURE_CONFIG = new _angular_core__WEBPACK_IMPORTED_MODULE_1__.InjectionToken('HammerGestureConfig'); /** * Injection token used to provide a {@link HammerLoader} to Angular. * * @publicApi */ const HAMMER_LOADER = new _angular_core__WEBPACK_IMPORTED_MODULE_1__.InjectionToken('HammerLoader'); /** * An injectable [HammerJS Manager](https://hammerjs.github.io/api/#hammermanager) * for gesture recognition. Configures specific event recognition. * @publicApi */ class HammerGestureConfig { constructor() { /** * A set of supported event names for gestures to be used in Angular. * Angular supports all built-in recognizers, as listed in * [HammerJS documentation](https://hammerjs.github.io/). */ this.events = []; /** * Maps gesture event names to a set of configuration options * that specify overrides to the default values for specific properties. * * The key is a supported event name to be configured, * and the options object contains a set of properties, with override values * to be applied to the named recognizer event. * For example, to disable recognition of the rotate event, specify * `{"rotate": {"enable": false}}`. * * Properties that are not present take the HammerJS default values. * For information about which properties are supported for which events, * and their allowed and default values, see * [HammerJS documentation](https://hammerjs.github.io/). * */ this.overrides = {}; } /** * Creates a [HammerJS Manager](https://hammerjs.github.io/api/#hammermanager) * and attaches it to a given HTML element. * @param element The element that will recognize gestures. * @returns A HammerJS event-manager object. */ buildHammer(element) { const mc = new Hammer(element, this.options); mc.get('pinch').set({ enable: true }); mc.get('rotate').set({ enable: true }); for (const eventName in this.overrides) { mc.get(eventName).set(this.overrides[eventName]); } return mc; } } HammerGestureConfig.ɵfac = function HammerGestureConfig_Factory(t) { return new (t || HammerGestureConfig)(); }; HammerGestureConfig.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjectable"]({ token: HammerGestureConfig, factory: HammerGestureConfig.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](HammerGestureConfig, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injectable }], null, null); })(); /** * Event plugin that adds Hammer support to an application. * * @ngModule HammerModule */ class HammerGesturesPlugin extends EventManagerPlugin { constructor(doc, _config, console, loader) { super(doc); this._config = _config; this.console = console; this.loader = loader; this._loaderPromise = null; } supports(eventName) { if (!EVENT_NAMES.hasOwnProperty(eventName.toLowerCase()) && !this.isCustomEvent(eventName)) { return false; } if (!window.Hammer && !this.loader) { if (typeof ngDevMode === 'undefined' || ngDevMode) { this.console.warn(`The "${eventName}" event cannot be bound because Hammer.JS is not ` + `loaded and no custom loader has been specified.`); } return false; } return true; } addEventListener(element, eventName, handler) { const zone = this.manager.getZone(); eventName = eventName.toLowerCase(); // If Hammer is not present but a loader is specified, we defer adding the event listener // until Hammer is loaded. if (!window.Hammer && this.loader) { this._loaderPromise = this._loaderPromise || zone.runOutsideAngular(() => this.loader()); // This `addEventListener` method returns a function to remove the added listener. // Until Hammer is loaded, the returned function needs to *cancel* the registration rather // than remove anything. let cancelRegistration = false; let deregister = () => { cancelRegistration = true; }; zone.runOutsideAngular(() => this._loaderPromise.then(() => { // If Hammer isn't actually loaded when the custom loader resolves, give up. if (!window.Hammer) { if (typeof ngDevMode === 'undefined' || ngDevMode) { this.console.warn(`The custom HAMMER_LOADER completed, but Hammer.JS is not present.`); } deregister = () => {}; return; } if (!cancelRegistration) { // Now that Hammer is loaded and the listener is being loaded for real, // the deregistration function changes from canceling registration to // removal. deregister = this.addEventListener(element, eventName, handler); } }).catch(() => { if (typeof ngDevMode === 'undefined' || ngDevMode) { this.console.warn(`The "${eventName}" event cannot be bound because the custom ` + `Hammer.JS loader failed.`); } deregister = () => {}; })); // Return a function that *executes* `deregister` (and not `deregister` itself) so that we // can change the behavior of `deregister` once the listener is added. Using a closure in // this way allows us to avoid any additional data structures to track listener removal. return () => { deregister(); }; } return zone.runOutsideAngular(() => { // Creating the manager bind events, must be done outside of angular const mc = this._config.buildHammer(element); const callback = function (eventObj) { zone.runGuarded(function () { handler(eventObj); }); }; mc.on(eventName, callback); return () => { mc.off(eventName, callback); // destroy mc to prevent memory leak if (typeof mc.destroy === 'function') { mc.destroy(); } }; }); } isCustomEvent(eventName) { return this._config.events.indexOf(eventName) > -1; } } HammerGesturesPlugin.ɵfac = function HammerGesturesPlugin_Factory(t) { return new (t || HammerGesturesPlugin)(_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT), _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](HAMMER_GESTURE_CONFIG), _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵConsole"]), _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](HAMMER_LOADER, 8)); }; HammerGesturesPlugin.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjectable"]({ token: HammerGesturesPlugin, factory: HammerGesturesPlugin.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](HammerGesturesPlugin, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injectable }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Inject, args: [_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT] }] }, { type: HammerGestureConfig, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Inject, args: [HAMMER_GESTURE_CONFIG] }] }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵConsole"] }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Inject, args: [HAMMER_LOADER] }] }]; }, null); })(); /** * Adds support for HammerJS. * * Import this module at the root of your application so that Angular can work with * HammerJS to detect gesture events. * * Note that applications still need to include the HammerJS script itself. This module * simply sets up the coordination layer between HammerJS and Angular's EventManager. * * @publicApi */ class HammerModule {} HammerModule.ɵfac = function HammerModule_Factory(t) { return new (t || HammerModule)(); }; HammerModule.ɵmod = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineNgModule"]({ type: HammerModule }); HammerModule.ɵinj = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjector"]({ providers: [{ provide: EVENT_MANAGER_PLUGINS, useClass: HammerGesturesPlugin, multi: true, deps: [_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT, HAMMER_GESTURE_CONFIG, _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵConsole"], [new _angular_core__WEBPACK_IMPORTED_MODULE_1__.Optional(), HAMMER_LOADER]] }, { provide: HAMMER_GESTURE_CONFIG, useClass: HammerGestureConfig, deps: [] }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](HammerModule, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.NgModule, args: [{ providers: [{ provide: EVENT_MANAGER_PLUGINS, useClass: HammerGesturesPlugin, multi: true, deps: [_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT, HAMMER_GESTURE_CONFIG, _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵConsole"], [new _angular_core__WEBPACK_IMPORTED_MODULE_1__.Optional(), HAMMER_LOADER]] }, { provide: HAMMER_GESTURE_CONFIG, useClass: HammerGestureConfig, deps: [] }] }] }], null, null); })(); /** * DomSanitizer helps preventing Cross Site Scripting Security bugs (XSS) by sanitizing * values to be safe to use in the different DOM contexts. * * For example, when binding a URL in an `<a [href]="someValue">` hyperlink, `someValue` will be * sanitized so that an attacker cannot inject e.g. a `javascript:` URL that would execute code on * the website. * * In specific situations, it might be necessary to disable sanitization, for example if the * application genuinely needs to produce a `javascript:` style link with a dynamic value in it. * Users can bypass security by constructing a value with one of the `bypassSecurityTrust...` * methods, and then binding to that value from the template. * * These situations should be very rare, and extraordinary care must be taken to avoid creating a * Cross Site Scripting (XSS) security bug! * * When using `bypassSecurityTrust...`, make sure to call the method as early as possible and as * close as possible to the source of the value, to make it easy to verify no security bug is * created by its use. * * It is not required (and not recommended) to bypass security if the value is safe, e.g. a URL that * does not start with a suspicious protocol, or an HTML snippet that does not contain dangerous * code. The sanitizer leaves safe values intact. * * @security Calling any of the `bypassSecurityTrust...` APIs disables Angular's built-in * sanitization for the value passed in. Carefully check and audit all values and code paths going * into this call. Make sure any user data is appropriately escaped for this security context. * For more detail, see the [Security Guide](https://g.co/ng/security). * * @publicApi */ class DomSanitizer {} DomSanitizer.ɵfac = function DomSanitizer_Factory(t) { return new (t || DomSanitizer)(); }; DomSanitizer.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjectable"]({ token: DomSanitizer, factory: function DomSanitizer_Factory(t) { let r = null; if (t) { r = new (t || DomSanitizer)(); } else { r = _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](DomSanitizerImpl); } return r; }, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](DomSanitizer, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injectable, args: [{ providedIn: 'root', useExisting: (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__.forwardRef)(() => DomSanitizerImpl) }] }], null, null); })(); function domSanitizerImplFactory(injector) { return new DomSanitizerImpl(injector.get(_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT)); } class DomSanitizerImpl extends DomSanitizer { constructor(_doc) { super(); this._doc = _doc; } sanitize(ctx, value) { if (value == null) return null; switch (ctx) { case _angular_core__WEBPACK_IMPORTED_MODULE_1__.SecurityContext.NONE: return value; case _angular_core__WEBPACK_IMPORTED_MODULE_1__.SecurityContext.HTML: if ((0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵallowSanitizationBypassAndThrow"])(value, "HTML" /* BypassType.Html */)) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵunwrapSafeValue"])(value); } return (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵ_sanitizeHtml"])(this._doc, String(value)).toString(); case _angular_core__WEBPACK_IMPORTED_MODULE_1__.SecurityContext.STYLE: if ((0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵallowSanitizationBypassAndThrow"])(value, "Style" /* BypassType.Style */)) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵunwrapSafeValue"])(value); } return value; case _angular_core__WEBPACK_IMPORTED_MODULE_1__.SecurityContext.SCRIPT: if ((0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵallowSanitizationBypassAndThrow"])(value, "Script" /* BypassType.Script */)) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵunwrapSafeValue"])(value); } throw new Error('unsafe value used in a script context'); case _angular_core__WEBPACK_IMPORTED_MODULE_1__.SecurityContext.URL: if ((0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵallowSanitizationBypassAndThrow"])(value, "URL" /* BypassType.Url */)) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵunwrapSafeValue"])(value); } return (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵ_sanitizeUrl"])(String(value)); case _angular_core__WEBPACK_IMPORTED_MODULE_1__.SecurityContext.RESOURCE_URL: if ((0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵallowSanitizationBypassAndThrow"])(value, "ResourceURL" /* BypassType.ResourceUrl */)) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵunwrapSafeValue"])(value); } throw new Error(`unsafe value used in a resource URL context (see ${_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵXSS_SECURITY_URL"]})`); default: throw new Error(`Unexpected SecurityContext ${ctx} (see ${_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵXSS_SECURITY_URL"]})`); } } bypassSecurityTrustHtml(value) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵbypassSanitizationTrustHtml"])(value); } bypassSecurityTrustStyle(value) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵbypassSanitizationTrustStyle"])(value); } bypassSecurityTrustScript(value) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵbypassSanitizationTrustScript"])(value); } bypassSecurityTrustUrl(value) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵbypassSanitizationTrustUrl"])(value); } bypassSecurityTrustResourceUrl(value) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵbypassSanitizationTrustResourceUrl"])(value); } } DomSanitizerImpl.ɵfac = function DomSanitizerImpl_Factory(t) { return new (t || DomSanitizerImpl)(_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT)); }; DomSanitizerImpl.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineInjectable"]({ token: DomSanitizerImpl, factory: function DomSanitizerImpl_Factory(t) { let r = null; if (t) { r = new t(); } else { r = domSanitizerImplFactory(_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵinject"](_angular_core__WEBPACK_IMPORTED_MODULE_1__.Injector)); } return r; }, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵsetClassMetadata"](DomSanitizerImpl, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Injectable, args: [{ providedIn: 'root', useFactory: domSanitizerImplFactory, deps: [_angular_core__WEBPACK_IMPORTED_MODULE_1__.Injector] }] }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_1__.Inject, args: [_angular_common__WEBPACK_IMPORTED_MODULE_0__.DOCUMENT] }] }]; }, null); })(); /** * @module * @description * Entry point for all public APIs of the platform-browser package. */ /** * @publicApi */ const VERSION = new _angular_core__WEBPACK_IMPORTED_MODULE_1__.Version('15.2.9'); /** * @module * @description * Entry point for all public APIs of this package. */ // This file only reexports content of the `src` folder. Keep it that way. // This file is not used to build this module. It is only used during editing /** * Generated bundle index. Do not edit. */ /***/ }), /***/ 124: /*!**********************************************************!*\ !*** ./node_modules/@angular/router/fesm2020/router.mjs ***! \**********************************************************/ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "ActivatedRoute": () => (/* binding */ ActivatedRoute), /* harmony export */ "ActivatedRouteSnapshot": () => (/* binding */ ActivatedRouteSnapshot), /* harmony export */ "ActivationEnd": () => (/* binding */ ActivationEnd), /* harmony export */ "ActivationStart": () => (/* binding */ ActivationStart), /* harmony export */ "BaseRouteReuseStrategy": () => (/* binding */ BaseRouteReuseStrategy), /* harmony export */ "ChildActivationEnd": () => (/* binding */ ChildActivationEnd), /* harmony export */ "ChildActivationStart": () => (/* binding */ ChildActivationStart), /* harmony export */ "ChildrenOutletContexts": () => (/* binding */ ChildrenOutletContexts), /* harmony export */ "DefaultTitleStrategy": () => (/* binding */ DefaultTitleStrategy), /* harmony export */ "DefaultUrlSerializer": () => (/* binding */ DefaultUrlSerializer), /* harmony export */ "GuardsCheckEnd": () => (/* binding */ GuardsCheckEnd), /* harmony export */ "GuardsCheckStart": () => (/* binding */ GuardsCheckStart), /* harmony export */ "NavigationCancel": () => (/* binding */ NavigationCancel), /* harmony export */ "NavigationEnd": () => (/* binding */ NavigationEnd), /* harmony export */ "NavigationError": () => (/* binding */ NavigationError), /* harmony export */ "NavigationSkipped": () => (/* binding */ NavigationSkipped), /* harmony export */ "NavigationStart": () => (/* binding */ NavigationStart), /* harmony export */ "NoPreloading": () => (/* binding */ NoPreloading), /* harmony export */ "OutletContext": () => (/* binding */ OutletContext), /* harmony export */ "PRIMARY_OUTLET": () => (/* binding */ PRIMARY_OUTLET), /* harmony export */ "PreloadAllModules": () => (/* binding */ PreloadAllModules), /* harmony export */ "PreloadingStrategy": () => (/* binding */ PreloadingStrategy), /* harmony export */ "ROUTER_CONFIGURATION": () => (/* binding */ ROUTER_CONFIGURATION), /* harmony export */ "ROUTER_INITIALIZER": () => (/* binding */ ROUTER_INITIALIZER), /* harmony export */ "ROUTES": () => (/* binding */ ROUTES), /* harmony export */ "ResolveEnd": () => (/* binding */ ResolveEnd), /* harmony export */ "ResolveStart": () => (/* binding */ ResolveStart), /* harmony export */ "RouteConfigLoadEnd": () => (/* binding */ RouteConfigLoadEnd), /* harmony export */ "RouteConfigLoadStart": () => (/* binding */ RouteConfigLoadStart), /* harmony export */ "RouteReuseStrategy": () => (/* binding */ RouteReuseStrategy), /* harmony export */ "Router": () => (/* binding */ Router), /* harmony export */ "RouterEvent": () => (/* binding */ RouterEvent), /* harmony export */ "RouterLink": () => (/* binding */ RouterLink), /* harmony export */ "RouterLinkActive": () => (/* binding */ RouterLinkActive), /* harmony export */ "RouterLinkWithHref": () => (/* binding */ RouterLink), /* harmony export */ "RouterModule": () => (/* binding */ RouterModule), /* harmony export */ "RouterOutlet": () => (/* binding */ RouterOutlet), /* harmony export */ "RouterPreloader": () => (/* binding */ RouterPreloader), /* harmony export */ "RouterState": () => (/* binding */ RouterState), /* harmony export */ "RouterStateSnapshot": () => (/* binding */ RouterStateSnapshot), /* harmony export */ "RoutesRecognized": () => (/* binding */ RoutesRecognized), /* harmony export */ "Scroll": () => (/* binding */ Scroll), /* harmony export */ "TitleStrategy": () => (/* binding */ TitleStrategy), /* harmony export */ "UrlHandlingStrategy": () => (/* binding */ UrlHandlingStrategy), /* harmony export */ "UrlSegment": () => (/* binding */ UrlSegment), /* harmony export */ "UrlSegmentGroup": () => (/* binding */ UrlSegmentGroup), /* harmony export */ "UrlSerializer": () => (/* binding */ UrlSerializer), /* harmony export */ "UrlTree": () => (/* binding */ UrlTree), /* harmony export */ "VERSION": () => (/* binding */ VERSION), /* harmony export */ "convertToParamMap": () => (/* binding */ convertToParamMap), /* harmony export */ "createUrlTreeFromSnapshot": () => (/* binding */ createUrlTreeFromSnapshot), /* harmony export */ "defaultUrlMatcher": () => (/* binding */ defaultUrlMatcher), /* harmony export */ "provideRouter": () => (/* binding */ provideRouter), /* harmony export */ "provideRoutes": () => (/* binding */ provideRoutes), /* harmony export */ "withDebugTracing": () => (/* binding */ withDebugTracing), /* harmony export */ "withDisabledInitialNavigation": () => (/* binding */ withDisabledInitialNavigation), /* harmony export */ "withEnabledBlockingInitialNavigation": () => (/* binding */ withEnabledBlockingInitialNavigation), /* harmony export */ "withHashLocation": () => (/* binding */ withHashLocation), /* harmony export */ "withInMemoryScrolling": () => (/* binding */ withInMemoryScrolling), /* harmony export */ "withNavigationErrorHandler": () => (/* binding */ withNavigationErrorHandler), /* harmony export */ "withPreloading": () => (/* binding */ withPreloading), /* harmony export */ "withRouterConfig": () => (/* binding */ withRouterConfig), /* harmony export */ "ɵEmptyOutletComponent": () => (/* binding */ ɵEmptyOutletComponent), /* harmony export */ "ɵROUTER_PROVIDERS": () => (/* binding */ ROUTER_PROVIDERS), /* harmony export */ "ɵafterNextNavigation": () => (/* binding */ afterNextNavigation), /* harmony export */ "ɵflatten": () => (/* binding */ flatten), /* harmony export */ "ɵwithPreloading": () => (/* binding */ withPreloading) /* harmony export */ }); /* harmony import */ var _angular_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @angular/core */ 2560); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! rxjs */ 9346); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! rxjs */ 745); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! rxjs */ 6317); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! rxjs */ 4423); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! rxjs */ 6562); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! rxjs */ 4240); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! rxjs */ 1954); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! rxjs */ 629); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! rxjs */ 5474); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! rxjs */ 833); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! rxjs */ 591); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! rxjs */ 3932); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! rxjs */ 228); /* harmony import */ var _angular_common__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(/*! @angular/common */ 4666); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! rxjs/operators */ 635); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! rxjs/operators */ 2673); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! rxjs/operators */ 9295); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! rxjs/operators */ 4874); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! rxjs/operators */ 116); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! rxjs/operators */ 1353); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! rxjs/operators */ 155); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! rxjs/operators */ 3853); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! rxjs/operators */ 9337); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! rxjs/operators */ 3158); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! rxjs/operators */ 4503); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! rxjs/operators */ 1955); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! rxjs/operators */ 2566); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! rxjs/operators */ 4744); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! rxjs/operators */ 9601); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! rxjs/operators */ 73); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! rxjs/operators */ 2313); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! rxjs/operators */ 6074); /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(/*! rxjs/operators */ 1308); /* harmony import */ var _angular_platform_browser__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(/*! @angular/platform-browser */ 4497); /** * @license Angular v15.2.9 * (c) 2010-2022 Google LLC. https://angular.io/ * License: MIT */ /** * The primary routing outlet. * * @publicApi */ const PRIMARY_OUTLET = 'primary'; /** * A private symbol used to store the value of `Route.title` inside the `Route.data` if it is a * static string or `Route.resolve` if anything else. This allows us to reuse the existing route * data/resolvers to support the title feature without new instrumentation in the `Router` pipeline. */ const RouteTitleKey = Symbol('RouteTitle'); class ParamsAsMap { constructor(params) { this.params = params || {}; } has(name) { return Object.prototype.hasOwnProperty.call(this.params, name); } get(name) { if (this.has(name)) { const v = this.params[name]; return Array.isArray(v) ? v[0] : v; } return null; } getAll(name) { if (this.has(name)) { const v = this.params[name]; return Array.isArray(v) ? v : [v]; } return []; } get keys() { return Object.keys(this.params); } } /** * Converts a `Params` instance to a `ParamMap`. * @param params The instance to convert. * @returns The new map instance. * * @publicApi */ function convertToParamMap(params) { return new ParamsAsMap(params); } /** * Matches the route configuration (`route`) against the actual URL (`segments`). * * When no matcher is defined on a `Route`, this is the matcher used by the Router by default. * * @param segments The remaining unmatched segments in the current navigation * @param segmentGroup The current segment group being matched * @param route The `Route` to match against. * * @see UrlMatchResult * @see Route * * @returns The resulting match information or `null` if the `route` should not match. * @publicApi */ function defaultUrlMatcher(segments, segmentGroup, route) { const parts = route.path.split('/'); if (parts.length > segments.length) { // The actual URL is shorter than the config, no match return null; } if (route.pathMatch === 'full' && (segmentGroup.hasChildren() || parts.length < segments.length)) { // The config is longer than the actual URL but we are looking for a full match, return null return null; } const posParams = {}; // Check each config part against the actual URL for (let index = 0; index < parts.length; index++) { const part = parts[index]; const segment = segments[index]; const isParameter = part.startsWith(':'); if (isParameter) { posParams[part.substring(1)] = segment; } else if (part !== segment.path) { // The actual URL part does not match the config, no match return null; } } return { consumed: segments.slice(0, parts.length), posParams }; } function shallowEqualArrays(a, b) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; ++i) { if (!shallowEqual(a[i], b[i])) return false; } return true; } function shallowEqual(a, b) { // While `undefined` should never be possible, it would sometimes be the case in IE 11 // and pre-chromium Edge. The check below accounts for this edge case. const k1 = a ? Object.keys(a) : undefined; const k2 = b ? Object.keys(b) : undefined; if (!k1 || !k2 || k1.length != k2.length) { return false; } let key; for (let i = 0; i < k1.length; i++) { key = k1[i]; if (!equalArraysOrString(a[key], b[key])) { return false; } } return true; } /** * Test equality for arrays of strings or a string. */ function equalArraysOrString(a, b) { if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) return false; const aSorted = [...a].sort(); const bSorted = [...b].sort(); return aSorted.every((val, index) => bSorted[index] === val); } else { return a === b; } } /** * Flattens single-level nested arrays. */ function flatten(arr) { return Array.prototype.concat.apply([], arr); } /** * Return the last element of an array. */ function last(a) { return a.length > 0 ? a[a.length - 1] : null; } /** * Verifys all booleans in an array are `true`. */ function and(bools) { return !bools.some(v => !v); } function forEach(map, callback) { for (const prop in map) { if (map.hasOwnProperty(prop)) { callback(map[prop], prop); } } } function wrapIntoObservable(value) { if ((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵisObservable"])(value)) { return value; } if ((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵisPromise"])(value)) { // Use `Promise.resolve()` to wrap promise-like instances. // Required ie when a Resolver returns a AngularJS `$q` promise to correctly trigger the // change detection. return (0,rxjs__WEBPACK_IMPORTED_MODULE_1__.from)(Promise.resolve(value)); } return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(value); } const NG_DEV_MODE$b = typeof ngDevMode === 'undefined' || ngDevMode; const pathCompareMap = { 'exact': equalSegmentGroups, 'subset': containsSegmentGroup }; const paramCompareMap = { 'exact': equalParams, 'subset': containsParams, 'ignored': () => true }; function containsTree(container, containee, options) { return pathCompareMap[options.paths](container.root, containee.root, options.matrixParams) && paramCompareMap[options.queryParams](container.queryParams, containee.queryParams) && !(options.fragment === 'exact' && container.fragment !== containee.fragment); } function equalParams(container, containee) { // TODO: This does not handle array params correctly. return shallowEqual(container, containee); } function equalSegmentGroups(container, containee, matrixParams) { if (!equalPath(container.segments, containee.segments)) return false; if (!matrixParamsMatch(container.segments, containee.segments, matrixParams)) { return false; } if (container.numberOfChildren !== containee.numberOfChildren) return false; for (const c in containee.children) { if (!container.children[c]) return false; if (!equalSegmentGroups(container.children[c], containee.children[c], matrixParams)) return false; } return true; } function containsParams(container, containee) { return Object.keys(containee).length <= Object.keys(container).length && Object.keys(containee).every(key => equalArraysOrString(container[key], containee[key])); } function containsSegmentGroup(container, containee, matrixParams) { return containsSegmentGroupHelper(container, containee, containee.segments, matrixParams); } function containsSegmentGroupHelper(container, containee, containeePaths, matrixParams) { if (container.segments.length > containeePaths.length) { const current = container.segments.slice(0, containeePaths.length); if (!equalPath(current, containeePaths)) return false; if (containee.hasChildren()) return false; if (!matrixParamsMatch(current, containeePaths, matrixParams)) return false; return true; } else if (container.segments.length === containeePaths.length) { if (!equalPath(container.segments, containeePaths)) return false; if (!matrixParamsMatch(container.segments, containeePaths, matrixParams)) return false; for (const c in containee.children) { if (!container.children[c]) return false; if (!containsSegmentGroup(container.children[c], containee.children[c], matrixParams)) { return false; } } return true; } else { const current = containeePaths.slice(0, container.segments.length); const next = containeePaths.slice(container.segments.length); if (!equalPath(container.segments, current)) return false; if (!matrixParamsMatch(container.segments, current, matrixParams)) return false; if (!container.children[PRIMARY_OUTLET]) return false; return containsSegmentGroupHelper(container.children[PRIMARY_OUTLET], containee, next, matrixParams); } } function matrixParamsMatch(containerPaths, containeePaths, options) { return containeePaths.every((containeeSegment, i) => { return paramCompareMap[options](containerPaths[i].parameters, containeeSegment.parameters); }); } /** * @description * * Represents the parsed URL. * * Since a router state is a tree, and the URL is nothing but a serialized state, the URL is a * serialized tree. * UrlTree is a data structure that provides a lot of affordances in dealing with URLs * * @usageNotes * ### Example * * ``` * @Component({templateUrl:'template.html'}) * class MyComponent { * constructor(router: Router) { * const tree: UrlTree = * router.parseUrl('/team/33/(user/victor//support:help)?debug=true#fragment'); * const f = tree.fragment; // return 'fragment' * const q = tree.queryParams; // returns {debug: 'true'} * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET]; * const s: UrlSegment[] = g.segments; // returns 2 segments 'team' and '33' * g.children[PRIMARY_OUTLET].segments; // returns 2 segments 'user' and 'victor' * g.children['support'].segments; // return 1 segment 'help' * } * } * ``` * * @publicApi */ class UrlTree { constructor( /** The root segment group of the URL tree */ root = new UrlSegmentGroup([], {}), /** The query params of the URL */ queryParams = {}, /** The fragment of the URL */ fragment = null) { this.root = root; this.queryParams = queryParams; this.fragment = fragment; if (NG_DEV_MODE$b) { if (root.segments.length > 0) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4015 /* RuntimeErrorCode.INVALID_ROOT_URL_SEGMENT */, 'The root `UrlSegmentGroup` should not contain `segments`. ' + 'Instead, these segments belong in the `children` so they can be associated with a named outlet.'); } } } get queryParamMap() { if (!this._queryParamMap) { this._queryParamMap = convertToParamMap(this.queryParams); } return this._queryParamMap; } /** @docsNotRequired */ toString() { return DEFAULT_SERIALIZER.serialize(this); } } /** * @description * * Represents the parsed URL segment group. * * See `UrlTree` for more information. * * @publicApi */ class UrlSegmentGroup { constructor( /** The URL segments of this group. See `UrlSegment` for more information */ segments, /** The list of children of this group */ children) { this.segments = segments; this.children = children; /** The parent node in the url tree */ this.parent = null; forEach(children, (v, k) => v.parent = this); } /** Whether the segment has child segments */ hasChildren() { return this.numberOfChildren > 0; } /** Number of child segments */ get numberOfChildren() { return Object.keys(this.children).length; } /** @docsNotRequired */ toString() { return serializePaths(this); } } /** * @description * * Represents a single URL segment. * * A UrlSegment is a part of a URL between the two slashes. It contains a path and the matrix * parameters associated with the segment. * * @usageNotes * ### Example * * ``` * @Component({templateUrl:'template.html'}) * class MyComponent { * constructor(router: Router) { * const tree: UrlTree = router.parseUrl('/team;id=33'); * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET]; * const s: UrlSegment[] = g.segments; * s[0].path; // returns 'team' * s[0].parameters; // returns {id: 33} * } * } * ``` * * @publicApi */ class UrlSegment { constructor( /** The path part of a URL segment */ path, /** The matrix parameters associated with a segment */ parameters) { this.path = path; this.parameters = parameters; } get parameterMap() { if (!this._parameterMap) { this._parameterMap = convertToParamMap(this.parameters); } return this._parameterMap; } /** @docsNotRequired */ toString() { return serializePath(this); } } function equalSegments(as, bs) { return equalPath(as, bs) && as.every((a, i) => shallowEqual(a.parameters, bs[i].parameters)); } function equalPath(as, bs) { if (as.length !== bs.length) return false; return as.every((a, i) => a.path === bs[i].path); } function mapChildrenIntoArray(segment, fn) { let res = []; forEach(segment.children, (child, childOutlet) => { if (childOutlet === PRIMARY_OUTLET) { res = res.concat(fn(child, childOutlet)); } }); forEach(segment.children, (child, childOutlet) => { if (childOutlet !== PRIMARY_OUTLET) { res = res.concat(fn(child, childOutlet)); } }); return res; } /** * @description * * Serializes and deserializes a URL string into a URL tree. * * The url serialization strategy is customizable. You can * make all URLs case insensitive by providing a custom UrlSerializer. * * See `DefaultUrlSerializer` for an example of a URL serializer. * * @publicApi */ class UrlSerializer {} UrlSerializer.ɵfac = function UrlSerializer_Factory(t) { return new (t || UrlSerializer)(); }; UrlSerializer.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: UrlSerializer, factory: function () { return (() => new DefaultUrlSerializer())(); }, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](UrlSerializer, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root', useFactory: () => new DefaultUrlSerializer() }] }], null, null); })(); /** * @description * * A default implementation of the `UrlSerializer`. * * Example URLs: * * ``` * /inbox/33(popup:compose) * /inbox/33;open=true/messages/44 * ``` * * DefaultUrlSerializer uses parentheses to serialize secondary segments (e.g., popup:compose), the * colon syntax to specify the outlet, and the ';parameter=value' syntax (e.g., open=true) to * specify route specific parameters. * * @publicApi */ class DefaultUrlSerializer { /** Parses a url into a `UrlTree` */ parse(url) { const p = new UrlParser(url); return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment()); } /** Converts a `UrlTree` into a url */ serialize(tree) { const segment = `/${serializeSegment(tree.root, true)}`; const query = serializeQueryParams(tree.queryParams); const fragment = typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment)}` : ''; return `${segment}${query}${fragment}`; } } const DEFAULT_SERIALIZER = new DefaultUrlSerializer(); function serializePaths(segment) { return segment.segments.map(p => serializePath(p)).join('/'); } function serializeSegment(segment, root) { if (!segment.hasChildren()) { return serializePaths(segment); } if (root) { const primary = segment.children[PRIMARY_OUTLET] ? serializeSegment(segment.children[PRIMARY_OUTLET], false) : ''; const children = []; forEach(segment.children, (v, k) => { if (k !== PRIMARY_OUTLET) { children.push(`${k}:${serializeSegment(v, false)}`); } }); return children.length > 0 ? `${primary}(${children.join('//')})` : primary; } else { const children = mapChildrenIntoArray(segment, (v, k) => { if (k === PRIMARY_OUTLET) { return [serializeSegment(segment.children[PRIMARY_OUTLET], false)]; } return [`${k}:${serializeSegment(v, false)}`]; }); // use no parenthesis if the only child is a primary outlet route if (Object.keys(segment.children).length === 1 && segment.children[PRIMARY_OUTLET] != null) { return `${serializePaths(segment)}/${children[0]}`; } return `${serializePaths(segment)}/(${children.join('//')})`; } } /** * Encodes a URI string with the default encoding. This function will only ever be called from * `encodeUriQuery` or `encodeUriSegment` as it's the base set of encodings to be used. We need * a custom encoding because encodeURIComponent is too aggressive and encodes stuff that doesn't * have to be encoded per https://url.spec.whatwg.org. */ function encodeUriString(s) { return encodeURIComponent(s).replace(/%40/g, '@').replace(/%3A/gi, ':').replace(/%24/g, '$').replace(/%2C/gi, ','); } /** * This function should be used to encode both keys and values in a query string key/value. In * the following URL, you need to call encodeUriQuery on "k" and "v": * * http://www.site.org/html;mk=mv?k=v#f */ function encodeUriQuery(s) { return encodeUriString(s).replace(/%3B/gi, ';'); } /** * This function should be used to encode a URL fragment. In the following URL, you need to call * encodeUriFragment on "f": * * http://www.site.org/html;mk=mv?k=v#f */ function encodeUriFragment(s) { return encodeURI(s); } /** * This function should be run on any URI segment as well as the key and value in a key/value * pair for matrix params. In the following URL, you need to call encodeUriSegment on "html", * "mk", and "mv": * * http://www.site.org/html;mk=mv?k=v#f */ function encodeUriSegment(s) { return encodeUriString(s).replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/%26/gi, '&'); } function decode(s) { return decodeURIComponent(s); } // Query keys/values should have the "+" replaced first, as "+" in a query string is " ". // decodeURIComponent function will not decode "+" as a space. function decodeQuery(s) { return decode(s.replace(/\+/g, '%20')); } function serializePath(path) { return `${encodeUriSegment(path.path)}${serializeMatrixParams(path.parameters)}`; } function serializeMatrixParams(params) { return Object.keys(params).map(key => `;${encodeUriSegment(key)}=${encodeUriSegment(params[key])}`).join(''); } function serializeQueryParams(params) { const strParams = Object.keys(params).map(name => { const value = params[name]; return Array.isArray(value) ? value.map(v => `${encodeUriQuery(name)}=${encodeUriQuery(v)}`).join('&') : `${encodeUriQuery(name)}=${encodeUriQuery(value)}`; }).filter(s => !!s); return strParams.length ? `?${strParams.join('&')}` : ''; } const SEGMENT_RE = /^[^\/()?;=#]+/; function matchSegments(str) { const match = str.match(SEGMENT_RE); return match ? match[0] : ''; } const QUERY_PARAM_RE = /^[^=?&#]+/; // Return the name of the query param at the start of the string or an empty string function matchQueryParams(str) { const match = str.match(QUERY_PARAM_RE); return match ? match[0] : ''; } const QUERY_PARAM_VALUE_RE = /^[^&#]+/; // Return the value of the query param at the start of the string or an empty string function matchUrlQueryParamValue(str) { const match = str.match(QUERY_PARAM_VALUE_RE); return match ? match[0] : ''; } class UrlParser { constructor(url) { this.url = url; this.remaining = url; } parseRootSegment() { this.consumeOptional('/'); if (this.remaining === '' || this.peekStartsWith('?') || this.peekStartsWith('#')) { return new UrlSegmentGroup([], {}); } // The root segment group never has segments return new UrlSegmentGroup([], this.parseChildren()); } parseQueryParams() { const params = {}; if (this.consumeOptional('?')) { do { this.parseQueryParam(params); } while (this.consumeOptional('&')); } return params; } parseFragment() { return this.consumeOptional('#') ? decodeURIComponent(this.remaining) : null; } parseChildren() { if (this.remaining === '') { return {}; } this.consumeOptional('/'); const segments = []; if (!this.peekStartsWith('(')) { segments.push(this.parseSegment()); } while (this.peekStartsWith('/') && !this.peekStartsWith('//') && !this.peekStartsWith('/(')) { this.capture('/'); segments.push(this.parseSegment()); } let children = {}; if (this.peekStartsWith('/(')) { this.capture('/'); children = this.parseParens(true); } let res = {}; if (this.peekStartsWith('(')) { res = this.parseParens(false); } if (segments.length > 0 || Object.keys(children).length > 0) { res[PRIMARY_OUTLET] = new UrlSegmentGroup(segments, children); } return res; } // parse a segment with its matrix parameters // ie `name;k1=v1;k2` parseSegment() { const path = matchSegments(this.remaining); if (path === '' && this.peekStartsWith(';')) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4009 /* RuntimeErrorCode.EMPTY_PATH_WITH_PARAMS */, NG_DEV_MODE$b && `Empty path url segment cannot have parameters: '${this.remaining}'.`); } this.capture(path); return new UrlSegment(decode(path), this.parseMatrixParams()); } parseMatrixParams() { const params = {}; while (this.consumeOptional(';')) { this.parseParam(params); } return params; } parseParam(params) { const key = matchSegments(this.remaining); if (!key) { return; } this.capture(key); let value = ''; if (this.consumeOptional('=')) { const valueMatch = matchSegments(this.remaining); if (valueMatch) { value = valueMatch; this.capture(value); } } params[decode(key)] = decode(value); } // Parse a single query parameter `name[=value]` parseQueryParam(params) { const key = matchQueryParams(this.remaining); if (!key) { return; } this.capture(key); let value = ''; if (this.consumeOptional('=')) { const valueMatch = matchUrlQueryParamValue(this.remaining); if (valueMatch) { value = valueMatch; this.capture(value); } } const decodedKey = decodeQuery(key); const decodedVal = decodeQuery(value); if (params.hasOwnProperty(decodedKey)) { // Append to existing values let currentVal = params[decodedKey]; if (!Array.isArray(currentVal)) { currentVal = [currentVal]; params[decodedKey] = currentVal; } currentVal.push(decodedVal); } else { // Create a new value params[decodedKey] = decodedVal; } } // parse `(a/b//outlet_name:c/d)` parseParens(allowPrimary) { const segments = {}; this.capture('('); while (!this.consumeOptional(')') && this.remaining.length > 0) { const path = matchSegments(this.remaining); const next = this.remaining[path.length]; // if is is not one of these characters, then the segment was unescaped // or the group was not closed if (next !== '/' && next !== ')' && next !== ';') { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4010 /* RuntimeErrorCode.UNPARSABLE_URL */, NG_DEV_MODE$b && `Cannot parse url '${this.url}'`); } let outletName = undefined; if (path.indexOf(':') > -1) { outletName = path.slice(0, path.indexOf(':')); this.capture(outletName); this.capture(':'); } else if (allowPrimary) { outletName = PRIMARY_OUTLET; } const children = this.parseChildren(); segments[outletName] = Object.keys(children).length === 1 ? children[PRIMARY_OUTLET] : new UrlSegmentGroup([], children); this.consumeOptional('//'); } return segments; } peekStartsWith(str) { return this.remaining.startsWith(str); } // Consumes the prefix when it is present and returns whether it has been consumed consumeOptional(str) { if (this.peekStartsWith(str)) { this.remaining = this.remaining.substring(str.length); return true; } return false; } capture(str) { if (!this.consumeOptional(str)) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4011 /* RuntimeErrorCode.UNEXPECTED_VALUE_IN_URL */, NG_DEV_MODE$b && `Expected "${str}".`); } } } function createRoot(rootCandidate) { return rootCandidate.segments.length > 0 ? new UrlSegmentGroup([], { [PRIMARY_OUTLET]: rootCandidate }) : rootCandidate; } /** * Recursively merges primary segment children into their parents and also drops empty children * (those which have no segments and no children themselves). The latter prevents serializing a * group into something like `/a(aux:)`, where `aux` is an empty child segment. */ function squashSegmentGroup(segmentGroup) { const newChildren = {}; for (const childOutlet of Object.keys(segmentGroup.children)) { const child = segmentGroup.children[childOutlet]; const childCandidate = squashSegmentGroup(child); // don't add empty children if (childCandidate.segments.length > 0 || childCandidate.hasChildren()) { newChildren[childOutlet] = childCandidate; } } const s = new UrlSegmentGroup(segmentGroup.segments, newChildren); return mergeTrivialChildren(s); } /** * When possible, merges the primary outlet child into the parent `UrlSegmentGroup`. * * When a segment group has only one child which is a primary outlet, merges that child into the * parent. That is, the child segment group's segments are merged into the `s` and the child's * children become the children of `s`. Think of this like a 'squash', merging the child segment * group into the parent. */ function mergeTrivialChildren(s) { if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) { const c = s.children[PRIMARY_OUTLET]; return new UrlSegmentGroup(s.segments.concat(c.segments), c.children); } return s; } function isUrlTree(v) { return v instanceof UrlTree; } const NG_DEV_MODE$a = typeof ngDevMode === 'undefined' || ngDevMode; /** * Creates a `UrlTree` relative to an `ActivatedRouteSnapshot`. * * @publicApi * * * @param relativeTo The `ActivatedRouteSnapshot` to apply the commands to * @param commands An array of URL fragments with which to construct the new URL tree. * If the path is static, can be the literal URL string. For a dynamic path, pass an array of path * segments, followed by the parameters for each segment. * The fragments are applied to the one provided in the `relativeTo` parameter. * @param queryParams The query parameters for the `UrlTree`. `null` if the `UrlTree` does not have * any query parameters. * @param fragment The fragment for the `UrlTree`. `null` if the `UrlTree` does not have a fragment. * * @usageNotes * * ``` * // create /team/33/user/11 * createUrlTreeFromSnapshot(snapshot, ['/team', 33, 'user', 11]); * * // create /team/33;expand=true/user/11 * createUrlTreeFromSnapshot(snapshot, ['/team', 33, {expand: true}, 'user', 11]); * * // you can collapse static segments like this (this works only with the first passed-in value): * createUrlTreeFromSnapshot(snapshot, ['/team/33/user', userId]); * * // If the first segment can contain slashes, and you do not want the router to split it, * // you can do the following: * createUrlTreeFromSnapshot(snapshot, [{segmentPath: '/one/two'}]); * * // create /team/33/(user/11//right:chat) * createUrlTreeFromSnapshot(snapshot, ['/team', 33, {outlets: {primary: 'user/11', right: * 'chat'}}], null, null); * * // remove the right secondary node * createUrlTreeFromSnapshot(snapshot, ['/team', 33, {outlets: {primary: 'user/11', right: null}}]); * * // For the examples below, assume the current URL is for the `/team/33/user/11` and the * `ActivatedRouteSnapshot` points to `user/11`: * * // navigate to /team/33/user/11/details * createUrlTreeFromSnapshot(snapshot, ['details']); * * // navigate to /team/33/user/22 * createUrlTreeFromSnapshot(snapshot, ['../22']); * * // navigate to /team/44/user/22 * createUrlTreeFromSnapshot(snapshot, ['../../team/44/user/22']); * ``` */ function createUrlTreeFromSnapshot(relativeTo, commands, queryParams = null, fragment = null) { const relativeToUrlSegmentGroup = createSegmentGroupFromRoute(relativeTo); return createUrlTreeFromSegmentGroup(relativeToUrlSegmentGroup, commands, queryParams, fragment); } function createSegmentGroupFromRoute(route) { let targetGroup; function createSegmentGroupFromRouteRecursive(currentRoute) { const childOutlets = {}; for (const childSnapshot of currentRoute.children) { const root = createSegmentGroupFromRouteRecursive(childSnapshot); childOutlets[childSnapshot.outlet] = root; } const segmentGroup = new UrlSegmentGroup(currentRoute.url, childOutlets); if (currentRoute === route) { targetGroup = segmentGroup; } return segmentGroup; } const rootCandidate = createSegmentGroupFromRouteRecursive(route.root); const rootSegmentGroup = createRoot(rootCandidate); return targetGroup ?? rootSegmentGroup; } function createUrlTreeFromSegmentGroup(relativeTo, commands, queryParams, fragment) { let root = relativeTo; while (root.parent) { root = root.parent; } // There are no commands so the `UrlTree` goes to the same path as the one created from the // `UrlSegmentGroup`. All we need to do is update the `queryParams` and `fragment` without // applying any other logic. if (commands.length === 0) { return tree(root, root, root, queryParams, fragment); } const nav = computeNavigation(commands); if (nav.toRoot()) { return tree(root, root, new UrlSegmentGroup([], {}), queryParams, fragment); } const position = findStartingPositionForTargetGroup(nav, root, relativeTo); const newSegmentGroup = position.processChildren ? updateSegmentGroupChildren(position.segmentGroup, position.index, nav.commands) : updateSegmentGroup(position.segmentGroup, position.index, nav.commands); return tree(root, position.segmentGroup, newSegmentGroup, queryParams, fragment); } function createUrlTree(route, urlTree, commands, queryParams, fragment) { if (commands.length === 0) { return tree(urlTree.root, urlTree.root, urlTree.root, queryParams, fragment); } const nav = computeNavigation(commands); if (nav.toRoot()) { return tree(urlTree.root, urlTree.root, new UrlSegmentGroup([], {}), queryParams, fragment); } function createTreeUsingPathIndex(lastPathIndex) { const startingPosition = findStartingPosition(nav, urlTree, route.snapshot?._urlSegment, lastPathIndex); const segmentGroup = startingPosition.processChildren ? updateSegmentGroupChildren(startingPosition.segmentGroup, startingPosition.index, nav.commands) : updateSegmentGroup(startingPosition.segmentGroup, startingPosition.index, nav.commands); return tree(urlTree.root, startingPosition.segmentGroup, segmentGroup, queryParams, fragment); } // Note: The types should disallow `snapshot` from being `undefined` but due to test mocks, this // may be the case. Since we try to access it at an earlier point before the refactor to add the // warning for `relativeLinkResolution: 'legacy'`, this may cause failures in tests where it // didn't before. const result = createTreeUsingPathIndex(route.snapshot?._lastPathIndex); return result; } function isMatrixParams(command) { return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath; } /** * Determines if a given command has an `outlets` map. When we encounter a command * with an outlets k/v map, we need to apply each outlet individually to the existing segment. */ function isCommandWithOutlets(command) { return typeof command === 'object' && command != null && command.outlets; } function tree(oldRoot, oldSegmentGroup, newSegmentGroup, queryParams, fragment) { let qp = {}; if (queryParams) { forEach(queryParams, (value, name) => { qp[name] = Array.isArray(value) ? value.map(v => `${v}`) : `${value}`; }); } let rootCandidate; if (oldRoot === oldSegmentGroup) { rootCandidate = newSegmentGroup; } else { rootCandidate = replaceSegment(oldRoot, oldSegmentGroup, newSegmentGroup); } const newRoot = createRoot(squashSegmentGroup(rootCandidate)); return new UrlTree(newRoot, qp, fragment); } /** * Replaces the `oldSegment` which is located in some child of the `current` with the `newSegment`. * This also has the effect of creating new `UrlSegmentGroup` copies to update references. This * shouldn't be necessary but the fallback logic for an invalid ActivatedRoute in the creation uses * the Router's current url tree. If we don't create new segment groups, we end up modifying that * value. */ function replaceSegment(current, oldSegment, newSegment) { const children = {}; forEach(current.children, (c, outletName) => { if (c === oldSegment) { children[outletName] = newSegment; } else { children[outletName] = replaceSegment(c, oldSegment, newSegment); } }); return new UrlSegmentGroup(current.segments, children); } class Navigation { constructor(isAbsolute, numberOfDoubleDots, commands) { this.isAbsolute = isAbsolute; this.numberOfDoubleDots = numberOfDoubleDots; this.commands = commands; if (isAbsolute && commands.length > 0 && isMatrixParams(commands[0])) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4003 /* RuntimeErrorCode.ROOT_SEGMENT_MATRIX_PARAMS */, NG_DEV_MODE$a && 'Root segment cannot have matrix parameters'); } const cmdWithOutlet = commands.find(isCommandWithOutlets); if (cmdWithOutlet && cmdWithOutlet !== last(commands)) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4004 /* RuntimeErrorCode.MISPLACED_OUTLETS_COMMAND */, NG_DEV_MODE$a && '{outlets:{}} has to be the last command'); } } toRoot() { return this.isAbsolute && this.commands.length === 1 && this.commands[0] == '/'; } } /** Transforms commands to a normalized `Navigation` */ function computeNavigation(commands) { if (typeof commands[0] === 'string' && commands.length === 1 && commands[0] === '/') { return new Navigation(true, 0, commands); } let numberOfDoubleDots = 0; let isAbsolute = false; const res = commands.reduce((res, cmd, cmdIdx) => { if (typeof cmd === 'object' && cmd != null) { if (cmd.outlets) { const outlets = {}; forEach(cmd.outlets, (commands, name) => { outlets[name] = typeof commands === 'string' ? commands.split('/') : commands; }); return [...res, { outlets }]; } if (cmd.segmentPath) { return [...res, cmd.segmentPath]; } } if (!(typeof cmd === 'string')) { return [...res, cmd]; } if (cmdIdx === 0) { cmd.split('/').forEach((urlPart, partIndex) => { if (partIndex == 0 && urlPart === '.') { // skip './a' } else if (partIndex == 0 && urlPart === '') { // '/a' isAbsolute = true; } else if (urlPart === '..') { // '../a' numberOfDoubleDots++; } else if (urlPart != '') { res.push(urlPart); } }); return res; } return [...res, cmd]; }, []); return new Navigation(isAbsolute, numberOfDoubleDots, res); } class Position { constructor(segmentGroup, processChildren, index) { this.segmentGroup = segmentGroup; this.processChildren = processChildren; this.index = index; } } function findStartingPositionForTargetGroup(nav, root, target) { if (nav.isAbsolute) { return new Position(root, true, 0); } if (!target) { // `NaN` is used only to maintain backwards compatibility with incorrectly mocked // `ActivatedRouteSnapshot` in tests. In prior versions of this code, the position here was // determined based on an internal property that was rarely mocked, resulting in `NaN`. In // reality, this code path should _never_ be touched since `target` is not allowed to be falsey. return new Position(root, false, NaN); } if (target.parent === null) { return new Position(target, true, 0); } const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1; const index = target.segments.length - 1 + modifier; return createPositionApplyingDoubleDots(target, index, nav.numberOfDoubleDots); } function findStartingPosition(nav, tree, segmentGroup, lastPathIndex) { if (nav.isAbsolute) { return new Position(tree.root, true, 0); } if (lastPathIndex === -1) { // Pathless ActivatedRoute has _lastPathIndex === -1 but should not process children // see issue #26224, #13011, #35687 // However, if the ActivatedRoute is the root we should process children like above. const processChildren = segmentGroup === tree.root; return new Position(segmentGroup, processChildren, 0); } const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1; const index = lastPathIndex + modifier; return createPositionApplyingDoubleDots(segmentGroup, index, nav.numberOfDoubleDots); } function createPositionApplyingDoubleDots(group, index, numberOfDoubleDots) { let g = group; let ci = index; let dd = numberOfDoubleDots; while (dd > ci) { dd -= ci; g = g.parent; if (!g) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4005 /* RuntimeErrorCode.INVALID_DOUBLE_DOTS */, NG_DEV_MODE$a && 'Invalid number of \'../\''); } ci = g.segments.length; } return new Position(g, false, ci - dd); } function getOutlets(commands) { if (isCommandWithOutlets(commands[0])) { return commands[0].outlets; } return { [PRIMARY_OUTLET]: commands }; } function updateSegmentGroup(segmentGroup, startIndex, commands) { if (!segmentGroup) { segmentGroup = new UrlSegmentGroup([], {}); } if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) { return updateSegmentGroupChildren(segmentGroup, startIndex, commands); } const m = prefixedWith(segmentGroup, startIndex, commands); const slicedCommands = commands.slice(m.commandIndex); if (m.match && m.pathIndex < segmentGroup.segments.length) { const g = new UrlSegmentGroup(segmentGroup.segments.slice(0, m.pathIndex), {}); g.children[PRIMARY_OUTLET] = new UrlSegmentGroup(segmentGroup.segments.slice(m.pathIndex), segmentGroup.children); return updateSegmentGroupChildren(g, 0, slicedCommands); } else if (m.match && slicedCommands.length === 0) { return new UrlSegmentGroup(segmentGroup.segments, {}); } else if (m.match && !segmentGroup.hasChildren()) { return createNewSegmentGroup(segmentGroup, startIndex, commands); } else if (m.match) { return updateSegmentGroupChildren(segmentGroup, 0, slicedCommands); } else { return createNewSegmentGroup(segmentGroup, startIndex, commands); } } function updateSegmentGroupChildren(segmentGroup, startIndex, commands) { if (commands.length === 0) { return new UrlSegmentGroup(segmentGroup.segments, {}); } else { const outlets = getOutlets(commands); const children = {}; // If the set of commands does not apply anything to the primary outlet and the child segment is // an empty path primary segment on its own, we want to apply the commands to the empty child // path rather than here. The outcome is that the empty primary child is effectively removed // from the final output UrlTree. Imagine the following config: // // {path: '', children: [{path: '**', outlet: 'popup'}]}. // // Navigation to /(popup:a) will activate the child outlet correctly Given a follow-up // navigation with commands // ['/', {outlets: {'popup': 'b'}}], we _would not_ want to apply the outlet commands to the // root segment because that would result in // //(popup:a)(popup:b) since the outlet command got applied one level above where it appears in // the `ActivatedRoute` rather than updating the existing one. // // Because empty paths do not appear in the URL segments and the fact that the segments used in // the output `UrlTree` are squashed to eliminate these empty paths where possible // https://github.com/angular/angular/blob/13f10de40e25c6900ca55bd83b36bd533dacfa9e/packages/router/src/url_tree.ts#L755 // it can be hard to determine what is the right thing to do when applying commands to a // `UrlSegmentGroup` that is created from an "unsquashed"/expanded `ActivatedRoute` tree. // This code effectively "squashes" empty path primary routes when they have no siblings on // the same level of the tree. if (!outlets[PRIMARY_OUTLET] && segmentGroup.children[PRIMARY_OUTLET] && segmentGroup.numberOfChildren === 1 && segmentGroup.children[PRIMARY_OUTLET].segments.length === 0) { const childrenOfEmptyChild = updateSegmentGroupChildren(segmentGroup.children[PRIMARY_OUTLET], startIndex, commands); return new UrlSegmentGroup(segmentGroup.segments, childrenOfEmptyChild.children); } forEach(outlets, (commands, outlet) => { if (typeof commands === 'string') { commands = [commands]; } if (commands !== null) { children[outlet] = updateSegmentGroup(segmentGroup.children[outlet], startIndex, commands); } }); forEach(segmentGroup.children, (child, childOutlet) => { if (outlets[childOutlet] === undefined) { children[childOutlet] = child; } }); return new UrlSegmentGroup(segmentGroup.segments, children); } } function prefixedWith(segmentGroup, startIndex, commands) { let currentCommandIndex = 0; let currentPathIndex = startIndex; const noMatch = { match: false, pathIndex: 0, commandIndex: 0 }; while (currentPathIndex < segmentGroup.segments.length) { if (currentCommandIndex >= commands.length) return noMatch; const path = segmentGroup.segments[currentPathIndex]; const command = commands[currentCommandIndex]; // Do not try to consume command as part of the prefixing if it has outlets because it can // contain outlets other than the one being processed. Consuming the outlets command would // result in other outlets being ignored. if (isCommandWithOutlets(command)) { break; } const curr = `${command}`; const next = currentCommandIndex < commands.length - 1 ? commands[currentCommandIndex + 1] : null; if (currentPathIndex > 0 && curr === undefined) break; if (curr && next && typeof next === 'object' && next.outlets === undefined) { if (!compare(curr, next, path)) return noMatch; currentCommandIndex += 2; } else { if (!compare(curr, {}, path)) return noMatch; currentCommandIndex++; } currentPathIndex++; } return { match: true, pathIndex: currentPathIndex, commandIndex: currentCommandIndex }; } function createNewSegmentGroup(segmentGroup, startIndex, commands) { const paths = segmentGroup.segments.slice(0, startIndex); let i = 0; while (i < commands.length) { const command = commands[i]; if (isCommandWithOutlets(command)) { const children = createNewSegmentChildren(command.outlets); return new UrlSegmentGroup(paths, children); } // if we start with an object literal, we need to reuse the path part from the segment if (i === 0 && isMatrixParams(commands[0])) { const p = segmentGroup.segments[startIndex]; paths.push(new UrlSegment(p.path, stringify(commands[0]))); i++; continue; } const curr = isCommandWithOutlets(command) ? command.outlets[PRIMARY_OUTLET] : `${command}`; const next = i < commands.length - 1 ? commands[i + 1] : null; if (curr && next && isMatrixParams(next)) { paths.push(new UrlSegment(curr, stringify(next))); i += 2; } else { paths.push(new UrlSegment(curr, {})); i++; } } return new UrlSegmentGroup(paths, {}); } function createNewSegmentChildren(outlets) { const children = {}; forEach(outlets, (commands, outlet) => { if (typeof commands === 'string') { commands = [commands]; } if (commands !== null) { children[outlet] = createNewSegmentGroup(new UrlSegmentGroup([], {}), 0, commands); } }); return children; } function stringify(params) { const res = {}; forEach(params, (v, k) => res[k] = `${v}`); return res; } function compare(path, params, segment) { return path == segment.path && shallowEqual(params, segment.parameters); } const IMPERATIVE_NAVIGATION = 'imperative'; /** * Base for events the router goes through, as opposed to events tied to a specific * route. Fired one time for any given navigation. * * The following code shows how a class subscribes to router events. * * ```ts * import {Event, RouterEvent, Router} from '@angular/router'; * * class MyService { * constructor(public router: Router) { * router.events.pipe( * filter((e: Event): e is RouterEvent => e instanceof RouterEvent) * ).subscribe((e: RouterEvent) => { * // Do something * }); * } * } * ``` * * @see `Event` * @see [Router events summary](guide/router-reference#router-events) * @publicApi */ class RouterEvent { constructor( /** A unique ID that the router assigns to every router navigation. */ id, /** The URL that is the destination for this navigation. */ url) { this.id = id; this.url = url; } } /** * An event triggered when a navigation starts. * * @publicApi */ class NavigationStart extends RouterEvent { constructor( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ navigationTrigger = 'imperative', /** @docsNotRequired */ restoredState = null) { super(id, url); this.type = 0 /* EventType.NavigationStart */; this.navigationTrigger = navigationTrigger; this.restoredState = restoredState; } /** @docsNotRequired */ toString() { return `NavigationStart(id: ${this.id}, url: '${this.url}')`; } } /** * An event triggered when a navigation ends successfully. * * @see `NavigationStart` * @see `NavigationCancel` * @see `NavigationError` * * @publicApi */ class NavigationEnd extends RouterEvent { constructor( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ urlAfterRedirects) { super(id, url); this.urlAfterRedirects = urlAfterRedirects; this.type = 1 /* EventType.NavigationEnd */; } /** @docsNotRequired */ toString() { return `NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}')`; } } /** * An event triggered when a navigation is canceled, directly or indirectly. * This can happen for several reasons including when a route guard * returns `false` or initiates a redirect by returning a `UrlTree`. * * @see `NavigationStart` * @see `NavigationEnd` * @see `NavigationError` * * @publicApi */ class NavigationCancel extends RouterEvent { constructor( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** * A description of why the navigation was cancelled. For debug purposes only. Use `code` * instead for a stable cancellation reason that can be used in production. */ reason, /** * A code to indicate why the navigation was canceled. This cancellation code is stable for * the reason and can be relied on whereas the `reason` string could change and should not be * used in production. */ code) { super(id, url); this.reason = reason; this.code = code; this.type = 2 /* EventType.NavigationCancel */; } /** @docsNotRequired */ toString() { return `NavigationCancel(id: ${this.id}, url: '${this.url}')`; } } /** * An event triggered when a navigation is skipped. * This can happen for a couple reasons including onSameUrlHandling * is set to `ignore` and the navigation URL is not different than the * current state. * * @publicApi */ class NavigationSkipped extends RouterEvent { constructor( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** * A description of why the navigation was skipped. For debug purposes only. Use `code` * instead for a stable skipped reason that can be used in production. */ reason, /** * A code to indicate why the navigation was skipped. This code is stable for * the reason and can be relied on whereas the `reason` string could change and should not be * used in production. */ code) { super(id, url); this.reason = reason; this.code = code; this.type = 16 /* EventType.NavigationSkipped */; } } /** * An event triggered when a navigation fails due to an unexpected error. * * @see `NavigationStart` * @see `NavigationEnd` * @see `NavigationCancel` * * @publicApi */ class NavigationError extends RouterEvent { constructor( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ error, /** * The target of the navigation when the error occurred. * * Note that this can be `undefined` because an error could have occurred before the * `RouterStateSnapshot` was created for the navigation. */ target) { super(id, url); this.error = error; this.target = target; this.type = 3 /* EventType.NavigationError */; } /** @docsNotRequired */ toString() { return `NavigationError(id: ${this.id}, url: '${this.url}', error: ${this.error})`; } } /** * An event triggered when routes are recognized. * * @publicApi */ class RoutesRecognized extends RouterEvent { constructor( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ urlAfterRedirects, /** @docsNotRequired */ state) { super(id, url); this.urlAfterRedirects = urlAfterRedirects; this.state = state; this.type = 4 /* EventType.RoutesRecognized */; } /** @docsNotRequired */ toString() { return `RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`; } } /** * An event triggered at the start of the Guard phase of routing. * * @see `GuardsCheckEnd` * * @publicApi */ class GuardsCheckStart extends RouterEvent { constructor( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ urlAfterRedirects, /** @docsNotRequired */ state) { super(id, url); this.urlAfterRedirects = urlAfterRedirects; this.state = state; this.type = 7 /* EventType.GuardsCheckStart */; } toString() { return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`; } } /** * An event triggered at the end of the Guard phase of routing. * * @see `GuardsCheckStart` * * @publicApi */ class GuardsCheckEnd extends RouterEvent { constructor( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ urlAfterRedirects, /** @docsNotRequired */ state, /** @docsNotRequired */ shouldActivate) { super(id, url); this.urlAfterRedirects = urlAfterRedirects; this.state = state; this.shouldActivate = shouldActivate; this.type = 8 /* EventType.GuardsCheckEnd */; } toString() { return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`; } } /** * An event triggered at the start of the Resolve phase of routing. * * Runs in the "resolve" phase whether or not there is anything to resolve. * In future, may change to only run when there are things to be resolved. * * @see `ResolveEnd` * * @publicApi */ class ResolveStart extends RouterEvent { constructor( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ urlAfterRedirects, /** @docsNotRequired */ state) { super(id, url); this.urlAfterRedirects = urlAfterRedirects; this.state = state; this.type = 5 /* EventType.ResolveStart */; } toString() { return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`; } } /** * An event triggered at the end of the Resolve phase of routing. * @see `ResolveStart`. * * @publicApi */ class ResolveEnd extends RouterEvent { constructor( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ urlAfterRedirects, /** @docsNotRequired */ state) { super(id, url); this.urlAfterRedirects = urlAfterRedirects; this.state = state; this.type = 6 /* EventType.ResolveEnd */; } toString() { return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`; } } /** * An event triggered before lazy loading a route configuration. * * @see `RouteConfigLoadEnd` * * @publicApi */ class RouteConfigLoadStart { constructor( /** @docsNotRequired */ route) { this.route = route; this.type = 9 /* EventType.RouteConfigLoadStart */; } toString() { return `RouteConfigLoadStart(path: ${this.route.path})`; } } /** * An event triggered when a route has been lazy loaded. * * @see `RouteConfigLoadStart` * * @publicApi */ class RouteConfigLoadEnd { constructor( /** @docsNotRequired */ route) { this.route = route; this.type = 10 /* EventType.RouteConfigLoadEnd */; } toString() { return `RouteConfigLoadEnd(path: ${this.route.path})`; } } /** * An event triggered at the start of the child-activation * part of the Resolve phase of routing. * @see `ChildActivationEnd` * @see `ResolveStart` * * @publicApi */ class ChildActivationStart { constructor( /** @docsNotRequired */ snapshot) { this.snapshot = snapshot; this.type = 11 /* EventType.ChildActivationStart */; } toString() { const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || ''; return `ChildActivationStart(path: '${path}')`; } } /** * An event triggered at the end of the child-activation part * of the Resolve phase of routing. * @see `ChildActivationStart` * @see `ResolveStart` * @publicApi */ class ChildActivationEnd { constructor( /** @docsNotRequired */ snapshot) { this.snapshot = snapshot; this.type = 12 /* EventType.ChildActivationEnd */; } toString() { const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || ''; return `ChildActivationEnd(path: '${path}')`; } } /** * An event triggered at the start of the activation part * of the Resolve phase of routing. * @see `ActivationEnd` * @see `ResolveStart` * * @publicApi */ class ActivationStart { constructor( /** @docsNotRequired */ snapshot) { this.snapshot = snapshot; this.type = 13 /* EventType.ActivationStart */; } toString() { const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || ''; return `ActivationStart(path: '${path}')`; } } /** * An event triggered at the end of the activation part * of the Resolve phase of routing. * @see `ActivationStart` * @see `ResolveStart` * * @publicApi */ class ActivationEnd { constructor( /** @docsNotRequired */ snapshot) { this.snapshot = snapshot; this.type = 14 /* EventType.ActivationEnd */; } toString() { const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || ''; return `ActivationEnd(path: '${path}')`; } } /** * An event triggered by scrolling. * * @publicApi */ class Scroll { constructor( /** @docsNotRequired */ routerEvent, /** @docsNotRequired */ position, /** @docsNotRequired */ anchor) { this.routerEvent = routerEvent; this.position = position; this.anchor = anchor; this.type = 15 /* EventType.Scroll */; } toString() { const pos = this.position ? `${this.position[0]}, ${this.position[1]}` : null; return `Scroll(anchor: '${this.anchor}', position: '${pos}')`; } } function stringifyEvent(routerEvent) { if (!('type' in routerEvent)) { return `Unknown Router Event: ${routerEvent.constructor.name}`; } switch (routerEvent.type) { case 14 /* EventType.ActivationEnd */: return `ActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`; case 13 /* EventType.ActivationStart */: return `ActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`; case 12 /* EventType.ChildActivationEnd */: return `ChildActivationEnd(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`; case 11 /* EventType.ChildActivationStart */: return `ChildActivationStart(path: '${routerEvent.snapshot.routeConfig?.path || ''}')`; case 8 /* EventType.GuardsCheckEnd */: return `GuardsCheckEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state}, shouldActivate: ${routerEvent.shouldActivate})`; case 7 /* EventType.GuardsCheckStart */: return `GuardsCheckStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`; case 2 /* EventType.NavigationCancel */: return `NavigationCancel(id: ${routerEvent.id}, url: '${routerEvent.url}')`; case 16 /* EventType.NavigationSkipped */: return `NavigationSkipped(id: ${routerEvent.id}, url: '${routerEvent.url}')`; case 1 /* EventType.NavigationEnd */: return `NavigationEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}')`; case 3 /* EventType.NavigationError */: return `NavigationError(id: ${routerEvent.id}, url: '${routerEvent.url}', error: ${routerEvent.error})`; case 0 /* EventType.NavigationStart */: return `NavigationStart(id: ${routerEvent.id}, url: '${routerEvent.url}')`; case 6 /* EventType.ResolveEnd */: return `ResolveEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`; case 5 /* EventType.ResolveStart */: return `ResolveStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`; case 10 /* EventType.RouteConfigLoadEnd */: return `RouteConfigLoadEnd(path: ${routerEvent.route.path})`; case 9 /* EventType.RouteConfigLoadStart */: return `RouteConfigLoadStart(path: ${routerEvent.route.path})`; case 4 /* EventType.RoutesRecognized */: return `RoutesRecognized(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`; case 15 /* EventType.Scroll */: const pos = routerEvent.position ? `${routerEvent.position[0]}, ${routerEvent.position[1]}` : null; return `Scroll(anchor: '${routerEvent.anchor}', position: '${pos}')`; } } const NG_DEV_MODE$9 = typeof ngDevMode === 'undefined' || ngDevMode; class LegacyCreateUrlTree { createUrlTree(relativeTo, currentState, currentUrlTree, commands, queryParams, fragment) { const a = relativeTo || currentState.root; const tree = createUrlTree(a, currentUrlTree, commands, queryParams, fragment); if (NG_DEV_MODE$9) { const treeFromSnapshotStrategy = new CreateUrlTreeUsingSnapshot().createUrlTree(relativeTo, currentState, currentUrlTree, commands, queryParams, fragment); if (treeFromSnapshotStrategy.toString() !== tree.toString()) { let warningString = `The navigation to ${tree.toString()} will instead go to ${treeFromSnapshotStrategy.toString()} in an upcoming version of Angular.`; if (!!relativeTo) { warningString += ' `relativeTo` might need to be removed from the `UrlCreationOptions`.'; } tree._warnIfUsedForNavigation = warningString; } } return tree; } } LegacyCreateUrlTree.ɵfac = function LegacyCreateUrlTree_Factory(t) { return new (t || LegacyCreateUrlTree)(); }; LegacyCreateUrlTree.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: LegacyCreateUrlTree, factory: LegacyCreateUrlTree.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](LegacyCreateUrlTree, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable }], null, null); })(); class CreateUrlTreeUsingSnapshot { createUrlTree(relativeTo, currentState, currentUrlTree, commands, queryParams, fragment) { let relativeToUrlSegmentGroup; try { const relativeToSnapshot = relativeTo ? relativeTo.snapshot : currentState.snapshot.root; relativeToUrlSegmentGroup = createSegmentGroupFromRoute(relativeToSnapshot); } catch (e) { // This is strictly for backwards compatibility with tests that create // invalid `ActivatedRoute` mocks. // Note: the difference between having this fallback for invalid `ActivatedRoute` setups and // just throwing is ~500 test failures. Fixing all of those tests by hand is not feasible at // the moment. if (typeof commands[0] !== 'string' || !commands[0].startsWith('/')) { // Navigations that were absolute in the old way of creating UrlTrees // would still work because they wouldn't attempt to match the // segments in the `ActivatedRoute` to the `currentUrlTree` but // instead just replace the root segment with the navigation result. // Non-absolute navigations would fail to apply the commands because // the logic could not find the segment to replace (so they'd act like there were no // commands). commands = []; } relativeToUrlSegmentGroup = currentUrlTree.root; } return createUrlTreeFromSegmentGroup(relativeToUrlSegmentGroup, commands, queryParams, fragment); } } CreateUrlTreeUsingSnapshot.ɵfac = function CreateUrlTreeUsingSnapshot_Factory(t) { return new (t || CreateUrlTreeUsingSnapshot)(); }; CreateUrlTreeUsingSnapshot.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: CreateUrlTreeUsingSnapshot, factory: CreateUrlTreeUsingSnapshot.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](CreateUrlTreeUsingSnapshot, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable }], null, null); })(); class CreateUrlTreeStrategy {} CreateUrlTreeStrategy.ɵfac = function CreateUrlTreeStrategy_Factory(t) { return new (t || CreateUrlTreeStrategy)(); }; CreateUrlTreeStrategy.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: CreateUrlTreeStrategy, factory: function (t) { return LegacyCreateUrlTree.ɵfac(t); }, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](CreateUrlTreeStrategy, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root', useClass: LegacyCreateUrlTree }] }], null, null); })(); class Tree { constructor(root) { this._root = root; } get root() { return this._root.value; } /** * @internal */ parent(t) { const p = this.pathFromRoot(t); return p.length > 1 ? p[p.length - 2] : null; } /** * @internal */ children(t) { const n = findNode(t, this._root); return n ? n.children.map(t => t.value) : []; } /** * @internal */ firstChild(t) { const n = findNode(t, this._root); return n && n.children.length > 0 ? n.children[0].value : null; } /** * @internal */ siblings(t) { const p = findPath(t, this._root); if (p.length < 2) return []; const c = p[p.length - 2].children.map(c => c.value); return c.filter(cc => cc !== t); } /** * @internal */ pathFromRoot(t) { return findPath(t, this._root).map(s => s.value); } } // DFS for the node matching the value function findNode(value, node) { if (value === node.value) return node; for (const child of node.children) { const node = findNode(value, child); if (node) return node; } return null; } // Return the path to the node with the given value using DFS function findPath(value, node) { if (value === node.value) return [node]; for (const child of node.children) { const path = findPath(value, child); if (path.length) { path.unshift(node); return path; } } return []; } class TreeNode { constructor(value, children) { this.value = value; this.children = children; } toString() { return `TreeNode(${this.value})`; } } // Return the list of T indexed by outlet name function nodeChildrenAsMap(node) { const map = {}; if (node) { node.children.forEach(child => map[child.value.outlet] = child); } return map; } /** * Represents the state of the router as a tree of activated routes. * * @usageNotes * * Every node in the route tree is an `ActivatedRoute` instance * that knows about the "consumed" URL segments, the extracted parameters, * and the resolved data. * Use the `ActivatedRoute` properties to traverse the tree from any node. * * The following fragment shows how a component gets the root node * of the current state to establish its own route tree: * * ``` * @Component({templateUrl:'template.html'}) * class MyComponent { * constructor(router: Router) { * const state: RouterState = router.routerState; * const root: ActivatedRoute = state.root; * const child = root.firstChild; * const id: Observable<string> = child.params.map(p => p.id); * //... * } * } * ``` * * @see `ActivatedRoute` * @see [Getting route information](guide/router#getting-route-information) * * @publicApi */ class RouterState extends Tree { /** @internal */ constructor(root, /** The current snapshot of the router state */ snapshot) { super(root); this.snapshot = snapshot; setRouterState(this, root); } toString() { return this.snapshot.toString(); } } function createEmptyState(urlTree, rootComponent) { const snapshot = createEmptyStateSnapshot(urlTree, rootComponent); const emptyUrl = new rxjs__WEBPACK_IMPORTED_MODULE_3__.BehaviorSubject([new UrlSegment('', {})]); const emptyParams = new rxjs__WEBPACK_IMPORTED_MODULE_3__.BehaviorSubject({}); const emptyData = new rxjs__WEBPACK_IMPORTED_MODULE_3__.BehaviorSubject({}); const emptyQueryParams = new rxjs__WEBPACK_IMPORTED_MODULE_3__.BehaviorSubject({}); const fragment = new rxjs__WEBPACK_IMPORTED_MODULE_3__.BehaviorSubject(''); const activated = new ActivatedRoute(emptyUrl, emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, snapshot.root); activated.snapshot = snapshot.root; return new RouterState(new TreeNode(activated, []), snapshot); } function createEmptyStateSnapshot(urlTree, rootComponent) { const emptyParams = {}; const emptyData = {}; const emptyQueryParams = {}; const fragment = ''; const activated = new ActivatedRouteSnapshot([], emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, null, urlTree.root, -1, {}); return new RouterStateSnapshot('', new TreeNode(activated, [])); } /** * Provides access to information about a route associated with a component * that is loaded in an outlet. * Use to traverse the `RouterState` tree and extract information from nodes. * * The following example shows how to construct a component using information from a * currently activated route. * * Note: the observables in this class only emit when the current and previous values differ based * on shallow equality. For example, changing deeply nested properties in resolved `data` will not * cause the `ActivatedRoute.data` `Observable` to emit a new value. * * {@example router/activated-route/module.ts region="activated-route" * header="activated-route.component.ts"} * * @see [Getting route information](guide/router#getting-route-information) * * @publicApi */ class ActivatedRoute { /** @internal */ constructor( /** An observable of the URL segments matched by this route. */ url, /** An observable of the matrix parameters scoped to this route. */ params, /** An observable of the query parameters shared by all the routes. */ queryParams, /** An observable of the URL fragment shared by all the routes. */ fragment, /** An observable of the static and resolved data of this route. */ data, /** The outlet name of the route, a constant. */ outlet, /** The component of the route, a constant. */ component, futureSnapshot) { this.url = url; this.params = params; this.queryParams = queryParams; this.fragment = fragment; this.data = data; this.outlet = outlet; this.component = component; /** An Observable of the resolved route title */ this.title = this.data?.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(d => d[RouteTitleKey])) ?? (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(undefined); this._futureSnapshot = futureSnapshot; } /** The configuration used to match this route. */ get routeConfig() { return this._futureSnapshot.routeConfig; } /** The root of the router state. */ get root() { return this._routerState.root; } /** The parent of this route in the router state tree. */ get parent() { return this._routerState.parent(this); } /** The first child of this route in the router state tree. */ get firstChild() { return this._routerState.firstChild(this); } /** The children of this route in the router state tree. */ get children() { return this._routerState.children(this); } /** The path from the root of the router state tree to this route. */ get pathFromRoot() { return this._routerState.pathFromRoot(this); } /** * An Observable that contains a map of the required and optional parameters * specific to the route. * The map supports retrieving single and multiple values from the same parameter. */ get paramMap() { if (!this._paramMap) { this._paramMap = this.params.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(p => convertToParamMap(p))); } return this._paramMap; } /** * An Observable that contains a map of the query parameters available to all routes. * The map supports retrieving single and multiple values from the query parameter. */ get queryParamMap() { if (!this._queryParamMap) { this._queryParamMap = this.queryParams.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(p => convertToParamMap(p))); } return this._queryParamMap; } toString() { return this.snapshot ? this.snapshot.toString() : `Future(${this._futureSnapshot})`; } } /** * Returns the inherited params, data, and resolve for a given route. * By default, this only inherits values up to the nearest path-less or component-less route. * @internal */ function inheritedParamsDataResolve(route, paramsInheritanceStrategy = 'emptyOnly') { const pathFromRoot = route.pathFromRoot; let inheritingStartingFrom = 0; if (paramsInheritanceStrategy !== 'always') { inheritingStartingFrom = pathFromRoot.length - 1; while (inheritingStartingFrom >= 1) { const current = pathFromRoot[inheritingStartingFrom]; const parent = pathFromRoot[inheritingStartingFrom - 1]; // current route is an empty path => inherits its parent's params and data if (current.routeConfig && current.routeConfig.path === '') { inheritingStartingFrom--; // parent is componentless => current route should inherit its params and data } else if (!parent.component) { inheritingStartingFrom--; } else { break; } } } return flattenInherited(pathFromRoot.slice(inheritingStartingFrom)); } /** @internal */ function flattenInherited(pathFromRoot) { return pathFromRoot.reduce((res, curr) => { const params = { ...res.params, ...curr.params }; const data = { ...res.data, ...curr.data }; const resolve = { ...curr.data, ...res.resolve, ...curr.routeConfig?.data, ...curr._resolvedData }; return { params, data, resolve }; }, { params: {}, data: {}, resolve: {} }); } /** * @description * * Contains the information about a route associated with a component loaded in an * outlet at a particular moment in time. ActivatedRouteSnapshot can also be used to * traverse the router state tree. * * The following example initializes a component with route information extracted * from the snapshot of the root node at the time of creation. * * ``` * @Component({templateUrl:'./my-component.html'}) * class MyComponent { * constructor(route: ActivatedRoute) { * const id: string = route.snapshot.params.id; * const url: string = route.snapshot.url.join(''); * const user = route.snapshot.data.user; * } * } * ``` * * @publicApi */ class ActivatedRouteSnapshot { /** The resolved route title */ get title() { // Note: This _must_ be a getter because the data is mutated in the resolvers. Title will not be // available at the time of class instantiation. return this.data?.[RouteTitleKey]; } /** @internal */ constructor( /** The URL segments matched by this route */ url, /** * The matrix parameters scoped to this route. * * You can compute all params (or data) in the router state or to get params outside * of an activated component by traversing the `RouterState` tree as in the following * example: * ``` * collectRouteParams(router: Router) { * let params = {}; * let stack: ActivatedRouteSnapshot[] = [router.routerState.snapshot.root]; * while (stack.length > 0) { * const route = stack.pop()!; * params = {...params, ...route.params}; * stack.push(...route.children); * } * return params; * } * ``` */ params, /** The query parameters shared by all the routes */ queryParams, /** The URL fragment shared by all the routes */ fragment, /** The static and resolved data of this route */ data, /** The outlet name of the route */ outlet, /** The component of the route */ component, routeConfig, urlSegment, lastPathIndex, resolve) { this.url = url; this.params = params; this.queryParams = queryParams; this.fragment = fragment; this.data = data; this.outlet = outlet; this.component = component; this.routeConfig = routeConfig; this._urlSegment = urlSegment; this._lastPathIndex = lastPathIndex; this._resolve = resolve; } /** The root of the router state */ get root() { return this._routerState.root; } /** The parent of this route in the router state tree */ get parent() { return this._routerState.parent(this); } /** The first child of this route in the router state tree */ get firstChild() { return this._routerState.firstChild(this); } /** The children of this route in the router state tree */ get children() { return this._routerState.children(this); } /** The path from the root of the router state tree to this route */ get pathFromRoot() { return this._routerState.pathFromRoot(this); } get paramMap() { if (!this._paramMap) { this._paramMap = convertToParamMap(this.params); } return this._paramMap; } get queryParamMap() { if (!this._queryParamMap) { this._queryParamMap = convertToParamMap(this.queryParams); } return this._queryParamMap; } toString() { const url = this.url.map(segment => segment.toString()).join('/'); const matched = this.routeConfig ? this.routeConfig.path : ''; return `Route(url:'${url}', path:'${matched}')`; } } /** * @description * * Represents the state of the router at a moment in time. * * This is a tree of activated route snapshots. Every node in this tree knows about * the "consumed" URL segments, the extracted parameters, and the resolved data. * * The following example shows how a component is initialized with information * from the snapshot of the root node's state at the time of creation. * * ``` * @Component({templateUrl:'template.html'}) * class MyComponent { * constructor(router: Router) { * const state: RouterState = router.routerState; * const snapshot: RouterStateSnapshot = state.snapshot; * const root: ActivatedRouteSnapshot = snapshot.root; * const child = root.firstChild; * const id: Observable<string> = child.params.map(p => p.id); * //... * } * } * ``` * * @publicApi */ class RouterStateSnapshot extends Tree { /** @internal */ constructor( /** The url from which this snapshot was created */ url, root) { super(root); this.url = url; setRouterState(this, root); } toString() { return serializeNode(this._root); } } function setRouterState(state, node) { node.value._routerState = state; node.children.forEach(c => setRouterState(state, c)); } function serializeNode(node) { const c = node.children.length > 0 ? ` { ${node.children.map(serializeNode).join(', ')} } ` : ''; return `${node.value}${c}`; } /** * The expectation is that the activate route is created with the right set of parameters. * So we push new values into the observables only when they are not the initial values. * And we detect that by checking if the snapshot field is set. */ function advanceActivatedRoute(route) { if (route.snapshot) { const currentSnapshot = route.snapshot; const nextSnapshot = route._futureSnapshot; route.snapshot = nextSnapshot; if (!shallowEqual(currentSnapshot.queryParams, nextSnapshot.queryParams)) { route.queryParams.next(nextSnapshot.queryParams); } if (currentSnapshot.fragment !== nextSnapshot.fragment) { route.fragment.next(nextSnapshot.fragment); } if (!shallowEqual(currentSnapshot.params, nextSnapshot.params)) { route.params.next(nextSnapshot.params); } if (!shallowEqualArrays(currentSnapshot.url, nextSnapshot.url)) { route.url.next(nextSnapshot.url); } if (!shallowEqual(currentSnapshot.data, nextSnapshot.data)) { route.data.next(nextSnapshot.data); } } else { route.snapshot = route._futureSnapshot; // this is for resolved data route.data.next(route._futureSnapshot.data); } } function equalParamsAndUrlSegments(a, b) { const equalUrlParams = shallowEqual(a.params, b.params) && equalSegments(a.url, b.url); const parentsMismatch = !a.parent !== !b.parent; return equalUrlParams && !parentsMismatch && (!a.parent || equalParamsAndUrlSegments(a.parent, b.parent)); } function createRouterState(routeReuseStrategy, curr, prevState) { const root = createNode(routeReuseStrategy, curr._root, prevState ? prevState._root : undefined); return new RouterState(root, curr); } function createNode(routeReuseStrategy, curr, prevState) { // reuse an activated route that is currently displayed on the screen if (prevState && routeReuseStrategy.shouldReuseRoute(curr.value, prevState.value.snapshot)) { const value = prevState.value; value._futureSnapshot = curr.value; const children = createOrReuseChildren(routeReuseStrategy, curr, prevState); return new TreeNode(value, children); } else { if (routeReuseStrategy.shouldAttach(curr.value)) { // retrieve an activated route that is used to be displayed, but is not currently displayed const detachedRouteHandle = routeReuseStrategy.retrieve(curr.value); if (detachedRouteHandle !== null) { const tree = detachedRouteHandle.route; tree.value._futureSnapshot = curr.value; tree.children = curr.children.map(c => createNode(routeReuseStrategy, c)); return tree; } } const value = createActivatedRoute(curr.value); const children = curr.children.map(c => createNode(routeReuseStrategy, c)); return new TreeNode(value, children); } } function createOrReuseChildren(routeReuseStrategy, curr, prevState) { return curr.children.map(child => { for (const p of prevState.children) { if (routeReuseStrategy.shouldReuseRoute(child.value, p.value.snapshot)) { return createNode(routeReuseStrategy, child, p); } } return createNode(routeReuseStrategy, child); }); } function createActivatedRoute(c) { return new ActivatedRoute(new rxjs__WEBPACK_IMPORTED_MODULE_3__.BehaviorSubject(c.url), new rxjs__WEBPACK_IMPORTED_MODULE_3__.BehaviorSubject(c.params), new rxjs__WEBPACK_IMPORTED_MODULE_3__.BehaviorSubject(c.queryParams), new rxjs__WEBPACK_IMPORTED_MODULE_3__.BehaviorSubject(c.fragment), new rxjs__WEBPACK_IMPORTED_MODULE_3__.BehaviorSubject(c.data), c.outlet, c.component, c); } const NAVIGATION_CANCELING_ERROR = 'ngNavigationCancelingError'; function redirectingNavigationError(urlSerializer, redirect) { const { redirectTo, navigationBehaviorOptions } = isUrlTree(redirect) ? { redirectTo: redirect, navigationBehaviorOptions: undefined } : redirect; const error = navigationCancelingError(ngDevMode && `Redirecting to "${urlSerializer.serialize(redirectTo)}"`, 0 /* NavigationCancellationCode.Redirect */, redirect); error.url = redirectTo; error.navigationBehaviorOptions = navigationBehaviorOptions; return error; } function navigationCancelingError(message, code, redirectUrl) { const error = new Error('NavigationCancelingError: ' + (message || '')); error[NAVIGATION_CANCELING_ERROR] = true; error.cancellationCode = code; if (redirectUrl) { error.url = redirectUrl; } return error; } function isRedirectingNavigationCancelingError$1(error) { return isNavigationCancelingError$1(error) && isUrlTree(error.url); } function isNavigationCancelingError$1(error) { return error && error[NAVIGATION_CANCELING_ERROR]; } /** * Store contextual information about a `RouterOutlet` * * @publicApi */ class OutletContext { constructor() { this.outlet = null; this.route = null; /** * @deprecated Passing a resolver to retrieve a component factory is not required and is * deprecated since v14. */ this.resolver = null; this.injector = null; this.children = new ChildrenOutletContexts(); this.attachRef = null; } } /** * Store contextual information about the children (= nested) `RouterOutlet` * * @publicApi */ class ChildrenOutletContexts { constructor() { // contexts for child outlets, by name. this.contexts = new Map(); } /** Called when a `RouterOutlet` directive is instantiated */ onChildOutletCreated(childName, outlet) { const context = this.getOrCreateContext(childName); context.outlet = outlet; this.contexts.set(childName, context); } /** * Called when a `RouterOutlet` directive is destroyed. * We need to keep the context as the outlet could be destroyed inside a NgIf and might be * re-created later. */ onChildOutletDestroyed(childName) { const context = this.getContext(childName); if (context) { context.outlet = null; context.attachRef = null; } } /** * Called when the corresponding route is deactivated during navigation. * Because the component get destroyed, all children outlet are destroyed. */ onOutletDeactivated() { const contexts = this.contexts; this.contexts = new Map(); return contexts; } onOutletReAttached(contexts) { this.contexts = contexts; } getOrCreateContext(childName) { let context = this.getContext(childName); if (!context) { context = new OutletContext(); this.contexts.set(childName, context); } return context; } getContext(childName) { return this.contexts.get(childName) || null; } } ChildrenOutletContexts.ɵfac = function ChildrenOutletContexts_Factory(t) { return new (t || ChildrenOutletContexts)(); }; ChildrenOutletContexts.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: ChildrenOutletContexts, factory: ChildrenOutletContexts.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](ChildrenOutletContexts, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], null, null); })(); const NG_DEV_MODE$8 = typeof ngDevMode === 'undefined' || ngDevMode; /** * @description * * Acts as a placeholder that Angular dynamically fills based on the current router state. * * Each outlet can have a unique name, determined by the optional `name` attribute. * The name cannot be set or changed dynamically. If not set, default value is "primary". * * ``` * <router-outlet></router-outlet> * <router-outlet name='left'></router-outlet> * <router-outlet name='right'></router-outlet> * ``` * * Named outlets can be the targets of secondary routes. * The `Route` object for a secondary route has an `outlet` property to identify the target outlet: * * `{path: <base-path>, component: <component>, outlet: <target_outlet_name>}` * * Using named outlets and secondary routes, you can target multiple outlets in * the same `RouterLink` directive. * * The router keeps track of separate branches in a navigation tree for each named outlet and * generates a representation of that tree in the URL. * The URL for a secondary route uses the following syntax to specify both the primary and secondary * routes at the same time: * * `http://base-path/primary-route-path(outlet-name:route-path)` * * A router outlet emits an activate event when a new component is instantiated, * deactivate event when a component is destroyed. * An attached event emits when the `RouteReuseStrategy` instructs the outlet to reattach the * subtree, and the detached event emits when the `RouteReuseStrategy` instructs the outlet to * detach the subtree. * * ``` * <router-outlet * (activate)='onActivate($event)' * (deactivate)='onDeactivate($event)' * (attach)='onAttach($event)' * (detach)='onDetach($event)'></router-outlet> * ``` * * @see [Routing tutorial](guide/router-tutorial-toh#named-outlets "Example of a named * outlet and secondary route configuration"). * @see `RouterLink` * @see `Route` * @ngModule RouterModule * * @publicApi */ class RouterOutlet { constructor() { this.activated = null; this._activatedRoute = null; /** * The name of the outlet * * @see [named outlets](guide/router-tutorial-toh#displaying-multiple-routes-in-named-outlets) */ this.name = PRIMARY_OUTLET; this.activateEvents = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.EventEmitter(); this.deactivateEvents = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.EventEmitter(); /** * Emits an attached component instance when the `RouteReuseStrategy` instructs to re-attach a * previously detached subtree. **/ this.attachEvents = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.EventEmitter(); /** * Emits a detached component instance when the `RouteReuseStrategy` instructs to detach the * subtree. */ this.detachEvents = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.EventEmitter(); this.parentContexts = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(ChildrenOutletContexts); this.location = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__.ViewContainerRef); this.changeDetector = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__.ChangeDetectorRef); this.environmentInjector = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__.EnvironmentInjector); } /** @nodoc */ ngOnChanges(changes) { if (changes['name']) { const { firstChange, previousValue } = changes['name']; if (firstChange) { // The first change is handled by ngOnInit. Because ngOnChanges doesn't get called when no // input is set at all, we need to centrally handle the first change there. return; } // unregister with the old name if (this.isTrackedInParentContexts(previousValue)) { this.deactivate(); this.parentContexts.onChildOutletDestroyed(previousValue); } // register the new name this.initializeOutletWithName(); } } /** @nodoc */ ngOnDestroy() { // Ensure that the registered outlet is this one before removing it on the context. if (this.isTrackedInParentContexts(this.name)) { this.parentContexts.onChildOutletDestroyed(this.name); } } isTrackedInParentContexts(outletName) { return this.parentContexts.getContext(outletName)?.outlet === this; } /** @nodoc */ ngOnInit() { this.initializeOutletWithName(); } initializeOutletWithName() { this.parentContexts.onChildOutletCreated(this.name, this); if (this.activated) { return; } // If the outlet was not instantiated at the time the route got activated we need to populate // the outlet when it is initialized (ie inside a NgIf) const context = this.parentContexts.getContext(this.name); if (context?.route) { if (context.attachRef) { // `attachRef` is populated when there is an existing component to mount this.attach(context.attachRef, context.route); } else { // otherwise the component defined in the configuration is created this.activateWith(context.route, context.injector); } } } get isActivated() { return !!this.activated; } /** * @returns The currently activated component instance. * @throws An error if the outlet is not activated. */ get component() { if (!this.activated) throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4012 /* RuntimeErrorCode.OUTLET_NOT_ACTIVATED */, NG_DEV_MODE$8 && 'Outlet is not activated'); return this.activated.instance; } get activatedRoute() { if (!this.activated) throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4012 /* RuntimeErrorCode.OUTLET_NOT_ACTIVATED */, NG_DEV_MODE$8 && 'Outlet is not activated'); return this._activatedRoute; } get activatedRouteData() { if (this._activatedRoute) { return this._activatedRoute.snapshot.data; } return {}; } /** * Called when the `RouteReuseStrategy` instructs to detach the subtree */ detach() { if (!this.activated) throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4012 /* RuntimeErrorCode.OUTLET_NOT_ACTIVATED */, NG_DEV_MODE$8 && 'Outlet is not activated'); this.location.detach(); const cmp = this.activated; this.activated = null; this._activatedRoute = null; this.detachEvents.emit(cmp.instance); return cmp; } /** * Called when the `RouteReuseStrategy` instructs to re-attach a previously detached subtree */ attach(ref, activatedRoute) { this.activated = ref; this._activatedRoute = activatedRoute; this.location.insert(ref.hostView); this.attachEvents.emit(ref.instance); } deactivate() { if (this.activated) { const c = this.component; this.activated.destroy(); this.activated = null; this._activatedRoute = null; this.deactivateEvents.emit(c); } } activateWith(activatedRoute, resolverOrInjector) { if (this.isActivated) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4013 /* RuntimeErrorCode.OUTLET_ALREADY_ACTIVATED */, NG_DEV_MODE$8 && 'Cannot activate an already activated outlet'); } this._activatedRoute = activatedRoute; const location = this.location; const snapshot = activatedRoute.snapshot; const component = snapshot.component; const childContexts = this.parentContexts.getOrCreateContext(this.name).children; const injector = new OutletInjector(activatedRoute, childContexts, location.injector); if (resolverOrInjector && isComponentFactoryResolver(resolverOrInjector)) { const factory = resolverOrInjector.resolveComponentFactory(component); this.activated = location.createComponent(factory, location.length, injector); } else { const environmentInjector = resolverOrInjector ?? this.environmentInjector; this.activated = location.createComponent(component, { index: location.length, injector, environmentInjector }); } // Calling `markForCheck` to make sure we will run the change detection when the // `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component. this.changeDetector.markForCheck(); this.activateEvents.emit(this.activated.instance); } } RouterOutlet.ɵfac = function RouterOutlet_Factory(t) { return new (t || RouterOutlet)(); }; RouterOutlet.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: RouterOutlet, selectors: [["router-outlet"]], inputs: { name: "name" }, outputs: { activateEvents: "activate", deactivateEvents: "deactivate", attachEvents: "attach", detachEvents: "detach" }, exportAs: ["outlet"], standalone: true, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵNgOnChangesFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](RouterOutlet, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: 'router-outlet', exportAs: 'outlet', standalone: true }] }], null, { name: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], activateEvents: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Output, args: ['activate'] }], deactivateEvents: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Output, args: ['deactivate'] }], attachEvents: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Output, args: ['attach'] }], detachEvents: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Output, args: ['detach'] }] }); })(); class OutletInjector { constructor(route, childContexts, parent) { this.route = route; this.childContexts = childContexts; this.parent = parent; } get(token, notFoundValue) { if (token === ActivatedRoute) { return this.route; } if (token === ChildrenOutletContexts) { return this.childContexts; } return this.parent.get(token, notFoundValue); } } function isComponentFactoryResolver(item) { return !!item.resolveComponentFactory; } /** * This component is used internally within the router to be a placeholder when an empty * router-outlet is needed. For example, with a config such as: * * `{path: 'parent', outlet: 'nav', children: [...]}` * * In order to render, there needs to be a component on this config, which will default * to this `EmptyOutletComponent`. */ class ɵEmptyOutletComponent {} ɵEmptyOutletComponent.ɵfac = function ɵEmptyOutletComponent_Factory(t) { return new (t || ɵEmptyOutletComponent)(); }; ɵEmptyOutletComponent.ɵcmp = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineComponent"]({ type: ɵEmptyOutletComponent, selectors: [["ng-component"]], standalone: true, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵStandaloneFeature"]], decls: 1, vars: 0, template: function ɵEmptyOutletComponent_Template(rf, ctx) { if (rf & 1) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelement"](0, "router-outlet"); } }, dependencies: [RouterOutlet], encapsulation: 2 }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](ɵEmptyOutletComponent, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Component, args: [{ template: `<router-outlet></router-outlet>`, imports: [RouterOutlet], standalone: true }] }], null, null); })(); /** * Creates an `EnvironmentInjector` if the `Route` has providers and one does not already exist * and returns the injector. Otherwise, if the `Route` does not have `providers`, returns the * `currentInjector`. * * @param route The route that might have providers * @param currentInjector The parent injector of the `Route` */ function getOrCreateRouteInjectorIfNeeded(route, currentInjector) { if (route.providers && !route._injector) { route._injector = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.createEnvironmentInjector)(route.providers, currentInjector, `Route: ${route.path}`); } return route._injector ?? currentInjector; } function getLoadedRoutes(route) { return route._loadedRoutes; } function getLoadedInjector(route) { return route._loadedInjector; } function getLoadedComponent(route) { return route._loadedComponent; } function getProvidersInjector(route) { return route._injector; } function validateConfig(config, parentPath = '', requireStandaloneComponents = false) { // forEach doesn't iterate undefined values for (let i = 0; i < config.length; i++) { const route = config[i]; const fullPath = getFullPath(parentPath, route); validateNode(route, fullPath, requireStandaloneComponents); } } function assertStandalone(fullPath, component) { if (component && (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵisNgModule"])(component)) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '${fullPath}'. You are using 'loadComponent' with a module, ` + `but it must be used with standalone components. Use 'loadChildren' instead.`); } else if (component && !(0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.isStandalone)(component)) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '${fullPath}'. The component must be standalone.`); } } function validateNode(route, fullPath, requireStandaloneComponents) { if (typeof ngDevMode === 'undefined' || ngDevMode) { if (!route) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, ` Invalid configuration of route '${fullPath}': Encountered undefined route. The reason might be an extra comma. Example: const routes: Routes = [ { path: '', redirectTo: '/dashboard', pathMatch: 'full' }, { path: 'dashboard', component: DashboardComponent },, << two commas { path: 'detail/:id', component: HeroDetailComponent } ]; `); } if (Array.isArray(route)) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '${fullPath}': Array cannot be specified`); } if (!route.redirectTo && !route.component && !route.loadComponent && !route.children && !route.loadChildren && route.outlet && route.outlet !== PRIMARY_OUTLET) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '${fullPath}': a componentless route without children or loadChildren cannot have a named outlet set`); } if (route.redirectTo && route.children) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '${fullPath}': redirectTo and children cannot be used together`); } if (route.redirectTo && route.loadChildren) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '${fullPath}': redirectTo and loadChildren cannot be used together`); } if (route.children && route.loadChildren) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '${fullPath}': children and loadChildren cannot be used together`); } if (route.redirectTo && (route.component || route.loadComponent)) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '${fullPath}': redirectTo and component/loadComponent cannot be used together`); } if (route.component && route.loadComponent) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '${fullPath}': component and loadComponent cannot be used together`); } if (route.redirectTo && route.canActivate) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '${fullPath}': redirectTo and canActivate cannot be used together. Redirects happen before activation ` + `so canActivate will never be executed.`); } if (route.path && route.matcher) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '${fullPath}': path and matcher cannot be used together`); } if (route.redirectTo === void 0 && !route.component && !route.loadComponent && !route.children && !route.loadChildren) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '${fullPath}'. One of the following must be provided: component, loadComponent, redirectTo, children or loadChildren`); } if (route.path === void 0 && route.matcher === void 0) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '${fullPath}': routes must have either a path or a matcher specified`); } if (typeof route.path === 'string' && route.path.charAt(0) === '/') { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '${fullPath}': path cannot start with a slash`); } if (route.path === '' && route.redirectTo !== void 0 && route.pathMatch === void 0) { const exp = `The default value of 'pathMatch' is 'prefix', but often the intent is to use 'full'.`; throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4014 /* RuntimeErrorCode.INVALID_ROUTE_CONFIG */, `Invalid configuration of route '{path: "${fullPath}", redirectTo: "${route.redirectTo}"}': please provide 'pathMatch'. ${exp}`); } if (requireStandaloneComponents) { assertStandalone(fullPath, route.component); } } if (route.children) { validateConfig(route.children, fullPath, requireStandaloneComponents); } } function getFullPath(parentPath, currentRoute) { if (!currentRoute) { return parentPath; } if (!parentPath && !currentRoute.path) { return ''; } else if (parentPath && !currentRoute.path) { return `${parentPath}/`; } else if (!parentPath && currentRoute.path) { return currentRoute.path; } else { return `${parentPath}/${currentRoute.path}`; } } /** * Makes a copy of the config and adds any default required properties. */ function standardizeConfig(r) { const children = r.children && r.children.map(standardizeConfig); const c = children ? { ...r, children } : { ...r }; if (!c.component && !c.loadComponent && (children || c.loadChildren) && c.outlet && c.outlet !== PRIMARY_OUTLET) { c.component = ɵEmptyOutletComponent; } return c; } /** Returns the `route.outlet` or PRIMARY_OUTLET if none exists. */ function getOutlet(route) { return route.outlet || PRIMARY_OUTLET; } /** * Sorts the `routes` such that the ones with an outlet matching `outletName` come first. * The order of the configs is otherwise preserved. */ function sortByMatchingOutlets(routes, outletName) { const sortedConfig = routes.filter(r => getOutlet(r) === outletName); sortedConfig.push(...routes.filter(r => getOutlet(r) !== outletName)); return sortedConfig; } /** * Gets the first injector in the snapshot's parent tree. * * If the `Route` has a static list of providers, the returned injector will be the one created from * those. If it does not exist, the returned injector may come from the parents, which may be from a * loaded config or their static providers. * * Returns `null` if there is neither this nor any parents have a stored injector. * * Generally used for retrieving the injector to use for getting tokens for guards/resolvers and * also used for getting the correct injector to use for creating components. */ function getClosestRouteInjector(snapshot) { if (!snapshot) return null; // If the current route has its own injector, which is created from the static providers on the // route itself, we should use that. Otherwise, we start at the parent since we do not want to // include the lazy loaded injector from this route. if (snapshot.routeConfig?._injector) { return snapshot.routeConfig._injector; } for (let s = snapshot.parent; s; s = s.parent) { const route = s.routeConfig; // Note that the order here is important. `_loadedInjector` stored on the route with // `loadChildren: () => NgModule` so it applies to child routes with priority. The `_injector` // is created from the static providers on that parent route, so it applies to the children as // well, but only if there is no lazy loaded NgModuleRef injector. if (route?._loadedInjector) return route._loadedInjector; if (route?._injector) return route._injector; } return null; } const activateRoutes = (rootContexts, routeReuseStrategy, forwardEvent) => (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(t => { new ActivateRoutes(routeReuseStrategy, t.targetRouterState, t.currentRouterState, forwardEvent).activate(rootContexts); return t; }); class ActivateRoutes { constructor(routeReuseStrategy, futureState, currState, forwardEvent) { this.routeReuseStrategy = routeReuseStrategy; this.futureState = futureState; this.currState = currState; this.forwardEvent = forwardEvent; } activate(parentContexts) { const futureRoot = this.futureState._root; const currRoot = this.currState ? this.currState._root : null; this.deactivateChildRoutes(futureRoot, currRoot, parentContexts); advanceActivatedRoute(this.futureState.root); this.activateChildRoutes(futureRoot, currRoot, parentContexts); } // De-activate the child route that are not re-used for the future state deactivateChildRoutes(futureNode, currNode, contexts) { const children = nodeChildrenAsMap(currNode); // Recurse on the routes active in the future state to de-activate deeper children futureNode.children.forEach(futureChild => { const childOutletName = futureChild.value.outlet; this.deactivateRoutes(futureChild, children[childOutletName], contexts); delete children[childOutletName]; }); // De-activate the routes that will not be re-used forEach(children, (v, childName) => { this.deactivateRouteAndItsChildren(v, contexts); }); } deactivateRoutes(futureNode, currNode, parentContext) { const future = futureNode.value; const curr = currNode ? currNode.value : null; if (future === curr) { // Reusing the node, check to see if the children need to be de-activated if (future.component) { // If we have a normal route, we need to go through an outlet. const context = parentContext.getContext(future.outlet); if (context) { this.deactivateChildRoutes(futureNode, currNode, context.children); } } else { // if we have a componentless route, we recurse but keep the same outlet map. this.deactivateChildRoutes(futureNode, currNode, parentContext); } } else { if (curr) { // Deactivate the current route which will not be re-used this.deactivateRouteAndItsChildren(currNode, parentContext); } } } deactivateRouteAndItsChildren(route, parentContexts) { // If there is no component, the Route is never attached to an outlet (because there is no // component to attach). if (route.value.component && this.routeReuseStrategy.shouldDetach(route.value.snapshot)) { this.detachAndStoreRouteSubtree(route, parentContexts); } else { this.deactivateRouteAndOutlet(route, parentContexts); } } detachAndStoreRouteSubtree(route, parentContexts) { const context = parentContexts.getContext(route.value.outlet); const contexts = context && route.value.component ? context.children : parentContexts; const children = nodeChildrenAsMap(route); for (const childOutlet of Object.keys(children)) { this.deactivateRouteAndItsChildren(children[childOutlet], contexts); } if (context && context.outlet) { const componentRef = context.outlet.detach(); const contexts = context.children.onOutletDeactivated(); this.routeReuseStrategy.store(route.value.snapshot, { componentRef, route, contexts }); } } deactivateRouteAndOutlet(route, parentContexts) { const context = parentContexts.getContext(route.value.outlet); // The context could be `null` if we are on a componentless route but there may still be // children that need deactivating. const contexts = context && route.value.component ? context.children : parentContexts; const children = nodeChildrenAsMap(route); for (const childOutlet of Object.keys(children)) { this.deactivateRouteAndItsChildren(children[childOutlet], contexts); } if (context) { if (context.outlet) { // Destroy the component context.outlet.deactivate(); // Destroy the contexts for all the outlets that were in the component context.children.onOutletDeactivated(); } // Clear the information about the attached component on the context but keep the reference to // the outlet. Clear even if outlet was not yet activated to avoid activating later with old // info context.attachRef = null; context.resolver = null; context.route = null; } } activateChildRoutes(futureNode, currNode, contexts) { const children = nodeChildrenAsMap(currNode); futureNode.children.forEach(c => { this.activateRoutes(c, children[c.value.outlet], contexts); this.forwardEvent(new ActivationEnd(c.value.snapshot)); }); if (futureNode.children.length) { this.forwardEvent(new ChildActivationEnd(futureNode.value.snapshot)); } } activateRoutes(futureNode, currNode, parentContexts) { const future = futureNode.value; const curr = currNode ? currNode.value : null; advanceActivatedRoute(future); // reusing the node if (future === curr) { if (future.component) { // If we have a normal route, we need to go through an outlet. const context = parentContexts.getOrCreateContext(future.outlet); this.activateChildRoutes(futureNode, currNode, context.children); } else { // if we have a componentless route, we recurse but keep the same outlet map. this.activateChildRoutes(futureNode, currNode, parentContexts); } } else { if (future.component) { // if we have a normal route, we need to place the component into the outlet and recurse. const context = parentContexts.getOrCreateContext(future.outlet); if (this.routeReuseStrategy.shouldAttach(future.snapshot)) { const stored = this.routeReuseStrategy.retrieve(future.snapshot); this.routeReuseStrategy.store(future.snapshot, null); context.children.onOutletReAttached(stored.contexts); context.attachRef = stored.componentRef; context.route = stored.route.value; if (context.outlet) { // Attach right away when the outlet has already been instantiated // Otherwise attach from `RouterOutlet.ngOnInit` when it is instantiated context.outlet.attach(stored.componentRef, stored.route.value); } advanceActivatedRoute(stored.route.value); this.activateChildRoutes(futureNode, null, context.children); } else { const injector = getClosestRouteInjector(future.snapshot); const cmpFactoryResolver = injector?.get(_angular_core__WEBPACK_IMPORTED_MODULE_0__.ComponentFactoryResolver) ?? null; context.attachRef = null; context.route = future; context.resolver = cmpFactoryResolver; context.injector = injector; if (context.outlet) { // Activate the outlet when it has already been instantiated // Otherwise it will get activated from its `ngOnInit` when instantiated context.outlet.activateWith(future, context.injector); } this.activateChildRoutes(futureNode, null, context.children); } } else { // if we have a componentless route, we recurse but keep the same outlet map. this.activateChildRoutes(futureNode, null, parentContexts); } } } } class CanActivate { constructor(path) { this.path = path; this.route = this.path[this.path.length - 1]; } } class CanDeactivate { constructor(component, route) { this.component = component; this.route = route; } } function getAllRouteGuards(future, curr, parentContexts) { const futureRoot = future._root; const currRoot = curr ? curr._root : null; return getChildRouteGuards(futureRoot, currRoot, parentContexts, [futureRoot.value]); } function getCanActivateChild(p) { const canActivateChild = p.routeConfig ? p.routeConfig.canActivateChild : null; if (!canActivateChild || canActivateChild.length === 0) return null; return { node: p, guards: canActivateChild }; } function getTokenOrFunctionIdentity(tokenOrFunction, injector) { const NOT_FOUND = Symbol(); const result = injector.get(tokenOrFunction, NOT_FOUND); if (result === NOT_FOUND) { if (typeof tokenOrFunction === 'function' && !(0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵisInjectable"])(tokenOrFunction)) { // We think the token is just a function so return it as-is return tokenOrFunction; } else { // This will throw the not found error return injector.get(tokenOrFunction); } } return result; } function getChildRouteGuards(futureNode, currNode, contexts, futurePath, checks = { canDeactivateChecks: [], canActivateChecks: [] }) { const prevChildren = nodeChildrenAsMap(currNode); // Process the children of the future route futureNode.children.forEach(c => { getRouteGuards(c, prevChildren[c.value.outlet], contexts, futurePath.concat([c.value]), checks); delete prevChildren[c.value.outlet]; }); // Process any children left from the current route (not active for the future route) forEach(prevChildren, (v, k) => deactivateRouteAndItsChildren(v, contexts.getContext(k), checks)); return checks; } function getRouteGuards(futureNode, currNode, parentContexts, futurePath, checks = { canDeactivateChecks: [], canActivateChecks: [] }) { const future = futureNode.value; const curr = currNode ? currNode.value : null; const context = parentContexts ? parentContexts.getContext(futureNode.value.outlet) : null; // reusing the node if (curr && future.routeConfig === curr.routeConfig) { const shouldRun = shouldRunGuardsAndResolvers(curr, future, future.routeConfig.runGuardsAndResolvers); if (shouldRun) { checks.canActivateChecks.push(new CanActivate(futurePath)); } else { // we need to set the data future.data = curr.data; future._resolvedData = curr._resolvedData; } // If we have a component, we need to go through an outlet. if (future.component) { getChildRouteGuards(futureNode, currNode, context ? context.children : null, futurePath, checks); // if we have a componentless route, we recurse but keep the same outlet map. } else { getChildRouteGuards(futureNode, currNode, parentContexts, futurePath, checks); } if (shouldRun && context && context.outlet && context.outlet.isActivated) { checks.canDeactivateChecks.push(new CanDeactivate(context.outlet.component, curr)); } } else { if (curr) { deactivateRouteAndItsChildren(currNode, context, checks); } checks.canActivateChecks.push(new CanActivate(futurePath)); // If we have a component, we need to go through an outlet. if (future.component) { getChildRouteGuards(futureNode, null, context ? context.children : null, futurePath, checks); // if we have a componentless route, we recurse but keep the same outlet map. } else { getChildRouteGuards(futureNode, null, parentContexts, futurePath, checks); } } return checks; } function shouldRunGuardsAndResolvers(curr, future, mode) { if (typeof mode === 'function') { return mode(curr, future); } switch (mode) { case 'pathParamsChange': return !equalPath(curr.url, future.url); case 'pathParamsOrQueryParamsChange': return !equalPath(curr.url, future.url) || !shallowEqual(curr.queryParams, future.queryParams); case 'always': return true; case 'paramsOrQueryParamsChange': return !equalParamsAndUrlSegments(curr, future) || !shallowEqual(curr.queryParams, future.queryParams); case 'paramsChange': default: return !equalParamsAndUrlSegments(curr, future); } } function deactivateRouteAndItsChildren(route, context, checks) { const children = nodeChildrenAsMap(route); const r = route.value; forEach(children, (node, childName) => { if (!r.component) { deactivateRouteAndItsChildren(node, context, checks); } else if (context) { deactivateRouteAndItsChildren(node, context.children.getContext(childName), checks); } else { deactivateRouteAndItsChildren(node, null, checks); } }); if (!r.component) { checks.canDeactivateChecks.push(new CanDeactivate(null, r)); } else if (context && context.outlet && context.outlet.isActivated) { checks.canDeactivateChecks.push(new CanDeactivate(context.outlet.component, r)); } else { checks.canDeactivateChecks.push(new CanDeactivate(null, r)); } } /** * Simple function check, but generic so type inference will flow. Example: * * function product(a: number, b: number) { * return a * b; * } * * if (isFunction<product>(fn)) { * return fn(1, 2); * } else { * throw "Must provide the `product` function"; * } */ function isFunction(v) { return typeof v === 'function'; } function isBoolean(v) { return typeof v === 'boolean'; } function isCanLoad(guard) { return guard && isFunction(guard.canLoad); } function isCanActivate(guard) { return guard && isFunction(guard.canActivate); } function isCanActivateChild(guard) { return guard && isFunction(guard.canActivateChild); } function isCanDeactivate(guard) { return guard && isFunction(guard.canDeactivate); } function isCanMatch(guard) { return guard && isFunction(guard.canMatch); } function isRedirectingNavigationCancelingError(error) { return isNavigationCancelingError(error) && isUrlTree(error.url); } function isNavigationCancelingError(error) { return error && error[NAVIGATION_CANCELING_ERROR]; } function isEmptyError(e) { return e instanceof rxjs__WEBPACK_IMPORTED_MODULE_5__.EmptyError || e?.name === 'EmptyError'; } const INITIAL_VALUE = Symbol('INITIAL_VALUE'); function prioritizedGuardValue() { return (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_6__.switchMap)(obs => { return (0,rxjs__WEBPACK_IMPORTED_MODULE_7__.combineLatest)(obs.map(o => o.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_8__.take)(1), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_9__.startWith)(INITIAL_VALUE)))).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(results => { for (const result of results) { if (result === true) { // If result is true, check the next one continue; } else if (result === INITIAL_VALUE) { // If guard has not finished, we need to stop processing. return INITIAL_VALUE; } else if (result === false || result instanceof UrlTree) { // Result finished and was not true. Return the result. // Note that we only allow false/UrlTree. Other values are considered invalid and // ignored. return result; } } // Everything resolved to true. Return true. return true; }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_10__.filter)(item => item !== INITIAL_VALUE), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_8__.take)(1)); }); } function checkGuards(injector, forwardEvent) { return (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_11__.mergeMap)(t => { const { targetSnapshot, currentSnapshot, guards: { canActivateChecks, canDeactivateChecks } } = t; if (canDeactivateChecks.length === 0 && canActivateChecks.length === 0) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)({ ...t, guardsResult: true }); } return runCanDeactivateChecks(canDeactivateChecks, targetSnapshot, currentSnapshot, injector).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_11__.mergeMap)(canDeactivate => { return canDeactivate && isBoolean(canDeactivate) ? runCanActivateChecks(targetSnapshot, canActivateChecks, injector, forwardEvent) : (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(canDeactivate); }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(guardsResult => ({ ...t, guardsResult }))); }); } function runCanDeactivateChecks(checks, futureRSS, currRSS, injector) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_1__.from)(checks).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_11__.mergeMap)(check => runCanDeactivate(check.component, check.route, currRSS, futureRSS, injector)), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_12__.first)(result => { return result !== true; }, true)); } function runCanActivateChecks(futureSnapshot, checks, injector, forwardEvent) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_1__.from)(checks).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_13__.concatMap)(check => { return (0,rxjs__WEBPACK_IMPORTED_MODULE_14__.concat)(fireChildActivationStart(check.route.parent, forwardEvent), fireActivationStart(check.route, forwardEvent), runCanActivateChild(futureSnapshot, check.path, injector), runCanActivate(futureSnapshot, check.route, injector)); }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_12__.first)(result => { return result !== true; }, true)); } /** * This should fire off `ActivationStart` events for each route being activated at this * level. * In other words, if you're activating `a` and `b` below, `path` will contain the * `ActivatedRouteSnapshot`s for both and we will fire `ActivationStart` for both. Always * return * `true` so checks continue to run. */ function fireActivationStart(snapshot, forwardEvent) { if (snapshot !== null && forwardEvent) { forwardEvent(new ActivationStart(snapshot)); } return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(true); } /** * This should fire off `ChildActivationStart` events for each route being activated at this * level. * In other words, if you're activating `a` and `b` below, `path` will contain the * `ActivatedRouteSnapshot`s for both and we will fire `ChildActivationStart` for both. Always * return * `true` so checks continue to run. */ function fireChildActivationStart(snapshot, forwardEvent) { if (snapshot !== null && forwardEvent) { forwardEvent(new ChildActivationStart(snapshot)); } return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(true); } function runCanActivate(futureRSS, futureARS, injector) { const canActivate = futureARS.routeConfig ? futureARS.routeConfig.canActivate : null; if (!canActivate || canActivate.length === 0) return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(true); const canActivateObservables = canActivate.map(canActivate => { return (0,rxjs__WEBPACK_IMPORTED_MODULE_15__.defer)(() => { const closestInjector = getClosestRouteInjector(futureARS) ?? injector; const guard = getTokenOrFunctionIdentity(canActivate, closestInjector); const guardVal = isCanActivate(guard) ? guard.canActivate(futureARS, futureRSS) : closestInjector.runInContext(() => guard(futureARS, futureRSS)); return wrapIntoObservable(guardVal).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_12__.first)()); }); }); return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(canActivateObservables).pipe(prioritizedGuardValue()); } function runCanActivateChild(futureRSS, path, injector) { const futureARS = path[path.length - 1]; const canActivateChildGuards = path.slice(0, path.length - 1).reverse().map(p => getCanActivateChild(p)).filter(_ => _ !== null); const canActivateChildGuardsMapped = canActivateChildGuards.map(d => { return (0,rxjs__WEBPACK_IMPORTED_MODULE_15__.defer)(() => { const guardsMapped = d.guards.map(canActivateChild => { const closestInjector = getClosestRouteInjector(d.node) ?? injector; const guard = getTokenOrFunctionIdentity(canActivateChild, closestInjector); const guardVal = isCanActivateChild(guard) ? guard.canActivateChild(futureARS, futureRSS) : closestInjector.runInContext(() => guard(futureARS, futureRSS)); return wrapIntoObservable(guardVal).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_12__.first)()); }); return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(guardsMapped).pipe(prioritizedGuardValue()); }); }); return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(canActivateChildGuardsMapped).pipe(prioritizedGuardValue()); } function runCanDeactivate(component, currARS, currRSS, futureRSS, injector) { const canDeactivate = currARS && currARS.routeConfig ? currARS.routeConfig.canDeactivate : null; if (!canDeactivate || canDeactivate.length === 0) return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(true); const canDeactivateObservables = canDeactivate.map(c => { const closestInjector = getClosestRouteInjector(currARS) ?? injector; const guard = getTokenOrFunctionIdentity(c, closestInjector); const guardVal = isCanDeactivate(guard) ? guard.canDeactivate(component, currARS, currRSS, futureRSS) : closestInjector.runInContext(() => guard(component, currARS, currRSS, futureRSS)); return wrapIntoObservable(guardVal).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_12__.first)()); }); return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(canDeactivateObservables).pipe(prioritizedGuardValue()); } function runCanLoadGuards(injector, route, segments, urlSerializer) { const canLoad = route.canLoad; if (canLoad === undefined || canLoad.length === 0) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(true); } const canLoadObservables = canLoad.map(injectionToken => { const guard = getTokenOrFunctionIdentity(injectionToken, injector); const guardVal = isCanLoad(guard) ? guard.canLoad(route, segments) : injector.runInContext(() => guard(route, segments)); return wrapIntoObservable(guardVal); }); return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(canLoadObservables).pipe(prioritizedGuardValue(), redirectIfUrlTree(urlSerializer)); } function redirectIfUrlTree(urlSerializer) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_16__.pipe)((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)(result => { if (!isUrlTree(result)) return; throw redirectingNavigationError(urlSerializer, result); }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(result => result === true)); } function runCanMatchGuards(injector, route, segments, urlSerializer) { const canMatch = route.canMatch; if (!canMatch || canMatch.length === 0) return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(true); const canMatchObservables = canMatch.map(injectionToken => { const guard = getTokenOrFunctionIdentity(injectionToken, injector); const guardVal = isCanMatch(guard) ? guard.canMatch(route, segments) : injector.runInContext(() => guard(route, segments)); return wrapIntoObservable(guardVal); }); return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(canMatchObservables).pipe(prioritizedGuardValue(), redirectIfUrlTree(urlSerializer)); } const noMatch$1 = { matched: false, consumedSegments: [], remainingSegments: [], parameters: {}, positionalParamSegments: {} }; function matchWithChecks(segmentGroup, route, segments, injector, urlSerializer) { const result = match(segmentGroup, route, segments); if (!result.matched) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(result); } // Only create the Route's `EnvironmentInjector` if it matches the attempted // navigation injector = getOrCreateRouteInjectorIfNeeded(route, injector); return runCanMatchGuards(injector, route, segments, urlSerializer).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(v => v === true ? result : { ...noMatch$1 })); } function match(segmentGroup, route, segments) { if (route.path === '') { if (route.pathMatch === 'full' && (segmentGroup.hasChildren() || segments.length > 0)) { return { ...noMatch$1 }; } return { matched: true, consumedSegments: [], remainingSegments: segments, parameters: {}, positionalParamSegments: {} }; } const matcher = route.matcher || defaultUrlMatcher; const res = matcher(segments, segmentGroup, route); if (!res) return { ...noMatch$1 }; const posParams = {}; forEach(res.posParams, (v, k) => { posParams[k] = v.path; }); const parameters = res.consumed.length > 0 ? { ...posParams, ...res.consumed[res.consumed.length - 1].parameters } : posParams; return { matched: true, consumedSegments: res.consumed, remainingSegments: segments.slice(res.consumed.length), // TODO(atscott): investigate combining parameters and positionalParamSegments parameters, positionalParamSegments: res.posParams ?? {} }; } function split(segmentGroup, consumedSegments, slicedSegments, config) { if (slicedSegments.length > 0 && containsEmptyPathMatchesWithNamedOutlets(segmentGroup, slicedSegments, config)) { const s = new UrlSegmentGroup(consumedSegments, createChildrenForEmptyPaths(segmentGroup, consumedSegments, config, new UrlSegmentGroup(slicedSegments, segmentGroup.children))); s._sourceSegment = segmentGroup; s._segmentIndexShift = consumedSegments.length; return { segmentGroup: s, slicedSegments: [] }; } if (slicedSegments.length === 0 && containsEmptyPathMatches(segmentGroup, slicedSegments, config)) { const s = new UrlSegmentGroup(segmentGroup.segments, addEmptyPathsToChildrenIfNeeded(segmentGroup, consumedSegments, slicedSegments, config, segmentGroup.children)); s._sourceSegment = segmentGroup; s._segmentIndexShift = consumedSegments.length; return { segmentGroup: s, slicedSegments }; } const s = new UrlSegmentGroup(segmentGroup.segments, segmentGroup.children); s._sourceSegment = segmentGroup; s._segmentIndexShift = consumedSegments.length; return { segmentGroup: s, slicedSegments }; } function addEmptyPathsToChildrenIfNeeded(segmentGroup, consumedSegments, slicedSegments, routes, children) { const res = {}; for (const r of routes) { if (emptyPathMatch(segmentGroup, slicedSegments, r) && !children[getOutlet(r)]) { const s = new UrlSegmentGroup([], {}); s._sourceSegment = segmentGroup; s._segmentIndexShift = consumedSegments.length; res[getOutlet(r)] = s; } } return { ...children, ...res }; } function createChildrenForEmptyPaths(segmentGroup, consumedSegments, routes, primarySegment) { const res = {}; res[PRIMARY_OUTLET] = primarySegment; primarySegment._sourceSegment = segmentGroup; primarySegment._segmentIndexShift = consumedSegments.length; for (const r of routes) { if (r.path === '' && getOutlet(r) !== PRIMARY_OUTLET) { const s = new UrlSegmentGroup([], {}); s._sourceSegment = segmentGroup; s._segmentIndexShift = consumedSegments.length; res[getOutlet(r)] = s; } } return res; } function containsEmptyPathMatchesWithNamedOutlets(segmentGroup, slicedSegments, routes) { return routes.some(r => emptyPathMatch(segmentGroup, slicedSegments, r) && getOutlet(r) !== PRIMARY_OUTLET); } function containsEmptyPathMatches(segmentGroup, slicedSegments, routes) { return routes.some(r => emptyPathMatch(segmentGroup, slicedSegments, r)); } function emptyPathMatch(segmentGroup, slicedSegments, r) { if ((segmentGroup.hasChildren() || slicedSegments.length > 0) && r.pathMatch === 'full') { return false; } return r.path === ''; } /** * Determines if `route` is a path match for the `rawSegment`, `segments`, and `outlet` without * verifying that its children are a full match for the remainder of the `rawSegment` children as * well. */ function isImmediateMatch(route, rawSegment, segments, outlet) { // We allow matches to empty paths when the outlets differ so we can match a url like `/(b:b)` to // a config like // * `{path: '', children: [{path: 'b', outlet: 'b'}]}` // or even // * `{path: '', outlet: 'a', children: [{path: 'b', outlet: 'b'}]` // // The exception here is when the segment outlet is for the primary outlet. This would // result in a match inside the named outlet because all children there are written as primary // outlets. So we need to prevent child named outlet matches in a url like `/b` in a config like // * `{path: '', outlet: 'x' children: [{path: 'b'}]}` // This should only match if the url is `/(x:b)`. if (getOutlet(route) !== outlet && (outlet === PRIMARY_OUTLET || !emptyPathMatch(rawSegment, segments, route))) { return false; } if (route.path === '**') { return true; } return match(rawSegment, route, segments).matched; } function noLeftoversInUrl(segmentGroup, segments, outlet) { return segments.length === 0 && !segmentGroup.children[outlet]; } const NG_DEV_MODE$7 = typeof ngDevMode === 'undefined' || ngDevMode; class NoMatch$1 { constructor(segmentGroup) { this.segmentGroup = segmentGroup || null; } } class AbsoluteRedirect { constructor(urlTree) { this.urlTree = urlTree; } } function noMatch(segmentGroup) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_18__.throwError)(new NoMatch$1(segmentGroup)); } function absoluteRedirect(newTree) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_18__.throwError)(new AbsoluteRedirect(newTree)); } function namedOutletsRedirect(redirectTo) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_18__.throwError)(new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4000 /* RuntimeErrorCode.NAMED_OUTLET_REDIRECT */, NG_DEV_MODE$7 && `Only absolute redirects can have named outlets. redirectTo: '${redirectTo}'`)); } function canLoadFails(route) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_18__.throwError)(navigationCancelingError(NG_DEV_MODE$7 && `Cannot load children because the guard of the route "path: '${route.path}'" returned false`, 3 /* NavigationCancellationCode.GuardRejected */)); } /** * Returns the `UrlTree` with the redirection applied. * * Lazy modules are loaded along the way. */ function applyRedirects$1(injector, configLoader, urlSerializer, urlTree, config) { return new ApplyRedirects(injector, configLoader, urlSerializer, urlTree, config).apply(); } class ApplyRedirects { constructor(injector, configLoader, urlSerializer, urlTree, config) { this.injector = injector; this.configLoader = configLoader; this.urlSerializer = urlSerializer; this.urlTree = urlTree; this.config = config; this.allowRedirects = true; } apply() { const splitGroup = split(this.urlTree.root, [], [], this.config).segmentGroup; // TODO(atscott): creating a new segment removes the _sourceSegment _segmentIndexShift, which is // only necessary to prevent failures in tests which assert exact object matches. The `split` is // now shared between `applyRedirects` and `recognize` but only the `recognize` step needs these // properties. Before the implementations were merged, the `applyRedirects` would not assign // them. We should be able to remove this logic as a "breaking change" but should do some more // investigation into the failures first. const rootSegmentGroup = new UrlSegmentGroup(splitGroup.segments, splitGroup.children); const expanded$ = this.expandSegmentGroup(this.injector, this.config, rootSegmentGroup, PRIMARY_OUTLET); const urlTrees$ = expanded$.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(rootSegmentGroup => { return this.createUrlTree(squashSegmentGroup(rootSegmentGroup), this.urlTree.queryParams, this.urlTree.fragment); })); return urlTrees$.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_19__.catchError)(e => { if (e instanceof AbsoluteRedirect) { // After an absolute redirect we do not apply any more redirects! // If this implementation changes, update the documentation note in `redirectTo`. this.allowRedirects = false; // we need to run matching, so we can fetch all lazy-loaded modules return this.match(e.urlTree); } if (e instanceof NoMatch$1) { throw this.noMatchError(e); } throw e; })); } match(tree) { const expanded$ = this.expandSegmentGroup(this.injector, this.config, tree.root, PRIMARY_OUTLET); const mapped$ = expanded$.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(rootSegmentGroup => { return this.createUrlTree(squashSegmentGroup(rootSegmentGroup), tree.queryParams, tree.fragment); })); return mapped$.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_19__.catchError)(e => { if (e instanceof NoMatch$1) { throw this.noMatchError(e); } throw e; })); } noMatchError(e) { return new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4002 /* RuntimeErrorCode.NO_MATCH */, NG_DEV_MODE$7 && `Cannot match any routes. URL Segment: '${e.segmentGroup}'`); } createUrlTree(rootCandidate, queryParams, fragment) { const root = createRoot(rootCandidate); return new UrlTree(root, queryParams, fragment); } expandSegmentGroup(injector, routes, segmentGroup, outlet) { if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) { return this.expandChildren(injector, routes, segmentGroup).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(children => new UrlSegmentGroup([], children))); } return this.expandSegment(injector, segmentGroup, routes, segmentGroup.segments, outlet, true); } // Recursively expand segment groups for all the child outlets expandChildren(injector, routes, segmentGroup) { // Expand outlets one at a time, starting with the primary outlet. We need to do it this way // because an absolute redirect from the primary outlet takes precedence. const childOutlets = []; for (const child of Object.keys(segmentGroup.children)) { if (child === 'primary') { childOutlets.unshift(child); } else { childOutlets.push(child); } } return (0,rxjs__WEBPACK_IMPORTED_MODULE_1__.from)(childOutlets).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_13__.concatMap)(childOutlet => { const child = segmentGroup.children[childOutlet]; // Sort the routes so routes with outlets that match the segment appear // first, followed by routes for other outlets, which might match if they have an // empty path. const sortedRoutes = sortByMatchingOutlets(routes, childOutlet); return this.expandSegmentGroup(injector, sortedRoutes, child, childOutlet).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(s => ({ segment: s, outlet: childOutlet }))); }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_20__.scan)((children, expandedChild) => { children[expandedChild.outlet] = expandedChild.segment; return children; }, {}), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_21__.last)()); } expandSegment(injector, segmentGroup, routes, segments, outlet, allowRedirects) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_1__.from)(routes).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_13__.concatMap)(r => { const expanded$ = this.expandSegmentAgainstRoute(injector, segmentGroup, routes, r, segments, outlet, allowRedirects); return expanded$.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_19__.catchError)(e => { if (e instanceof NoMatch$1) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(null); } throw e; })); }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_12__.first)(s => !!s), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_19__.catchError)((e, _) => { if (isEmptyError(e)) { if (noLeftoversInUrl(segmentGroup, segments, outlet)) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(new UrlSegmentGroup([], {})); } return noMatch(segmentGroup); } throw e; })); } expandSegmentAgainstRoute(injector, segmentGroup, routes, route, paths, outlet, allowRedirects) { if (!isImmediateMatch(route, segmentGroup, paths, outlet)) { return noMatch(segmentGroup); } if (route.redirectTo === undefined) { return this.matchSegmentAgainstRoute(injector, segmentGroup, route, paths, outlet); } if (allowRedirects && this.allowRedirects) { return this.expandSegmentAgainstRouteUsingRedirect(injector, segmentGroup, routes, route, paths, outlet); } return noMatch(segmentGroup); } expandSegmentAgainstRouteUsingRedirect(injector, segmentGroup, routes, route, segments, outlet) { if (route.path === '**') { return this.expandWildCardWithParamsAgainstRouteUsingRedirect(injector, routes, route, outlet); } return this.expandRegularSegmentAgainstRouteUsingRedirect(injector, segmentGroup, routes, route, segments, outlet); } expandWildCardWithParamsAgainstRouteUsingRedirect(injector, routes, route, outlet) { const newTree = this.applyRedirectCommands([], route.redirectTo, {}); if (route.redirectTo.startsWith('/')) { return absoluteRedirect(newTree); } return this.lineralizeSegments(route, newTree).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_11__.mergeMap)(newSegments => { const group = new UrlSegmentGroup(newSegments, {}); return this.expandSegment(injector, group, routes, newSegments, outlet, false); })); } expandRegularSegmentAgainstRouteUsingRedirect(injector, segmentGroup, routes, route, segments, outlet) { const { matched, consumedSegments, remainingSegments, positionalParamSegments } = match(segmentGroup, route, segments); if (!matched) return noMatch(segmentGroup); const newTree = this.applyRedirectCommands(consumedSegments, route.redirectTo, positionalParamSegments); if (route.redirectTo.startsWith('/')) { return absoluteRedirect(newTree); } return this.lineralizeSegments(route, newTree).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_11__.mergeMap)(newSegments => { return this.expandSegment(injector, segmentGroup, routes, newSegments.concat(remainingSegments), outlet, false); })); } matchSegmentAgainstRoute(injector, rawSegmentGroup, route, segments, outlet) { if (route.path === '**') { // Only create the Route's `EnvironmentInjector` if it matches the attempted navigation injector = getOrCreateRouteInjectorIfNeeded(route, injector); if (route.loadChildren) { const loaded$ = route._loadedRoutes ? (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)({ routes: route._loadedRoutes, injector: route._loadedInjector }) : this.configLoader.loadChildren(injector, route); return loaded$.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(cfg => { route._loadedRoutes = cfg.routes; route._loadedInjector = cfg.injector; return new UrlSegmentGroup(segments, {}); })); } return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(new UrlSegmentGroup(segments, {})); } return matchWithChecks(rawSegmentGroup, route, segments, injector, this.urlSerializer).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_6__.switchMap)(({ matched, consumedSegments, remainingSegments }) => { if (!matched) return noMatch(rawSegmentGroup); // If the route has an injector created from providers, we should start using that. injector = route._injector ?? injector; const childConfig$ = this.getChildConfig(injector, route, segments); return childConfig$.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_11__.mergeMap)(routerConfig => { const childInjector = routerConfig.injector ?? injector; const childConfig = routerConfig.routes; const { segmentGroup: splitSegmentGroup, slicedSegments } = split(rawSegmentGroup, consumedSegments, remainingSegments, childConfig); // See comment on the other call to `split` about why this is necessary. const segmentGroup = new UrlSegmentGroup(splitSegmentGroup.segments, splitSegmentGroup.children); if (slicedSegments.length === 0 && segmentGroup.hasChildren()) { const expanded$ = this.expandChildren(childInjector, childConfig, segmentGroup); return expanded$.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(children => new UrlSegmentGroup(consumedSegments, children))); } if (childConfig.length === 0 && slicedSegments.length === 0) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(new UrlSegmentGroup(consumedSegments, {})); } const matchedOnOutlet = getOutlet(route) === outlet; const expanded$ = this.expandSegment(childInjector, segmentGroup, childConfig, slicedSegments, matchedOnOutlet ? PRIMARY_OUTLET : outlet, true); return expanded$.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(cs => new UrlSegmentGroup(consumedSegments.concat(cs.segments), cs.children))); })); })); } getChildConfig(injector, route, segments) { if (route.children) { // The children belong to the same module return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)({ routes: route.children, injector }); } if (route.loadChildren) { // lazy children belong to the loaded module if (route._loadedRoutes !== undefined) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)({ routes: route._loadedRoutes, injector: route._loadedInjector }); } return runCanLoadGuards(injector, route, segments, this.urlSerializer).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_11__.mergeMap)(shouldLoadResult => { if (shouldLoadResult) { return this.configLoader.loadChildren(injector, route).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)(cfg => { route._loadedRoutes = cfg.routes; route._loadedInjector = cfg.injector; })); } return canLoadFails(route); })); } return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)({ routes: [], injector }); } lineralizeSegments(route, urlTree) { let res = []; let c = urlTree.root; while (true) { res = res.concat(c.segments); if (c.numberOfChildren === 0) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(res); } if (c.numberOfChildren > 1 || !c.children[PRIMARY_OUTLET]) { return namedOutletsRedirect(route.redirectTo); } c = c.children[PRIMARY_OUTLET]; } } applyRedirectCommands(segments, redirectTo, posParams) { return this.applyRedirectCreateUrlTree(redirectTo, this.urlSerializer.parse(redirectTo), segments, posParams); } applyRedirectCreateUrlTree(redirectTo, urlTree, segments, posParams) { const newRoot = this.createSegmentGroup(redirectTo, urlTree.root, segments, posParams); return new UrlTree(newRoot, this.createQueryParams(urlTree.queryParams, this.urlTree.queryParams), urlTree.fragment); } createQueryParams(redirectToParams, actualParams) { const res = {}; forEach(redirectToParams, (v, k) => { const copySourceValue = typeof v === 'string' && v.startsWith(':'); if (copySourceValue) { const sourceName = v.substring(1); res[k] = actualParams[sourceName]; } else { res[k] = v; } }); return res; } createSegmentGroup(redirectTo, group, segments, posParams) { const updatedSegments = this.createSegments(redirectTo, group.segments, segments, posParams); let children = {}; forEach(group.children, (child, name) => { children[name] = this.createSegmentGroup(redirectTo, child, segments, posParams); }); return new UrlSegmentGroup(updatedSegments, children); } createSegments(redirectTo, redirectToSegments, actualSegments, posParams) { return redirectToSegments.map(s => s.path.startsWith(':') ? this.findPosParam(redirectTo, s, posParams) : this.findOrReturn(s, actualSegments)); } findPosParam(redirectTo, redirectToUrlSegment, posParams) { const pos = posParams[redirectToUrlSegment.path.substring(1)]; if (!pos) throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4001 /* RuntimeErrorCode.MISSING_REDIRECT */, NG_DEV_MODE$7 && `Cannot redirect to '${redirectTo}'. Cannot find '${redirectToUrlSegment.path}'.`); return pos; } findOrReturn(redirectToUrlSegment, actualSegments) { let idx = 0; for (const s of actualSegments) { if (s.path === redirectToUrlSegment.path) { actualSegments.splice(idx); return s; } idx++; } return redirectToUrlSegment; } } function applyRedirects(environmentInjector, configLoader, urlSerializer, config) { return (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_6__.switchMap)(t => applyRedirects$1(environmentInjector, configLoader, urlSerializer, t.extractedUrl, config).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(urlAfterRedirects => ({ ...t, urlAfterRedirects })))); } const NG_DEV_MODE$6 = typeof ngDevMode === 'undefined' || !!ngDevMode; class NoMatch {} function newObservableError(e) { // TODO(atscott): This pattern is used throughout the router code and can be `throwError` instead. return new rxjs__WEBPACK_IMPORTED_MODULE_22__.Observable(obs => obs.error(e)); } function recognize$1(injector, rootComponentType, config, urlTree, url, urlSerializer, paramsInheritanceStrategy = 'emptyOnly') { return new Recognizer(injector, rootComponentType, config, urlTree, url, paramsInheritanceStrategy, urlSerializer).recognize().pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_6__.switchMap)(result => { if (result === null) { return newObservableError(new NoMatch()); } else { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(result); } })); } class Recognizer { constructor(injector, rootComponentType, config, urlTree, url, paramsInheritanceStrategy, urlSerializer) { this.injector = injector; this.rootComponentType = rootComponentType; this.config = config; this.urlTree = urlTree; this.url = url; this.paramsInheritanceStrategy = paramsInheritanceStrategy; this.urlSerializer = urlSerializer; } recognize() { const rootSegmentGroup = split(this.urlTree.root, [], [], this.config.filter(c => c.redirectTo === undefined)).segmentGroup; return this.processSegmentGroup(this.injector, this.config, rootSegmentGroup, PRIMARY_OUTLET).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(children => { if (children === null) { return null; } // Use Object.freeze to prevent readers of the Router state from modifying it outside of a // navigation, resulting in the router being out of sync with the browser. const root = new ActivatedRouteSnapshot([], Object.freeze({}), Object.freeze({ ...this.urlTree.queryParams }), this.urlTree.fragment, {}, PRIMARY_OUTLET, this.rootComponentType, null, this.urlTree.root, -1, {}); const rootNode = new TreeNode(root, children); const routeState = new RouterStateSnapshot(this.url, rootNode); this.inheritParamsAndData(routeState._root); return routeState; })); } inheritParamsAndData(routeNode) { const route = routeNode.value; const i = inheritedParamsDataResolve(route, this.paramsInheritanceStrategy); route.params = Object.freeze(i.params); route.data = Object.freeze(i.data); routeNode.children.forEach(n => this.inheritParamsAndData(n)); } processSegmentGroup(injector, config, segmentGroup, outlet) { if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) { return this.processChildren(injector, config, segmentGroup); } return this.processSegment(injector, config, segmentGroup, segmentGroup.segments, outlet); } /** * Matches every child outlet in the `segmentGroup` to a `Route` in the config. Returns `null` if * we cannot find a match for _any_ of the children. * * @param config - The `Routes` to match against * @param segmentGroup - The `UrlSegmentGroup` whose children need to be matched against the * config. */ processChildren(injector, config, segmentGroup) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_1__.from)(Object.keys(segmentGroup.children)).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_13__.concatMap)(childOutlet => { const child = segmentGroup.children[childOutlet]; // Sort the config so that routes with outlets that match the one being activated // appear first, followed by routes for other outlets, which might match if they have // an empty path. const sortedConfig = sortByMatchingOutlets(config, childOutlet); return this.processSegmentGroup(injector, sortedConfig, child, childOutlet); }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_20__.scan)((children, outletChildren) => { if (!children || !outletChildren) return null; children.push(...outletChildren); return children; }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_23__.takeWhile)(children => children !== null), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_24__.defaultIfEmpty)(null), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_21__.last)(), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(children => { if (children === null) return null; // Because we may have matched two outlets to the same empty path segment, we can have // multiple activated results for the same outlet. We should merge the children of // these results so the final return value is only one `TreeNode` per outlet. const mergedChildren = mergeEmptyPathMatches(children); if (NG_DEV_MODE$6) { // This should really never happen - we are only taking the first match for each // outlet and merge the empty path matches. checkOutletNameUniqueness(mergedChildren); } sortActivatedRouteSnapshots(mergedChildren); return mergedChildren; })); } processSegment(injector, routes, segmentGroup, segments, outlet) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_1__.from)(routes).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_13__.concatMap)(r => { return this.processSegmentAgainstRoute(r._injector ?? injector, r, segmentGroup, segments, outlet); }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_12__.first)(x => !!x), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_19__.catchError)(e => { if (isEmptyError(e)) { if (noLeftoversInUrl(segmentGroup, segments, outlet)) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)([]); } return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(null); } throw e; })); } processSegmentAgainstRoute(injector, route, rawSegment, segments, outlet) { if (route.redirectTo || !isImmediateMatch(route, rawSegment, segments, outlet)) return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(null); let matchResult; if (route.path === '**') { const params = segments.length > 0 ? last(segments).parameters : {}; const pathIndexShift = getPathIndexShift(rawSegment) + segments.length; const snapshot = new ActivatedRouteSnapshot(segments, params, Object.freeze({ ...this.urlTree.queryParams }), this.urlTree.fragment, getData(route), getOutlet(route), route.component ?? route._loadedComponent ?? null, route, getSourceSegmentGroup(rawSegment), pathIndexShift, getResolve(route)); matchResult = (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)({ snapshot, consumedSegments: [], remainingSegments: [] }); } else { matchResult = matchWithChecks(rawSegment, route, segments, injector, this.urlSerializer).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(({ matched, consumedSegments, remainingSegments, parameters }) => { if (!matched) { return null; } const pathIndexShift = getPathIndexShift(rawSegment) + consumedSegments.length; const snapshot = new ActivatedRouteSnapshot(consumedSegments, parameters, Object.freeze({ ...this.urlTree.queryParams }), this.urlTree.fragment, getData(route), getOutlet(route), route.component ?? route._loadedComponent ?? null, route, getSourceSegmentGroup(rawSegment), pathIndexShift, getResolve(route)); return { snapshot, consumedSegments, remainingSegments }; })); } return matchResult.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_6__.switchMap)(result => { if (result === null) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(null); } const { snapshot, consumedSegments, remainingSegments } = result; // If the route has an injector created from providers, we should start using that. injector = route._injector ?? injector; const childInjector = route._loadedInjector ?? injector; const childConfig = getChildConfig(route); const { segmentGroup, slicedSegments } = split(rawSegment, consumedSegments, remainingSegments, // Filter out routes with redirectTo because we are trying to create activated route // snapshots and don't handle redirects here. That should have been done in // `applyRedirects`. childConfig.filter(c => c.redirectTo === undefined)); if (slicedSegments.length === 0 && segmentGroup.hasChildren()) { return this.processChildren(childInjector, childConfig, segmentGroup).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(children => { if (children === null) { return null; } return [new TreeNode(snapshot, children)]; })); } if (childConfig.length === 0 && slicedSegments.length === 0) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)([new TreeNode(snapshot, [])]); } const matchedOnOutlet = getOutlet(route) === outlet; // If we matched a config due to empty path match on a different outlet, we need to // continue passing the current outlet for the segment rather than switch to PRIMARY. // Note that we switch to primary when we have a match because outlet configs look like // this: {path: 'a', outlet: 'a', children: [ // {path: 'b', component: B}, // {path: 'c', component: C}, // ]} // Notice that the children of the named outlet are configured with the primary outlet return this.processSegment(childInjector, childConfig, segmentGroup, slicedSegments, matchedOnOutlet ? PRIMARY_OUTLET : outlet).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(children => { if (children === null) { return null; } return [new TreeNode(snapshot, children)]; })); })); } } function sortActivatedRouteSnapshots(nodes) { nodes.sort((a, b) => { if (a.value.outlet === PRIMARY_OUTLET) return -1; if (b.value.outlet === PRIMARY_OUTLET) return 1; return a.value.outlet.localeCompare(b.value.outlet); }); } function getChildConfig(route) { if (route.children) { return route.children; } if (route.loadChildren) { return route._loadedRoutes; } return []; } function hasEmptyPathConfig(node) { const config = node.value.routeConfig; return config && config.path === '' && config.redirectTo === undefined; } /** * Finds `TreeNode`s with matching empty path route configs and merges them into `TreeNode` with * the children from each duplicate. This is necessary because different outlets can match a * single empty path route config and the results need to then be merged. */ function mergeEmptyPathMatches(nodes) { const result = []; // The set of nodes which contain children that were merged from two duplicate empty path nodes. const mergedNodes = new Set(); for (const node of nodes) { if (!hasEmptyPathConfig(node)) { result.push(node); continue; } const duplicateEmptyPathNode = result.find(resultNode => node.value.routeConfig === resultNode.value.routeConfig); if (duplicateEmptyPathNode !== undefined) { duplicateEmptyPathNode.children.push(...node.children); mergedNodes.add(duplicateEmptyPathNode); } else { result.push(node); } } // For each node which has children from multiple sources, we need to recompute a new `TreeNode` // by also merging those children. This is necessary when there are multiple empty path configs // in a row. Put another way: whenever we combine children of two nodes, we need to also check // if any of those children can be combined into a single node as well. for (const mergedNode of mergedNodes) { const mergedChildren = mergeEmptyPathMatches(mergedNode.children); result.push(new TreeNode(mergedNode.value, mergedChildren)); } return result.filter(n => !mergedNodes.has(n)); } function checkOutletNameUniqueness(nodes) { const names = {}; nodes.forEach(n => { const routeWithSameOutletName = names[n.value.outlet]; if (routeWithSameOutletName) { const p = routeWithSameOutletName.url.map(s => s.toString()).join('/'); const c = n.value.url.map(s => s.toString()).join('/'); throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4006 /* RuntimeErrorCode.TWO_SEGMENTS_WITH_SAME_OUTLET */, NG_DEV_MODE$6 && `Two segments cannot have the same outlet name: '${p}' and '${c}'.`); } names[n.value.outlet] = n.value; }); } function getSourceSegmentGroup(segmentGroup) { let s = segmentGroup; while (s._sourceSegment) { s = s._sourceSegment; } return s; } function getPathIndexShift(segmentGroup) { let s = segmentGroup; let res = s._segmentIndexShift ?? 0; while (s._sourceSegment) { s = s._sourceSegment; res += s._segmentIndexShift ?? 0; } return res - 1; } function getCorrectedPathIndexShift(segmentGroup) { let s = segmentGroup; let res = s._segmentIndexShiftCorrected ?? s._segmentIndexShift ?? 0; while (s._sourceSegment) { s = s._sourceSegment; res += s._segmentIndexShiftCorrected ?? s._segmentIndexShift ?? 0; } return res - 1; } function getData(route) { return route.data || {}; } function getResolve(route) { return route.resolve || {}; } function recognize(injector, rootComponentType, config, serializer, paramsInheritanceStrategy) { return (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_11__.mergeMap)(t => recognize$1(injector, rootComponentType, config, t.urlAfterRedirects, serializer.serialize(t.urlAfterRedirects), serializer, paramsInheritanceStrategy).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(targetSnapshot => ({ ...t, targetSnapshot })))); } function resolveData(paramsInheritanceStrategy, injector) { return (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_11__.mergeMap)(t => { const { targetSnapshot, guards: { canActivateChecks } } = t; if (!canActivateChecks.length) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(t); } let canActivateChecksResolved = 0; return (0,rxjs__WEBPACK_IMPORTED_MODULE_1__.from)(canActivateChecks).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_13__.concatMap)(check => runResolve(check.route, targetSnapshot, paramsInheritanceStrategy, injector)), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)(() => canActivateChecksResolved++), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_25__.takeLast)(1), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_11__.mergeMap)(_ => canActivateChecksResolved === canActivateChecks.length ? (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(t) : rxjs__WEBPACK_IMPORTED_MODULE_26__.EMPTY)); }); } function runResolve(futureARS, futureRSS, paramsInheritanceStrategy, injector) { const config = futureARS.routeConfig; const resolve = futureARS._resolve; if (config?.title !== undefined && !hasStaticTitle(config)) { resolve[RouteTitleKey] = config.title; } return resolveNode(resolve, futureARS, futureRSS, injector).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(resolvedData => { futureARS._resolvedData = resolvedData; futureARS.data = inheritedParamsDataResolve(futureARS, paramsInheritanceStrategy).resolve; if (config && hasStaticTitle(config)) { futureARS.data[RouteTitleKey] = config.title; } return null; })); } function resolveNode(resolve, futureARS, futureRSS, injector) { const keys = getDataKeys(resolve); if (keys.length === 0) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)({}); } const data = {}; return (0,rxjs__WEBPACK_IMPORTED_MODULE_1__.from)(keys).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_11__.mergeMap)(key => getResolver(resolve[key], futureARS, futureRSS, injector).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_12__.first)(), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)(value => { data[key] = value; }))), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_25__.takeLast)(1), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_27__.mapTo)(data), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_19__.catchError)(e => isEmptyError(e) ? rxjs__WEBPACK_IMPORTED_MODULE_26__.EMPTY : (0,rxjs__WEBPACK_IMPORTED_MODULE_18__.throwError)(e))); } function getDataKeys(obj) { return [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)]; } function getResolver(injectionToken, futureARS, futureRSS, injector) { const closestInjector = getClosestRouteInjector(futureARS) ?? injector; const resolver = getTokenOrFunctionIdentity(injectionToken, closestInjector); const resolverValue = resolver.resolve ? resolver.resolve(futureARS, futureRSS) : closestInjector.runInContext(() => resolver(futureARS, futureRSS)); return wrapIntoObservable(resolverValue); } function hasStaticTitle(config) { return typeof config.title === 'string' || config.title === null; } /** * Perform a side effect through a switchMap for every emission on the source Observable, * but return an Observable that is identical to the source. It's essentially the same as * the `tap` operator, but if the side effectful `next` function returns an ObservableInput, * it will wait before continuing with the original value. */ function switchTap(next) { return (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_6__.switchMap)(v => { const nextResult = next(v); if (nextResult) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_1__.from)(nextResult).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(() => v)); } return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(v); }); } const NG_DEV_MODE$5 = typeof ngDevMode === 'undefined' || !!ngDevMode; /** * The [DI token](guide/glossary/#di-token) for a router configuration. * * `ROUTES` is a low level API for router configuration via dependency injection. * * We recommend that in almost all cases to use higher level APIs such as `RouterModule.forRoot()`, * `provideRouter`, or `Router.resetConfig()`. * * @publicApi */ const ROUTES = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('ROUTES'); class RouterConfigLoader { constructor() { this.componentLoaders = new WeakMap(); this.childrenLoaders = new WeakMap(); this.compiler = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__.Compiler); } loadComponent(route) { if (this.componentLoaders.get(route)) { return this.componentLoaders.get(route); } else if (route._loadedComponent) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(route._loadedComponent); } if (this.onLoadStartListener) { this.onLoadStartListener(route); } const loadRunner = wrapIntoObservable(route.loadComponent()).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(maybeUnwrapDefaultExport), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)(component => { if (this.onLoadEndListener) { this.onLoadEndListener(route); } NG_DEV_MODE$5 && assertStandalone(route.path ?? '', component); route._loadedComponent = component; }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_28__.finalize)(() => { this.componentLoaders.delete(route); })); // Use custom ConnectableObservable as share in runners pipe increasing the bundle size too much const loader = new rxjs__WEBPACK_IMPORTED_MODULE_29__.ConnectableObservable(loadRunner, () => new rxjs__WEBPACK_IMPORTED_MODULE_30__.Subject()).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_31__.refCount)()); this.componentLoaders.set(route, loader); return loader; } loadChildren(parentInjector, route) { if (this.childrenLoaders.get(route)) { return this.childrenLoaders.get(route); } else if (route._loadedRoutes) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)({ routes: route._loadedRoutes, injector: route._loadedInjector }); } if (this.onLoadStartListener) { this.onLoadStartListener(route); } const moduleFactoryOrRoutes$ = this.loadModuleFactoryOrRoutes(route.loadChildren); const loadRunner = moduleFactoryOrRoutes$.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(factoryOrRoutes => { if (this.onLoadEndListener) { this.onLoadEndListener(route); } // This injector comes from the `NgModuleRef` when lazy loading an `NgModule`. There is no // injector associated with lazy loading a `Route` array. let injector; let rawRoutes; let requireStandaloneComponents = false; if (Array.isArray(factoryOrRoutes)) { rawRoutes = factoryOrRoutes; requireStandaloneComponents = true; } else { injector = factoryOrRoutes.create(parentInjector).injector; // When loading a module that doesn't provide `RouterModule.forChild()` preloader // will get stuck in an infinite loop. The child module's Injector will look to // its parent `Injector` when it doesn't find any ROUTES so it will return routes // for it's parent module instead. rawRoutes = flatten(injector.get(ROUTES, [], _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectFlags.Self | _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectFlags.Optional)); } const routes = rawRoutes.map(standardizeConfig); NG_DEV_MODE$5 && validateConfig(routes, route.path, requireStandaloneComponents); return { routes, injector }; }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_28__.finalize)(() => { this.childrenLoaders.delete(route); })); // Use custom ConnectableObservable as share in runners pipe increasing the bundle size too much const loader = new rxjs__WEBPACK_IMPORTED_MODULE_29__.ConnectableObservable(loadRunner, () => new rxjs__WEBPACK_IMPORTED_MODULE_30__.Subject()).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_31__.refCount)()); this.childrenLoaders.set(route, loader); return loader; } loadModuleFactoryOrRoutes(loadChildren) { return wrapIntoObservable(loadChildren()).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(maybeUnwrapDefaultExport), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_11__.mergeMap)(t => { if (t instanceof _angular_core__WEBPACK_IMPORTED_MODULE_0__.NgModuleFactory || Array.isArray(t)) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(t); } else { return (0,rxjs__WEBPACK_IMPORTED_MODULE_1__.from)(this.compiler.compileModuleAsync(t)); } })); } } RouterConfigLoader.ɵfac = function RouterConfigLoader_Factory(t) { return new (t || RouterConfigLoader)(); }; RouterConfigLoader.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: RouterConfigLoader, factory: RouterConfigLoader.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](RouterConfigLoader, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], null, null); })(); function isWrappedDefaultExport(value) { // We use `in` here with a string key `'default'`, because we expect `DefaultExport` objects to be // dynamically imported ES modules with a spec-mandated `default` key. Thus we don't expect that // `default` will be a renamed property. return value && typeof value === 'object' && 'default' in value; } function maybeUnwrapDefaultExport(input) { // As per `isWrappedDefaultExport`, the `default` key here is generated by the browser and not // subject to property renaming, so we reference it with bracket access. return isWrappedDefaultExport(input) ? input['default'] : input; } const NG_DEV_MODE$4 = typeof ngDevMode === 'undefined' || !!ngDevMode; class NavigationTransitions { get hasRequestedNavigation() { return this.navigationId !== 0; } constructor() { this.currentNavigation = null; this.lastSuccessfulNavigation = null; this.events = new rxjs__WEBPACK_IMPORTED_MODULE_30__.Subject(); this.configLoader = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(RouterConfigLoader); this.environmentInjector = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__.EnvironmentInjector); this.urlSerializer = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(UrlSerializer); this.rootContexts = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(ChildrenOutletContexts); this.navigationId = 0; /** * Hook that enables you to pause navigation after the preactivation phase. * Used by `RouterModule`. * * @internal */ this.afterPreactivation = () => (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(void 0); /** @internal */ this.rootComponentType = null; const onLoadStart = r => this.events.next(new RouteConfigLoadStart(r)); const onLoadEnd = r => this.events.next(new RouteConfigLoadEnd(r)); this.configLoader.onLoadEndListener = onLoadEnd; this.configLoader.onLoadStartListener = onLoadStart; } complete() { this.transitions?.complete(); } handleNavigationRequest(request) { const id = ++this.navigationId; this.transitions?.next({ ...this.transitions.value, ...request, id }); } setupNavigations(router) { this.transitions = new rxjs__WEBPACK_IMPORTED_MODULE_3__.BehaviorSubject({ id: 0, targetPageId: 0, currentUrlTree: router.currentUrlTree, currentRawUrl: router.currentUrlTree, extractedUrl: router.urlHandlingStrategy.extract(router.currentUrlTree), urlAfterRedirects: router.urlHandlingStrategy.extract(router.currentUrlTree), rawUrl: router.currentUrlTree, extras: {}, resolve: null, reject: null, promise: Promise.resolve(true), source: IMPERATIVE_NAVIGATION, restoredState: null, currentSnapshot: router.routerState.snapshot, targetSnapshot: null, currentRouterState: router.routerState, targetRouterState: null, guards: { canActivateChecks: [], canDeactivateChecks: [] }, guardsResult: null }); return this.transitions.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_10__.filter)(t => t.id !== 0), // Extract URL (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(t => ({ ...t, extractedUrl: router.urlHandlingStrategy.extract(t.rawUrl) })), // Using switchMap so we cancel executing navigations when a new one comes in (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_6__.switchMap)(overallTransitionState => { let completed = false; let errored = false; return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(overallTransitionState).pipe( // Store the Navigation object (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)(t => { this.currentNavigation = { id: t.id, initialUrl: t.rawUrl, extractedUrl: t.extractedUrl, trigger: t.source, extras: t.extras, previousNavigation: !this.lastSuccessfulNavigation ? null : { ...this.lastSuccessfulNavigation, previousNavigation: null } }; }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_6__.switchMap)(t => { const browserUrlTree = router.browserUrlTree.toString(); const urlTransition = !router.navigated || t.extractedUrl.toString() !== browserUrlTree || // Navigations which succeed or ones which fail and are cleaned up // correctly should result in `browserUrlTree` and `currentUrlTree` // matching. If this is not the case, assume something went wrong and // try processing the URL again. browserUrlTree !== router.currentUrlTree.toString(); const onSameUrlNavigation = t.extras.onSameUrlNavigation ?? router.onSameUrlNavigation; if (!urlTransition && onSameUrlNavigation !== 'reload') { const reason = NG_DEV_MODE$4 ? `Navigation to ${t.rawUrl} was ignored because it is the same as the current Router URL.` : ''; this.events.next(new NavigationSkipped(t.id, router.serializeUrl(overallTransitionState.rawUrl), reason, 0 /* NavigationSkippedCode.IgnoredSameUrlNavigation */)); router.rawUrlTree = t.rawUrl; t.resolve(null); return rxjs__WEBPACK_IMPORTED_MODULE_26__.EMPTY; } if (router.urlHandlingStrategy.shouldProcessUrl(t.rawUrl)) { // If the source of the navigation is from a browser event, the URL is // already updated. We already need to sync the internal state. if (isBrowserTriggeredNavigation(t.source)) { router.browserUrlTree = t.extractedUrl; } return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(t).pipe( // Fire NavigationStart event (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_6__.switchMap)(t => { const transition = this.transitions?.getValue(); this.events.next(new NavigationStart(t.id, this.urlSerializer.serialize(t.extractedUrl), t.source, t.restoredState)); if (transition !== this.transitions?.getValue()) { return rxjs__WEBPACK_IMPORTED_MODULE_26__.EMPTY; } // This delay is required to match old behavior that forced // navigation to always be async return Promise.resolve(t); }), // ApplyRedirects applyRedirects(this.environmentInjector, this.configLoader, this.urlSerializer, router.config), // Update the currentNavigation // `urlAfterRedirects` is guaranteed to be set after this point (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)(t => { this.currentNavigation = { ...this.currentNavigation, finalUrl: t.urlAfterRedirects }; overallTransitionState.urlAfterRedirects = t.urlAfterRedirects; }), // Recognize recognize(this.environmentInjector, this.rootComponentType, router.config, this.urlSerializer, router.paramsInheritanceStrategy), // Update URL if in `eager` update mode (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)(t => { overallTransitionState.targetSnapshot = t.targetSnapshot; if (router.urlUpdateStrategy === 'eager') { if (!t.extras.skipLocationChange) { const rawUrl = router.urlHandlingStrategy.merge(t.urlAfterRedirects, t.rawUrl); router.setBrowserUrl(rawUrl, t); } router.browserUrlTree = t.urlAfterRedirects; } // Fire RoutesRecognized const routesRecognized = new RoutesRecognized(t.id, this.urlSerializer.serialize(t.extractedUrl), this.urlSerializer.serialize(t.urlAfterRedirects), t.targetSnapshot); this.events.next(routesRecognized); })); } else if (urlTransition && router.urlHandlingStrategy.shouldProcessUrl(router.rawUrlTree)) { /* When the current URL shouldn't be processed, but the previous one * was, we handle this "error condition" by navigating to the * previously successful URL, but leaving the URL intact.*/ const { id, extractedUrl, source, restoredState, extras } = t; const navStart = new NavigationStart(id, this.urlSerializer.serialize(extractedUrl), source, restoredState); this.events.next(navStart); const targetSnapshot = createEmptyState(extractedUrl, this.rootComponentType).snapshot; overallTransitionState = { ...t, targetSnapshot, urlAfterRedirects: extractedUrl, extras: { ...extras, skipLocationChange: false, replaceUrl: false } }; return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(overallTransitionState); } else { /* When neither the current or previous URL can be processed, do * nothing other than update router's internal reference to the * current "settled" URL. This way the next navigation will be coming * from the current URL in the browser. */ const reason = NG_DEV_MODE$4 ? `Navigation was ignored because the UrlHandlingStrategy` + ` indicated neither the current URL ${router.rawUrlTree} nor target URL ${t.rawUrl} should be processed.` : ''; this.events.next(new NavigationSkipped(t.id, router.serializeUrl(overallTransitionState.extractedUrl), reason, 1 /* NavigationSkippedCode.IgnoredByUrlHandlingStrategy */)); router.rawUrlTree = t.rawUrl; t.resolve(null); return rxjs__WEBPACK_IMPORTED_MODULE_26__.EMPTY; } }), // --- GUARDS --- (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)(t => { const guardsStart = new GuardsCheckStart(t.id, this.urlSerializer.serialize(t.extractedUrl), this.urlSerializer.serialize(t.urlAfterRedirects), t.targetSnapshot); this.events.next(guardsStart); }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(t => { overallTransitionState = { ...t, guards: getAllRouteGuards(t.targetSnapshot, t.currentSnapshot, this.rootContexts) }; return overallTransitionState; }), checkGuards(this.environmentInjector, evt => this.events.next(evt)), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)(t => { overallTransitionState.guardsResult = t.guardsResult; if (isUrlTree(t.guardsResult)) { throw redirectingNavigationError(this.urlSerializer, t.guardsResult); } const guardsEnd = new GuardsCheckEnd(t.id, this.urlSerializer.serialize(t.extractedUrl), this.urlSerializer.serialize(t.urlAfterRedirects), t.targetSnapshot, !!t.guardsResult); this.events.next(guardsEnd); }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_10__.filter)(t => { if (!t.guardsResult) { router.restoreHistory(t); this.cancelNavigationTransition(t, '', 3 /* NavigationCancellationCode.GuardRejected */); return false; } return true; }), // --- RESOLVE --- switchTap(t => { if (t.guards.canActivateChecks.length) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(t).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)(t => { const resolveStart = new ResolveStart(t.id, this.urlSerializer.serialize(t.extractedUrl), this.urlSerializer.serialize(t.urlAfterRedirects), t.targetSnapshot); this.events.next(resolveStart); }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_6__.switchMap)(t => { let dataResolved = false; return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(t).pipe(resolveData(router.paramsInheritanceStrategy, this.environmentInjector), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)({ next: () => dataResolved = true, complete: () => { if (!dataResolved) { router.restoreHistory(t); this.cancelNavigationTransition(t, NG_DEV_MODE$4 ? `At least one route resolver didn't emit any value.` : '', 2 /* NavigationCancellationCode.NoDataFromResolver */); } } })); }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)(t => { const resolveEnd = new ResolveEnd(t.id, this.urlSerializer.serialize(t.extractedUrl), this.urlSerializer.serialize(t.urlAfterRedirects), t.targetSnapshot); this.events.next(resolveEnd); })); } return undefined; }), // --- LOAD COMPONENTS --- switchTap(t => { const loadComponents = route => { const loaders = []; if (route.routeConfig?.loadComponent && !route.routeConfig._loadedComponent) { loaders.push(this.configLoader.loadComponent(route.routeConfig).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)(loadedComponent => { route.component = loadedComponent; }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(() => void 0))); } for (const child of route.children) { loaders.push(...loadComponents(child)); } return loaders; }; return (0,rxjs__WEBPACK_IMPORTED_MODULE_7__.combineLatest)(loadComponents(t.targetSnapshot.root)).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_24__.defaultIfEmpty)(), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_8__.take)(1)); }), switchTap(() => this.afterPreactivation()), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(t => { const targetRouterState = createRouterState(router.routeReuseStrategy, t.targetSnapshot, t.currentRouterState); overallTransitionState = { ...t, targetRouterState }; return overallTransitionState; }), /* Once here, we are about to activate synchronously. The assumption is this will succeed, and user code may read from the Router service. Therefore before activation, we need to update router properties storing the current URL and the RouterState, as well as updated the browser URL. All this should happen *before* activating. */ (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)(t => { router.currentUrlTree = t.urlAfterRedirects; router.rawUrlTree = router.urlHandlingStrategy.merge(t.urlAfterRedirects, t.rawUrl); router.routerState = t.targetRouterState; if (router.urlUpdateStrategy === 'deferred') { if (!t.extras.skipLocationChange) { router.setBrowserUrl(router.rawUrlTree, t); } router.browserUrlTree = t.urlAfterRedirects; } }), activateRoutes(this.rootContexts, router.routeReuseStrategy, evt => this.events.next(evt)), // Ensure that if some observable used to drive the transition doesn't // complete, the navigation still finalizes This should never happen, but // this is done as a safety measure to avoid surfacing this error (#49567). (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_8__.take)(1), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_17__.tap)({ next: t => { completed = true; this.lastSuccessfulNavigation = this.currentNavigation; router.navigated = true; this.events.next(new NavigationEnd(t.id, this.urlSerializer.serialize(t.extractedUrl), this.urlSerializer.serialize(router.currentUrlTree))); router.titleStrategy?.updateTitle(t.targetRouterState.snapshot); t.resolve(true); }, complete: () => { completed = true; } }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_28__.finalize)(() => { /* When the navigation stream finishes either through error or success, * we set the `completed` or `errored` flag. However, there are some * situations where we could get here without either of those being set. * For instance, a redirect during NavigationStart. Therefore, this is a * catch-all to make sure the NavigationCancel event is fired when a * navigation gets cancelled but not caught by other means. */ if (!completed && !errored) { const cancelationReason = NG_DEV_MODE$4 ? `Navigation ID ${overallTransitionState.id} is not equal to the current navigation id ${this.navigationId}` : ''; this.cancelNavigationTransition(overallTransitionState, cancelationReason, 1 /* NavigationCancellationCode.SupersededByNewNavigation */); } // Only clear current navigation if it is still set to the one that // finalized. if (this.currentNavigation?.id === overallTransitionState.id) { this.currentNavigation = null; } }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_19__.catchError)(e => { errored = true; /* This error type is issued during Redirect, and is handled as a * cancellation rather than an error. */ if (isNavigationCancelingError$1(e)) { if (!isRedirectingNavigationCancelingError$1(e)) { // Set property only if we're not redirecting. If we landed on a page // and redirect to `/` route, the new navigation is going to see the // `/` isn't a change from the default currentUrlTree and won't // navigate. This is only applicable with initial navigation, so // setting `navigated` only when not redirecting resolves this // scenario. router.navigated = true; router.restoreHistory(overallTransitionState, true); } const navCancel = new NavigationCancel(overallTransitionState.id, this.urlSerializer.serialize(overallTransitionState.extractedUrl), e.message, e.cancellationCode); this.events.next(navCancel); // When redirecting, we need to delay resolving the navigation // promise and push it to the redirect navigation if (!isRedirectingNavigationCancelingError$1(e)) { overallTransitionState.resolve(false); } else { const mergedTree = router.urlHandlingStrategy.merge(e.url, router.rawUrlTree); const extras = { skipLocationChange: overallTransitionState.extras.skipLocationChange, // The URL is already updated at this point if we have 'eager' URL // updates or if the navigation was triggered by the browser (back // button, URL bar, etc). We want to replace that item in history // if the navigation is rejected. replaceUrl: router.urlUpdateStrategy === 'eager' || isBrowserTriggeredNavigation(overallTransitionState.source) }; router.scheduleNavigation(mergedTree, IMPERATIVE_NAVIGATION, null, extras, { resolve: overallTransitionState.resolve, reject: overallTransitionState.reject, promise: overallTransitionState.promise }); } /* All other errors should reset to the router's internal URL reference * to the pre-error state. */ } else { router.restoreHistory(overallTransitionState, true); const navError = new NavigationError(overallTransitionState.id, this.urlSerializer.serialize(overallTransitionState.extractedUrl), e, overallTransitionState.targetSnapshot ?? undefined); this.events.next(navError); try { overallTransitionState.resolve(router.errorHandler(e)); } catch (ee) { overallTransitionState.reject(ee); } } return rxjs__WEBPACK_IMPORTED_MODULE_26__.EMPTY; })); // casting because `pipe` returns observable({}) when called with 8+ arguments })); } cancelNavigationTransition(t, reason, code) { const navCancel = new NavigationCancel(t.id, this.urlSerializer.serialize(t.extractedUrl), reason, code); this.events.next(navCancel); t.resolve(false); } } NavigationTransitions.ɵfac = function NavigationTransitions_Factory(t) { return new (t || NavigationTransitions)(); }; NavigationTransitions.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: NavigationTransitions, factory: NavigationTransitions.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NavigationTransitions, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], function () { return []; }, null); })(); function isBrowserTriggeredNavigation(source) { return source !== IMPERATIVE_NAVIGATION; } /** * Provides a strategy for setting the page title after a router navigation. * * The built-in implementation traverses the router state snapshot and finds the deepest primary * outlet with `title` property. Given the `Routes` below, navigating to * `/base/child(popup:aux)` would result in the document title being set to "child". * ``` * [ * {path: 'base', title: 'base', children: [ * {path: 'child', title: 'child'}, * ], * {path: 'aux', outlet: 'popup', title: 'popupTitle'} * ] * ``` * * This class can be used as a base class for custom title strategies. That is, you can create your * own class that extends the `TitleStrategy`. Note that in the above example, the `title` * from the named outlet is never used. However, a custom strategy might be implemented to * incorporate titles in named outlets. * * @publicApi * @see [Page title guide](guide/router#setting-the-page-title) */ class TitleStrategy { /** * @returns The `title` of the deepest primary route. */ buildTitle(snapshot) { let pageTitle; let route = snapshot.root; while (route !== undefined) { pageTitle = this.getResolvedTitleForRoute(route) ?? pageTitle; route = route.children.find(child => child.outlet === PRIMARY_OUTLET); } return pageTitle; } /** * Given an `ActivatedRouteSnapshot`, returns the final value of the * `Route.title` property, which can either be a static string or a resolved value. */ getResolvedTitleForRoute(snapshot) { return snapshot.data[RouteTitleKey]; } } TitleStrategy.ɵfac = function TitleStrategy_Factory(t) { return new (t || TitleStrategy)(); }; TitleStrategy.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: TitleStrategy, factory: function () { return (() => (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(DefaultTitleStrategy))(); }, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](TitleStrategy, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root', useFactory: () => (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(DefaultTitleStrategy) }] }], null, null); })(); /** * The default `TitleStrategy` used by the router that updates the title using the `Title` service. */ class DefaultTitleStrategy extends TitleStrategy { constructor(title) { super(); this.title = title; } /** * Sets the title of the browser to the given value. * * @param title The `pageTitle` from the deepest primary route. */ updateTitle(snapshot) { const title = this.buildTitle(snapshot); if (title !== undefined) { this.title.setTitle(title); } } } DefaultTitleStrategy.ɵfac = function DefaultTitleStrategy_Factory(t) { return new (t || DefaultTitleStrategy)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](_angular_platform_browser__WEBPACK_IMPORTED_MODULE_32__.Title)); }; DefaultTitleStrategy.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: DefaultTitleStrategy, factory: DefaultTitleStrategy.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](DefaultTitleStrategy, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], function () { return [{ type: _angular_platform_browser__WEBPACK_IMPORTED_MODULE_32__.Title }]; }, null); })(); /** * @description * * Provides a way to customize when activated routes get reused. * * @publicApi */ class RouteReuseStrategy {} RouteReuseStrategy.ɵfac = function RouteReuseStrategy_Factory(t) { return new (t || RouteReuseStrategy)(); }; RouteReuseStrategy.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: RouteReuseStrategy, factory: function () { return (() => (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(DefaultRouteReuseStrategy))(); }, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](RouteReuseStrategy, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root', useFactory: () => (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(DefaultRouteReuseStrategy) }] }], null, null); })(); /** * @description * * This base route reuse strategy only reuses routes when the matched router configs are * identical. This prevents components from being destroyed and recreated * when just the route parameters, query parameters or fragment change * (that is, the existing component is _reused_). * * This strategy does not store any routes for later reuse. * * Angular uses this strategy by default. * * * It can be used as a base class for custom route reuse strategies, i.e. you can create your own * class that extends the `BaseRouteReuseStrategy` one. * @publicApi */ class BaseRouteReuseStrategy { /** * Whether the given route should detach for later reuse. * Always returns false for `BaseRouteReuseStrategy`. * */ shouldDetach(route) { return false; } /** * A no-op; the route is never stored since this strategy never detaches routes for later re-use. */ store(route, detachedTree) {} /** Returns `false`, meaning the route (and its subtree) is never reattached */ shouldAttach(route) { return false; } /** Returns `null` because this strategy does not store routes for later re-use. */ retrieve(route) { return null; } /** * Determines if a route should be reused. * This strategy returns `true` when the future route config and current route config are * identical. */ shouldReuseRoute(future, curr) { return future.routeConfig === curr.routeConfig; } } class DefaultRouteReuseStrategy extends BaseRouteReuseStrategy {} DefaultRouteReuseStrategy.ɵfac = /* @__PURE__ */function () { let ɵDefaultRouteReuseStrategy_BaseFactory; return function DefaultRouteReuseStrategy_Factory(t) { return (ɵDefaultRouteReuseStrategy_BaseFactory || (ɵDefaultRouteReuseStrategy_BaseFactory = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵgetInheritedFactory"](DefaultRouteReuseStrategy)))(t || DefaultRouteReuseStrategy); }; }(); DefaultRouteReuseStrategy.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: DefaultRouteReuseStrategy, factory: DefaultRouteReuseStrategy.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](DefaultRouteReuseStrategy, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], null, null); })(); const NG_DEV_MODE$3 = typeof ngDevMode === 'undefined' || !!ngDevMode; /** * A [DI token](guide/glossary/#di-token) for the router service. * * @publicApi */ const ROUTER_CONFIGURATION = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken(NG_DEV_MODE$3 ? 'router config' : '', { providedIn: 'root', factory: () => ({}) }); /** * @description * * Provides a way to migrate AngularJS applications to Angular. * * @publicApi */ class UrlHandlingStrategy {} UrlHandlingStrategy.ɵfac = function UrlHandlingStrategy_Factory(t) { return new (t || UrlHandlingStrategy)(); }; UrlHandlingStrategy.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: UrlHandlingStrategy, factory: function () { return (() => (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(DefaultUrlHandlingStrategy))(); }, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](UrlHandlingStrategy, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root', useFactory: () => (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(DefaultUrlHandlingStrategy) }] }], null, null); })(); /** * @publicApi */ class DefaultUrlHandlingStrategy { shouldProcessUrl(url) { return true; } extract(url) { return url; } merge(newUrlPart, wholeUrl) { return newUrlPart; } } DefaultUrlHandlingStrategy.ɵfac = function DefaultUrlHandlingStrategy_Factory(t) { return new (t || DefaultUrlHandlingStrategy)(); }; DefaultUrlHandlingStrategy.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: DefaultUrlHandlingStrategy, factory: DefaultUrlHandlingStrategy.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](DefaultUrlHandlingStrategy, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], null, null); })(); const NG_DEV_MODE$2 = typeof ngDevMode === 'undefined' || !!ngDevMode; function defaultErrorHandler(error) { throw error; } function defaultMalformedUriErrorHandler(error, urlSerializer, url) { return urlSerializer.parse('/'); } /** * The equivalent `IsActiveMatchOptions` options for `Router.isActive` is called with `true` * (exact = true). */ const exactMatchOptions = { paths: 'exact', fragment: 'ignored', matrixParams: 'ignored', queryParams: 'exact' }; /** * The equivalent `IsActiveMatchOptions` options for `Router.isActive` is called with `false` * (exact = false). */ const subsetMatchOptions = { paths: 'subset', fragment: 'ignored', matrixParams: 'ignored', queryParams: 'subset' }; /** * @description * * A service that provides navigation among views and URL manipulation capabilities. * * @see `Route`. * @see [Routing and Navigation Guide](guide/router). * * @ngModule RouterModule * * @publicApi */ class Router { // TODO(b/260747083): This should not exist and navigationId should be private in // `NavigationTransitions` get navigationId() { return this.navigationTransitions.navigationId; } /** * The ɵrouterPageId of whatever page is currently active in the browser history. This is * important for computing the target page id for new navigations because we need to ensure each * page id in the browser history is 1 more than the previous entry. */ get browserPageId() { if (this.canceledNavigationResolution !== 'computed') { return undefined; } return this.location.getState()?.ɵrouterPageId; } /** * An event stream for routing events. */ get events() { // TODO(atscott): This _should_ be events.asObservable(). However, this change requires internal // cleanup: tests are doing `(route.events as Subject<Event>).next(...)`. This isn't // allowed/supported but we still have to fix these or file bugs against the teams before making // the change. return this.navigationTransitions.events; } constructor() { this.disposed = false; /** * The id of the currently active page in the router. * Updated to the transition's target id on a successful navigation. * * This is used to track what page the router last activated. When an attempted navigation fails, * the router can then use this to compute how to restore the state back to the previously active * page. */ this.currentPageId = 0; this.console = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵConsole"]); this.isNgZoneEnabled = false; this.options = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(ROUTER_CONFIGURATION, { optional: true }) || {}; /** * A handler for navigation errors in this NgModule. * * @deprecated Subscribe to the `Router` events and watch for `NavigationError` instead. * `provideRouter` has the `withNavigationErrorHandler` feature to make this easier. * @see `withNavigationErrorHandler` */ this.errorHandler = this.options.errorHandler || defaultErrorHandler; /** * A handler for errors thrown by `Router.parseUrl(url)` * when `url` contains an invalid character. * The most common case is a `%` sign * that's not encoded and is not part of a percent encoded sequence. * * @deprecated URI parsing errors should be handled in the `UrlSerializer`. * * @see `RouterModule` */ this.malformedUriErrorHandler = this.options.malformedUriErrorHandler || defaultMalformedUriErrorHandler; /** * True if at least one navigation event has occurred, * false otherwise. */ this.navigated = false; this.lastSuccessfulId = -1; /** * A strategy for extracting and merging URLs. * Used for AngularJS to Angular migrations. * * @deprecated Configure using `providers` instead: * `{provide: UrlHandlingStrategy, useClass: MyStrategy}`. */ this.urlHandlingStrategy = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(UrlHandlingStrategy); /** * A strategy for re-using routes. * * @deprecated Configure using `providers` instead: * `{provide: RouteReuseStrategy, useClass: MyStrategy}`. */ this.routeReuseStrategy = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(RouteReuseStrategy); /** Strategy used to create a UrlTree. */ this.urlCreationStrategy = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(CreateUrlTreeStrategy); /** * A strategy for setting the title based on the `routerState`. * * @deprecated Configure using `providers` instead: * `{provide: TitleStrategy, useClass: MyStrategy}`. */ this.titleStrategy = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(TitleStrategy); /** * How to handle a navigation request to the current URL. * * * @deprecated Configure this through `provideRouter` or `RouterModule.forRoot` instead. * @see `withRouterConfig` * @see `provideRouter` * @see `RouterModule` */ this.onSameUrlNavigation = this.options.onSameUrlNavigation || 'ignore'; /** * How to merge parameters, data, resolved data, and title from parent to child * routes. One of: * * - `'emptyOnly'` : Inherit parent parameters, data, and resolved data * for path-less or component-less routes. * - `'always'` : Inherit parent parameters, data, and resolved data * for all child routes. * * @deprecated Configure this through `provideRouter` or `RouterModule.forRoot` instead. * @see `withRouterConfig` * @see `provideRouter` * @see `RouterModule` */ this.paramsInheritanceStrategy = this.options.paramsInheritanceStrategy || 'emptyOnly'; /** * Determines when the router updates the browser URL. * By default (`"deferred"`), updates the browser URL after navigation has finished. * Set to `'eager'` to update the browser URL at the beginning of navigation. * You can choose to update early so that, if navigation fails, * you can show an error message with the URL that failed. * * @deprecated Configure this through `provideRouter` or `RouterModule.forRoot` instead. * @see `withRouterConfig` * @see `provideRouter` * @see `RouterModule` */ this.urlUpdateStrategy = this.options.urlUpdateStrategy || 'deferred'; /** * Configures how the Router attempts to restore state when a navigation is cancelled. * * 'replace' - Always uses `location.replaceState` to set the browser state to the state of the * router before the navigation started. This means that if the URL of the browser is updated * _before_ the navigation is canceled, the Router will simply replace the item in history rather * than trying to restore to the previous location in the session history. This happens most * frequently with `urlUpdateStrategy: 'eager'` and navigations with the browser back/forward * buttons. * * 'computed' - Will attempt to return to the same index in the session history that corresponds * to the Angular route when the navigation gets cancelled. For example, if the browser back * button is clicked and the navigation is cancelled, the Router will trigger a forward navigation * and vice versa. * * Note: the 'computed' option is incompatible with any `UrlHandlingStrategy` which only * handles a portion of the URL because the history restoration navigates to the previous place in * the browser history rather than simply resetting a portion of the URL. * * The default value is `replace`. * * @deprecated Configure this through `provideRouter` or `RouterModule.forRoot` instead. * @see `withRouterConfig` * @see `provideRouter` * @see `RouterModule` */ this.canceledNavigationResolution = this.options.canceledNavigationResolution || 'replace'; this.config = flatten((0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(ROUTES, { optional: true }) ?? []); this.navigationTransitions = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(NavigationTransitions); this.urlSerializer = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(UrlSerializer); this.location = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_common__WEBPACK_IMPORTED_MODULE_33__.Location); this.isNgZoneEnabled = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__.NgZone) instanceof _angular_core__WEBPACK_IMPORTED_MODULE_0__.NgZone && _angular_core__WEBPACK_IMPORTED_MODULE_0__.NgZone.isInAngularZone(); this.resetConfig(this.config); this.currentUrlTree = new UrlTree(); this.rawUrlTree = this.currentUrlTree; this.browserUrlTree = this.currentUrlTree; this.routerState = createEmptyState(this.currentUrlTree, null); this.navigationTransitions.setupNavigations(this).subscribe(t => { this.lastSuccessfulId = t.id; this.currentPageId = this.browserPageId ?? 0; }, e => { this.console.warn(`Unhandled Navigation Error: ${e}`); }); } /** @internal */ resetRootComponentType(rootComponentType) { // TODO: vsavkin router 4.0 should make the root component set to null // this will simplify the lifecycle of the router. this.routerState.root.component = rootComponentType; this.navigationTransitions.rootComponentType = rootComponentType; } /** * Sets up the location change listener and performs the initial navigation. */ initialNavigation() { this.setUpLocationChangeListener(); if (!this.navigationTransitions.hasRequestedNavigation) { const state = this.location.getState(); this.navigateToSyncWithBrowser(this.location.path(true), IMPERATIVE_NAVIGATION, state); } } /** * Sets up the location change listener. This listener detects navigations triggered from outside * the Router (the browser back/forward buttons, for example) and schedules a corresponding Router * navigation so that the correct events, guards, etc. are triggered. */ setUpLocationChangeListener() { // Don't need to use Zone.wrap any more, because zone.js // already patch onPopState, so location change callback will // run into ngZone if (!this.locationSubscription) { this.locationSubscription = this.location.subscribe(event => { const source = event['type'] === 'popstate' ? 'popstate' : 'hashchange'; if (source === 'popstate') { // The `setTimeout` was added in #12160 and is likely to support Angular/AngularJS // hybrid apps. setTimeout(() => { this.navigateToSyncWithBrowser(event['url'], source, event.state); }, 0); } }); } } /** * Schedules a router navigation to synchronize Router state with the browser state. * * This is done as a response to a popstate event and the initial navigation. These * two scenarios represent times when the browser URL/state has been updated and * the Router needs to respond to ensure its internal state matches. */ navigateToSyncWithBrowser(url, source, state) { const extras = { replaceUrl: true }; // TODO: restoredState should always include the entire state, regardless // of navigationId. This requires a breaking change to update the type on // NavigationStart’s restoredState, which currently requires navigationId // to always be present. The Router used to only restore history state if // a navigationId was present. // The stored navigationId is used by the RouterScroller to retrieve the scroll // position for the page. const restoredState = state?.navigationId ? state : null; // Separate to NavigationStart.restoredState, we must also restore the state to // history.state and generate a new navigationId, since it will be overwritten if (state) { const stateCopy = { ...state }; delete stateCopy.navigationId; delete stateCopy.ɵrouterPageId; if (Object.keys(stateCopy).length !== 0) { extras.state = stateCopy; } } const urlTree = this.parseUrl(url); this.scheduleNavigation(urlTree, source, restoredState, extras); } /** The current URL. */ get url() { return this.serializeUrl(this.currentUrlTree); } /** * Returns the current `Navigation` object when the router is navigating, * and `null` when idle. */ getCurrentNavigation() { return this.navigationTransitions.currentNavigation; } /** * Resets the route configuration used for navigation and generating links. * * @param config The route array for the new configuration. * * @usageNotes * * ``` * router.resetConfig([ * { path: 'team/:id', component: TeamCmp, children: [ * { path: 'simple', component: SimpleCmp }, * { path: 'user/:name', component: UserCmp } * ]} * ]); * ``` */ resetConfig(config) { NG_DEV_MODE$2 && validateConfig(config); this.config = config.map(standardizeConfig); this.navigated = false; this.lastSuccessfulId = -1; } /** @nodoc */ ngOnDestroy() { this.dispose(); } /** Disposes of the router. */ dispose() { this.navigationTransitions.complete(); if (this.locationSubscription) { this.locationSubscription.unsubscribe(); this.locationSubscription = undefined; } this.disposed = true; } /** * Appends URL segments to the current URL tree to create a new URL tree. * * @param commands An array of URL fragments with which to construct the new URL tree. * If the path is static, can be the literal URL string. For a dynamic path, pass an array of path * segments, followed by the parameters for each segment. * The fragments are applied to the current URL tree or the one provided in the `relativeTo` * property of the options object, if supplied. * @param navigationExtras Options that control the navigation strategy. * @returns The new URL tree. * * @usageNotes * * ``` * // create /team/33/user/11 * router.createUrlTree(['/team', 33, 'user', 11]); * * // create /team/33;expand=true/user/11 * router.createUrlTree(['/team', 33, {expand: true}, 'user', 11]); * * // you can collapse static segments like this (this works only with the first passed-in value): * router.createUrlTree(['/team/33/user', userId]); * * // If the first segment can contain slashes, and you do not want the router to split it, * // you can do the following: * router.createUrlTree([{segmentPath: '/one/two'}]); * * // create /team/33/(user/11//right:chat) * router.createUrlTree(['/team', 33, {outlets: {primary: 'user/11', right: 'chat'}}]); * * // remove the right secondary node * router.createUrlTree(['/team', 33, {outlets: {primary: 'user/11', right: null}}]); * * // assuming the current url is `/team/33/user/11` and the route points to `user/11` * * // navigate to /team/33/user/11/details * router.createUrlTree(['details'], {relativeTo: route}); * * // navigate to /team/33/user/22 * router.createUrlTree(['../22'], {relativeTo: route}); * * // navigate to /team/44/user/22 * router.createUrlTree(['../../team/44/user/22'], {relativeTo: route}); * * Note that a value of `null` or `undefined` for `relativeTo` indicates that the * tree should be created relative to the root. * ``` */ createUrlTree(commands, navigationExtras = {}) { const { relativeTo, queryParams, fragment, queryParamsHandling, preserveFragment } = navigationExtras; const f = preserveFragment ? this.currentUrlTree.fragment : fragment; let q = null; switch (queryParamsHandling) { case 'merge': q = { ...this.currentUrlTree.queryParams, ...queryParams }; break; case 'preserve': q = this.currentUrlTree.queryParams; break; default: q = queryParams || null; } if (q !== null) { q = this.removeEmptyProps(q); } return this.urlCreationStrategy.createUrlTree(relativeTo, this.routerState, this.currentUrlTree, commands, q, f ?? null); } /** * Navigates to a view using an absolute route path. * * @param url An absolute path for a defined route. The function does not apply any delta to the * current URL. * @param extras An object containing properties that modify the navigation strategy. * * @returns A Promise that resolves to 'true' when navigation succeeds, * to 'false' when navigation fails, or is rejected on error. * * @usageNotes * * The following calls request navigation to an absolute path. * * ``` * router.navigateByUrl("/team/33/user/11"); * * // Navigate without updating the URL * router.navigateByUrl("/team/33/user/11", { skipLocationChange: true }); * ``` * * @see [Routing and Navigation guide](guide/router) * */ navigateByUrl(url, extras = { skipLocationChange: false }) { if (NG_DEV_MODE$2) { if (this.isNgZoneEnabled && !_angular_core__WEBPACK_IMPORTED_MODULE_0__.NgZone.isInAngularZone()) { this.console.warn(`Navigation triggered outside Angular zone, did you forget to call 'ngZone.run()'?`); } if (url instanceof UrlTree && url._warnIfUsedForNavigation) { this.console.warn(url._warnIfUsedForNavigation); } } const urlTree = isUrlTree(url) ? url : this.parseUrl(url); const mergedTree = this.urlHandlingStrategy.merge(urlTree, this.rawUrlTree); return this.scheduleNavigation(mergedTree, IMPERATIVE_NAVIGATION, null, extras); } /** * Navigate based on the provided array of commands and a starting point. * If no starting route is provided, the navigation is absolute. * * @param commands An array of URL fragments with which to construct the target URL. * If the path is static, can be the literal URL string. For a dynamic path, pass an array of path * segments, followed by the parameters for each segment. * The fragments are applied to the current URL or the one provided in the `relativeTo` property * of the options object, if supplied. * @param extras An options object that determines how the URL should be constructed or * interpreted. * * @returns A Promise that resolves to `true` when navigation succeeds, to `false` when navigation * fails, * or is rejected on error. * * @usageNotes * * The following calls request navigation to a dynamic route path relative to the current URL. * * ``` * router.navigate(['team', 33, 'user', 11], {relativeTo: route}); * * // Navigate without updating the URL, overriding the default behavior * router.navigate(['team', 33, 'user', 11], {relativeTo: route, skipLocationChange: true}); * ``` * * @see [Routing and Navigation guide](guide/router) * */ navigate(commands, extras = { skipLocationChange: false }) { validateCommands(commands); return this.navigateByUrl(this.createUrlTree(commands, extras), extras); } /** Serializes a `UrlTree` into a string */ serializeUrl(url) { return this.urlSerializer.serialize(url); } /** Parses a string into a `UrlTree` */ parseUrl(url) { let urlTree; try { urlTree = this.urlSerializer.parse(url); } catch (e) { urlTree = this.malformedUriErrorHandler(e, this.urlSerializer, url); } return urlTree; } isActive(url, matchOptions) { let options; if (matchOptions === true) { options = { ...exactMatchOptions }; } else if (matchOptions === false) { options = { ...subsetMatchOptions }; } else { options = matchOptions; } if (isUrlTree(url)) { return containsTree(this.currentUrlTree, url, options); } const urlTree = this.parseUrl(url); return containsTree(this.currentUrlTree, urlTree, options); } removeEmptyProps(params) { return Object.keys(params).reduce((result, key) => { const value = params[key]; if (value !== null && value !== undefined) { result[key] = value; } return result; }, {}); } /** @internal */ scheduleNavigation(rawUrl, source, restoredState, extras, priorPromise) { if (this.disposed) { return Promise.resolve(false); } let resolve; let reject; let promise; if (priorPromise) { resolve = priorPromise.resolve; reject = priorPromise.reject; promise = priorPromise.promise; } else { promise = new Promise((res, rej) => { resolve = res; reject = rej; }); } let targetPageId; if (this.canceledNavigationResolution === 'computed') { // If the `ɵrouterPageId` exist in the state then `targetpageId` should have the value of // `ɵrouterPageId`. This is the case for something like a page refresh where we assign the // target id to the previously set value for that page. if (restoredState && restoredState.ɵrouterPageId) { targetPageId = restoredState.ɵrouterPageId; } else { // Otherwise, targetPageId should be the next number in the event of a `pushState` // navigation. targetPageId = (this.browserPageId ?? 0) + 1; } } else { // This is unused when `canceledNavigationResolution` is not computed. targetPageId = 0; } this.navigationTransitions.handleNavigationRequest({ targetPageId, source, restoredState, currentUrlTree: this.currentUrlTree, currentRawUrl: this.currentUrlTree, rawUrl, extras, resolve, reject, promise, currentSnapshot: this.routerState.snapshot, currentRouterState: this.routerState }); // Make sure that the error is propagated even though `processNavigations` catch // handler does not rethrow return promise.catch(e => { return Promise.reject(e); }); } /** @internal */ setBrowserUrl(url, transition) { const path = this.urlSerializer.serialize(url); if (this.location.isCurrentPathEqualTo(path) || !!transition.extras.replaceUrl) { // replacements do not update the target page const currentBrowserPageId = this.browserPageId; const state = { ...transition.extras.state, ...this.generateNgRouterState(transition.id, currentBrowserPageId) }; this.location.replaceState(path, '', state); } else { const state = { ...transition.extras.state, ...this.generateNgRouterState(transition.id, transition.targetPageId) }; this.location.go(path, '', state); } } /** * Performs the necessary rollback action to restore the browser URL to the * state before the transition. * @internal */ restoreHistory(transition, restoringFromCaughtError = false) { if (this.canceledNavigationResolution === 'computed') { const currentBrowserPageId = this.browserPageId ?? this.currentPageId; const targetPagePosition = this.currentPageId - currentBrowserPageId; if (targetPagePosition !== 0) { this.location.historyGo(targetPagePosition); } else if (this.currentUrlTree === this.getCurrentNavigation()?.finalUrl && targetPagePosition === 0) { // We got to the activation stage (where currentUrlTree is set to the navigation's // finalUrl), but we weren't moving anywhere in history (skipLocationChange or replaceUrl). // We still need to reset the router state back to what it was when the navigation started. this.resetState(transition); // TODO(atscott): resetting the `browserUrlTree` should really be done in `resetState`. // Investigate if this can be done by running TGP. this.browserUrlTree = transition.currentUrlTree; this.resetUrlToCurrentUrlTree(); } else { // The browser URL and router state was not updated before the navigation cancelled so // there's no restoration needed. } } else if (this.canceledNavigationResolution === 'replace') { // TODO(atscott): It seems like we should _always_ reset the state here. It would be a no-op // for `deferred` navigations that haven't change the internal state yet because guards // reject. For 'eager' navigations, it seems like we also really should reset the state // because the navigation was cancelled. Investigate if this can be done by running TGP. if (restoringFromCaughtError) { this.resetState(transition); } this.resetUrlToCurrentUrlTree(); } } resetState(t) { this.routerState = t.currentRouterState; this.currentUrlTree = t.currentUrlTree; // Note here that we use the urlHandlingStrategy to get the reset `rawUrlTree` because it may be // configured to handle only part of the navigation URL. This means we would only want to reset // the part of the navigation handled by the Angular router rather than the whole URL. In // addition, the URLHandlingStrategy may be configured to specifically preserve parts of the URL // when merging, such as the query params so they are not lost on a refresh. this.rawUrlTree = this.urlHandlingStrategy.merge(this.currentUrlTree, t.rawUrl); } resetUrlToCurrentUrlTree() { this.location.replaceState(this.urlSerializer.serialize(this.rawUrlTree), '', this.generateNgRouterState(this.lastSuccessfulId, this.currentPageId)); } generateNgRouterState(navigationId, routerPageId) { if (this.canceledNavigationResolution === 'computed') { return { navigationId, ɵrouterPageId: routerPageId }; } return { navigationId }; } } Router.ɵfac = function Router_Factory(t) { return new (t || Router)(); }; Router.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: Router, factory: Router.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](Router, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], function () { return []; }, null); })(); function validateCommands(commands) { for (let i = 0; i < commands.length; i++) { const cmd = commands[i]; if (cmd == null) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4008 /* RuntimeErrorCode.NULLISH_COMMAND */, NG_DEV_MODE$2 && `The requested path contains ${cmd} segment at index ${i}`); } } } /** * @description * * When applied to an element in a template, makes that element a link * that initiates navigation to a route. Navigation opens one or more routed components * in one or more `<router-outlet>` locations on the page. * * Given a route configuration `[{ path: 'user/:name', component: UserCmp }]`, * the following creates a static link to the route: * `<a routerLink="/user/bob">link to user component</a>` * * You can use dynamic values to generate the link. * For a dynamic link, pass an array of path segments, * followed by the params for each segment. * For example, `['/team', teamId, 'user', userName, {details: true}]` * generates a link to `/team/11/user/bob;details=true`. * * Multiple static segments can be merged into one term and combined with dynamic segments. * For example, `['/team/11/user', userName, {details: true}]` * * The input that you provide to the link is treated as a delta to the current URL. * For instance, suppose the current URL is `/user/(box//aux:team)`. * The link `<a [routerLink]="['/user/jim']">Jim</a>` creates the URL * `/user/(jim//aux:team)`. * See {@link Router#createUrlTree createUrlTree} for more information. * * @usageNotes * * You can use absolute or relative paths in a link, set query parameters, * control how parameters are handled, and keep a history of navigation states. * * ### Relative link paths * * The first segment name can be prepended with `/`, `./`, or `../`. * * If the first segment begins with `/`, the router looks up the route from the root of the * app. * * If the first segment begins with `./`, or doesn't begin with a slash, the router * looks in the children of the current activated route. * * If the first segment begins with `../`, the router goes up one level in the route tree. * * ### Setting and handling query params and fragments * * The following link adds a query parameter and a fragment to the generated URL: * * ``` * <a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" fragment="education"> * link to user component * </a> * ``` * By default, the directive constructs the new URL using the given query parameters. * The example generates the link: `/user/bob?debug=true#education`. * * You can instruct the directive to handle query parameters differently * by specifying the `queryParamsHandling` option in the link. * Allowed values are: * * - `'merge'`: Merge the given `queryParams` into the current query params. * - `'preserve'`: Preserve the current query params. * * For example: * * ``` * <a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" queryParamsHandling="merge"> * link to user component * </a> * ``` * * See {@link UrlCreationOptions.queryParamsHandling UrlCreationOptions#queryParamsHandling}. * * ### Preserving navigation history * * You can provide a `state` value to be persisted to the browser's * [`History.state` property](https://developer.mozilla.org/en-US/docs/Web/API/History#Properties). * For example: * * ``` * <a [routerLink]="['/user/bob']" [state]="{tracingId: 123}"> * link to user component * </a> * ``` * * Use {@link Router.getCurrentNavigation() Router#getCurrentNavigation} to retrieve a saved * navigation-state value. For example, to capture the `tracingId` during the `NavigationStart` * event: * * ``` * // Get NavigationStart events * router.events.pipe(filter(e => e instanceof NavigationStart)).subscribe(e => { * const navigation = router.getCurrentNavigation(); * tracingService.trace({id: navigation.extras.state.tracingId}); * }); * ``` * * @ngModule RouterModule * * @publicApi */ class RouterLink { constructor(router, route, tabIndexAttribute, renderer, el, locationStrategy) { this.router = router; this.route = route; this.tabIndexAttribute = tabIndexAttribute; this.renderer = renderer; this.el = el; this.locationStrategy = locationStrategy; this._preserveFragment = false; this._skipLocationChange = false; this._replaceUrl = false; /** * Represents an `href` attribute value applied to a host element, * when a host element is `<a>`. For other tags, the value is `null`. */ this.href = null; this.commands = null; /** @internal */ this.onChanges = new rxjs__WEBPACK_IMPORTED_MODULE_30__.Subject(); const tagName = el.nativeElement.tagName?.toLowerCase(); this.isAnchorElement = tagName === 'a' || tagName === 'area'; if (this.isAnchorElement) { this.subscription = router.events.subscribe(s => { if (s instanceof NavigationEnd) { this.updateHref(); } }); } else { this.setTabIndexIfNotOnNativeEl('0'); } } /** * Passed to {@link Router#createUrlTree Router#createUrlTree} as part of the * `UrlCreationOptions`. * @see {@link UrlCreationOptions#preserveFragment UrlCreationOptions#preserveFragment} * @see {@link Router#createUrlTree Router#createUrlTree} */ set preserveFragment(preserveFragment) { this._preserveFragment = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵcoerceToBoolean"])(preserveFragment); } get preserveFragment() { return this._preserveFragment; } /** * Passed to {@link Router#navigateByUrl Router#navigateByUrl} as part of the * `NavigationBehaviorOptions`. * @see {@link NavigationBehaviorOptions#skipLocationChange NavigationBehaviorOptions#skipLocationChange} * @see {@link Router#navigateByUrl Router#navigateByUrl} */ set skipLocationChange(skipLocationChange) { this._skipLocationChange = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵcoerceToBoolean"])(skipLocationChange); } get skipLocationChange() { return this._skipLocationChange; } /** * Passed to {@link Router#navigateByUrl Router#navigateByUrl} as part of the * `NavigationBehaviorOptions`. * @see {@link NavigationBehaviorOptions#replaceUrl NavigationBehaviorOptions#replaceUrl} * @see {@link Router#navigateByUrl Router#navigateByUrl} */ set replaceUrl(replaceUrl) { this._replaceUrl = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵcoerceToBoolean"])(replaceUrl); } get replaceUrl() { return this._replaceUrl; } /** * Modifies the tab index if there was not a tabindex attribute on the element during * instantiation. */ setTabIndexIfNotOnNativeEl(newTabIndex) { if (this.tabIndexAttribute != null /* both `null` and `undefined` */ || this.isAnchorElement) { return; } this.applyAttributeValue('tabindex', newTabIndex); } /** @nodoc */ ngOnChanges(changes) { if (this.isAnchorElement) { this.updateHref(); } // This is subscribed to by `RouterLinkActive` so that it knows to update when there are changes // to the RouterLinks it's tracking. this.onChanges.next(this); } /** * Commands to pass to {@link Router#createUrlTree Router#createUrlTree}. * - **array**: commands to pass to {@link Router#createUrlTree Router#createUrlTree}. * - **string**: shorthand for array of commands with just the string, i.e. `['/route']` * - **null|undefined**: effectively disables the `routerLink` * @see {@link Router#createUrlTree Router#createUrlTree} */ set routerLink(commands) { if (commands != null) { this.commands = Array.isArray(commands) ? commands : [commands]; this.setTabIndexIfNotOnNativeEl('0'); } else { this.commands = null; this.setTabIndexIfNotOnNativeEl(null); } } /** @nodoc */ onClick(button, ctrlKey, shiftKey, altKey, metaKey) { if (this.urlTree === null) { return true; } if (this.isAnchorElement) { if (button !== 0 || ctrlKey || shiftKey || altKey || metaKey) { return true; } if (typeof this.target === 'string' && this.target != '_self') { return true; } } const extras = { skipLocationChange: this.skipLocationChange, replaceUrl: this.replaceUrl, state: this.state }; this.router.navigateByUrl(this.urlTree, extras); // Return `false` for `<a>` elements to prevent default action // and cancel the native behavior, since the navigation is handled // by the Router. return !this.isAnchorElement; } /** @nodoc */ ngOnDestroy() { this.subscription?.unsubscribe(); } updateHref() { this.href = this.urlTree !== null && this.locationStrategy ? this.locationStrategy?.prepareExternalUrl(this.router.serializeUrl(this.urlTree)) : null; const sanitizedValue = this.href === null ? null : // This class represents a directive that can be added to both `<a>` elements, // as well as other elements. As a result, we can't define security context at // compile time. So the security context is deferred to runtime. // The `ɵɵsanitizeUrlOrResourceUrl` selects the necessary sanitizer function // based on the tag and property names. The logic mimics the one from // `packages/compiler/src/schema/dom_security_schema.ts`, which is used at compile time. // // Note: we should investigate whether we can switch to using `@HostBinding('attr.href')` // instead of applying a value via a renderer, after a final merge of the // `RouterLinkWithHref` directive. (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵsanitizeUrlOrResourceUrl"])(this.href, this.el.nativeElement.tagName.toLowerCase(), 'href'); this.applyAttributeValue('href', sanitizedValue); } applyAttributeValue(attrName, attrValue) { const renderer = this.renderer; const nativeElement = this.el.nativeElement; if (attrValue !== null) { renderer.setAttribute(nativeElement, attrName, attrValue); } else { renderer.removeAttribute(nativeElement, attrName); } } get urlTree() { if (this.commands === null) { return null; } return this.router.createUrlTree(this.commands, { // If the `relativeTo` input is not defined, we want to use `this.route` by default. // Otherwise, we should use the value provided by the user in the input. relativeTo: this.relativeTo !== undefined ? this.relativeTo : this.route, queryParams: this.queryParams, fragment: this.fragment, queryParamsHandling: this.queryParamsHandling, preserveFragment: this.preserveFragment }); } } RouterLink.ɵfac = function RouterLink_Factory(t) { return new (t || RouterLink)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](Router), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](ActivatedRoute), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinjectAttribute"]('tabindex'), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_common__WEBPACK_IMPORTED_MODULE_33__.LocationStrategy)); }; RouterLink.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: RouterLink, selectors: [["", "routerLink", ""]], hostVars: 1, hostBindings: function RouterLink_HostBindings(rf, ctx) { if (rf & 1) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵlistener"]("click", function RouterLink_click_HostBindingHandler($event) { return ctx.onClick($event.button, $event.ctrlKey, $event.shiftKey, $event.altKey, $event.metaKey); }); } if (rf & 2) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵattribute"]("target", ctx.target); } }, inputs: { target: "target", queryParams: "queryParams", fragment: "fragment", queryParamsHandling: "queryParamsHandling", state: "state", relativeTo: "relativeTo", preserveFragment: "preserveFragment", skipLocationChange: "skipLocationChange", replaceUrl: "replaceUrl", routerLink: "routerLink" }, standalone: true, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵNgOnChangesFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](RouterLink, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[routerLink]', standalone: true }] }], function () { return [{ type: Router }, { type: ActivatedRoute }, { type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Attribute, args: ['tabindex'] }] }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2 }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef }, { type: _angular_common__WEBPACK_IMPORTED_MODULE_33__.LocationStrategy }]; }, { target: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.HostBinding, args: ['attr.target'] }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], queryParams: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], fragment: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], queryParamsHandling: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], state: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], relativeTo: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], preserveFragment: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], skipLocationChange: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], replaceUrl: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], routerLink: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], onClick: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.HostListener, args: ['click', ['$event.button', '$event.ctrlKey', '$event.shiftKey', '$event.altKey', '$event.metaKey']] }] }); })(); /** * * @description * * Tracks whether the linked route of an element is currently active, and allows you * to specify one or more CSS classes to add to the element when the linked route * is active. * * Use this directive to create a visual distinction for elements associated with an active route. * For example, the following code highlights the word "Bob" when the router * activates the associated route: * * ``` * <a routerLink="/user/bob" routerLinkActive="active-link">Bob</a> * ``` * * Whenever the URL is either '/user' or '/user/bob', the "active-link" class is * added to the anchor tag. If the URL changes, the class is removed. * * You can set more than one class using a space-separated string or an array. * For example: * * ``` * <a routerLink="/user/bob" routerLinkActive="class1 class2">Bob</a> * <a routerLink="/user/bob" [routerLinkActive]="['class1', 'class2']">Bob</a> * ``` * * To add the classes only when the URL matches the link exactly, add the option `exact: true`: * * ``` * <a routerLink="/user/bob" routerLinkActive="active-link" [routerLinkActiveOptions]="{exact: * true}">Bob</a> * ``` * * To directly check the `isActive` status of the link, assign the `RouterLinkActive` * instance to a template variable. * For example, the following checks the status without assigning any CSS classes: * * ``` * <a routerLink="/user/bob" routerLinkActive #rla="routerLinkActive"> * Bob {{ rla.isActive ? '(already open)' : ''}} * </a> * ``` * * You can apply the `RouterLinkActive` directive to an ancestor of linked elements. * For example, the following sets the active-link class on the `<div>` parent tag * when the URL is either '/user/jim' or '/user/bob'. * * ``` * <div routerLinkActive="active-link" [routerLinkActiveOptions]="{exact: true}"> * <a routerLink="/user/jim">Jim</a> * <a routerLink="/user/bob">Bob</a> * </div> * ``` * * The `RouterLinkActive` directive can also be used to set the aria-current attribute * to provide an alternative distinction for active elements to visually impaired users. * * For example, the following code adds the 'active' class to the Home Page link when it is * indeed active and in such case also sets its aria-current attribute to 'page': * * ``` * <a routerLink="/" routerLinkActive="active" ariaCurrentWhenActive="page">Home Page</a> * ``` * * @ngModule RouterModule * * @publicApi */ class RouterLinkActive { get isActive() { return this._isActive; } constructor(router, element, renderer, cdr, link) { this.router = router; this.element = element; this.renderer = renderer; this.cdr = cdr; this.link = link; this.classes = []; this._isActive = false; /** * Options to configure how to determine if the router link is active. * * These options are passed to the `Router.isActive()` function. * * @see Router.isActive */ this.routerLinkActiveOptions = { exact: false }; /** * * You can use the output `isActiveChange` to get notified each time the link becomes * active or inactive. * * Emits: * true -> Route is active * false -> Route is inactive * * ``` * <a * routerLink="/user/bob" * routerLinkActive="active-link" * (isActiveChange)="this.onRouterLinkActive($event)">Bob</a> * ``` */ this.isActiveChange = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.EventEmitter(); this.routerEventsSubscription = router.events.subscribe(s => { if (s instanceof NavigationEnd) { this.update(); } }); } /** @nodoc */ ngAfterContentInit() { // `of(null)` is used to force subscribe body to execute once immediately (like `startWith`). (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(this.links.changes, (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(null)).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_34__.mergeAll)()).subscribe(_ => { this.update(); this.subscribeToEachLinkOnChanges(); }); } subscribeToEachLinkOnChanges() { this.linkInputChangesSubscription?.unsubscribe(); const allLinkChanges = [...this.links.toArray(), this.link].filter(link => !!link).map(link => link.onChanges); this.linkInputChangesSubscription = (0,rxjs__WEBPACK_IMPORTED_MODULE_1__.from)(allLinkChanges).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_34__.mergeAll)()).subscribe(link => { if (this._isActive !== this.isLinkActive(this.router)(link)) { this.update(); } }); } set routerLinkActive(data) { const classes = Array.isArray(data) ? data : data.split(' '); this.classes = classes.filter(c => !!c); } /** @nodoc */ ngOnChanges(changes) { this.update(); } /** @nodoc */ ngOnDestroy() { this.routerEventsSubscription.unsubscribe(); this.linkInputChangesSubscription?.unsubscribe(); } update() { if (!this.links || !this.router.navigated) return; Promise.resolve().then(() => { const hasActiveLinks = this.hasActiveLinks(); if (this._isActive !== hasActiveLinks) { this._isActive = hasActiveLinks; this.cdr.markForCheck(); this.classes.forEach(c => { if (hasActiveLinks) { this.renderer.addClass(this.element.nativeElement, c); } else { this.renderer.removeClass(this.element.nativeElement, c); } }); if (hasActiveLinks && this.ariaCurrentWhenActive !== undefined) { this.renderer.setAttribute(this.element.nativeElement, 'aria-current', this.ariaCurrentWhenActive.toString()); } else { this.renderer.removeAttribute(this.element.nativeElement, 'aria-current'); } // Emit on isActiveChange after classes are updated this.isActiveChange.emit(hasActiveLinks); } }); } isLinkActive(router) { const options = isActiveMatchOptions(this.routerLinkActiveOptions) ? this.routerLinkActiveOptions : // While the types should disallow `undefined` here, it's possible without strict inputs this.routerLinkActiveOptions.exact || false; return link => link.urlTree ? router.isActive(link.urlTree, options) : false; } hasActiveLinks() { const isActiveCheckFn = this.isLinkActive(this.router); return this.link && isActiveCheckFn(this.link) || this.links.some(isActiveCheckFn); } } RouterLinkActive.ɵfac = function RouterLinkActive_Factory(t) { return new (t || RouterLinkActive)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](Router), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.ChangeDetectorRef), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdirectiveInject"](RouterLink, 8)); }; RouterLinkActive.ɵdir = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineDirective"]({ type: RouterLinkActive, selectors: [["", "routerLinkActive", ""]], contentQueries: function RouterLinkActive_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵcontentQuery"](dirIndex, RouterLink, 5); } if (rf & 2) { let _t; _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵqueryRefresh"](_t = _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵloadQuery"]()) && (ctx.links = _t); } }, inputs: { routerLinkActiveOptions: "routerLinkActiveOptions", ariaCurrentWhenActive: "ariaCurrentWhenActive", routerLinkActive: "routerLinkActive" }, outputs: { isActiveChange: "isActiveChange" }, exportAs: ["routerLinkActive"], standalone: true, features: [_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵNgOnChangesFeature"]] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](RouterLinkActive, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Directive, args: [{ selector: '[routerLinkActive]', exportAs: 'routerLinkActive', standalone: true }] }], function () { return [{ type: Router }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ElementRef }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Renderer2 }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ChangeDetectorRef }, { type: RouterLink, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }] }]; }, { links: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ContentChildren, args: [RouterLink, { descendants: true }] }], routerLinkActiveOptions: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], ariaCurrentWhenActive: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }], isActiveChange: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Output }], routerLinkActive: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Input }] }); })(); /** * Use instead of `'paths' in options` to be compatible with property renaming */ function isActiveMatchOptions(options) { return !!options.paths; } /** * @description * * Provides a preloading strategy. * * @publicApi */ class PreloadingStrategy {} /** * @description * * Provides a preloading strategy that preloads all modules as quickly as possible. * * ``` * RouterModule.forRoot(ROUTES, {preloadingStrategy: PreloadAllModules}) * ``` * * @publicApi */ class PreloadAllModules { preload(route, fn) { return fn().pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_19__.catchError)(() => (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(null))); } } PreloadAllModules.ɵfac = function PreloadAllModules_Factory(t) { return new (t || PreloadAllModules)(); }; PreloadAllModules.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: PreloadAllModules, factory: PreloadAllModules.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](PreloadAllModules, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], null, null); })(); /** * @description * * Provides a preloading strategy that does not preload any modules. * * This strategy is enabled by default. * * @publicApi */ class NoPreloading { preload(route, fn) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(null); } } NoPreloading.ɵfac = function NoPreloading_Factory(t) { return new (t || NoPreloading)(); }; NoPreloading.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: NoPreloading, factory: NoPreloading.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](NoPreloading, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], null, null); })(); /** * The preloader optimistically loads all router configurations to * make navigations into lazily-loaded sections of the application faster. * * The preloader runs in the background. When the router bootstraps, the preloader * starts listening to all navigation events. After every such event, the preloader * will check if any configurations can be loaded lazily. * * If a route is protected by `canLoad` guards, the preloaded will not load it. * * @publicApi */ class RouterPreloader { constructor(router, compiler, injector, preloadingStrategy, loader) { this.router = router; this.injector = injector; this.preloadingStrategy = preloadingStrategy; this.loader = loader; } setUpPreloading() { this.subscription = this.router.events.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_10__.filter)(e => e instanceof NavigationEnd), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_13__.concatMap)(() => this.preload())).subscribe(() => {}); } preload() { return this.processRoutes(this.injector, this.router.config); } /** @nodoc */ ngOnDestroy() { if (this.subscription) { this.subscription.unsubscribe(); } } processRoutes(injector, routes) { const res = []; for (const route of routes) { if (route.providers && !route._injector) { route._injector = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.createEnvironmentInjector)(route.providers, injector, `Route: ${route.path}`); } const injectorForCurrentRoute = route._injector ?? injector; const injectorForChildren = route._loadedInjector ?? injectorForCurrentRoute; // Note that `canLoad` is only checked as a condition that prevents `loadChildren` and not // `loadComponent`. `canLoad` guards only block loading of child routes by design. This // happens as a consequence of needing to descend into children for route matching immediately // while component loading is deferred until route activation. Because `canLoad` guards can // have side effects, we cannot execute them here so we instead skip preloading altogether // when present. Lastly, it remains to be decided whether `canLoad` should behave this way // at all. Code splitting and lazy loading is separate from client-side authorization checks // and should not be used as a security measure to prevent loading of code. if (route.loadChildren && !route._loadedRoutes && route.canLoad === undefined || route.loadComponent && !route._loadedComponent) { res.push(this.preloadConfig(injectorForCurrentRoute, route)); } if (route.children || route._loadedRoutes) { res.push(this.processRoutes(injectorForChildren, route.children ?? route._loadedRoutes)); } } return (0,rxjs__WEBPACK_IMPORTED_MODULE_1__.from)(res).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_34__.mergeAll)()); } preloadConfig(injector, route) { return this.preloadingStrategy.preload(route, () => { let loadedChildren$; if (route.loadChildren && route.canLoad === undefined) { loadedChildren$ = this.loader.loadChildren(injector, route); } else { loadedChildren$ = (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(null); } const recursiveLoadChildren$ = loadedChildren$.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_11__.mergeMap)(config => { if (config === null) { return (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(void 0); } route._loadedRoutes = config.routes; route._loadedInjector = config.injector; // If the loaded config was a module, use that as the module/module injector going // forward. Otherwise, continue using the current module/module injector. return this.processRoutes(config.injector ?? injector, config.routes); })); if (route.loadComponent && !route._loadedComponent) { const loadComponent$ = this.loader.loadComponent(route); return (0,rxjs__WEBPACK_IMPORTED_MODULE_1__.from)([recursiveLoadChildren$, loadComponent$]).pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_34__.mergeAll)()); } else { return recursiveLoadChildren$; } }); } } RouterPreloader.ɵfac = function RouterPreloader_Factory(t) { return new (t || RouterPreloader)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](Router), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.Compiler), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](_angular_core__WEBPACK_IMPORTED_MODULE_0__.EnvironmentInjector), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](PreloadingStrategy), _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](RouterConfigLoader)); }; RouterPreloader.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: RouterPreloader, factory: RouterPreloader.ɵfac, providedIn: 'root' }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](RouterPreloader, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable, args: [{ providedIn: 'root' }] }], function () { return [{ type: Router }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Compiler }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.EnvironmentInjector }, { type: PreloadingStrategy }, { type: RouterConfigLoader }]; }, null); })(); const ROUTER_SCROLLER = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken(''); class RouterScroller { /** @nodoc */ constructor(urlSerializer, transitions, viewportScroller, zone, options = {}) { this.urlSerializer = urlSerializer; this.transitions = transitions; this.viewportScroller = viewportScroller; this.zone = zone; this.options = options; this.lastId = 0; this.lastSource = 'imperative'; this.restoredId = 0; this.store = {}; // Default both options to 'disabled' options.scrollPositionRestoration = options.scrollPositionRestoration || 'disabled'; options.anchorScrolling = options.anchorScrolling || 'disabled'; } init() { // we want to disable the automatic scrolling because having two places // responsible for scrolling results race conditions, especially given // that browser don't implement this behavior consistently if (this.options.scrollPositionRestoration !== 'disabled') { this.viewportScroller.setHistoryScrollRestoration('manual'); } this.routerEventsSubscription = this.createScrollEvents(); this.scrollEventsSubscription = this.consumeScrollEvents(); } createScrollEvents() { return this.transitions.events.subscribe(e => { if (e instanceof NavigationStart) { // store the scroll position of the current stable navigations. this.store[this.lastId] = this.viewportScroller.getScrollPosition(); this.lastSource = e.navigationTrigger; this.restoredId = e.restoredState ? e.restoredState.navigationId : 0; } else if (e instanceof NavigationEnd) { this.lastId = e.id; this.scheduleScrollEvent(e, this.urlSerializer.parse(e.urlAfterRedirects).fragment); } }); } consumeScrollEvents() { return this.transitions.events.subscribe(e => { if (!(e instanceof Scroll)) return; // a popstate event. The pop state event will always ignore anchor scrolling. if (e.position) { if (this.options.scrollPositionRestoration === 'top') { this.viewportScroller.scrollToPosition([0, 0]); } else if (this.options.scrollPositionRestoration === 'enabled') { this.viewportScroller.scrollToPosition(e.position); } // imperative navigation "forward" } else { if (e.anchor && this.options.anchorScrolling === 'enabled') { this.viewportScroller.scrollToAnchor(e.anchor); } else if (this.options.scrollPositionRestoration !== 'disabled') { this.viewportScroller.scrollToPosition([0, 0]); } } }); } scheduleScrollEvent(routerEvent, anchor) { this.zone.runOutsideAngular(() => { // The scroll event needs to be delayed until after change detection. Otherwise, we may // attempt to restore the scroll position before the router outlet has fully rendered the // component by executing its update block of the template function. setTimeout(() => { this.zone.run(() => { this.transitions.events.next(new Scroll(routerEvent, this.lastSource === 'popstate' ? this.store[this.restoredId] : null, anchor)); }); }, 0); }); } /** @nodoc */ ngOnDestroy() { this.routerEventsSubscription?.unsubscribe(); this.scrollEventsSubscription?.unsubscribe(); } } RouterScroller.ɵfac = function RouterScroller_Factory(t) { _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinvalidFactory"](); }; RouterScroller.ɵprov = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjectable"]({ token: RouterScroller, factory: RouterScroller.ɵfac }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](RouterScroller, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Injectable }], function () { return [{ type: UrlSerializer }, { type: NavigationTransitions }, { type: _angular_common__WEBPACK_IMPORTED_MODULE_33__.ViewportScroller }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.NgZone }, { type: undefined }]; }, null); })(); var NavigationResult; (function (NavigationResult) { NavigationResult[NavigationResult["COMPLETE"] = 0] = "COMPLETE"; NavigationResult[NavigationResult["FAILED"] = 1] = "FAILED"; NavigationResult[NavigationResult["REDIRECTING"] = 2] = "REDIRECTING"; })(NavigationResult || (NavigationResult = {})); /** * Performs the given action once the router finishes its next/current navigation. * * The navigation is considered complete under the following conditions: * - `NavigationCancel` event emits and the code is not `NavigationCancellationCode.Redirect` or * `NavigationCancellationCode.SupersededByNewNavigation`. In these cases, the * redirecting/superseding navigation must finish. * - `NavigationError`, `NavigationEnd`, or `NavigationSkipped` event emits */ function afterNextNavigation(router, action) { router.events.pipe((0,rxjs_operators__WEBPACK_IMPORTED_MODULE_10__.filter)(e => e instanceof NavigationEnd || e instanceof NavigationCancel || e instanceof NavigationError || e instanceof NavigationSkipped), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_4__.map)(e => { if (e instanceof NavigationEnd || e instanceof NavigationSkipped) { return NavigationResult.COMPLETE; } const redirecting = e instanceof NavigationCancel ? e.code === 0 /* NavigationCancellationCode.Redirect */ || e.code === 1 /* NavigationCancellationCode.SupersededByNewNavigation */ : false; return redirecting ? NavigationResult.REDIRECTING : NavigationResult.FAILED; }), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_10__.filter)(result => result !== NavigationResult.REDIRECTING), (0,rxjs_operators__WEBPACK_IMPORTED_MODULE_8__.take)(1)).subscribe(() => { action(); }); } const NG_DEV_MODE$1 = typeof ngDevMode === 'undefined' || ngDevMode; /** * Sets up providers necessary to enable `Router` functionality for the application. * Allows to configure a set of routes as well as extra features that should be enabled. * * @usageNotes * * Basic example of how you can add a Router to your application: * ``` * const appRoutes: Routes = []; * bootstrapApplication(AppComponent, { * providers: [provideRouter(appRoutes)] * }); * ``` * * You can also enable optional features in the Router by adding functions from the `RouterFeatures` * type: * ``` * const appRoutes: Routes = []; * bootstrapApplication(AppComponent, * { * providers: [ * provideRouter(appRoutes, * withDebugTracing(), * withRouterConfig({paramsInheritanceStrategy: 'always'})) * ] * } * ); * ``` * * @see `RouterFeatures` * * @publicApi * @param routes A set of `Route`s to use for the application routing table. * @param features Optional features to configure additional router behaviors. * @returns A set of providers to setup a Router. */ function provideRouter(routes, ...features) { return (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.makeEnvironmentProviders)([{ provide: ROUTES, multi: true, useValue: routes }, NG_DEV_MODE$1 ? { provide: ROUTER_IS_PROVIDED, useValue: true } : [], { provide: ActivatedRoute, useFactory: rootRoute, deps: [Router] }, { provide: _angular_core__WEBPACK_IMPORTED_MODULE_0__.APP_BOOTSTRAP_LISTENER, multi: true, useFactory: getBootstrapListener }, features.map(feature => feature.ɵproviders)]); } function rootRoute(router) { return router.routerState.root; } /** * Helper function to create an object that represents a Router feature. */ function routerFeature(kind, providers) { return { ɵkind: kind, ɵproviders: providers }; } /** * An Injection token used to indicate whether `provideRouter` or `RouterModule.forRoot` was ever * called. */ const ROUTER_IS_PROVIDED = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken('', { providedIn: 'root', factory: () => false }); const routerIsProvidedDevModeCheck = { provide: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ENVIRONMENT_INITIALIZER, multi: true, useFactory() { return () => { if (!(0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(ROUTER_IS_PROVIDED)) { console.warn('`provideRoutes` was called without `provideRouter` or `RouterModule.forRoot`. ' + 'This is likely a mistake.'); } }; } }; /** * Registers a [DI provider](guide/glossary#provider) for a set of routes. * @param routes The route configuration to provide. * * @usageNotes * * ``` * @NgModule({ * providers: [provideRoutes(ROUTES)] * }) * class LazyLoadedChildModule {} * ``` * * @deprecated If necessary, provide routes using the `ROUTES` `InjectionToken`. * @see `ROUTES` * @publicApi */ function provideRoutes(routes) { return [{ provide: ROUTES, multi: true, useValue: routes }, NG_DEV_MODE$1 ? routerIsProvidedDevModeCheck : []]; } /** * Enables customizable scrolling behavior for router navigations. * * @usageNotes * * Basic example of how you can enable scrolling feature: * ``` * const appRoutes: Routes = []; * bootstrapApplication(AppComponent, * { * providers: [ * provideRouter(appRoutes, withInMemoryScrolling()) * ] * } * ); * ``` * * @see `provideRouter` * @see `ViewportScroller` * * @publicApi * @param options Set of configuration parameters to customize scrolling behavior, see * `InMemoryScrollingOptions` for additional information. * @returns A set of providers for use with `provideRouter`. */ function withInMemoryScrolling(options = {}) { const providers = [{ provide: ROUTER_SCROLLER, useFactory: () => { const viewportScroller = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_common__WEBPACK_IMPORTED_MODULE_33__.ViewportScroller); const zone = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__.NgZone); const transitions = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(NavigationTransitions); const urlSerializer = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(UrlSerializer); return new RouterScroller(urlSerializer, transitions, viewportScroller, zone, options); } }]; return routerFeature(4 /* RouterFeatureKind.InMemoryScrollingFeature */, providers); } function getBootstrapListener() { const injector = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__.Injector); return bootstrappedComponentRef => { const ref = injector.get(_angular_core__WEBPACK_IMPORTED_MODULE_0__.ApplicationRef); if (bootstrappedComponentRef !== ref.components[0]) { return; } const router = injector.get(Router); const bootstrapDone = injector.get(BOOTSTRAP_DONE); if (injector.get(INITIAL_NAVIGATION) === 1 /* InitialNavigation.EnabledNonBlocking */) { router.initialNavigation(); } injector.get(ROUTER_PRELOADER, null, _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectFlags.Optional)?.setUpPreloading(); injector.get(ROUTER_SCROLLER, null, _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectFlags.Optional)?.init(); router.resetRootComponentType(ref.componentTypes[0]); if (!bootstrapDone.closed) { bootstrapDone.next(); bootstrapDone.complete(); bootstrapDone.unsubscribe(); } }; } /** * A subject used to indicate that the bootstrapping phase is done. When initial navigation is * `enabledBlocking`, the first navigation waits until bootstrapping is finished before continuing * to the activation phase. */ const BOOTSTRAP_DONE = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken(NG_DEV_MODE$1 ? 'bootstrap done indicator' : '', { factory: () => { return new rxjs__WEBPACK_IMPORTED_MODULE_30__.Subject(); } }); const INITIAL_NAVIGATION = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken(NG_DEV_MODE$1 ? 'initial navigation' : '', { providedIn: 'root', factory: () => 1 /* InitialNavigation.EnabledNonBlocking */ }); /** * Configures initial navigation to start before the root component is created. * * The bootstrap is blocked until the initial navigation is complete. This value is required for * [server-side rendering](guide/universal) to work. * * @usageNotes * * Basic example of how you can enable this navigation behavior: * ``` * const appRoutes: Routes = []; * bootstrapApplication(AppComponent, * { * providers: [ * provideRouter(appRoutes, withEnabledBlockingInitialNavigation()) * ] * } * ); * ``` * * @see `provideRouter` * * @publicApi * @returns A set of providers for use with `provideRouter`. */ function withEnabledBlockingInitialNavigation() { const providers = [{ provide: INITIAL_NAVIGATION, useValue: 0 /* InitialNavigation.EnabledBlocking */ }, { provide: _angular_core__WEBPACK_IMPORTED_MODULE_0__.APP_INITIALIZER, multi: true, deps: [_angular_core__WEBPACK_IMPORTED_MODULE_0__.Injector], useFactory: injector => { const locationInitialized = injector.get(_angular_common__WEBPACK_IMPORTED_MODULE_33__.LOCATION_INITIALIZED, Promise.resolve()); return () => { return locationInitialized.then(() => { return new Promise(resolve => { const router = injector.get(Router); const bootstrapDone = injector.get(BOOTSTRAP_DONE); afterNextNavigation(router, () => { // Unblock APP_INITIALIZER in case the initial navigation was canceled or errored // without a redirect. resolve(true); }); injector.get(NavigationTransitions).afterPreactivation = () => { // Unblock APP_INITIALIZER once we get to `afterPreactivation`. At this point, we // assume activation will complete successfully (even though this is not // guaranteed). resolve(true); return bootstrapDone.closed ? (0,rxjs__WEBPACK_IMPORTED_MODULE_2__.of)(void 0) : bootstrapDone; }; router.initialNavigation(); }); }); }; } }]; return routerFeature(2 /* RouterFeatureKind.EnabledBlockingInitialNavigationFeature */, providers); } /** * Disables initial navigation. * * Use if there is a reason to have more control over when the router starts its initial navigation * due to some complex initialization logic. * * @usageNotes * * Basic example of how you can disable initial navigation: * ``` * const appRoutes: Routes = []; * bootstrapApplication(AppComponent, * { * providers: [ * provideRouter(appRoutes, withDisabledInitialNavigation()) * ] * } * ); * ``` * * @see `provideRouter` * * @returns A set of providers for use with `provideRouter`. * * @publicApi */ function withDisabledInitialNavigation() { const providers = [{ provide: _angular_core__WEBPACK_IMPORTED_MODULE_0__.APP_INITIALIZER, multi: true, useFactory: () => { const router = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(Router); return () => { router.setUpLocationChangeListener(); }; } }, { provide: INITIAL_NAVIGATION, useValue: 2 /* InitialNavigation.Disabled */ }]; return routerFeature(3 /* RouterFeatureKind.DisabledInitialNavigationFeature */, providers); } /** * Enables logging of all internal navigation events to the console. * Extra logging might be useful for debugging purposes to inspect Router event sequence. * * @usageNotes * * Basic example of how you can enable debug tracing: * ``` * const appRoutes: Routes = []; * bootstrapApplication(AppComponent, * { * providers: [ * provideRouter(appRoutes, withDebugTracing()) * ] * } * ); * ``` * * @see `provideRouter` * * @returns A set of providers for use with `provideRouter`. * * @publicApi */ function withDebugTracing() { let providers = []; if (NG_DEV_MODE$1) { providers = [{ provide: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ENVIRONMENT_INITIALIZER, multi: true, useFactory: () => { const router = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(Router); return () => router.events.subscribe(e => { // tslint:disable:no-console console.group?.(`Router Event: ${e.constructor.name}`); console.log(stringifyEvent(e)); console.log(e); console.groupEnd?.(); // tslint:enable:no-console }); } }]; } else { providers = []; } return routerFeature(1 /* RouterFeatureKind.DebugTracingFeature */, providers); } const ROUTER_PRELOADER = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken(NG_DEV_MODE$1 ? 'router preloader' : ''); /** * Allows to configure a preloading strategy to use. The strategy is configured by providing a * reference to a class that implements a `PreloadingStrategy`. * * @usageNotes * * Basic example of how you can configure preloading: * ``` * const appRoutes: Routes = []; * bootstrapApplication(AppComponent, * { * providers: [ * provideRouter(appRoutes, withPreloading(PreloadAllModules)) * ] * } * ); * ``` * * @see `provideRouter` * * @param preloadingStrategy A reference to a class that implements a `PreloadingStrategy` that * should be used. * @returns A set of providers for use with `provideRouter`. * * @publicApi */ function withPreloading(preloadingStrategy) { const providers = [{ provide: ROUTER_PRELOADER, useExisting: RouterPreloader }, { provide: PreloadingStrategy, useExisting: preloadingStrategy }]; return routerFeature(0 /* RouterFeatureKind.PreloadingFeature */, providers); } /** * Allows to provide extra parameters to configure Router. * * @usageNotes * * Basic example of how you can provide extra configuration options: * ``` * const appRoutes: Routes = []; * bootstrapApplication(AppComponent, * { * providers: [ * provideRouter(appRoutes, withRouterConfig({ * onSameUrlNavigation: 'reload' * })) * ] * } * ); * ``` * * @see `provideRouter` * * @param options A set of parameters to configure Router, see `RouterConfigOptions` for * additional information. * @returns A set of providers for use with `provideRouter`. * * @publicApi */ function withRouterConfig(options) { const providers = [{ provide: ROUTER_CONFIGURATION, useValue: options }]; return routerFeature(5 /* RouterFeatureKind.RouterConfigurationFeature */, providers); } /** * Provides the location strategy that uses the URL fragment instead of the history API. * * @usageNotes * * Basic example of how you can use the hash location option: * ``` * const appRoutes: Routes = []; * bootstrapApplication(AppComponent, * { * providers: [ * provideRouter(appRoutes, withHashLocation()) * ] * } * ); * ``` * * @see `provideRouter` * @see `HashLocationStrategy` * * @returns A set of providers for use with `provideRouter`. * * @publicApi */ function withHashLocation() { const providers = [{ provide: _angular_common__WEBPACK_IMPORTED_MODULE_33__.LocationStrategy, useClass: _angular_common__WEBPACK_IMPORTED_MODULE_33__.HashLocationStrategy }]; return routerFeature(5 /* RouterFeatureKind.RouterConfigurationFeature */, providers); } /** * Subscribes to the Router's navigation events and calls the given function when a * `NavigationError` happens. * * This function is run inside application's injection context so you can use the `inject` function. * * @usageNotes * * Basic example of how you can use the error handler option: * ``` * const appRoutes: Routes = []; * bootstrapApplication(AppComponent, * { * providers: [ * provideRouter(appRoutes, withNavigationErrorHandler((e: NavigationError) => * inject(MyErrorTracker).trackError(e))) * ] * } * ); * ``` * * @see `NavigationError` * @see `inject` * @see `EnvironmentInjector#runInContext` * * @returns A set of providers for use with `provideRouter`. * * @publicApi */ function withNavigationErrorHandler(fn) { const providers = [{ provide: _angular_core__WEBPACK_IMPORTED_MODULE_0__.ENVIRONMENT_INITIALIZER, multi: true, useValue: () => { const injector = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__.EnvironmentInjector); (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(Router).events.subscribe(e => { if (e instanceof NavigationError) { injector.runInContext(() => fn(e)); } }); } }]; return routerFeature(7 /* RouterFeatureKind.NavigationErrorHandlerFeature */, providers); } const NG_DEV_MODE = typeof ngDevMode === 'undefined' || ngDevMode; /** * The directives defined in the `RouterModule`. */ const ROUTER_DIRECTIVES = [RouterOutlet, RouterLink, RouterLinkActive, ɵEmptyOutletComponent]; /** * @docsNotRequired */ const ROUTER_FORROOT_GUARD = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken(NG_DEV_MODE ? 'router duplicate forRoot guard' : 'ROUTER_FORROOT_GUARD'); // TODO(atscott): All of these except `ActivatedRoute` are `providedIn: 'root'`. They are only kept // here to avoid a breaking change whereby the provider order matters based on where the // `RouterModule`/`RouterTestingModule` is imported. These can/should be removed as a "breaking" // change in a major version. const ROUTER_PROVIDERS = [_angular_common__WEBPACK_IMPORTED_MODULE_33__.Location, { provide: UrlSerializer, useClass: DefaultUrlSerializer }, Router, ChildrenOutletContexts, { provide: ActivatedRoute, useFactory: rootRoute, deps: [Router] }, RouterConfigLoader, // Only used to warn when `provideRoutes` is used without `RouterModule` or `provideRouter`. Can // be removed when `provideRoutes` is removed. NG_DEV_MODE ? { provide: ROUTER_IS_PROVIDED, useValue: true } : []]; function routerNgProbeToken() { return new _angular_core__WEBPACK_IMPORTED_MODULE_0__.NgProbeToken('Router', Router); } /** * @description * * Adds directives and providers for in-app navigation among views defined in an application. * Use the Angular `Router` service to declaratively specify application states and manage state * transitions. * * You can import this NgModule multiple times, once for each lazy-loaded bundle. * However, only one `Router` service can be active. * To ensure this, there are two ways to register routes when importing this module: * * * The `forRoot()` method creates an `NgModule` that contains all the directives, the given * routes, and the `Router` service itself. * * The `forChild()` method creates an `NgModule` that contains all the directives and the given * routes, but does not include the `Router` service. * * @see [Routing and Navigation guide](guide/router) for an * overview of how the `Router` service should be used. * * @publicApi */ class RouterModule { constructor(guard) {} /** * Creates and configures a module with all the router providers and directives. * Optionally sets up an application listener to perform an initial navigation. * * When registering the NgModule at the root, import as follows: * * ``` * @NgModule({ * imports: [RouterModule.forRoot(ROUTES)] * }) * class MyNgModule {} * ``` * * @param routes An array of `Route` objects that define the navigation paths for the application. * @param config An `ExtraOptions` configuration object that controls how navigation is performed. * @return The new `NgModule`. * */ static forRoot(routes, config) { return { ngModule: RouterModule, providers: [ROUTER_PROVIDERS, NG_DEV_MODE ? config?.enableTracing ? withDebugTracing().ɵproviders : [] : [], { provide: ROUTES, multi: true, useValue: routes }, { provide: ROUTER_FORROOT_GUARD, useFactory: provideForRootGuard, deps: [[Router, new _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional(), new _angular_core__WEBPACK_IMPORTED_MODULE_0__.SkipSelf()]] }, { provide: ROUTER_CONFIGURATION, useValue: config ? config : {} }, config?.useHash ? provideHashLocationStrategy() : providePathLocationStrategy(), provideRouterScroller(), config?.preloadingStrategy ? withPreloading(config.preloadingStrategy).ɵproviders : [], { provide: _angular_core__WEBPACK_IMPORTED_MODULE_0__.NgProbeToken, multi: true, useFactory: routerNgProbeToken }, config?.initialNavigation ? provideInitialNavigation(config) : [], provideRouterInitializer()] }; } /** * Creates a module with all the router directives and a provider registering routes, * without creating a new Router service. * When registering for submodules and lazy-loaded submodules, create the NgModule as follows: * * ``` * @NgModule({ * imports: [RouterModule.forChild(ROUTES)] * }) * class MyNgModule {} * ``` * * @param routes An array of `Route` objects that define the navigation paths for the submodule. * @return The new NgModule. * */ static forChild(routes) { return { ngModule: RouterModule, providers: [{ provide: ROUTES, multi: true, useValue: routes }] }; } } RouterModule.ɵfac = function RouterModule_Factory(t) { return new (t || RouterModule)(_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵinject"](ROUTER_FORROOT_GUARD, 8)); }; RouterModule.ɵmod = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineNgModule"]({ type: RouterModule }); RouterModule.ɵinj = /* @__PURE__ */_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineInjector"]({ imports: [ɵEmptyOutletComponent] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵsetClassMetadata"](RouterModule, [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.NgModule, args: [{ imports: ROUTER_DIRECTIVES, exports: ROUTER_DIRECTIVES }] }], function () { return [{ type: undefined, decorators: [{ type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Optional }, { type: _angular_core__WEBPACK_IMPORTED_MODULE_0__.Inject, args: [ROUTER_FORROOT_GUARD] }] }]; }, null); })(); /** * For internal use by `RouterModule` only. Note that this differs from `withInMemoryRouterScroller` * because it reads from the `ExtraOptions` which should not be used in the standalone world. */ function provideRouterScroller() { return { provide: ROUTER_SCROLLER, useFactory: () => { const viewportScroller = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_common__WEBPACK_IMPORTED_MODULE_33__.ViewportScroller); const zone = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(_angular_core__WEBPACK_IMPORTED_MODULE_0__.NgZone); const config = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(ROUTER_CONFIGURATION); const transitions = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(NavigationTransitions); const urlSerializer = (0,_angular_core__WEBPACK_IMPORTED_MODULE_0__.inject)(UrlSerializer); if (config.scrollOffset) { viewportScroller.setOffset(config.scrollOffset); } return new RouterScroller(urlSerializer, transitions, viewportScroller, zone, config); } }; } // Note: For internal use only with `RouterModule`. Standalone setup via `provideRouter` should // provide hash location directly via `{provide: LocationStrategy, useClass: HashLocationStrategy}`. function provideHashLocationStrategy() { return { provide: _angular_common__WEBPACK_IMPORTED_MODULE_33__.LocationStrategy, useClass: _angular_common__WEBPACK_IMPORTED_MODULE_33__.HashLocationStrategy }; } // Note: For internal use only with `RouterModule`. Standalone setup via `provideRouter` does not // need this at all because `PathLocationStrategy` is the default factory for `LocationStrategy`. function providePathLocationStrategy() { return { provide: _angular_common__WEBPACK_IMPORTED_MODULE_33__.LocationStrategy, useClass: _angular_common__WEBPACK_IMPORTED_MODULE_33__.PathLocationStrategy }; } function provideForRootGuard(router) { if (NG_DEV_MODE && router) { throw new _angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵRuntimeError"](4007 /* RuntimeErrorCode.FOR_ROOT_CALLED_TWICE */, `The Router was provided more than once. This can happen if 'forRoot' is used outside of the root injector.` + ` Lazy loaded modules should use RouterModule.forChild() instead.`); } return 'guarded'; } // Note: For internal use only with `RouterModule`. Standalone router setup with `provideRouter` // users call `withXInitialNavigation` directly. function provideInitialNavigation(config) { return [config.initialNavigation === 'disabled' ? withDisabledInitialNavigation().ɵproviders : [], config.initialNavigation === 'enabledBlocking' ? withEnabledBlockingInitialNavigation().ɵproviders : []]; } // TODO(atscott): This should not be in the public API /** * A [DI token](guide/glossary/#di-token) for the router initializer that * is called after the app is bootstrapped. * * @publicApi */ const ROUTER_INITIALIZER = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.InjectionToken(NG_DEV_MODE ? 'Router Initializer' : ''); function provideRouterInitializer() { return [ // ROUTER_INITIALIZER token should be removed. It's public API but shouldn't be. We can just // have `getBootstrapListener` directly attached to APP_BOOTSTRAP_LISTENER. { provide: ROUTER_INITIALIZER, useFactory: getBootstrapListener }, { provide: _angular_core__WEBPACK_IMPORTED_MODULE_0__.APP_BOOTSTRAP_LISTENER, multi: true, useExisting: ROUTER_INITIALIZER }]; } /** * @module * @description * Entry point for all public APIs of the router package. */ /** * @publicApi */ const VERSION = new _angular_core__WEBPACK_IMPORTED_MODULE_0__.Version('15.2.9'); /** * @module * @description * Entry point for all public APIs of this package. */ // This file only reexports content of the `src` folder. Keep it that way. // This file is not used to build this module. It is only used during editing /** * Generated bundle index. Do not edit. */ /***/ }), /***/ 2845: /*!**************************************************!*\ !*** ./node_modules/three/build/three.module.js ***! \**************************************************/ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "ACESFilmicToneMapping": () => (/* binding */ ACESFilmicToneMapping), /* harmony export */ "AddEquation": () => (/* binding */ AddEquation), /* harmony export */ "AddOperation": () => (/* binding */ AddOperation), /* harmony export */ "AdditiveAnimationBlendMode": () => (/* binding */ AdditiveAnimationBlendMode), /* harmony export */ "AdditiveBlending": () => (/* binding */ AdditiveBlending), /* harmony export */ "AlphaFormat": () => (/* binding */ AlphaFormat), /* harmony export */ "AlwaysCompare": () => (/* binding */ AlwaysCompare), /* harmony export */ "AlwaysDepth": () => (/* binding */ AlwaysDepth), /* harmony export */ "AlwaysStencilFunc": () => (/* binding */ AlwaysStencilFunc), /* harmony export */ "AmbientLight": () => (/* binding */ AmbientLight), /* harmony export */ "AmbientLightProbe": () => (/* binding */ AmbientLightProbe), /* harmony export */ "AnimationAction": () => (/* binding */ AnimationAction), /* harmony export */ "AnimationClip": () => (/* binding */ AnimationClip), /* harmony export */ "AnimationLoader": () => (/* binding */ AnimationLoader), /* harmony export */ "AnimationMixer": () => (/* binding */ AnimationMixer), /* harmony export */ "AnimationObjectGroup": () => (/* binding */ AnimationObjectGroup), /* harmony export */ "AnimationUtils": () => (/* binding */ AnimationUtils), /* harmony export */ "ArcCurve": () => (/* binding */ ArcCurve), /* harmony export */ "ArrayCamera": () => (/* binding */ ArrayCamera), /* harmony export */ "ArrowHelper": () => (/* binding */ ArrowHelper), /* harmony export */ "Audio": () => (/* binding */ Audio), /* harmony export */ "AudioAnalyser": () => (/* binding */ AudioAnalyser), /* harmony export */ "AudioContext": () => (/* binding */ AudioContext), /* harmony export */ "AudioListener": () => (/* binding */ AudioListener), /* harmony export */ "AudioLoader": () => (/* binding */ AudioLoader), /* harmony export */ "AxesHelper": () => (/* binding */ AxesHelper), /* harmony export */ "BackSide": () => (/* binding */ BackSide), /* harmony export */ "BasicDepthPacking": () => (/* binding */ BasicDepthPacking), /* harmony export */ "BasicShadowMap": () => (/* binding */ BasicShadowMap), /* harmony export */ "Bone": () => (/* binding */ Bone), /* harmony export */ "BooleanKeyframeTrack": () => (/* binding */ BooleanKeyframeTrack), /* harmony export */ "Box2": () => (/* binding */ Box2), /* harmony export */ "Box3": () => (/* binding */ Box3), /* harmony export */ "Box3Helper": () => (/* binding */ Box3Helper), /* harmony export */ "BoxGeometry": () => (/* binding */ BoxGeometry), /* harmony export */ "BoxHelper": () => (/* binding */ BoxHelper), /* harmony export */ "BufferAttribute": () => (/* binding */ BufferAttribute), /* harmony export */ "BufferGeometry": () => (/* binding */ BufferGeometry), /* harmony export */ "BufferGeometryLoader": () => (/* binding */ BufferGeometryLoader), /* harmony export */ "ByteType": () => (/* binding */ ByteType), /* harmony export */ "Cache": () => (/* binding */ Cache), /* harmony export */ "Camera": () => (/* binding */ Camera), /* harmony export */ "CameraHelper": () => (/* binding */ CameraHelper), /* harmony export */ "CanvasTexture": () => (/* binding */ CanvasTexture), /* harmony export */ "CapsuleGeometry": () => (/* binding */ CapsuleGeometry), /* harmony export */ "CatmullRomCurve3": () => (/* binding */ CatmullRomCurve3), /* harmony export */ "CineonToneMapping": () => (/* binding */ CineonToneMapping), /* harmony export */ "CircleGeometry": () => (/* binding */ CircleGeometry), /* harmony export */ "ClampToEdgeWrapping": () => (/* binding */ ClampToEdgeWrapping), /* harmony export */ "Clock": () => (/* binding */ Clock), /* harmony export */ "Color": () => (/* binding */ Color), /* harmony export */ "ColorKeyframeTrack": () => (/* binding */ ColorKeyframeTrack), /* harmony export */ "ColorManagement": () => (/* binding */ ColorManagement), /* harmony export */ "CompressedArrayTexture": () => (/* binding */ CompressedArrayTexture), /* harmony export */ "CompressedTexture": () => (/* binding */ CompressedTexture), /* harmony export */ "CompressedTextureLoader": () => (/* binding */ CompressedTextureLoader), /* harmony export */ "ConeGeometry": () => (/* binding */ ConeGeometry), /* harmony export */ "CubeCamera": () => (/* binding */ CubeCamera), /* harmony export */ "CubeReflectionMapping": () => (/* binding */ CubeReflectionMapping), /* harmony export */ "CubeRefractionMapping": () => (/* binding */ CubeRefractionMapping), /* harmony export */ "CubeTexture": () => (/* binding */ CubeTexture), /* harmony export */ "CubeTextureLoader": () => (/* binding */ CubeTextureLoader), /* harmony export */ "CubeUVReflectionMapping": () => (/* binding */ CubeUVReflectionMapping), /* harmony export */ "CubicBezierCurve": () => (/* binding */ CubicBezierCurve), /* harmony export */ "CubicBezierCurve3": () => (/* binding */ CubicBezierCurve3), /* harmony export */ "CubicInterpolant": () => (/* binding */ CubicInterpolant), /* harmony export */ "CullFaceBack": () => (/* binding */ CullFaceBack), /* harmony export */ "CullFaceFront": () => (/* binding */ CullFaceFront), /* harmony export */ "CullFaceFrontBack": () => (/* binding */ CullFaceFrontBack), /* harmony export */ "CullFaceNone": () => (/* binding */ CullFaceNone), /* harmony export */ "Curve": () => (/* binding */ Curve), /* harmony export */ "CurvePath": () => (/* binding */ CurvePath), /* harmony export */ "CustomBlending": () => (/* binding */ CustomBlending), /* harmony export */ "CustomToneMapping": () => (/* binding */ CustomToneMapping), /* harmony export */ "CylinderGeometry": () => (/* binding */ CylinderGeometry), /* harmony export */ "Cylindrical": () => (/* binding */ Cylindrical), /* harmony export */ "Data3DTexture": () => (/* binding */ Data3DTexture), /* harmony export */ "DataArrayTexture": () => (/* binding */ DataArrayTexture), /* harmony export */ "DataTexture": () => (/* binding */ DataTexture), /* harmony export */ "DataTextureLoader": () => (/* binding */ DataTextureLoader), /* harmony export */ "DataUtils": () => (/* binding */ DataUtils), /* harmony export */ "DecrementStencilOp": () => (/* binding */ DecrementStencilOp), /* harmony export */ "DecrementWrapStencilOp": () => (/* binding */ DecrementWrapStencilOp), /* harmony export */ "DefaultLoadingManager": () => (/* binding */ DefaultLoadingManager), /* harmony export */ "DepthFormat": () => (/* binding */ DepthFormat), /* harmony export */ "DepthStencilFormat": () => (/* binding */ DepthStencilFormat), /* harmony export */ "DepthTexture": () => (/* binding */ DepthTexture), /* harmony export */ "DirectionalLight": () => (/* binding */ DirectionalLight), /* harmony export */ "DirectionalLightHelper": () => (/* binding */ DirectionalLightHelper), /* harmony export */ "DiscreteInterpolant": () => (/* binding */ DiscreteInterpolant), /* harmony export */ "DisplayP3ColorSpace": () => (/* binding */ DisplayP3ColorSpace), /* harmony export */ "DodecahedronGeometry": () => (/* binding */ DodecahedronGeometry), /* harmony export */ "DoubleSide": () => (/* binding */ DoubleSide), /* harmony export */ "DstAlphaFactor": () => (/* binding */ DstAlphaFactor), /* harmony export */ "DstColorFactor": () => (/* binding */ DstColorFactor), /* harmony export */ "DynamicCopyUsage": () => (/* binding */ DynamicCopyUsage), /* harmony export */ "DynamicDrawUsage": () => (/* binding */ DynamicDrawUsage), /* harmony export */ "DynamicReadUsage": () => (/* binding */ DynamicReadUsage), /* harmony export */ "EdgesGeometry": () => (/* binding */ EdgesGeometry), /* harmony export */ "EllipseCurve": () => (/* binding */ EllipseCurve), /* harmony export */ "EqualCompare": () => (/* binding */ EqualCompare), /* harmony export */ "EqualDepth": () => (/* binding */ EqualDepth), /* harmony export */ "EqualStencilFunc": () => (/* binding */ EqualStencilFunc), /* harmony export */ "EquirectangularReflectionMapping": () => (/* binding */ EquirectangularReflectionMapping), /* harmony export */ "EquirectangularRefractionMapping": () => (/* binding */ EquirectangularRefractionMapping), /* harmony export */ "Euler": () => (/* binding */ Euler), /* harmony export */ "EventDispatcher": () => (/* binding */ EventDispatcher), /* harmony export */ "ExtrudeGeometry": () => (/* binding */ ExtrudeGeometry), /* harmony export */ "FileLoader": () => (/* binding */ FileLoader), /* harmony export */ "Float16BufferAttribute": () => (/* binding */ Float16BufferAttribute), /* harmony export */ "Float32BufferAttribute": () => (/* binding */ Float32BufferAttribute), /* harmony export */ "Float64BufferAttribute": () => (/* binding */ Float64BufferAttribute), /* harmony export */ "FloatType": () => (/* binding */ FloatType), /* harmony export */ "Fog": () => (/* binding */ Fog), /* harmony export */ "FogExp2": () => (/* binding */ FogExp2), /* harmony export */ "FramebufferTexture": () => (/* binding */ FramebufferTexture), /* harmony export */ "FrontSide": () => (/* binding */ FrontSide), /* harmony export */ "Frustum": () => (/* binding */ Frustum), /* harmony export */ "GLBufferAttribute": () => (/* binding */ GLBufferAttribute), /* harmony export */ "GLSL1": () => (/* binding */ GLSL1), /* harmony export */ "GLSL3": () => (/* binding */ GLSL3), /* harmony export */ "GreaterCompare": () => (/* binding */ GreaterCompare), /* harmony export */ "GreaterDepth": () => (/* binding */ GreaterDepth), /* harmony export */ "GreaterEqualCompare": () => (/* binding */ GreaterEqualCompare), /* harmony export */ "GreaterEqualDepth": () => (/* binding */ GreaterEqualDepth), /* harmony export */ "GreaterEqualStencilFunc": () => (/* binding */ GreaterEqualStencilFunc), /* harmony export */ "GreaterStencilFunc": () => (/* binding */ GreaterStencilFunc), /* harmony export */ "GridHelper": () => (/* binding */ GridHelper), /* harmony export */ "Group": () => (/* binding */ Group), /* harmony export */ "HalfFloatType": () => (/* binding */ HalfFloatType), /* harmony export */ "HemisphereLight": () => (/* binding */ HemisphereLight), /* harmony export */ "HemisphereLightHelper": () => (/* binding */ HemisphereLightHelper), /* harmony export */ "HemisphereLightProbe": () => (/* binding */ HemisphereLightProbe), /* harmony export */ "IcosahedronGeometry": () => (/* binding */ IcosahedronGeometry), /* harmony export */ "ImageBitmapLoader": () => (/* binding */ ImageBitmapLoader), /* harmony export */ "ImageLoader": () => (/* binding */ ImageLoader), /* harmony export */ "ImageUtils": () => (/* binding */ ImageUtils), /* harmony export */ "IncrementStencilOp": () => (/* binding */ IncrementStencilOp), /* harmony export */ "IncrementWrapStencilOp": () => (/* binding */ IncrementWrapStencilOp), /* harmony export */ "InstancedBufferAttribute": () => (/* binding */ InstancedBufferAttribute), /* harmony export */ "InstancedBufferGeometry": () => (/* binding */ InstancedBufferGeometry), /* harmony export */ "InstancedInterleavedBuffer": () => (/* binding */ InstancedInterleavedBuffer), /* harmony export */ "InstancedMesh": () => (/* binding */ InstancedMesh), /* harmony export */ "Int16BufferAttribute": () => (/* binding */ Int16BufferAttribute), /* harmony export */ "Int32BufferAttribute": () => (/* binding */ Int32BufferAttribute), /* harmony export */ "Int8BufferAttribute": () => (/* binding */ Int8BufferAttribute), /* harmony export */ "IntType": () => (/* binding */ IntType), /* harmony export */ "InterleavedBuffer": () => (/* binding */ InterleavedBuffer), /* harmony export */ "InterleavedBufferAttribute": () => (/* binding */ InterleavedBufferAttribute), /* harmony export */ "Interpolant": () => (/* binding */ Interpolant), /* harmony export */ "InterpolateDiscrete": () => (/* binding */ InterpolateDiscrete), /* harmony export */ "InterpolateLinear": () => (/* binding */ InterpolateLinear), /* harmony export */ "InterpolateSmooth": () => (/* binding */ InterpolateSmooth), /* harmony export */ "InvertStencilOp": () => (/* binding */ InvertStencilOp), /* harmony export */ "KeepStencilOp": () => (/* binding */ KeepStencilOp), /* harmony export */ "KeyframeTrack": () => (/* binding */ KeyframeTrack), /* harmony export */ "LOD": () => (/* binding */ LOD), /* harmony export */ "LatheGeometry": () => (/* binding */ LatheGeometry), /* harmony export */ "Layers": () => (/* binding */ Layers), /* harmony export */ "LessCompare": () => (/* binding */ LessCompare), /* harmony export */ "LessDepth": () => (/* binding */ LessDepth), /* harmony export */ "LessEqualCompare": () => (/* binding */ LessEqualCompare), /* harmony export */ "LessEqualDepth": () => (/* binding */ LessEqualDepth), /* harmony export */ "LessEqualStencilFunc": () => (/* binding */ LessEqualStencilFunc), /* harmony export */ "LessStencilFunc": () => (/* binding */ LessStencilFunc), /* harmony export */ "Light": () => (/* binding */ Light), /* harmony export */ "LightProbe": () => (/* binding */ LightProbe), /* harmony export */ "Line": () => (/* binding */ Line), /* harmony export */ "Line3": () => (/* binding */ Line3), /* harmony export */ "LineBasicMaterial": () => (/* binding */ LineBasicMaterial), /* harmony export */ "LineCurve": () => (/* binding */ LineCurve), /* harmony export */ "LineCurve3": () => (/* binding */ LineCurve3), /* harmony export */ "LineDashedMaterial": () => (/* binding */ LineDashedMaterial), /* harmony export */ "LineLoop": () => (/* binding */ LineLoop), /* harmony export */ "LineSegments": () => (/* binding */ LineSegments), /* harmony export */ "LinearEncoding": () => (/* binding */ LinearEncoding), /* harmony export */ "LinearFilter": () => (/* binding */ LinearFilter), /* harmony export */ "LinearInterpolant": () => (/* binding */ LinearInterpolant), /* harmony export */ "LinearMipMapLinearFilter": () => (/* binding */ LinearMipMapLinearFilter), /* harmony export */ "LinearMipMapNearestFilter": () => (/* binding */ LinearMipMapNearestFilter), /* harmony export */ "LinearMipmapLinearFilter": () => (/* binding */ LinearMipmapLinearFilter), /* harmony export */ "LinearMipmapNearestFilter": () => (/* binding */ LinearMipmapNearestFilter), /* harmony export */ "LinearSRGBColorSpace": () => (/* binding */ LinearSRGBColorSpace), /* harmony export */ "LinearToneMapping": () => (/* binding */ LinearToneMapping), /* harmony export */ "Loader": () => (/* binding */ Loader), /* harmony export */ "LoaderUtils": () => (/* binding */ LoaderUtils), /* harmony export */ "LoadingManager": () => (/* binding */ LoadingManager), /* harmony export */ "LoopOnce": () => (/* binding */ LoopOnce), /* harmony export */ "LoopPingPong": () => (/* binding */ LoopPingPong), /* harmony export */ "LoopRepeat": () => (/* binding */ LoopRepeat), /* harmony export */ "LuminanceAlphaFormat": () => (/* binding */ LuminanceAlphaFormat), /* harmony export */ "LuminanceFormat": () => (/* binding */ LuminanceFormat), /* harmony export */ "MOUSE": () => (/* binding */ MOUSE), /* harmony export */ "Material": () => (/* binding */ Material), /* harmony export */ "MaterialLoader": () => (/* binding */ MaterialLoader), /* harmony export */ "MathUtils": () => (/* binding */ MathUtils), /* harmony export */ "Matrix3": () => (/* binding */ Matrix3), /* harmony export */ "Matrix4": () => (/* binding */ Matrix4), /* harmony export */ "MaxEquation": () => (/* binding */ MaxEquation), /* harmony export */ "Mesh": () => (/* binding */ Mesh), /* harmony export */ "MeshBasicMaterial": () => (/* binding */ MeshBasicMaterial), /* harmony export */ "MeshDepthMaterial": () => (/* binding */ MeshDepthMaterial), /* harmony export */ "MeshDistanceMaterial": () => (/* binding */ MeshDistanceMaterial), /* harmony export */ "MeshLambertMaterial": () => (/* binding */ MeshLambertMaterial), /* harmony export */ "MeshMatcapMaterial": () => (/* binding */ MeshMatcapMaterial), /* harmony export */ "MeshNormalMaterial": () => (/* binding */ MeshNormalMaterial), /* harmony export */ "MeshPhongMaterial": () => (/* binding */ MeshPhongMaterial), /* harmony export */ "MeshPhysicalMaterial": () => (/* binding */ MeshPhysicalMaterial), /* harmony export */ "MeshStandardMaterial": () => (/* binding */ MeshStandardMaterial), /* harmony export */ "MeshToonMaterial": () => (/* binding */ MeshToonMaterial), /* harmony export */ "MinEquation": () => (/* binding */ MinEquation), /* harmony export */ "MirroredRepeatWrapping": () => (/* binding */ MirroredRepeatWrapping), /* harmony export */ "MixOperation": () => (/* binding */ MixOperation), /* harmony export */ "MultiplyBlending": () => (/* binding */ MultiplyBlending), /* harmony export */ "MultiplyOperation": () => (/* binding */ MultiplyOperation), /* harmony export */ "NearestFilter": () => (/* binding */ NearestFilter), /* harmony export */ "NearestMipMapLinearFilter": () => (/* binding */ NearestMipMapLinearFilter), /* harmony export */ "NearestMipMapNearestFilter": () => (/* binding */ NearestMipMapNearestFilter), /* harmony export */ "NearestMipmapLinearFilter": () => (/* binding */ NearestMipmapLinearFilter), /* harmony export */ "NearestMipmapNearestFilter": () => (/* binding */ NearestMipmapNearestFilter), /* harmony export */ "NeverCompare": () => (/* binding */ NeverCompare), /* harmony export */ "NeverDepth": () => (/* binding */ NeverDepth), /* harmony export */ "NeverStencilFunc": () => (/* binding */ NeverStencilFunc), /* harmony export */ "NoBlending": () => (/* binding */ NoBlending), /* harmony export */ "NoColorSpace": () => (/* binding */ NoColorSpace), /* harmony export */ "NoToneMapping": () => (/* binding */ NoToneMapping), /* harmony export */ "NormalAnimationBlendMode": () => (/* binding */ NormalAnimationBlendMode), /* harmony export */ "NormalBlending": () => (/* binding */ NormalBlending), /* harmony export */ "NotEqualCompare": () => (/* binding */ NotEqualCompare), /* harmony export */ "NotEqualDepth": () => (/* binding */ NotEqualDepth), /* harmony export */ "NotEqualStencilFunc": () => (/* binding */ NotEqualStencilFunc), /* harmony export */ "NumberKeyframeTrack": () => (/* binding */ NumberKeyframeTrack), /* harmony export */ "Object3D": () => (/* binding */ Object3D), /* harmony export */ "ObjectLoader": () => (/* binding */ ObjectLoader), /* harmony export */ "ObjectSpaceNormalMap": () => (/* binding */ ObjectSpaceNormalMap), /* harmony export */ "OctahedronGeometry": () => (/* binding */ OctahedronGeometry), /* harmony export */ "OneFactor": () => (/* binding */ OneFactor), /* harmony export */ "OneMinusDstAlphaFactor": () => (/* binding */ OneMinusDstAlphaFactor), /* harmony export */ "OneMinusDstColorFactor": () => (/* binding */ OneMinusDstColorFactor), /* harmony export */ "OneMinusSrcAlphaFactor": () => (/* binding */ OneMinusSrcAlphaFactor), /* harmony export */ "OneMinusSrcColorFactor": () => (/* binding */ OneMinusSrcColorFactor), /* harmony export */ "OrthographicCamera": () => (/* binding */ OrthographicCamera), /* harmony export */ "PCFShadowMap": () => (/* binding */ PCFShadowMap), /* harmony export */ "PCFSoftShadowMap": () => (/* binding */ PCFSoftShadowMap), /* harmony export */ "PMREMGenerator": () => (/* binding */ PMREMGenerator), /* harmony export */ "Path": () => (/* binding */ Path), /* harmony export */ "PerspectiveCamera": () => (/* binding */ PerspectiveCamera), /* harmony export */ "Plane": () => (/* binding */ Plane), /* harmony export */ "PlaneGeometry": () => (/* binding */ PlaneGeometry), /* harmony export */ "PlaneHelper": () => (/* binding */ PlaneHelper), /* harmony export */ "PointLight": () => (/* binding */ PointLight), /* harmony export */ "PointLightHelper": () => (/* binding */ PointLightHelper), /* harmony export */ "Points": () => (/* binding */ Points), /* harmony export */ "PointsMaterial": () => (/* binding */ PointsMaterial), /* harmony export */ "PolarGridHelper": () => (/* binding */ PolarGridHelper), /* harmony export */ "PolyhedronGeometry": () => (/* binding */ PolyhedronGeometry), /* harmony export */ "PositionalAudio": () => (/* binding */ PositionalAudio), /* harmony export */ "PropertyBinding": () => (/* binding */ PropertyBinding), /* harmony export */ "PropertyMixer": () => (/* binding */ PropertyMixer), /* harmony export */ "QuadraticBezierCurve": () => (/* binding */ QuadraticBezierCurve), /* harmony export */ "QuadraticBezierCurve3": () => (/* binding */ QuadraticBezierCurve3), /* harmony export */ "Quaternion": () => (/* binding */ Quaternion), /* harmony export */ "QuaternionKeyframeTrack": () => (/* binding */ QuaternionKeyframeTrack), /* harmony export */ "QuaternionLinearInterpolant": () => (/* binding */ QuaternionLinearInterpolant), /* harmony export */ "RED_GREEN_RGTC2_Format": () => (/* binding */ RED_GREEN_RGTC2_Format), /* harmony export */ "RED_RGTC1_Format": () => (/* binding */ RED_RGTC1_Format), /* harmony export */ "REVISION": () => (/* binding */ REVISION), /* harmony export */ "RGBADepthPacking": () => (/* binding */ RGBADepthPacking), /* harmony export */ "RGBAFormat": () => (/* binding */ RGBAFormat), /* harmony export */ "RGBAIntegerFormat": () => (/* binding */ RGBAIntegerFormat), /* harmony export */ "RGBA_ASTC_10x10_Format": () => (/* binding */ RGBA_ASTC_10x10_Format), /* harmony export */ "RGBA_ASTC_10x5_Format": () => (/* binding */ RGBA_ASTC_10x5_Format), /* harmony export */ "RGBA_ASTC_10x6_Format": () => (/* binding */ RGBA_ASTC_10x6_Format), /* harmony export */ "RGBA_ASTC_10x8_Format": () => (/* binding */ RGBA_ASTC_10x8_Format), /* harmony export */ "RGBA_ASTC_12x10_Format": () => (/* binding */ RGBA_ASTC_12x10_Format), /* harmony export */ "RGBA_ASTC_12x12_Format": () => (/* binding */ RGBA_ASTC_12x12_Format), /* harmony export */ "RGBA_ASTC_4x4_Format": () => (/* binding */ RGBA_ASTC_4x4_Format), /* harmony export */ "RGBA_ASTC_5x4_Format": () => (/* binding */ RGBA_ASTC_5x4_Format), /* harmony export */ "RGBA_ASTC_5x5_Format": () => (/* binding */ RGBA_ASTC_5x5_Format), /* harmony export */ "RGBA_ASTC_6x5_Format": () => (/* binding */ RGBA_ASTC_6x5_Format), /* harmony export */ "RGBA_ASTC_6x6_Format": () => (/* binding */ RGBA_ASTC_6x6_Format), /* harmony export */ "RGBA_ASTC_8x5_Format": () => (/* binding */ RGBA_ASTC_8x5_Format), /* harmony export */ "RGBA_ASTC_8x6_Format": () => (/* binding */ RGBA_ASTC_8x6_Format), /* harmony export */ "RGBA_ASTC_8x8_Format": () => (/* binding */ RGBA_ASTC_8x8_Format), /* harmony export */ "RGBA_BPTC_Format": () => (/* binding */ RGBA_BPTC_Format), /* harmony export */ "RGBA_ETC2_EAC_Format": () => (/* binding */ RGBA_ETC2_EAC_Format), /* harmony export */ "RGBA_PVRTC_2BPPV1_Format": () => (/* binding */ RGBA_PVRTC_2BPPV1_Format), /* harmony export */ "RGBA_PVRTC_4BPPV1_Format": () => (/* binding */ RGBA_PVRTC_4BPPV1_Format), /* harmony export */ "RGBA_S3TC_DXT1_Format": () => (/* binding */ RGBA_S3TC_DXT1_Format), /* harmony export */ "RGBA_S3TC_DXT3_Format": () => (/* binding */ RGBA_S3TC_DXT3_Format), /* harmony export */ "RGBA_S3TC_DXT5_Format": () => (/* binding */ RGBA_S3TC_DXT5_Format), /* harmony export */ "RGB_ETC1_Format": () => (/* binding */ RGB_ETC1_Format), /* harmony export */ "RGB_ETC2_Format": () => (/* binding */ RGB_ETC2_Format), /* harmony export */ "RGB_PVRTC_2BPPV1_Format": () => (/* binding */ RGB_PVRTC_2BPPV1_Format), /* harmony export */ "RGB_PVRTC_4BPPV1_Format": () => (/* binding */ RGB_PVRTC_4BPPV1_Format), /* harmony export */ "RGB_S3TC_DXT1_Format": () => (/* binding */ RGB_S3TC_DXT1_Format), /* harmony export */ "RGFormat": () => (/* binding */ RGFormat), /* harmony export */ "RGIntegerFormat": () => (/* binding */ RGIntegerFormat), /* harmony export */ "RawShaderMaterial": () => (/* binding */ RawShaderMaterial), /* harmony export */ "Ray": () => (/* binding */ Ray), /* harmony export */ "Raycaster": () => (/* binding */ Raycaster), /* harmony export */ "RectAreaLight": () => (/* binding */ RectAreaLight), /* harmony export */ "RedFormat": () => (/* binding */ RedFormat), /* harmony export */ "RedIntegerFormat": () => (/* binding */ RedIntegerFormat), /* harmony export */ "ReinhardToneMapping": () => (/* binding */ ReinhardToneMapping), /* harmony export */ "RepeatWrapping": () => (/* binding */ RepeatWrapping), /* harmony export */ "ReplaceStencilOp": () => (/* binding */ ReplaceStencilOp), /* harmony export */ "ReverseSubtractEquation": () => (/* binding */ ReverseSubtractEquation), /* harmony export */ "RingGeometry": () => (/* binding */ RingGeometry), /* harmony export */ "SIGNED_RED_GREEN_RGTC2_Format": () => (/* binding */ SIGNED_RED_GREEN_RGTC2_Format), /* harmony export */ "SIGNED_RED_RGTC1_Format": () => (/* binding */ SIGNED_RED_RGTC1_Format), /* harmony export */ "SRGBColorSpace": () => (/* binding */ SRGBColorSpace), /* harmony export */ "Scene": () => (/* binding */ Scene), /* harmony export */ "ShaderChunk": () => (/* binding */ ShaderChunk), /* harmony export */ "ShaderLib": () => (/* binding */ ShaderLib), /* harmony export */ "ShaderMaterial": () => (/* binding */ ShaderMaterial), /* harmony export */ "ShadowMaterial": () => (/* binding */ ShadowMaterial), /* harmony export */ "Shape": () => (/* binding */ Shape), /* harmony export */ "ShapeGeometry": () => (/* binding */ ShapeGeometry), /* harmony export */ "ShapePath": () => (/* binding */ ShapePath), /* harmony export */ "ShapeUtils": () => (/* binding */ ShapeUtils), /* harmony export */ "ShortType": () => (/* binding */ ShortType), /* harmony export */ "Skeleton": () => (/* binding */ Skeleton), /* harmony export */ "SkeletonHelper": () => (/* binding */ SkeletonHelper), /* harmony export */ "SkinnedMesh": () => (/* binding */ SkinnedMesh), /* harmony export */ "Source": () => (/* binding */ Source), /* harmony export */ "Sphere": () => (/* binding */ Sphere), /* harmony export */ "SphereGeometry": () => (/* binding */ SphereGeometry), /* harmony export */ "Spherical": () => (/* binding */ Spherical), /* harmony export */ "SphericalHarmonics3": () => (/* binding */ SphericalHarmonics3), /* harmony export */ "SplineCurve": () => (/* binding */ SplineCurve), /* harmony export */ "SpotLight": () => (/* binding */ SpotLight), /* harmony export */ "SpotLightHelper": () => (/* binding */ SpotLightHelper), /* harmony export */ "Sprite": () => (/* binding */ Sprite), /* harmony export */ "SpriteMaterial": () => (/* binding */ SpriteMaterial), /* harmony export */ "SrcAlphaFactor": () => (/* binding */ SrcAlphaFactor), /* harmony export */ "SrcAlphaSaturateFactor": () => (/* binding */ SrcAlphaSaturateFactor), /* harmony export */ "SrcColorFactor": () => (/* binding */ SrcColorFactor), /* harmony export */ "StaticCopyUsage": () => (/* binding */ StaticCopyUsage), /* harmony export */ "StaticDrawUsage": () => (/* binding */ StaticDrawUsage), /* harmony export */ "StaticReadUsage": () => (/* binding */ StaticReadUsage), /* harmony export */ "StereoCamera": () => (/* binding */ StereoCamera), /* harmony export */ "StreamCopyUsage": () => (/* binding */ StreamCopyUsage), /* harmony export */ "StreamDrawUsage": () => (/* binding */ StreamDrawUsage), /* harmony export */ "StreamReadUsage": () => (/* binding */ StreamReadUsage), /* harmony export */ "StringKeyframeTrack": () => (/* binding */ StringKeyframeTrack), /* harmony export */ "SubtractEquation": () => (/* binding */ SubtractEquation), /* harmony export */ "SubtractiveBlending": () => (/* binding */ SubtractiveBlending), /* harmony export */ "TOUCH": () => (/* binding */ TOUCH), /* harmony export */ "TangentSpaceNormalMap": () => (/* binding */ TangentSpaceNormalMap), /* harmony export */ "TetrahedronGeometry": () => (/* binding */ TetrahedronGeometry), /* harmony export */ "Texture": () => (/* binding */ Texture), /* harmony export */ "TextureLoader": () => (/* binding */ TextureLoader), /* harmony export */ "TorusGeometry": () => (/* binding */ TorusGeometry), /* harmony export */ "TorusKnotGeometry": () => (/* binding */ TorusKnotGeometry), /* harmony export */ "Triangle": () => (/* binding */ Triangle), /* harmony export */ "TriangleFanDrawMode": () => (/* binding */ TriangleFanDrawMode), /* harmony export */ "TriangleStripDrawMode": () => (/* binding */ TriangleStripDrawMode), /* harmony export */ "TrianglesDrawMode": () => (/* binding */ TrianglesDrawMode), /* harmony export */ "TubeGeometry": () => (/* binding */ TubeGeometry), /* harmony export */ "TwoPassDoubleSide": () => (/* binding */ TwoPassDoubleSide), /* harmony export */ "UVMapping": () => (/* binding */ UVMapping), /* harmony export */ "Uint16BufferAttribute": () => (/* binding */ Uint16BufferAttribute), /* harmony export */ "Uint32BufferAttribute": () => (/* binding */ Uint32BufferAttribute), /* harmony export */ "Uint8BufferAttribute": () => (/* binding */ Uint8BufferAttribute), /* harmony export */ "Uint8ClampedBufferAttribute": () => (/* binding */ Uint8ClampedBufferAttribute), /* harmony export */ "Uniform": () => (/* binding */ Uniform), /* harmony export */ "UniformsGroup": () => (/* binding */ UniformsGroup), /* harmony export */ "UniformsLib": () => (/* binding */ UniformsLib), /* harmony export */ "UniformsUtils": () => (/* binding */ UniformsUtils), /* harmony export */ "UnsignedByteType": () => (/* binding */ UnsignedByteType), /* harmony export */ "UnsignedInt248Type": () => (/* binding */ UnsignedInt248Type), /* harmony export */ "UnsignedIntType": () => (/* binding */ UnsignedIntType), /* harmony export */ "UnsignedShort4444Type": () => (/* binding */ UnsignedShort4444Type), /* harmony export */ "UnsignedShort5551Type": () => (/* binding */ UnsignedShort5551Type), /* harmony export */ "UnsignedShortType": () => (/* binding */ UnsignedShortType), /* harmony export */ "VSMShadowMap": () => (/* binding */ VSMShadowMap), /* harmony export */ "Vector2": () => (/* binding */ Vector2), /* harmony export */ "Vector3": () => (/* binding */ Vector3), /* harmony export */ "Vector4": () => (/* binding */ Vector4), /* harmony export */ "VectorKeyframeTrack": () => (/* binding */ VectorKeyframeTrack), /* harmony export */ "VideoTexture": () => (/* binding */ VideoTexture), /* harmony export */ "WebGL1Renderer": () => (/* binding */ WebGL1Renderer), /* harmony export */ "WebGL3DRenderTarget": () => (/* binding */ WebGL3DRenderTarget), /* harmony export */ "WebGLArrayRenderTarget": () => (/* binding */ WebGLArrayRenderTarget), /* harmony export */ "WebGLCoordinateSystem": () => (/* binding */ WebGLCoordinateSystem), /* harmony export */ "WebGLCubeRenderTarget": () => (/* binding */ WebGLCubeRenderTarget), /* harmony export */ "WebGLMultipleRenderTargets": () => (/* binding */ WebGLMultipleRenderTargets), /* harmony export */ "WebGLRenderTarget": () => (/* binding */ WebGLRenderTarget), /* harmony export */ "WebGLRenderer": () => (/* binding */ WebGLRenderer), /* harmony export */ "WebGLUtils": () => (/* binding */ WebGLUtils), /* harmony export */ "WebGPUCoordinateSystem": () => (/* binding */ WebGPUCoordinateSystem), /* harmony export */ "WireframeGeometry": () => (/* binding */ WireframeGeometry), /* harmony export */ "WrapAroundEnding": () => (/* binding */ WrapAroundEnding), /* harmony export */ "ZeroCurvatureEnding": () => (/* binding */ ZeroCurvatureEnding), /* harmony export */ "ZeroFactor": () => (/* binding */ ZeroFactor), /* harmony export */ "ZeroSlopeEnding": () => (/* binding */ ZeroSlopeEnding), /* harmony export */ "ZeroStencilOp": () => (/* binding */ ZeroStencilOp), /* harmony export */ "_SRGBAFormat": () => (/* binding */ _SRGBAFormat), /* harmony export */ "sRGBEncoding": () => (/* binding */ sRGBEncoding) /* harmony export */ }); /* harmony import */ var _home_sebastian_mindmap3d_node_modules_babel_runtime_helpers_esm_asyncToGenerator_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js */ 1670); /** * @license * Copyright 2010-2023 Three.js Authors * SPDX-License-Identifier: MIT */ const REVISION = '154'; const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; const CullFaceNone = 0; const CullFaceBack = 1; const CullFaceFront = 2; const CullFaceFrontBack = 3; const BasicShadowMap = 0; const PCFShadowMap = 1; const PCFSoftShadowMap = 2; const VSMShadowMap = 3; const FrontSide = 0; const BackSide = 1; const DoubleSide = 2; const TwoPassDoubleSide = 2; // r149 const NoBlending = 0; const NormalBlending = 1; const AdditiveBlending = 2; const SubtractiveBlending = 3; const MultiplyBlending = 4; const CustomBlending = 5; const AddEquation = 100; const SubtractEquation = 101; const ReverseSubtractEquation = 102; const MinEquation = 103; const MaxEquation = 104; const ZeroFactor = 200; const OneFactor = 201; const SrcColorFactor = 202; const OneMinusSrcColorFactor = 203; const SrcAlphaFactor = 204; const OneMinusSrcAlphaFactor = 205; const DstAlphaFactor = 206; const OneMinusDstAlphaFactor = 207; const DstColorFactor = 208; const OneMinusDstColorFactor = 209; const SrcAlphaSaturateFactor = 210; const NeverDepth = 0; const AlwaysDepth = 1; const LessDepth = 2; const LessEqualDepth = 3; const EqualDepth = 4; const GreaterEqualDepth = 5; const GreaterDepth = 6; const NotEqualDepth = 7; const MultiplyOperation = 0; const MixOperation = 1; const AddOperation = 2; const NoToneMapping = 0; const LinearToneMapping = 1; const ReinhardToneMapping = 2; const CineonToneMapping = 3; const ACESFilmicToneMapping = 4; const CustomToneMapping = 5; const UVMapping = 300; const CubeReflectionMapping = 301; const CubeRefractionMapping = 302; const EquirectangularReflectionMapping = 303; const EquirectangularRefractionMapping = 304; const CubeUVReflectionMapping = 306; const RepeatWrapping = 1000; const ClampToEdgeWrapping = 1001; const MirroredRepeatWrapping = 1002; const NearestFilter = 1003; const NearestMipmapNearestFilter = 1004; const NearestMipMapNearestFilter = 1004; const NearestMipmapLinearFilter = 1005; const NearestMipMapLinearFilter = 1005; const LinearFilter = 1006; const LinearMipmapNearestFilter = 1007; const LinearMipMapNearestFilter = 1007; const LinearMipmapLinearFilter = 1008; const LinearMipMapLinearFilter = 1008; const UnsignedByteType = 1009; const ByteType = 1010; const ShortType = 1011; const UnsignedShortType = 1012; const IntType = 1013; const UnsignedIntType = 1014; const FloatType = 1015; const HalfFloatType = 1016; const UnsignedShort4444Type = 1017; const UnsignedShort5551Type = 1018; const UnsignedInt248Type = 1020; const AlphaFormat = 1021; const RGBAFormat = 1023; const LuminanceFormat = 1024; const LuminanceAlphaFormat = 1025; const DepthFormat = 1026; const DepthStencilFormat = 1027; const RedFormat = 1028; const RedIntegerFormat = 1029; const RGFormat = 1030; const RGIntegerFormat = 1031; const RGBAIntegerFormat = 1033; const RGB_S3TC_DXT1_Format = 33776; const RGBA_S3TC_DXT1_Format = 33777; const RGBA_S3TC_DXT3_Format = 33778; const RGBA_S3TC_DXT5_Format = 33779; const RGB_PVRTC_4BPPV1_Format = 35840; const RGB_PVRTC_2BPPV1_Format = 35841; const RGBA_PVRTC_4BPPV1_Format = 35842; const RGBA_PVRTC_2BPPV1_Format = 35843; const RGB_ETC1_Format = 36196; const RGB_ETC2_Format = 37492; const RGBA_ETC2_EAC_Format = 37496; const RGBA_ASTC_4x4_Format = 37808; const RGBA_ASTC_5x4_Format = 37809; const RGBA_ASTC_5x5_Format = 37810; const RGBA_ASTC_6x5_Format = 37811; const RGBA_ASTC_6x6_Format = 37812; const RGBA_ASTC_8x5_Format = 37813; const RGBA_ASTC_8x6_Format = 37814; const RGBA_ASTC_8x8_Format = 37815; const RGBA_ASTC_10x5_Format = 37816; const RGBA_ASTC_10x6_Format = 37817; const RGBA_ASTC_10x8_Format = 37818; const RGBA_ASTC_10x10_Format = 37819; const RGBA_ASTC_12x10_Format = 37820; const RGBA_ASTC_12x12_Format = 37821; const RGBA_BPTC_Format = 36492; const RED_RGTC1_Format = 36283; const SIGNED_RED_RGTC1_Format = 36284; const RED_GREEN_RGTC2_Format = 36285; const SIGNED_RED_GREEN_RGTC2_Format = 36286; const LoopOnce = 2200; const LoopRepeat = 2201; const LoopPingPong = 2202; const InterpolateDiscrete = 2300; const InterpolateLinear = 2301; const InterpolateSmooth = 2302; const ZeroCurvatureEnding = 2400; const ZeroSlopeEnding = 2401; const WrapAroundEnding = 2402; const NormalAnimationBlendMode = 2500; const AdditiveAnimationBlendMode = 2501; const TrianglesDrawMode = 0; const TriangleStripDrawMode = 1; const TriangleFanDrawMode = 2; /** @deprecated Use LinearSRGBColorSpace or NoColorSpace in three.js r152+. */ const LinearEncoding = 3000; /** @deprecated Use SRGBColorSpace in three.js r152+. */ const sRGBEncoding = 3001; const BasicDepthPacking = 3200; const RGBADepthPacking = 3201; const TangentSpaceNormalMap = 0; const ObjectSpaceNormalMap = 1; // Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available. const NoColorSpace = ''; const SRGBColorSpace = 'srgb'; const LinearSRGBColorSpace = 'srgb-linear'; const DisplayP3ColorSpace = 'display-p3'; const ZeroStencilOp = 0; const KeepStencilOp = 7680; const ReplaceStencilOp = 7681; const IncrementStencilOp = 7682; const DecrementStencilOp = 7683; const IncrementWrapStencilOp = 34055; const DecrementWrapStencilOp = 34056; const InvertStencilOp = 5386; const NeverStencilFunc = 512; const LessStencilFunc = 513; const EqualStencilFunc = 514; const LessEqualStencilFunc = 515; const GreaterStencilFunc = 516; const NotEqualStencilFunc = 517; const GreaterEqualStencilFunc = 518; const AlwaysStencilFunc = 519; const NeverCompare = 512; const LessCompare = 513; const EqualCompare = 514; const LessEqualCompare = 515; const GreaterCompare = 516; const NotEqualCompare = 517; const GreaterEqualCompare = 518; const AlwaysCompare = 519; const StaticDrawUsage = 35044; const DynamicDrawUsage = 35048; const StreamDrawUsage = 35040; const StaticReadUsage = 35045; const DynamicReadUsage = 35049; const StreamReadUsage = 35041; const StaticCopyUsage = 35046; const DynamicCopyUsage = 35050; const StreamCopyUsage = 35042; const GLSL1 = '100'; const GLSL3 = '300 es'; const _SRGBAFormat = 1035; // fallback for WebGL 1 const WebGLCoordinateSystem = 2000; const WebGPUCoordinateSystem = 2001; /** * https://github.com/mrdoob/eventdispatcher.js/ */ class EventDispatcher { addEventListener(type, listener) { if (this._listeners === undefined) this._listeners = {}; const listeners = this._listeners; if (listeners[type] === undefined) { listeners[type] = []; } if (listeners[type].indexOf(listener) === -1) { listeners[type].push(listener); } } hasEventListener(type, listener) { if (this._listeners === undefined) return false; const listeners = this._listeners; return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1; } removeEventListener(type, listener) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[type]; if (listenerArray !== undefined) { const index = listenerArray.indexOf(listener); if (index !== -1) { listenerArray.splice(index, 1); } } } dispatchEvent(event) { if (this._listeners === undefined) return; const listeners = this._listeners; const listenerArray = listeners[event.type]; if (listenerArray !== undefined) { event.target = this; // Make a copy, in case listeners are removed while iterating. const array = listenerArray.slice(0); for (let i = 0, l = array.length; i < l; i++) { array[i].call(this, event); } event.target = null; } } } const _lut = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '0a', '0b', '0c', '0d', '0e', '0f', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1a', '1b', '1c', '1d', '1e', '1f', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '3a', '3b', '3c', '3d', '3e', '3f', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a', '5b', '5c', '5d', '5e', '5f', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '7a', '7b', '7c', '7d', '7e', '7f', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '8a', '8b', '8c', '8d', '8e', '8f', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '9a', '9b', '9c', '9d', '9e', '9f', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'aa', 'ab', 'ac', 'ad', 'ae', 'af', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'ba', 'bb', 'bc', 'bd', 'be', 'bf', 'c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'ca', 'cb', 'cc', 'cd', 'ce', 'cf', 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'da', 'db', 'dc', 'dd', 'de', 'df', 'e0', 'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9', 'ea', 'eb', 'ec', 'ed', 'ee', 'ef', 'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'fa', 'fb', 'fc', 'fd', 'fe', 'ff']; let _seed = 1234567; const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 function generateUUID() { const d0 = Math.random() * 0xffffffff | 0; const d1 = Math.random() * 0xffffffff | 0; const d2 = Math.random() * 0xffffffff | 0; const d3 = Math.random() * 0xffffffff | 0; const uuid = _lut[d0 & 0xff] + _lut[d0 >> 8 & 0xff] + _lut[d0 >> 16 & 0xff] + _lut[d0 >> 24 & 0xff] + '-' + _lut[d1 & 0xff] + _lut[d1 >> 8 & 0xff] + '-' + _lut[d1 >> 16 & 0x0f | 0x40] + _lut[d1 >> 24 & 0xff] + '-' + _lut[d2 & 0x3f | 0x80] + _lut[d2 >> 8 & 0xff] + '-' + _lut[d2 >> 16 & 0xff] + _lut[d2 >> 24 & 0xff] + _lut[d3 & 0xff] + _lut[d3 >> 8 & 0xff] + _lut[d3 >> 16 & 0xff] + _lut[d3 >> 24 & 0xff]; // .toLowerCase() here flattens concatenated strings to save heap memory space. return uuid.toLowerCase(); } function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } // compute euclidean modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation function euclideanModulo(n, m) { return (n % m + m) % m; } // Linear mapping from range <a1, a2> to range <b1, b2> function mapLinear(x, a1, a2, b1, b2) { return b1 + (x - a1) * (b2 - b1) / (a2 - a1); } // https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ function inverseLerp(x, y, value) { if (x !== y) { return (value - x) / (y - x); } else { return 0; } } // https://en.wikipedia.org/wiki/Linear_interpolation function lerp(x, y, t) { return (1 - t) * x + t * y; } // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ function damp(x, y, lambda, dt) { return lerp(x, y, 1 - Math.exp(-lambda * dt)); } // https://www.desmos.com/calculator/vcsjnyz7x4 function pingpong(x, length = 1) { return length - Math.abs(euclideanModulo(x, length * 2) - length); } // http://en.wikipedia.org/wiki/Smoothstep function smoothstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * (3 - 2 * x); } function smootherstep(x, min, max) { if (x <= min) return 0; if (x >= max) return 1; x = (x - min) / (max - min); return x * x * x * (x * (x * 6 - 15) + 10); } // Random integer from <low, high> interval function randInt(low, high) { return low + Math.floor(Math.random() * (high - low + 1)); } // Random float from <low, high> interval function randFloat(low, high) { return low + Math.random() * (high - low); } // Random float from <-range/2, range/2> interval function randFloatSpread(range) { return range * (0.5 - Math.random()); } // Deterministic pseudo-random float in the interval [ 0, 1 ] function seededRandom(s) { if (s !== undefined) _seed = s; // Mulberry32 generator let t = _seed += 0x6D2B79F5; t = Math.imul(t ^ t >>> 15, t | 1); t ^= t + Math.imul(t ^ t >>> 7, t | 61); return ((t ^ t >>> 14) >>> 0) / 4294967296; } function degToRad(degrees) { return degrees * DEG2RAD; } function radToDeg(radians) { return radians * RAD2DEG; } function isPowerOfTwo(value) { return (value & value - 1) === 0 && value !== 0; } function ceilPowerOfTwo(value) { return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2)); } function floorPowerOfTwo(value) { return Math.pow(2, Math.floor(Math.log(value) / Math.LN2)); } function setQuaternionFromProperEuler(q, a, b, c, order) { // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles // rotations are applied to the axes in the order specified by 'order' // rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c' // angles are in radians const cos = Math.cos; const sin = Math.sin; const c2 = cos(b / 2); const s2 = sin(b / 2); const c13 = cos((a + c) / 2); const s13 = sin((a + c) / 2); const c1_3 = cos((a - c) / 2); const s1_3 = sin((a - c) / 2); const c3_1 = cos((c - a) / 2); const s3_1 = sin((c - a) / 2); switch (order) { case 'XYX': q.set(c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13); break; case 'YZY': q.set(s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13); break; case 'ZXZ': q.set(s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13); break; case 'XZX': q.set(c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13); break; case 'YXY': q.set(s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13); break; case 'ZYZ': q.set(s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13); break; default: console.warn('THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order); } } function denormalize(value, array) { switch (array.constructor) { case Float32Array: return value; case Uint32Array: return value / 4294967295.0; case Uint16Array: return value / 65535.0; case Uint8Array: return value / 255.0; case Int32Array: return Math.max(value / 2147483647.0, -1.0); case Int16Array: return Math.max(value / 32767.0, -1.0); case Int8Array: return Math.max(value / 127.0, -1.0); default: throw new Error('Invalid component type.'); } } function normalize(value, array) { switch (array.constructor) { case Float32Array: return value; case Uint32Array: return Math.round(value * 4294967295.0); case Uint16Array: return Math.round(value * 65535.0); case Uint8Array: return Math.round(value * 255.0); case Int32Array: return Math.round(value * 2147483647.0); case Int16Array: return Math.round(value * 32767.0); case Int8Array: return Math.round(value * 127.0); default: throw new Error('Invalid component type.'); } } const MathUtils = { DEG2RAD: DEG2RAD, RAD2DEG: RAD2DEG, generateUUID: generateUUID, clamp: clamp, euclideanModulo: euclideanModulo, mapLinear: mapLinear, inverseLerp: inverseLerp, lerp: lerp, damp: damp, pingpong: pingpong, smoothstep: smoothstep, smootherstep: smootherstep, randInt: randInt, randFloat: randFloat, randFloatSpread: randFloatSpread, seededRandom: seededRandom, degToRad: degToRad, radToDeg: radToDeg, isPowerOfTwo: isPowerOfTwo, ceilPowerOfTwo: ceilPowerOfTwo, floorPowerOfTwo: floorPowerOfTwo, setQuaternionFromProperEuler: setQuaternionFromProperEuler, normalize: normalize, denormalize: denormalize }; class Vector2 { constructor(x = 0, y = 0) { Vector2.prototype.isVector2 = true; this.x = x; this.y = y; } get width() { return this.x; } set width(value) { this.x = value; } get height() { return this.y; } set height(value) { this.y = value; } set(x, y) { this.x = x; this.y = y; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error('index is out of range: ' + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; default: throw new Error('index is out of range: ' + index); } } clone() { return new this.constructor(this.x, this.y); } copy(v) { this.x = v.x; this.y = v.y; return this; } add(v) { this.x += v.x; this.y += v.y; return this; } addScalar(s) { this.x += s; this.y += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; return this; } sub(v) { this.x -= v.x; this.y -= v.y; return this; } subScalar(s) { this.x -= s; this.y -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; return this; } divide(v) { this.x /= v.x; this.y /= v.y; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } applyMatrix3(m) { const x = this.x, y = this.y; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6]; this.y = e[1] * x + e[4] * y + e[7]; return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); return this; } negate() { this.x = -this.x; this.y = -this.y; return this; } dot(v) { return this.x * v.x + this.y * v.y; } cross(v) { return this.x * v.y - this.y * v.x; } lengthSq() { return this.x * this.x + this.y * this.y; } length() { return Math.sqrt(this.x * this.x + this.y * this.y); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y); } normalize() { return this.divideScalar(this.length() || 1); } angle() { // computes the angle in radians with respect to the positive x-axis const angle = Math.atan2(-this.y, -this.x) + Math.PI; return angle; } angleTo(v) { const denominator = Math.sqrt(this.lengthSq() * v.lengthSq()); if (denominator === 0) return Math.PI / 2; const theta = this.dot(v) / denominator; // clamp, to handle numerical problems return Math.acos(clamp(theta, -1, 1)); } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; return array; } fromBufferAttribute(attribute, index) { this.x = attribute.getX(index); this.y = attribute.getY(index); return this; } rotateAround(center, angle) { const c = Math.cos(angle), s = Math.sin(angle); const x = this.x - center.x; const y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } random() { this.x = Math.random(); this.y = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; } } class Matrix3 { constructor(n11, n12, n13, n21, n22, n23, n31, n32, n33) { Matrix3.prototype.isMatrix3 = true; this.elements = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (n11 !== undefined) { this.set(n11, n12, n13, n21, n22, n23, n31, n32, n33); } } set(n11, n12, n13, n21, n22, n23, n31, n32, n33) { const te = this.elements; te[0] = n11; te[1] = n21; te[2] = n31; te[3] = n12; te[4] = n22; te[5] = n32; te[6] = n13; te[7] = n23; te[8] = n33; return this; } identity() { this.set(1, 0, 0, 0, 1, 0, 0, 0, 1); return this; } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrix3Column(this, 0); yAxis.setFromMatrix3Column(this, 1); zAxis.setFromMatrix3Column(this, 2); return this; } setFromMatrix4(m) { const me = m.elements; this.set(me[0], me[4], me[8], me[1], me[5], me[9], me[2], me[6], me[10]); return this; } multiply(m) { return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[3], a13 = ae[6]; const a21 = ae[1], a22 = ae[4], a23 = ae[7]; const a31 = ae[2], a32 = ae[5], a33 = ae[8]; const b11 = be[0], b12 = be[3], b13 = be[6]; const b21 = be[1], b22 = be[4], b23 = be[7]; const b31 = be[2], b32 = be[5], b33 = be[8]; te[0] = a11 * b11 + a12 * b21 + a13 * b31; te[3] = a11 * b12 + a12 * b22 + a13 * b32; te[6] = a11 * b13 + a12 * b23 + a13 * b33; te[1] = a21 * b11 + a22 * b21 + a23 * b31; te[4] = a21 * b12 + a22 * b22 + a23 * b32; te[7] = a21 * b13 + a22 * b23 + a23 * b33; te[2] = a31 * b11 + a32 * b21 + a33 * b31; te[5] = a31 * b12 + a32 * b22 + a33 * b32; te[8] = a31 * b13 + a32 * b23 + a33 * b33; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[3] *= s; te[6] *= s; te[1] *= s; te[4] *= s; te[7] *= s; te[2] *= s; te[5] *= s; te[8] *= s; return this; } determinant() { const te = this.elements; const a = te[0], b = te[1], c = te[2], d = te[3], e = te[4], f = te[5], g = te[6], h = te[7], i = te[8]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; } invert() { const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n12 = te[3], n22 = te[4], n32 = te[5], n13 = te[6], n23 = te[7], n33 = te[8], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n31 * n23 - n33 * n21) * detInv; te[2] = (n32 * n21 - n31 * n22) * detInv; te[3] = t12 * detInv; te[4] = (n33 * n11 - n31 * n13) * detInv; te[5] = (n31 * n12 - n32 * n11) * detInv; te[6] = t13 * detInv; te[7] = (n21 * n13 - n23 * n11) * detInv; te[8] = (n22 * n11 - n21 * n12) * detInv; return this; } transpose() { let tmp; const m = this.elements; tmp = m[1]; m[1] = m[3]; m[3] = tmp; tmp = m[2]; m[2] = m[6]; m[6] = tmp; tmp = m[5]; m[5] = m[7]; m[7] = tmp; return this; } getNormalMatrix(matrix4) { return this.setFromMatrix4(matrix4).invert().transpose(); } transposeIntoArray(r) { const m = this.elements; r[0] = m[0]; r[1] = m[3]; r[2] = m[6]; r[3] = m[1]; r[4] = m[4]; r[5] = m[7]; r[6] = m[2]; r[7] = m[5]; r[8] = m[8]; return this; } setUvTransform(tx, ty, sx, sy, rotation, cx, cy) { const c = Math.cos(rotation); const s = Math.sin(rotation); this.set(sx * c, sx * s, -sx * (c * cx + s * cy) + cx + tx, -sy * s, sy * c, -sy * (-s * cx + c * cy) + cy + ty, 0, 0, 1); return this; } // scale(sx, sy) { this.premultiply(_m3.makeScale(sx, sy)); return this; } rotate(theta) { this.premultiply(_m3.makeRotation(-theta)); return this; } translate(tx, ty) { this.premultiply(_m3.makeTranslation(tx, ty)); return this; } // for 2D Transforms makeTranslation(x, y) { if (x.isVector2) { this.set(1, 0, x.x, 0, 1, x.y, 0, 0, 1); } else { this.set(1, 0, x, 0, 1, y, 0, 0, 1); } return this; } makeRotation(theta) { // counterclockwise const c = Math.cos(theta); const s = Math.sin(theta); this.set(c, -s, 0, s, c, 0, 0, 0, 1); return this; } makeScale(x, y) { this.set(x, 0, 0, 0, y, 0, 0, 0, 1); return this; } // equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 9; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 9; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; return array; } clone() { return new this.constructor().fromArray(this.elements); } } const _m3 = /*@__PURE__*/new Matrix3(); function arrayNeedsUint32(array) { // assumes larger values usually on last for (let i = array.length - 1; i >= 0; --i) { if (array[i] >= 65535) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565 } return false; } const TYPED_ARRAYS = { Int8Array: Int8Array, Uint8Array: Uint8Array, Uint8ClampedArray: Uint8ClampedArray, Int16Array: Int16Array, Uint16Array: Uint16Array, Int32Array: Int32Array, Uint32Array: Uint32Array, Float32Array: Float32Array, Float64Array: Float64Array }; function getTypedArray(type, buffer) { return new TYPED_ARRAYS[type](buffer); } function createElementNS(name) { return document.createElementNS('http://www.w3.org/1999/xhtml', name); } const _cache = {}; function warnOnce(message) { if (message in _cache) return; _cache[message] = true; console.warn(message); } function SRGBToLinear(c) { return c < 0.04045 ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4); } function LinearToSRGB(c) { return c < 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 0.41666) - 0.055; } /** * Matrices converting P3 <-> Rec. 709 primaries, without gamut mapping * or clipping. Based on W3C specifications for sRGB and Display P3, * and ICC specifications for the D50 connection space. Values in/out * are _linear_ sRGB and _linear_ Display P3. * * Note that both sRGB and Display P3 use the sRGB transfer functions. * * Reference: * - http://www.russellcottrell.com/photo/matrixCalculator.htm */ const LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = /*@__PURE__*/new Matrix3().fromArray([0.8224621, 0.0331941, 0.0170827, 0.1775380, 0.9668058, 0.0723974, -0.0000001, 0.0000001, 0.9105199]); const LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = /*@__PURE__*/new Matrix3().fromArray([1.2249401, -0.0420569, -0.0196376, -0.2249404, 1.0420571, -0.0786361, 0.0000001, 0.0000000, 1.0982735]); function DisplayP3ToLinearSRGB(color) { // Display P3 uses the sRGB transfer functions return color.convertSRGBToLinear().applyMatrix3(LINEAR_DISPLAY_P3_TO_LINEAR_SRGB); } function LinearSRGBToDisplayP3(color) { // Display P3 uses the sRGB transfer functions return color.applyMatrix3(LINEAR_SRGB_TO_LINEAR_DISPLAY_P3).convertLinearToSRGB(); } // Conversions from <source> to Linear-sRGB reference space. const TO_LINEAR = { [LinearSRGBColorSpace]: color => color, [SRGBColorSpace]: color => color.convertSRGBToLinear(), [DisplayP3ColorSpace]: DisplayP3ToLinearSRGB }; // Conversions to <target> from Linear-sRGB reference space. const FROM_LINEAR = { [LinearSRGBColorSpace]: color => color, [SRGBColorSpace]: color => color.convertLinearToSRGB(), [DisplayP3ColorSpace]: LinearSRGBToDisplayP3 }; const ColorManagement = { enabled: true, get legacyMode() { console.warn('THREE.ColorManagement: .legacyMode=false renamed to .enabled=true in r150.'); return !this.enabled; }, set legacyMode(legacyMode) { console.warn('THREE.ColorManagement: .legacyMode=false renamed to .enabled=true in r150.'); this.enabled = !legacyMode; }, get workingColorSpace() { return LinearSRGBColorSpace; }, set workingColorSpace(colorSpace) { console.warn('THREE.ColorManagement: .workingColorSpace is readonly.'); }, convert: function (color, sourceColorSpace, targetColorSpace) { if (this.enabled === false || sourceColorSpace === targetColorSpace || !sourceColorSpace || !targetColorSpace) { return color; } const sourceToLinear = TO_LINEAR[sourceColorSpace]; const targetFromLinear = FROM_LINEAR[targetColorSpace]; if (sourceToLinear === undefined || targetFromLinear === undefined) { throw new Error(`Unsupported color space conversion, "${sourceColorSpace}" to "${targetColorSpace}".`); } return targetFromLinear(sourceToLinear(color)); }, fromWorkingColorSpace: function (color, targetColorSpace) { return this.convert(color, this.workingColorSpace, targetColorSpace); }, toWorkingColorSpace: function (color, sourceColorSpace) { return this.convert(color, sourceColorSpace, this.workingColorSpace); } }; let _canvas; class ImageUtils { static getDataURL(image) { if (/^data:/i.test(image.src)) { return image.src; } if (typeof HTMLCanvasElement === 'undefined') { return image.src; } let canvas; if (image instanceof HTMLCanvasElement) { canvas = image; } else { if (_canvas === undefined) _canvas = createElementNS('canvas'); _canvas.width = image.width; _canvas.height = image.height; const context = _canvas.getContext('2d'); if (image instanceof ImageData) { context.putImageData(image, 0, 0); } else { context.drawImage(image, 0, 0, image.width, image.height); } canvas = _canvas; } if (canvas.width > 2048 || canvas.height > 2048) { console.warn('THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons', image); return canvas.toDataURL('image/jpeg', 0.6); } else { return canvas.toDataURL('image/png'); } } static sRGBToLinear(image) { if (typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement || typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap) { const canvas = createElementNS('canvas'); canvas.width = image.width; canvas.height = image.height; const context = canvas.getContext('2d'); context.drawImage(image, 0, 0, image.width, image.height); const imageData = context.getImageData(0, 0, image.width, image.height); const data = imageData.data; for (let i = 0; i < data.length; i++) { data[i] = SRGBToLinear(data[i] / 255) * 255; } context.putImageData(imageData, 0, 0); return canvas; } else if (image.data) { const data = image.data.slice(0); for (let i = 0; i < data.length; i++) { if (data instanceof Uint8Array || data instanceof Uint8ClampedArray) { data[i] = Math.floor(SRGBToLinear(data[i] / 255) * 255); } else { // assuming float data[i] = SRGBToLinear(data[i]); } } return { data: data, width: image.width, height: image.height }; } else { console.warn('THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.'); return image; } } } let sourceId = 0; class Source { constructor(data = null) { this.isSource = true; Object.defineProperty(this, 'id', { value: sourceId++ }); this.uuid = generateUUID(); this.data = data; this.version = 0; } set needsUpdate(value) { if (value === true) this.version++; } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === 'string'; if (!isRootObject && meta.images[this.uuid] !== undefined) { return meta.images[this.uuid]; } const output = { uuid: this.uuid, url: '' }; const data = this.data; if (data !== null) { let url; if (Array.isArray(data)) { // cube texture url = []; for (let i = 0, l = data.length; i < l; i++) { if (data[i].isDataTexture) { url.push(serializeImage(data[i].image)); } else { url.push(serializeImage(data[i])); } } } else { // texture url = serializeImage(data); } output.url = url; } if (!isRootObject) { meta.images[this.uuid] = output; } return output; } } function serializeImage(image) { if (typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement || typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap) { // default images return ImageUtils.getDataURL(image); } else { if (image.data) { // images of DataTexture return { data: Array.from(image.data), width: image.width, height: image.height, type: image.data.constructor.name }; } else { console.warn('THREE.Texture: Unable to serialize Texture.'); return {}; } } } let textureId = 0; class Texture extends EventDispatcher { constructor(image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace) { super(); this.isTexture = true; Object.defineProperty(this, 'id', { value: textureId++ }); this.uuid = generateUUID(); this.name = ''; this.source = new Source(image); this.mipmaps = []; this.mapping = mapping; this.channel = 0; this.wrapS = wrapS; this.wrapT = wrapT; this.magFilter = magFilter; this.minFilter = minFilter; this.anisotropy = anisotropy; this.format = format; this.internalFormat = null; this.type = type; this.offset = new Vector2(0, 0); this.repeat = new Vector2(1, 1); this.center = new Vector2(0, 0); this.rotation = 0; this.matrixAutoUpdate = true; this.matrix = new Matrix3(); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) if (typeof colorSpace === 'string') { this.colorSpace = colorSpace; } else { // @deprecated, r152 warnOnce('THREE.Texture: Property .encoding has been replaced by .colorSpace.'); this.colorSpace = colorSpace === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } this.userData = {}; this.version = 0; this.onUpdate = null; this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) } get image() { return this.source.data; } set image(value = null) { this.source.data = value; } updateMatrix() { this.matrix.setUvTransform(this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y); } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.source = source.source; this.mipmaps = source.mipmaps.slice(0); this.mapping = source.mapping; this.channel = source.channel; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.internalFormat = source.internalFormat; this.type = source.type; this.offset.copy(source.offset); this.repeat.copy(source.repeat); this.center.copy(source.center); this.rotation = source.rotation; this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrix.copy(source.matrix); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.colorSpace = source.colorSpace; this.userData = JSON.parse(JSON.stringify(source.userData)); this.needsUpdate = true; return this; } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === 'string'; if (!isRootObject && meta.textures[this.uuid] !== undefined) { return meta.textures[this.uuid]; } const output = { metadata: { version: 4.6, type: 'Texture', generator: 'Texture.toJSON' }, uuid: this.uuid, name: this.name, image: this.source.toJSON(meta).uuid, mapping: this.mapping, channel: this.channel, repeat: [this.repeat.x, this.repeat.y], offset: [this.offset.x, this.offset.y], center: [this.center.x, this.center.y], rotation: this.rotation, wrap: [this.wrapS, this.wrapT], format: this.format, internalFormat: this.internalFormat, type: this.type, colorSpace: this.colorSpace, minFilter: this.minFilter, magFilter: this.magFilter, anisotropy: this.anisotropy, flipY: this.flipY, generateMipmaps: this.generateMipmaps, premultiplyAlpha: this.premultiplyAlpha, unpackAlignment: this.unpackAlignment }; if (Object.keys(this.userData).length > 0) output.userData = this.userData; if (!isRootObject) { meta.textures[this.uuid] = output; } return output; } dispose() { this.dispatchEvent({ type: 'dispose' }); } transformUv(uv) { if (this.mapping !== UVMapping) return uv; uv.applyMatrix3(this.matrix); if (uv.x < 0 || uv.x > 1) { switch (this.wrapS) { case RepeatWrapping: uv.x = uv.x - Math.floor(uv.x); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.x) % 2) === 1) { uv.x = Math.ceil(uv.x) - uv.x; } else { uv.x = uv.x - Math.floor(uv.x); } break; } } if (uv.y < 0 || uv.y > 1) { switch (this.wrapT) { case RepeatWrapping: uv.y = uv.y - Math.floor(uv.y); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if (Math.abs(Math.floor(uv.y) % 2) === 1) { uv.y = Math.ceil(uv.y) - uv.y; } else { uv.y = uv.y - Math.floor(uv.y); } break; } } if (this.flipY) { uv.y = 1 - uv.y; } return uv; } set needsUpdate(value) { if (value === true) { this.version++; this.source.needsUpdate = true; } } get encoding() { // @deprecated, r152 warnOnce('THREE.Texture: Property .encoding has been replaced by .colorSpace.'); return this.colorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding; } set encoding(encoding) { // @deprecated, r152 warnOnce('THREE.Texture: Property .encoding has been replaced by .colorSpace.'); this.colorSpace = encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } } Texture.DEFAULT_IMAGE = null; Texture.DEFAULT_MAPPING = UVMapping; Texture.DEFAULT_ANISOTROPY = 1; class Vector4 { constructor(x = 0, y = 0, z = 0, w = 1) { Vector4.prototype.isVector4 = true; this.x = x; this.y = y; this.z = z; this.w = w; } get width() { return this.z; } set width(value) { this.z = value; } get height() { return this.w; } set height(value) { this.w = value; } set(x, y, z, w) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setW(w) { this.w = w; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error('index is out of range: ' + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error('index is out of range: ' + index); } } clone() { return new this.constructor(this.x, this.y, this.z, this.w); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = v.w !== undefined ? v.w : 1; return this; } add(v) { this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; this.w += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; } sub(v) { this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; this.z *= v.z; this.w *= v.w; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; return this; } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z, w = this.w; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z + e[12] * w; this.y = e[1] * x + e[5] * y + e[9] * z + e[13] * w; this.z = e[2] * x + e[6] * y + e[10] * z + e[14] * w; this.w = e[3] * x + e[7] * y + e[11] * z + e[15] * w; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } setAxisAngleFromQuaternion(q) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos(q.w); const s = Math.sqrt(1 - q.w * q.w); if (s < 0.0001) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; } setAxisAngleFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let angle, x, y, z; // variables for result const epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10]; if (Math.abs(m12 - m21) < epsilon && Math.abs(m13 - m31) < epsilon && Math.abs(m23 - m32) < epsilon) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if (Math.abs(m12 + m21) < epsilon2 && Math.abs(m13 + m31) < epsilon2 && Math.abs(m23 + m32) < epsilon2 && Math.abs(m11 + m22 + m33 - 3) < epsilon2) { // this singularity is identity matrix so angle = 0 this.set(1, 0, 0, 0); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; const xx = (m11 + 1) / 2; const yy = (m22 + 1) / 2; const zz = (m33 + 1) / 2; const xy = (m12 + m21) / 4; const xz = (m13 + m31) / 4; const yz = (m23 + m32) / 4; if (xx > yy && xx > zz) { // m11 is the largest diagonal term if (xx < epsilon) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt(xx); y = xy / x; z = xz / x; } } else if (yy > zz) { // m22 is the largest diagonal term if (yy < epsilon) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt(yy); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if (zz < epsilon) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt(zz); x = xz / z; y = yz / z; } } this.set(x, y, z, angle); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally let s = Math.sqrt((m32 - m23) * (m32 - m23) + (m13 - m31) * (m13 - m31) + (m21 - m12) * (m21 - m12)); // used to normalize if (Math.abs(s) < 0.001) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I've left it in just in case this.x = (m32 - m23) / s; this.y = (m13 - m31) / s; this.z = (m21 - m12) / s; this.w = Math.acos((m11 + m22 + m33 - 1) / 2); return this; } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); this.w = Math.min(this.w, v.w); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); this.w = Math.max(this.w, v.w); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); this.w = Math.max(min.w, Math.min(max.w, this.w)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); this.w = Math.max(minVal, Math.min(maxVal, this.w)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); this.w = Math.floor(this.w); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); this.w = Math.ceil(this.w); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); this.w = Math.round(this.w); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); this.w = this.w < 0 ? Math.ceil(this.w) : Math.floor(this.w); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; this.w = -this.w; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; } lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z) + Math.abs(this.w); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; this.w += (v.w - this.w) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; this.w = v1.w + (v2.w - v1.w) * alpha; return this; } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z && v.w === this.w; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; this.w = array[offset + 3]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; array[offset + 3] = this.w; return array; } fromBufferAttribute(attribute, index) { this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); this.w = attribute.getW(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); this.w = Math.random(); return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; yield this.w; } } /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ class WebGLRenderTarget extends EventDispatcher { constructor(width = 1, height = 1, options = {}) { super(); this.isWebGLRenderTarget = true; this.width = width; this.height = height; this.depth = 1; this.scissor = new Vector4(0, 0, width, height); this.scissorTest = false; this.viewport = new Vector4(0, 0, width, height); const image = { width: width, height: height, depth: 1 }; if (options.encoding !== undefined) { // @deprecated, r152 warnOnce('THREE.WebGLRenderTarget: option.encoding has been replaced by option.colorSpace.'); options.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } this.texture = new Texture(image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace); this.texture.isRenderTargetTexture = true; this.texture.flipY = false; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.internalFormat = options.internalFormat !== undefined ? options.internalFormat : null; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; this.samples = options.samples !== undefined ? options.samples : 0; } setSize(width, height, depth = 1) { if (this.width !== width || this.height !== height || this.depth !== depth) { this.width = width; this.height = height; this.depth = depth; this.texture.image.width = width; this.texture.image.height = height; this.texture.image.depth = depth; this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); } clone() { return new this.constructor().copy(this); } copy(source) { this.width = source.width; this.height = source.height; this.depth = source.depth; this.scissor.copy(source.scissor); this.scissorTest = source.scissorTest; this.viewport.copy(source.viewport); this.texture = source.texture.clone(); this.texture.isRenderTargetTexture = true; // ensure image object is not shared, see #20328 const image = Object.assign({}, source.texture.image); this.texture.source = new Source(image); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; if (source.depthTexture !== null) this.depthTexture = source.depthTexture.clone(); this.samples = source.samples; return this; } dispose() { this.dispatchEvent({ type: 'dispose' }); } } class DataArrayTexture extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { super(null); this.isDataArrayTexture = true; this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } class WebGLArrayRenderTarget extends WebGLRenderTarget { constructor(width = 1, height = 1, depth = 1) { super(width, height); this.isWebGLArrayRenderTarget = true; this.depth = depth; this.texture = new DataArrayTexture(null, width, height, depth); this.texture.isRenderTargetTexture = true; } } class Data3DTexture extends Texture { constructor(data = null, width = 1, height = 1, depth = 1) { // We're going to add .setXXX() methods for setting properties later. // Users can still set in DataTexture3D directly. // // const texture = new THREE.DataTexture3D( data, width, height, depth ); // texture.anisotropy = 16; // // See #14839 super(null); this.isData3DTexture = true; this.image = { data, width, height, depth }; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.wrapR = ClampToEdgeWrapping; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } class WebGL3DRenderTarget extends WebGLRenderTarget { constructor(width = 1, height = 1, depth = 1) { super(width, height); this.isWebGL3DRenderTarget = true; this.depth = depth; this.texture = new Data3DTexture(null, width, height, depth); this.texture.isRenderTargetTexture = true; } } class WebGLMultipleRenderTargets extends WebGLRenderTarget { constructor(width = 1, height = 1, count = 1, options = {}) { super(width, height, options); this.isWebGLMultipleRenderTargets = true; const texture = this.texture; this.texture = []; for (let i = 0; i < count; i++) { this.texture[i] = texture.clone(); this.texture[i].isRenderTargetTexture = true; } } setSize(width, height, depth = 1) { if (this.width !== width || this.height !== height || this.depth !== depth) { this.width = width; this.height = height; this.depth = depth; for (let i = 0, il = this.texture.length; i < il; i++) { this.texture[i].image.width = width; this.texture[i].image.height = height; this.texture[i].image.depth = depth; } this.dispose(); } this.viewport.set(0, 0, width, height); this.scissor.set(0, 0, width, height); return this; } copy(source) { this.dispose(); this.width = source.width; this.height = source.height; this.depth = source.depth; this.scissor.copy(source.scissor); this.scissorTest = source.scissorTest; this.viewport.copy(source.viewport); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; if (source.depthTexture !== null) this.depthTexture = source.depthTexture.clone(); this.texture.length = 0; for (let i = 0, il = source.texture.length; i < il; i++) { this.texture[i] = source.texture[i].clone(); this.texture[i].isRenderTargetTexture = true; } return this; } } class Quaternion { constructor(x = 0, y = 0, z = 0, w = 1) { this.isQuaternion = true; this._x = x; this._y = y; this._z = z; this._w = w; } static slerpFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t) { // fuzz-free, array-based Quaternion SLERP operation let x0 = src0[srcOffset0 + 0], y0 = src0[srcOffset0 + 1], z0 = src0[srcOffset0 + 2], w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1 + 0], y1 = src1[srcOffset1 + 1], z1 = src1[srcOffset1 + 2], w1 = src1[srcOffset1 + 3]; if (t === 0) { dst[dstOffset + 0] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; return; } if (t === 1) { dst[dstOffset + 0] = x1; dst[dstOffset + 1] = y1; dst[dstOffset + 2] = z1; dst[dstOffset + 3] = w1; return; } if (w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1) { let s = 1 - t; const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = cos >= 0 ? 1 : -1, sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if (sqrSin > Number.EPSILON) { const sin = Math.sqrt(sqrSin), len = Math.atan2(sin, cos * dir); s = Math.sin(s * len) / sin; t = Math.sin(t * len) / sin; } const tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if (s === 1 - t) { const f = 1 / Math.sqrt(x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[dstOffset] = x0; dst[dstOffset + 1] = y0; dst[dstOffset + 2] = z0; dst[dstOffset + 3] = w0; } static multiplyQuaternionsFlat(dst, dstOffset, src0, srcOffset0, src1, srcOffset1) { const x0 = src0[srcOffset0]; const y0 = src0[srcOffset0 + 1]; const z0 = src0[srcOffset0 + 2]; const w0 = src0[srcOffset0 + 3]; const x1 = src1[srcOffset1]; const y1 = src1[srcOffset1 + 1]; const z1 = src1[srcOffset1 + 2]; const w1 = src1[srcOffset1 + 3]; dst[dstOffset] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; dst[dstOffset + 1] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; dst[dstOffset + 2] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; dst[dstOffset + 3] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; return dst; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get w() { return this._w; } set w(value) { this._w = value; this._onChangeCallback(); } set(x, y, z, w) { this._x = x; this._y = y; this._z = z; this._w = w; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._w); } copy(quaternion) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this._onChangeCallback(); return this; } setFromEuler(euler, update) { const x = euler._x, y = euler._y, z = euler._z, order = euler._order; // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m const cos = Math.cos; const sin = Math.sin; const c1 = cos(x / 2); const c2 = cos(y / 2); const c3 = cos(z / 2); const s1 = sin(x / 2); const s2 = sin(y / 2); const s3 = sin(z / 2); switch (order) { case 'XYZ': this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case 'YXZ': this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case 'ZXY': this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case 'ZYX': this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; case 'YZX': this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; break; case 'XZY': this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; break; default: console.warn('THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order); } if (update !== false) this._onChangeCallback(); return this; } setFromAxisAngle(axis, angle) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized const halfAngle = angle / 2, s = Math.sin(halfAngle); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos(halfAngle); this._onChangeCallback(); return this; } setFromRotationMatrix(m) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements, m11 = te[0], m12 = te[4], m13 = te[8], m21 = te[1], m22 = te[5], m23 = te[9], m31 = te[2], m32 = te[6], m33 = te[10], trace = m11 + m22 + m33; if (trace > 0) { const s = 0.5 / Math.sqrt(trace + 1.0); this._w = 0.25 / s; this._x = (m32 - m23) * s; this._y = (m13 - m31) * s; this._z = (m21 - m12) * s; } else if (m11 > m22 && m11 > m33) { const s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33); this._w = (m32 - m23) / s; this._x = 0.25 * s; this._y = (m12 + m21) / s; this._z = (m13 + m31) / s; } else if (m22 > m33) { const s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33); this._w = (m13 - m31) / s; this._x = (m12 + m21) / s; this._y = 0.25 * s; this._z = (m23 + m32) / s; } else { const s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22); this._w = (m21 - m12) / s; this._x = (m13 + m31) / s; this._y = (m23 + m32) / s; this._z = 0.25 * s; } this._onChangeCallback(); return this; } setFromUnitVectors(vFrom, vTo) { // assumes direction vectors vFrom and vTo are normalized let r = vFrom.dot(vTo) + 1; if (r < Number.EPSILON) { // vFrom and vTo point in opposite directions r = 0; if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) { this._x = -vFrom.y; this._y = vFrom.x; this._z = 0; this._w = r; } else { this._x = 0; this._y = -vFrom.z; this._z = vFrom.y; this._w = r; } } else { // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; this._w = r; } return this.normalize(); } angleTo(q) { return 2 * Math.acos(Math.abs(clamp(this.dot(q), -1, 1))); } rotateTowards(q, step) { const angle = this.angleTo(q); if (angle === 0) return this; const t = Math.min(1, step / angle); this.slerp(q, t); return this; } identity() { return this.set(0, 0, 0, 1); } invert() { // quaternion is assumed to have unit length return this.conjugate(); } conjugate() { this._x *= -1; this._y *= -1; this._z *= -1; this._onChangeCallback(); return this; } dot(v) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; } lengthSq() { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; } length() { return Math.sqrt(this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w); } normalize() { let l = this.length(); if (l === 0) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this._onChangeCallback(); return this; } multiply(q) { return this.multiplyQuaternions(this, q); } premultiply(q) { return this.multiplyQuaternions(q, this); } multiplyQuaternions(a, b) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this._onChangeCallback(); return this; } slerp(qb, t) { if (t === 0) return this; if (t === 1) return this.copy(qb); const x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if (cosHalfTheta < 0) { this._w = -qb._w; this._x = -qb._x; this._y = -qb._y; this._z = -qb._z; cosHalfTheta = -cosHalfTheta; } else { this.copy(qb); } if (cosHalfTheta >= 1.0) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; if (sqrSinHalfTheta <= Number.EPSILON) { const s = 1 - t; this._w = s * w + t * this._w; this._x = s * x + t * this._x; this._y = s * y + t * this._y; this._z = s * z + t * this._z; this.normalize(); this._onChangeCallback(); return this; } const sinHalfTheta = Math.sqrt(sqrSinHalfTheta); const halfTheta = Math.atan2(sinHalfTheta, cosHalfTheta); const ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta, ratioB = Math.sin(t * halfTheta) / sinHalfTheta; this._w = w * ratioA + this._w * ratioB; this._x = x * ratioA + this._x * ratioB; this._y = y * ratioA + this._y * ratioB; this._z = z * ratioA + this._z * ratioB; this._onChangeCallback(); return this; } slerpQuaternions(qa, qb, t) { return this.copy(qa).slerp(qb, t); } random() { // Derived from http://planning.cs.uiuc.edu/node198.html // Note, this source uses w, x, y, z ordering, // so we swap the order below. const u1 = Math.random(); const sqrt1u1 = Math.sqrt(1 - u1); const sqrtu1 = Math.sqrt(u1); const u2 = 2 * Math.PI * Math.random(); const u3 = 2 * Math.PI * Math.random(); return this.set(sqrt1u1 * Math.cos(u2), sqrtu1 * Math.sin(u3), sqrtu1 * Math.cos(u3), sqrt1u1 * Math.sin(u2)); } equals(quaternion) { return quaternion._x === this._x && quaternion._y === this._y && quaternion._z === this._z && quaternion._w === this._w; } fromArray(array, offset = 0) { this._x = array[offset]; this._y = array[offset + 1]; this._z = array[offset + 2]; this._w = array[offset + 3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._w; return array; } fromBufferAttribute(attribute, index) { this._x = attribute.getX(index); this._y = attribute.getY(index); this._z = attribute.getZ(index); this._w = attribute.getW(index); return this; } toJSON() { return this.toArray(); } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} *[Symbol.iterator]() { yield this._x; yield this._y; yield this._z; yield this._w; } } class Vector3 { constructor(x = 0, y = 0, z = 0) { Vector3.prototype.isVector3 = true; this.x = x; this.y = y; this.z = z; } set(x, y, z) { if (z === undefined) z = this.z; // sprite.scale.set(x,y) this.x = x; this.y = y; this.z = z; return this; } setScalar(scalar) { this.x = scalar; this.y = scalar; this.z = scalar; return this; } setX(x) { this.x = x; return this; } setY(y) { this.y = y; return this; } setZ(z) { this.z = z; return this; } setComponent(index, value) { switch (index) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error('index is out of range: ' + index); } return this; } getComponent(index) { switch (index) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error('index is out of range: ' + index); } } clone() { return new this.constructor(this.x, this.y, this.z); } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; return this; } add(v) { this.x += v.x; this.y += v.y; this.z += v.z; return this; } addScalar(s) { this.x += s; this.y += s; this.z += s; return this; } addVectors(a, b) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; } addScaledVector(v, s) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; } sub(v) { this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; } subScalar(s) { this.x -= s; this.y -= s; this.z -= s; return this; } subVectors(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; } multiply(v) { this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; } multiplyScalar(scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this; } multiplyVectors(a, b) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; } applyEuler(euler) { return this.applyQuaternion(_quaternion$4.setFromEuler(euler)); } applyAxisAngle(axis, angle) { return this.applyQuaternion(_quaternion$4.setFromAxisAngle(axis, angle)); } applyMatrix3(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[3] * y + e[6] * z; this.y = e[1] * x + e[4] * y + e[7] * z; this.z = e[2] * x + e[5] * y + e[8] * z; return this; } applyNormalMatrix(m) { return this.applyMatrix3(m).normalize(); } applyMatrix4(m) { const x = this.x, y = this.y, z = this.z; const e = m.elements; const w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]); this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w; this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w; this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w; return this; } applyQuaternion(q) { const x = this.x, y = this.y, z = this.z; const qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector const ix = qw * x + qy * z - qz * y; const iy = qw * y + qz * x - qx * z; const iz = qw * z + qx * y - qy * x; const iw = -qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; return this; } project(camera) { return this.applyMatrix4(camera.matrixWorldInverse).applyMatrix4(camera.projectionMatrix); } unproject(camera) { return this.applyMatrix4(camera.projectionMatrixInverse).applyMatrix4(camera.matrixWorld); } transformDirection(m) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction const x = this.x, y = this.y, z = this.z; const e = m.elements; this.x = e[0] * x + e[4] * y + e[8] * z; this.y = e[1] * x + e[5] * y + e[9] * z; this.z = e[2] * x + e[6] * y + e[10] * z; return this.normalize(); } divide(v) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; } divideScalar(scalar) { return this.multiplyScalar(1 / scalar); } min(v) { this.x = Math.min(this.x, v.x); this.y = Math.min(this.y, v.y); this.z = Math.min(this.z, v.z); return this; } max(v) { this.x = Math.max(this.x, v.x); this.y = Math.max(this.y, v.y); this.z = Math.max(this.z, v.z); return this; } clamp(min, max) { // assumes min < max, componentwise this.x = Math.max(min.x, Math.min(max.x, this.x)); this.y = Math.max(min.y, Math.min(max.y, this.y)); this.z = Math.max(min.z, Math.min(max.z, this.z)); return this; } clampScalar(minVal, maxVal) { this.x = Math.max(minVal, Math.min(maxVal, this.x)); this.y = Math.max(minVal, Math.min(maxVal, this.y)); this.z = Math.max(minVal, Math.min(maxVal, this.z)); return this; } clampLength(min, max) { const length = this.length(); return this.divideScalar(length || 1).multiplyScalar(Math.max(min, Math.min(max, length))); } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); this.z = Math.floor(this.z); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.z = Math.ceil(this.z); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); this.z = Math.round(this.z); return this; } roundToZero() { this.x = this.x < 0 ? Math.ceil(this.x) : Math.floor(this.x); this.y = this.y < 0 ? Math.ceil(this.y) : Math.floor(this.y); this.z = this.z < 0 ? Math.ceil(this.z) : Math.floor(this.z); return this; } negate() { this.x = -this.x; this.y = -this.y; this.z = -this.z; return this; } dot(v) { return this.x * v.x + this.y * v.y + this.z * v.z; } // TODO lengthSquared? lengthSq() { return this.x * this.x + this.y * this.y + this.z * this.z; } length() { return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); } manhattanLength() { return Math.abs(this.x) + Math.abs(this.y) + Math.abs(this.z); } normalize() { return this.divideScalar(this.length() || 1); } setLength(length) { return this.normalize().multiplyScalar(length); } lerp(v, alpha) { this.x += (v.x - this.x) * alpha; this.y += (v.y - this.y) * alpha; this.z += (v.z - this.z) * alpha; return this; } lerpVectors(v1, v2, alpha) { this.x = v1.x + (v2.x - v1.x) * alpha; this.y = v1.y + (v2.y - v1.y) * alpha; this.z = v1.z + (v2.z - v1.z) * alpha; return this; } cross(v) { return this.crossVectors(this, v); } crossVectors(a, b) { const ax = a.x, ay = a.y, az = a.z; const bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; } projectOnVector(v) { const denominator = v.lengthSq(); if (denominator === 0) return this.set(0, 0, 0); const scalar = v.dot(this) / denominator; return this.copy(v).multiplyScalar(scalar); } projectOnPlane(planeNormal) { _vector$b.copy(this).projectOnVector(planeNormal); return this.sub(_vector$b); } reflect(normal) { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length return this.sub(_vector$b.copy(normal).multiplyScalar(2 * this.dot(normal))); } angleTo(v) { const denominator = Math.sqrt(this.lengthSq() * v.lengthSq()); if (denominator === 0) return Math.PI / 2; const theta = this.dot(v) / denominator; // clamp, to handle numerical problems return Math.acos(clamp(theta, -1, 1)); } distanceTo(v) { return Math.sqrt(this.distanceToSquared(v)); } distanceToSquared(v) { const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; } manhattanDistanceTo(v) { return Math.abs(this.x - v.x) + Math.abs(this.y - v.y) + Math.abs(this.z - v.z); } setFromSpherical(s) { return this.setFromSphericalCoords(s.radius, s.phi, s.theta); } setFromSphericalCoords(radius, phi, theta) { const sinPhiRadius = Math.sin(phi) * radius; this.x = sinPhiRadius * Math.sin(theta); this.y = Math.cos(phi) * radius; this.z = sinPhiRadius * Math.cos(theta); return this; } setFromCylindrical(c) { return this.setFromCylindricalCoords(c.radius, c.theta, c.y); } setFromCylindricalCoords(radius, theta, y) { this.x = radius * Math.sin(theta); this.y = y; this.z = radius * Math.cos(theta); return this; } setFromMatrixPosition(m) { const e = m.elements; this.x = e[12]; this.y = e[13]; this.z = e[14]; return this; } setFromMatrixScale(m) { const sx = this.setFromMatrixColumn(m, 0).length(); const sy = this.setFromMatrixColumn(m, 1).length(); const sz = this.setFromMatrixColumn(m, 2).length(); this.x = sx; this.y = sy; this.z = sz; return this; } setFromMatrixColumn(m, index) { return this.fromArray(m.elements, index * 4); } setFromMatrix3Column(m, index) { return this.fromArray(m.elements, index * 3); } setFromEuler(e) { this.x = e._x; this.y = e._y; this.z = e._z; return this; } setFromColor(c) { this.x = c.r; this.y = c.g; this.z = c.b; return this; } equals(v) { return v.x === this.x && v.y === this.y && v.z === this.z; } fromArray(array, offset = 0) { this.x = array[offset]; this.y = array[offset + 1]; this.z = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.x; array[offset + 1] = this.y; array[offset + 2] = this.z; return array; } fromBufferAttribute(attribute, index) { this.x = attribute.getX(index); this.y = attribute.getY(index); this.z = attribute.getZ(index); return this; } random() { this.x = Math.random(); this.y = Math.random(); this.z = Math.random(); return this; } randomDirection() { // Derived from https://mathworld.wolfram.com/SpherePointPicking.html const u = (Math.random() - 0.5) * 2; const t = Math.random() * Math.PI * 2; const f = Math.sqrt(1 - u ** 2); this.x = f * Math.cos(t); this.y = f * Math.sin(t); this.z = u; return this; } *[Symbol.iterator]() { yield this.x; yield this.y; yield this.z; } } const _vector$b = /*@__PURE__*/new Vector3(); const _quaternion$4 = /*@__PURE__*/new Quaternion(); class Box3 { constructor(min = new Vector3(+Infinity, +Infinity, +Infinity), max = new Vector3(-Infinity, -Infinity, -Infinity)) { this.isBox3 = true; this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromArray(array) { this.makeEmpty(); for (let i = 0, il = array.length; i < il; i += 3) { this.expandByPoint(_vector$a.fromArray(array, i)); } return this; } setFromBufferAttribute(attribute) { this.makeEmpty(); for (let i = 0, il = attribute.count; i < il; i++) { this.expandByPoint(_vector$a.fromBufferAttribute(attribute, i)); } return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$a.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } setFromObject(object, precise = false) { this.makeEmpty(); return this.expandByObject(object, precise); } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = this.min.z = +Infinity; this.max.x = this.max.y = this.max.z = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y || this.max.z < this.min.z; } getCenter(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } expandByObject(object, precise = false) { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object's, and children's, world transforms object.updateWorldMatrix(false, false); if (object.boundingBox !== undefined) { if (object.boundingBox === null) { object.computeBoundingBox(); } _box$3.copy(object.boundingBox); _box$3.applyMatrix4(object.matrixWorld); this.union(_box$3); } else { const geometry = object.geometry; if (geometry !== undefined) { if (precise && geometry.attributes !== undefined && geometry.attributes.position !== undefined) { const position = geometry.attributes.position; for (let i = 0, l = position.count; i < l; i++) { _vector$a.fromBufferAttribute(position, i).applyMatrix4(object.matrixWorld); this.expandByPoint(_vector$a); } } else { if (geometry.boundingBox === null) { geometry.computeBoundingBox(); } _box$3.copy(geometry.boundingBox); _box$3.applyMatrix4(object.matrixWorld); this.union(_box$3); } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { this.expandByObject(children[i], precise); } return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; } containsBox(box) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y), (point.z - this.min.z) / (this.max.z - this.min.z)); } intersectsBox(box) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; } intersectsSphere(sphere) { // Find the point on the AABB closest to the sphere center. this.clampPoint(sphere.center, _vector$a); // If that point is inside the sphere, the AABB and sphere intersect. return _vector$a.distanceToSquared(sphere.center) <= sphere.radius * sphere.radius; } intersectsPlane(plane) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. let min, max; if (plane.normal.x > 0) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if (plane.normal.y > 0) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if (plane.normal.z > 0) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return min <= -plane.constant && max >= -plane.constant; } intersectsTriangle(triangle) { if (this.isEmpty()) { return false; } // compute box center and extents this.getCenter(_center); _extents.subVectors(this.max, _center); // translate triangle to aabb origin _v0$2.subVectors(triangle.a, _center); _v1$7.subVectors(triangle.b, _center); _v2$4.subVectors(triangle.c, _center); // compute edge vectors for triangle _f0.subVectors(_v1$7, _v0$2); _f1.subVectors(_v2$4, _v1$7); _f2.subVectors(_v0$2, _v2$4); // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) let axes = [0, -_f0.z, _f0.y, 0, -_f1.z, _f1.y, 0, -_f2.z, _f2.y, _f0.z, 0, -_f0.x, _f1.z, 0, -_f1.x, _f2.z, 0, -_f2.x, -_f0.y, _f0.x, 0, -_f1.y, _f1.x, 0, -_f2.y, _f2.x, 0]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$4, _extents)) { return false; } // test 3 face normals from the aabb axes = [1, 0, 0, 0, 1, 0, 0, 0, 1]; if (!satForAxes(axes, _v0$2, _v1$7, _v2$4, _extents)) { return false; } // finally testing the face normal of the triangle // use already existing triangle edge vectors here _triangleNormal.crossVectors(_f0, _f1); axes = [_triangleNormal.x, _triangleNormal.y, _triangleNormal.z]; return satForAxes(axes, _v0$2, _v1$7, _v2$4, _extents); } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { return this.clampPoint(point, _vector$a).distanceTo(point); } getBoundingSphere(target) { if (this.isEmpty()) { target.makeEmpty(); } else { this.getCenter(target.center); target.radius = this.getSize(_vector$a).length() * 0.5; } return target; } intersect(box) { this.min.max(box.min); this.max.min(box.max); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if (this.isEmpty()) this.makeEmpty(); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } applyMatrix4(matrix) { // transform of empty box is an empty box. if (this.isEmpty()) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below _points[0].set(this.min.x, this.min.y, this.min.z).applyMatrix4(matrix); // 000 _points[1].set(this.min.x, this.min.y, this.max.z).applyMatrix4(matrix); // 001 _points[2].set(this.min.x, this.max.y, this.min.z).applyMatrix4(matrix); // 010 _points[3].set(this.min.x, this.max.y, this.max.z).applyMatrix4(matrix); // 011 _points[4].set(this.max.x, this.min.y, this.min.z).applyMatrix4(matrix); // 100 _points[5].set(this.max.x, this.min.y, this.max.z).applyMatrix4(matrix); // 101 _points[6].set(this.max.x, this.max.y, this.min.z).applyMatrix4(matrix); // 110 _points[7].set(this.max.x, this.max.y, this.max.z).applyMatrix4(matrix); // 111 this.setFromPoints(_points); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } const _points = [/*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3(), /*@__PURE__*/new Vector3()]; const _vector$a = /*@__PURE__*/new Vector3(); const _box$3 = /*@__PURE__*/new Box3(); // triangle centered vertices const _v0$2 = /*@__PURE__*/new Vector3(); const _v1$7 = /*@__PURE__*/new Vector3(); const _v2$4 = /*@__PURE__*/new Vector3(); // triangle edge vectors const _f0 = /*@__PURE__*/new Vector3(); const _f1 = /*@__PURE__*/new Vector3(); const _f2 = /*@__PURE__*/new Vector3(); const _center = /*@__PURE__*/new Vector3(); const _extents = /*@__PURE__*/new Vector3(); const _triangleNormal = /*@__PURE__*/new Vector3(); const _testAxis = /*@__PURE__*/new Vector3(); function satForAxes(axes, v0, v1, v2, extents) { for (let i = 0, j = axes.length - 3; i <= j; i += 3) { _testAxis.fromArray(axes, i); // project the aabb onto the separating axis const r = extents.x * Math.abs(_testAxis.x) + extents.y * Math.abs(_testAxis.y) + extents.z * Math.abs(_testAxis.z); // project all 3 vertices of the triangle onto the separating axis const p0 = v0.dot(_testAxis); const p1 = v1.dot(_testAxis); const p2 = v2.dot(_testAxis); // actual test, basically see if either of the most extreme of the triangle points intersects r if (Math.max(-Math.max(p0, p1, p2), Math.min(p0, p1, p2)) > r) { // points of the projected triangle are outside the projected half-length of the aabb // the axis is separating and we can exit return false; } } return true; } const _box$2 = /*@__PURE__*/new Box3(); const _v1$6 = /*@__PURE__*/new Vector3(); const _v2$3 = /*@__PURE__*/new Vector3(); class Sphere { constructor(center = new Vector3(), radius = -1) { this.center = center; this.radius = radius; } set(center, radius) { this.center.copy(center); this.radius = radius; return this; } setFromPoints(points, optionalCenter) { const center = this.center; if (optionalCenter !== undefined) { center.copy(optionalCenter); } else { _box$2.setFromPoints(points).getCenter(center); } let maxRadiusSq = 0; for (let i = 0, il = points.length; i < il; i++) { maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(points[i])); } this.radius = Math.sqrt(maxRadiusSq); return this; } copy(sphere) { this.center.copy(sphere.center); this.radius = sphere.radius; return this; } isEmpty() { return this.radius < 0; } makeEmpty() { this.center.set(0, 0, 0); this.radius = -1; return this; } containsPoint(point) { return point.distanceToSquared(this.center) <= this.radius * this.radius; } distanceToPoint(point) { return point.distanceTo(this.center) - this.radius; } intersectsSphere(sphere) { const radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared(this.center) <= radiusSum * radiusSum; } intersectsBox(box) { return box.intersectsSphere(this); } intersectsPlane(plane) { return Math.abs(plane.distanceToPoint(this.center)) <= this.radius; } clampPoint(point, target) { const deltaLengthSq = this.center.distanceToSquared(point); target.copy(point); if (deltaLengthSq > this.radius * this.radius) { target.sub(this.center).normalize(); target.multiplyScalar(this.radius).add(this.center); } return target; } getBoundingBox(target) { if (this.isEmpty()) { // Empty sphere produces empty bounding box target.makeEmpty(); return target; } target.set(this.center, this.center); target.expandByScalar(this.radius); return target; } applyMatrix4(matrix) { this.center.applyMatrix4(matrix); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; } translate(offset) { this.center.add(offset); return this; } expandByPoint(point) { if (this.isEmpty()) { this.center.copy(point); this.radius = 0; return this; } _v1$6.subVectors(point, this.center); const lengthSq = _v1$6.lengthSq(); if (lengthSq > this.radius * this.radius) { // calculate the minimal sphere const length = Math.sqrt(lengthSq); const delta = (length - this.radius) * 0.5; this.center.addScaledVector(_v1$6, delta / length); this.radius += delta; } return this; } union(sphere) { if (sphere.isEmpty()) { return this; } if (this.isEmpty()) { this.copy(sphere); return this; } if (this.center.equals(sphere.center) === true) { this.radius = Math.max(this.radius, sphere.radius); } else { _v2$3.subVectors(sphere.center, this.center).setLength(sphere.radius); this.expandByPoint(_v1$6.copy(sphere.center).add(_v2$3)); this.expandByPoint(_v1$6.copy(sphere.center).sub(_v2$3)); } return this; } equals(sphere) { return sphere.center.equals(this.center) && sphere.radius === this.radius; } clone() { return new this.constructor().copy(this); } } const _vector$9 = /*@__PURE__*/new Vector3(); const _segCenter = /*@__PURE__*/new Vector3(); const _segDir = /*@__PURE__*/new Vector3(); const _diff = /*@__PURE__*/new Vector3(); const _edge1 = /*@__PURE__*/new Vector3(); const _edge2 = /*@__PURE__*/new Vector3(); const _normal$1 = /*@__PURE__*/new Vector3(); class Ray { constructor(origin = new Vector3(), direction = new Vector3(0, 0, -1)) { this.origin = origin; this.direction = direction; } set(origin, direction) { this.origin.copy(origin); this.direction.copy(direction); return this; } copy(ray) { this.origin.copy(ray.origin); this.direction.copy(ray.direction); return this; } at(t, target) { return target.copy(this.origin).addScaledVector(this.direction, t); } lookAt(v) { this.direction.copy(v).sub(this.origin).normalize(); return this; } recast(t) { this.origin.copy(this.at(t, _vector$9)); return this; } closestPointToPoint(point, target) { target.subVectors(point, this.origin); const directionDistance = target.dot(this.direction); if (directionDistance < 0) { return target.copy(this.origin); } return target.copy(this.origin).addScaledVector(this.direction, directionDistance); } distanceToPoint(point) { return Math.sqrt(this.distanceSqToPoint(point)); } distanceSqToPoint(point) { const directionDistance = _vector$9.subVectors(point, this.origin).dot(this.direction); // point behind the ray if (directionDistance < 0) { return this.origin.distanceToSquared(point); } _vector$9.copy(this.origin).addScaledVector(this.direction, directionDistance); return _vector$9.distanceToSquared(point); } distanceSqToSegment(v0, v1, optionalPointOnRay, optionalPointOnSegment) { // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment _segCenter.copy(v0).add(v1).multiplyScalar(0.5); _segDir.copy(v1).sub(v0).normalize(); _diff.copy(this.origin).sub(_segCenter); const segExtent = v0.distanceTo(v1) * 0.5; const a01 = -this.direction.dot(_segDir); const b0 = _diff.dot(this.direction); const b1 = -_diff.dot(_segDir); const c = _diff.lengthSq(); const det = Math.abs(1 - a01 * a01); let s0, s1, sqrDist, extDet; if (det > 0) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if (s0 >= 0) { if (s1 >= -extDet) { if (s1 <= extDet) { // region 0 // Minimum at interior points of ray and segment. const invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * (s0 + a01 * s1 + 2 * b0) + s1 * (a01 * s0 + s1 + 2 * b1) + c; } else { // region 1 s1 = segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { // region 5 s1 = -segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } else { if (s1 <= -extDet) { // region 4 s0 = Math.max(0, -(-a01 * segExtent + b0)); s1 = s0 > 0 ? -segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } else if (s1 <= extDet) { // region 3 s0 = 0; s1 = Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = s1 * (s1 + 2 * b1) + c; } else { // region 2 s0 = Math.max(0, -(a01 * segExtent + b0)); s1 = s0 > 0 ? segExtent : Math.min(Math.max(-segExtent, -b1), segExtent); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } } } else { // Ray and segment are parallel. s1 = a01 > 0 ? -segExtent : segExtent; s0 = Math.max(0, -(a01 * s1 + b0)); sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c; } if (optionalPointOnRay) { optionalPointOnRay.copy(this.origin).addScaledVector(this.direction, s0); } if (optionalPointOnSegment) { optionalPointOnSegment.copy(_segCenter).addScaledVector(_segDir, s1); } return sqrDist; } intersectSphere(sphere, target) { _vector$9.subVectors(sphere.center, this.origin); const tca = _vector$9.dot(this.direction); const d2 = _vector$9.dot(_vector$9) - tca * tca; const radius2 = sphere.radius * sphere.radius; if (d2 > radius2) return null; const thc = Math.sqrt(radius2 - d2); // t0 = first intersect point - entrance on front of sphere const t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere const t1 = tca + thc; // test to see if t1 is behind the ray - if so, return null if (t1 < 0) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if (t0 < 0) return this.at(t1, target); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at(t0, target); } intersectsSphere(sphere) { return this.distanceSqToPoint(sphere.center) <= sphere.radius * sphere.radius; } distanceToPlane(plane) { const denominator = plane.normal.dot(this.direction); if (denominator === 0) { // line is coplanar, return origin if (plane.distanceToPoint(this.origin) === 0) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } const t = -(this.origin.dot(plane.normal) + plane.constant) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; } intersectPlane(plane, target) { const t = this.distanceToPlane(plane); if (t === null) { return null; } return this.at(t, target); } intersectsPlane(plane) { // check if the ray lies on the plane first const distToPoint = plane.distanceToPoint(this.origin); if (distToPoint === 0) { return true; } const denominator = plane.normal.dot(this.direction); if (denominator * distToPoint < 0) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; } intersectBox(box, target) { let tmin, tmax, tymin, tymax, tzmin, tzmax; const invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; const origin = this.origin; if (invdirx >= 0) { tmin = (box.min.x - origin.x) * invdirx; tmax = (box.max.x - origin.x) * invdirx; } else { tmin = (box.max.x - origin.x) * invdirx; tmax = (box.min.x - origin.x) * invdirx; } if (invdiry >= 0) { tymin = (box.min.y - origin.y) * invdiry; tymax = (box.max.y - origin.y) * invdiry; } else { tymin = (box.max.y - origin.y) * invdiry; tymax = (box.min.y - origin.y) * invdiry; } if (tmin > tymax || tymin > tmax) return null; if (tymin > tmin || isNaN(tmin)) tmin = tymin; if (tymax < tmax || isNaN(tmax)) tmax = tymax; if (invdirz >= 0) { tzmin = (box.min.z - origin.z) * invdirz; tzmax = (box.max.z - origin.z) * invdirz; } else { tzmin = (box.max.z - origin.z) * invdirz; tzmax = (box.min.z - origin.z) * invdirz; } if (tmin > tzmax || tzmin > tmax) return null; if (tzmin > tmin || tmin !== tmin) tmin = tzmin; if (tzmax < tmax || tmax !== tmax) tmax = tzmax; //return point closest to the ray (positive side) if (tmax < 0) return null; return this.at(tmin >= 0 ? tmin : tmax, target); } intersectsBox(box) { return this.intersectBox(box, _vector$9) !== null; } intersectTriangle(a, b, c, backfaceCulling, target) { // Compute the offset origin, edges, and normal. // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h _edge1.subVectors(b, a); _edge2.subVectors(c, a); _normal$1.crossVectors(_edge1, _edge2); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) let DdN = this.direction.dot(_normal$1); let sign; if (DdN > 0) { if (backfaceCulling) return null; sign = 1; } else if (DdN < 0) { sign = -1; DdN = -DdN; } else { return null; } _diff.subVectors(this.origin, a); const DdQxE2 = sign * this.direction.dot(_edge2.crossVectors(_diff, _edge2)); // b1 < 0, no intersection if (DdQxE2 < 0) { return null; } const DdE1xQ = sign * this.direction.dot(_edge1.cross(_diff)); // b2 < 0, no intersection if (DdE1xQ < 0) { return null; } // b1+b2 > 1, no intersection if (DdQxE2 + DdE1xQ > DdN) { return null; } // Line intersects triangle, check if ray does. const QdN = -sign * _diff.dot(_normal$1); // t < 0, no intersection if (QdN < 0) { return null; } // Ray intersects triangle. return this.at(QdN / DdN, target); } applyMatrix4(matrix4) { this.origin.applyMatrix4(matrix4); this.direction.transformDirection(matrix4); return this; } equals(ray) { return ray.origin.equals(this.origin) && ray.direction.equals(this.direction); } clone() { return new this.constructor().copy(this); } } class Matrix4 { constructor(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { Matrix4.prototype.isMatrix4 = true; this.elements = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; if (n11 !== undefined) { this.set(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44); } } set(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { const te = this.elements; te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; return this; } identity() { this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } clone() { return new Matrix4().fromArray(this.elements); } copy(m) { const te = this.elements; const me = m.elements; te[0] = me[0]; te[1] = me[1]; te[2] = me[2]; te[3] = me[3]; te[4] = me[4]; te[5] = me[5]; te[6] = me[6]; te[7] = me[7]; te[8] = me[8]; te[9] = me[9]; te[10] = me[10]; te[11] = me[11]; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; te[15] = me[15]; return this; } copyPosition(m) { const te = this.elements, me = m.elements; te[12] = me[12]; te[13] = me[13]; te[14] = me[14]; return this; } setFromMatrix3(m) { const me = m.elements; this.set(me[0], me[3], me[6], 0, me[1], me[4], me[7], 0, me[2], me[5], me[8], 0, 0, 0, 0, 1); return this; } extractBasis(xAxis, yAxis, zAxis) { xAxis.setFromMatrixColumn(this, 0); yAxis.setFromMatrixColumn(this, 1); zAxis.setFromMatrixColumn(this, 2); return this; } makeBasis(xAxis, yAxis, zAxis) { this.set(xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1); return this; } extractRotation(m) { // this method does not support reflection matrices const te = this.elements; const me = m.elements; const scaleX = 1 / _v1$5.setFromMatrixColumn(m, 0).length(); const scaleY = 1 / _v1$5.setFromMatrixColumn(m, 1).length(); const scaleZ = 1 / _v1$5.setFromMatrixColumn(m, 2).length(); te[0] = me[0] * scaleX; te[1] = me[1] * scaleX; te[2] = me[2] * scaleX; te[3] = 0; te[4] = me[4] * scaleY; te[5] = me[5] * scaleY; te[6] = me[6] * scaleY; te[7] = 0; te[8] = me[8] * scaleZ; te[9] = me[9] * scaleZ; te[10] = me[10] * scaleZ; te[11] = 0; te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromEuler(euler) { const te = this.elements; const x = euler.x, y = euler.y, z = euler.z; const a = Math.cos(x), b = Math.sin(x); const c = Math.cos(y), d = Math.sin(y); const e = Math.cos(z), f = Math.sin(z); if (euler.order === 'XYZ') { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = -c * f; te[8] = d; te[1] = af + be * d; te[5] = ae - bf * d; te[9] = -b * c; te[2] = bf - ae * d; te[6] = be + af * d; te[10] = a * c; } else if (euler.order === 'YXZ') { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce + df * b; te[4] = de * b - cf; te[8] = a * d; te[1] = a * f; te[5] = a * e; te[9] = -b; te[2] = cf * b - de; te[6] = df + ce * b; te[10] = a * c; } else if (euler.order === 'ZXY') { const ce = c * e, cf = c * f, de = d * e, df = d * f; te[0] = ce - df * b; te[4] = -a * f; te[8] = de + cf * b; te[1] = cf + de * b; te[5] = a * e; te[9] = df - ce * b; te[2] = -a * d; te[6] = b; te[10] = a * c; } else if (euler.order === 'ZYX') { const ae = a * e, af = a * f, be = b * e, bf = b * f; te[0] = c * e; te[4] = be * d - af; te[8] = ae * d + bf; te[1] = c * f; te[5] = bf * d + ae; te[9] = af * d - be; te[2] = -d; te[6] = b * c; te[10] = a * c; } else if (euler.order === 'YZX') { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = bd - ac * f; te[8] = bc * f + ad; te[1] = f; te[5] = a * e; te[9] = -b * e; te[2] = -d * e; te[6] = ad * f + bc; te[10] = ac - bd * f; } else if (euler.order === 'XZY') { const ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[0] = c * e; te[4] = -f; te[8] = d * e; te[1] = ac * f + bd; te[5] = a * e; te[9] = ad * f - bc; te[2] = bc * f - ad; te[6] = b * e; te[10] = bd * f + ac; } // bottom row te[3] = 0; te[7] = 0; te[11] = 0; // last column te[12] = 0; te[13] = 0; te[14] = 0; te[15] = 1; return this; } makeRotationFromQuaternion(q) { return this.compose(_zero, q, _one); } lookAt(eye, target, up) { const te = this.elements; _z.subVectors(eye, target); if (_z.lengthSq() === 0) { // eye and target are in the same position _z.z = 1; } _z.normalize(); _x.crossVectors(up, _z); if (_x.lengthSq() === 0) { // up and z are parallel if (Math.abs(up.z) === 1) { _z.x += 0.0001; } else { _z.z += 0.0001; } _z.normalize(); _x.crossVectors(up, _z); } _x.normalize(); _y.crossVectors(_z, _x); te[0] = _x.x; te[4] = _y.x; te[8] = _z.x; te[1] = _x.y; te[5] = _y.y; te[9] = _z.y; te[2] = _x.z; te[6] = _y.z; te[10] = _z.z; return this; } multiply(m) { return this.multiplyMatrices(this, m); } premultiply(m) { return this.multiplyMatrices(m, this); } multiplyMatrices(a, b) { const ae = a.elements; const be = b.elements; const te = this.elements; const a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; const a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; const a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; const a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; const b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12]; const b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13]; const b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14]; const b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15]; te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; } multiplyScalar(s) { const te = this.elements; te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s; te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s; te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s; te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s; return this; } determinant() { const te = this.elements; const n11 = te[0], n12 = te[4], n13 = te[8], n14 = te[12]; const n21 = te[1], n22 = te[5], n23 = te[9], n24 = te[13]; const n31 = te[2], n32 = te[6], n33 = te[10], n34 = te[14]; const n41 = te[3], n42 = te[7], n43 = te[11], n44 = te[15]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return n41 * (+n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34) + n42 * (+n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31) + n43 * (+n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31) + n44 * (-n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31); } transpose() { const te = this.elements; let tmp; tmp = te[1]; te[1] = te[4]; te[4] = tmp; tmp = te[2]; te[2] = te[8]; te[8] = tmp; tmp = te[6]; te[6] = te[9]; te[9] = tmp; tmp = te[3]; te[3] = te[12]; te[12] = tmp; tmp = te[7]; te[7] = te[13]; te[13] = tmp; tmp = te[11]; te[11] = te[14]; te[14] = tmp; return this; } setPosition(x, y, z) { const te = this.elements; if (x.isVector3) { te[12] = x.x; te[13] = x.y; te[14] = x.z; } else { te[12] = x; te[13] = y; te[14] = z; } return this; } invert() { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm const te = this.elements, n11 = te[0], n21 = te[1], n31 = te[2], n41 = te[3], n12 = te[4], n22 = te[5], n32 = te[6], n42 = te[7], n13 = te[8], n23 = te[9], n33 = te[10], n43 = te[11], n14 = te[12], n24 = te[13], n34 = te[14], n44 = te[15], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); const detInv = 1 / det; te[0] = t11 * detInv; te[1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * detInv; te[2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * detInv; te[3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * detInv; te[4] = t12 * detInv; te[5] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * detInv; te[6] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * detInv; te[7] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * detInv; te[8] = t13 * detInv; te[9] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * detInv; te[10] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * detInv; te[11] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * detInv; te[12] = t14 * detInv; te[13] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * detInv; te[14] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * detInv; te[15] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * detInv; return this; } scale(v) { const te = this.elements; const x = v.x, y = v.y, z = v.z; te[0] *= x; te[4] *= y; te[8] *= z; te[1] *= x; te[5] *= y; te[9] *= z; te[2] *= x; te[6] *= y; te[10] *= z; te[3] *= x; te[7] *= y; te[11] *= z; return this; } getMaxScaleOnAxis() { const te = this.elements; const scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2]; const scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6]; const scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10]; return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq)); } makeTranslation(x, y, z) { if (x.isVector3) { this.set(1, 0, 0, x.x, 0, 1, 0, x.y, 0, 0, 1, x.z, 0, 0, 0, 1); } else { this.set(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1); } return this; } makeRotationX(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(1, 0, 0, 0, 0, c, -s, 0, 0, s, c, 0, 0, 0, 0, 1); return this; } makeRotationY(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, 0, s, 0, 0, 1, 0, 0, -s, 0, c, 0, 0, 0, 0, 1); return this; } makeRotationZ(theta) { const c = Math.cos(theta), s = Math.sin(theta); this.set(c, -s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); return this; } makeRotationAxis(axis, angle) { // Based on http://www.gamedev.net/reference/articles/article1199.asp const c = Math.cos(angle); const s = Math.sin(angle); const t = 1 - c; const x = axis.x, y = axis.y, z = axis.z; const tx = t * x, ty = t * y; this.set(tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1); return this; } makeScale(x, y, z) { this.set(x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1); return this; } makeShear(xy, xz, yx, yz, zx, zy) { this.set(1, yx, zx, 0, xy, 1, zy, 0, xz, yz, 1, 0, 0, 0, 0, 1); return this; } compose(position, quaternion, scale) { const te = this.elements; const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; const x2 = x + x, y2 = y + y, z2 = z + z; const xx = x * x2, xy = x * y2, xz = x * z2; const yy = y * y2, yz = y * z2, zz = z * z2; const wx = w * x2, wy = w * y2, wz = w * z2; const sx = scale.x, sy = scale.y, sz = scale.z; te[0] = (1 - (yy + zz)) * sx; te[1] = (xy + wz) * sx; te[2] = (xz - wy) * sx; te[3] = 0; te[4] = (xy - wz) * sy; te[5] = (1 - (xx + zz)) * sy; te[6] = (yz + wx) * sy; te[7] = 0; te[8] = (xz + wy) * sz; te[9] = (yz - wx) * sz; te[10] = (1 - (xx + yy)) * sz; te[11] = 0; te[12] = position.x; te[13] = position.y; te[14] = position.z; te[15] = 1; return this; } decompose(position, quaternion, scale) { const te = this.elements; let sx = _v1$5.set(te[0], te[1], te[2]).length(); const sy = _v1$5.set(te[4], te[5], te[6]).length(); const sz = _v1$5.set(te[8], te[9], te[10]).length(); // if determine is negative, we need to invert one scale const det = this.determinant(); if (det < 0) sx = -sx; position.x = te[12]; position.y = te[13]; position.z = te[14]; // scale the rotation part _m1$2.copy(this); const invSX = 1 / sx; const invSY = 1 / sy; const invSZ = 1 / sz; _m1$2.elements[0] *= invSX; _m1$2.elements[1] *= invSX; _m1$2.elements[2] *= invSX; _m1$2.elements[4] *= invSY; _m1$2.elements[5] *= invSY; _m1$2.elements[6] *= invSY; _m1$2.elements[8] *= invSZ; _m1$2.elements[9] *= invSZ; _m1$2.elements[10] *= invSZ; quaternion.setFromRotationMatrix(_m1$2); scale.x = sx; scale.y = sy; scale.z = sz; return this; } makePerspective(left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem) { const te = this.elements; const x = 2 * near / (right - left); const y = 2 * near / (top - bottom); const a = (right + left) / (right - left); const b = (top + bottom) / (top - bottom); let c, d; if (coordinateSystem === WebGLCoordinateSystem) { c = -(far + near) / (far - near); d = -2 * far * near / (far - near); } else if (coordinateSystem === WebGPUCoordinateSystem) { c = -far / (far - near); d = -far * near / (far - near); } else { throw new Error('THREE.Matrix4.makePerspective(): Invalid coordinate system: ' + coordinateSystem); } te[0] = x; te[4] = 0; te[8] = a; te[12] = 0; te[1] = 0; te[5] = y; te[9] = b; te[13] = 0; te[2] = 0; te[6] = 0; te[10] = c; te[14] = d; te[3] = 0; te[7] = 0; te[11] = -1; te[15] = 0; return this; } makeOrthographic(left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem) { const te = this.elements; const w = 1.0 / (right - left); const h = 1.0 / (top - bottom); const p = 1.0 / (far - near); const x = (right + left) * w; const y = (top + bottom) * h; let z, zInv; if (coordinateSystem === WebGLCoordinateSystem) { z = (far + near) * p; zInv = -2 * p; } else if (coordinateSystem === WebGPUCoordinateSystem) { z = near * p; zInv = -1 * p; } else { throw new Error('THREE.Matrix4.makeOrthographic(): Invalid coordinate system: ' + coordinateSystem); } te[0] = 2 * w; te[4] = 0; te[8] = 0; te[12] = -x; te[1] = 0; te[5] = 2 * h; te[9] = 0; te[13] = -y; te[2] = 0; te[6] = 0; te[10] = zInv; te[14] = -z; te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1; return this; } equals(matrix) { const te = this.elements; const me = matrix.elements; for (let i = 0; i < 16; i++) { if (te[i] !== me[i]) return false; } return true; } fromArray(array, offset = 0) { for (let i = 0; i < 16; i++) { this.elements[i] = array[i + offset]; } return this; } toArray(array = [], offset = 0) { const te = this.elements; array[offset] = te[0]; array[offset + 1] = te[1]; array[offset + 2] = te[2]; array[offset + 3] = te[3]; array[offset + 4] = te[4]; array[offset + 5] = te[5]; array[offset + 6] = te[6]; array[offset + 7] = te[7]; array[offset + 8] = te[8]; array[offset + 9] = te[9]; array[offset + 10] = te[10]; array[offset + 11] = te[11]; array[offset + 12] = te[12]; array[offset + 13] = te[13]; array[offset + 14] = te[14]; array[offset + 15] = te[15]; return array; } } const _v1$5 = /*@__PURE__*/new Vector3(); const _m1$2 = /*@__PURE__*/new Matrix4(); const _zero = /*@__PURE__*/new Vector3(0, 0, 0); const _one = /*@__PURE__*/new Vector3(1, 1, 1); const _x = /*@__PURE__*/new Vector3(); const _y = /*@__PURE__*/new Vector3(); const _z = /*@__PURE__*/new Vector3(); const _matrix = /*@__PURE__*/new Matrix4(); const _quaternion$3 = /*@__PURE__*/new Quaternion(); class Euler { constructor(x = 0, y = 0, z = 0, order = Euler.DEFAULT_ORDER) { this.isEuler = true; this._x = x; this._y = y; this._z = z; this._order = order; } get x() { return this._x; } set x(value) { this._x = value; this._onChangeCallback(); } get y() { return this._y; } set y(value) { this._y = value; this._onChangeCallback(); } get z() { return this._z; } set z(value) { this._z = value; this._onChangeCallback(); } get order() { return this._order; } set order(value) { this._order = value; this._onChangeCallback(); } set(x, y, z, order = this._order) { this._x = x; this._y = y; this._z = z; this._order = order; this._onChangeCallback(); return this; } clone() { return new this.constructor(this._x, this._y, this._z, this._order); } copy(euler) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this._onChangeCallback(); return this; } setFromRotationMatrix(m, order = this._order, update = true) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements; const m11 = te[0], m12 = te[4], m13 = te[8]; const m21 = te[1], m22 = te[5], m23 = te[9]; const m31 = te[2], m32 = te[6], m33 = te[10]; switch (order) { case 'XYZ': this._y = Math.asin(clamp(m13, -1, 1)); if (Math.abs(m13) < 0.9999999) { this._x = Math.atan2(-m23, m33); this._z = Math.atan2(-m12, m11); } else { this._x = Math.atan2(m32, m22); this._z = 0; } break; case 'YXZ': this._x = Math.asin(-clamp(m23, -1, 1)); if (Math.abs(m23) < 0.9999999) { this._y = Math.atan2(m13, m33); this._z = Math.atan2(m21, m22); } else { this._y = Math.atan2(-m31, m11); this._z = 0; } break; case 'ZXY': this._x = Math.asin(clamp(m32, -1, 1)); if (Math.abs(m32) < 0.9999999) { this._y = Math.atan2(-m31, m33); this._z = Math.atan2(-m12, m22); } else { this._y = 0; this._z = Math.atan2(m21, m11); } break; case 'ZYX': this._y = Math.asin(-clamp(m31, -1, 1)); if (Math.abs(m31) < 0.9999999) { this._x = Math.atan2(m32, m33); this._z = Math.atan2(m21, m11); } else { this._x = 0; this._z = Math.atan2(-m12, m22); } break; case 'YZX': this._z = Math.asin(clamp(m21, -1, 1)); if (Math.abs(m21) < 0.9999999) { this._x = Math.atan2(-m23, m22); this._y = Math.atan2(-m31, m11); } else { this._x = 0; this._y = Math.atan2(m13, m33); } break; case 'XZY': this._z = Math.asin(-clamp(m12, -1, 1)); if (Math.abs(m12) < 0.9999999) { this._x = Math.atan2(m32, m22); this._y = Math.atan2(m13, m11); } else { this._x = Math.atan2(-m23, m33); this._y = 0; } break; default: console.warn('THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order); } this._order = order; if (update === true) this._onChangeCallback(); return this; } setFromQuaternion(q, order, update) { _matrix.makeRotationFromQuaternion(q); return this.setFromRotationMatrix(_matrix, order, update); } setFromVector3(v, order = this._order) { return this.set(v.x, v.y, v.z, order); } reorder(newOrder) { // WARNING: this discards revolution information -bhouston _quaternion$3.setFromEuler(this); return this.setFromQuaternion(_quaternion$3, newOrder); } equals(euler) { return euler._x === this._x && euler._y === this._y && euler._z === this._z && euler._order === this._order; } fromArray(array) { this._x = array[0]; this._y = array[1]; this._z = array[2]; if (array[3] !== undefined) this._order = array[3]; this._onChangeCallback(); return this; } toArray(array = [], offset = 0) { array[offset] = this._x; array[offset + 1] = this._y; array[offset + 2] = this._z; array[offset + 3] = this._order; return array; } _onChange(callback) { this._onChangeCallback = callback; return this; } _onChangeCallback() {} *[Symbol.iterator]() { yield this._x; yield this._y; yield this._z; yield this._order; } } Euler.DEFAULT_ORDER = 'XYZ'; class Layers { constructor() { this.mask = 1 | 0; } set(channel) { this.mask = (1 << channel | 0) >>> 0; } enable(channel) { this.mask |= 1 << channel | 0; } enableAll() { this.mask = 0xffffffff | 0; } toggle(channel) { this.mask ^= 1 << channel | 0; } disable(channel) { this.mask &= ~(1 << channel | 0); } disableAll() { this.mask = 0; } test(layers) { return (this.mask & layers.mask) !== 0; } isEnabled(channel) { return (this.mask & (1 << channel | 0)) !== 0; } } let _object3DId = 0; const _v1$4 = /*@__PURE__*/new Vector3(); const _q1 = /*@__PURE__*/new Quaternion(); const _m1$1 = /*@__PURE__*/new Matrix4(); const _target = /*@__PURE__*/new Vector3(); const _position$3 = /*@__PURE__*/new Vector3(); const _scale$2 = /*@__PURE__*/new Vector3(); const _quaternion$2 = /*@__PURE__*/new Quaternion(); const _xAxis = /*@__PURE__*/new Vector3(1, 0, 0); const _yAxis = /*@__PURE__*/new Vector3(0, 1, 0); const _zAxis = /*@__PURE__*/new Vector3(0, 0, 1); const _addedEvent = { type: 'added' }; const _removedEvent = { type: 'removed' }; class Object3D extends EventDispatcher { constructor() { super(); this.isObject3D = true; Object.defineProperty(this, 'id', { value: _object3DId++ }); this.uuid = generateUUID(); this.name = ''; this.type = 'Object3D'; this.parent = null; this.children = []; this.up = Object3D.DEFAULT_UP.clone(); const position = new Vector3(); const rotation = new Euler(); const quaternion = new Quaternion(); const scale = new Vector3(1, 1, 1); function onRotationChange() { quaternion.setFromEuler(rotation, false); } function onQuaternionChange() { rotation.setFromQuaternion(quaternion, undefined, false); } rotation._onChange(onRotationChange); quaternion._onChange(onQuaternionChange); Object.defineProperties(this, { position: { configurable: true, enumerable: true, value: position }, rotation: { configurable: true, enumerable: true, value: rotation }, quaternion: { configurable: true, enumerable: true, value: quaternion }, scale: { configurable: true, enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } }); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DEFAULT_MATRIX_AUTO_UPDATE; this.matrixWorldNeedsUpdate = false; this.matrixWorldAutoUpdate = Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE; // checked by the renderer this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.animations = []; this.userData = {}; } onBeforeRender( /* renderer, scene, camera, geometry, material, group */) {} onAfterRender( /* renderer, scene, camera, geometry, material, group */) {} applyMatrix4(matrix) { if (this.matrixAutoUpdate) this.updateMatrix(); this.matrix.premultiply(matrix); this.matrix.decompose(this.position, this.quaternion, this.scale); } applyQuaternion(q) { this.quaternion.premultiply(q); return this; } setRotationFromAxisAngle(axis, angle) { // assumes axis is normalized this.quaternion.setFromAxisAngle(axis, angle); } setRotationFromEuler(euler) { this.quaternion.setFromEuler(euler, true); } setRotationFromMatrix(m) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix(m); } setRotationFromQuaternion(q) { // assumes q is normalized this.quaternion.copy(q); } rotateOnAxis(axis, angle) { // rotate object on axis in object space // axis is assumed to be normalized _q1.setFromAxisAngle(axis, angle); this.quaternion.multiply(_q1); return this; } rotateOnWorldAxis(axis, angle) { // rotate object on axis in world space // axis is assumed to be normalized // method assumes no rotated parent _q1.setFromAxisAngle(axis, angle); this.quaternion.premultiply(_q1); return this; } rotateX(angle) { return this.rotateOnAxis(_xAxis, angle); } rotateY(angle) { return this.rotateOnAxis(_yAxis, angle); } rotateZ(angle) { return this.rotateOnAxis(_zAxis, angle); } translateOnAxis(axis, distance) { // translate object by distance along axis in object space // axis is assumed to be normalized _v1$4.copy(axis).applyQuaternion(this.quaternion); this.position.add(_v1$4.multiplyScalar(distance)); return this; } translateX(distance) { return this.translateOnAxis(_xAxis, distance); } translateY(distance) { return this.translateOnAxis(_yAxis, distance); } translateZ(distance) { return this.translateOnAxis(_zAxis, distance); } localToWorld(vector) { this.updateWorldMatrix(true, false); return vector.applyMatrix4(this.matrixWorld); } worldToLocal(vector) { this.updateWorldMatrix(true, false); return vector.applyMatrix4(_m1$1.copy(this.matrixWorld).invert()); } lookAt(x, y, z) { // This method does not support objects having non-uniformly-scaled parent(s) if (x.isVector3) { _target.copy(x); } else { _target.set(x, y, z); } const parent = this.parent; this.updateWorldMatrix(true, false); _position$3.setFromMatrixPosition(this.matrixWorld); if (this.isCamera || this.isLight) { _m1$1.lookAt(_position$3, _target, this.up); } else { _m1$1.lookAt(_target, _position$3, this.up); } this.quaternion.setFromRotationMatrix(_m1$1); if (parent) { _m1$1.extractRotation(parent.matrixWorld); _q1.setFromRotationMatrix(_m1$1); this.quaternion.premultiply(_q1.invert()); } } add(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.add(arguments[i]); } return this; } if (object === this) { console.error('THREE.Object3D.add: object can\'t be added as a child of itself.', object); return this; } if (object && object.isObject3D) { if (object.parent !== null) { object.parent.remove(object); } object.parent = this; this.children.push(object); object.dispatchEvent(_addedEvent); } else { console.error('THREE.Object3D.add: object not an instance of THREE.Object3D.', object); } return this; } remove(object) { if (arguments.length > 1) { for (let i = 0; i < arguments.length; i++) { this.remove(arguments[i]); } return this; } const index = this.children.indexOf(object); if (index !== -1) { object.parent = null; this.children.splice(index, 1); object.dispatchEvent(_removedEvent); } return this; } removeFromParent() { const parent = this.parent; if (parent !== null) { parent.remove(this); } return this; } clear() { for (let i = 0; i < this.children.length; i++) { const object = this.children[i]; object.parent = null; object.dispatchEvent(_removedEvent); } this.children.length = 0; return this; } attach(object) { // adds object as a child of this, while maintaining the object's world transform // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) this.updateWorldMatrix(true, false); _m1$1.copy(this.matrixWorld).invert(); if (object.parent !== null) { object.parent.updateWorldMatrix(true, false); _m1$1.multiply(object.parent.matrixWorld); } object.applyMatrix4(_m1$1); this.add(object); object.updateWorldMatrix(false, true); return this; } getObjectById(id) { return this.getObjectByProperty('id', id); } getObjectByName(name) { return this.getObjectByProperty('name', name); } getObjectByProperty(name, value) { if (this[name] === value) return this; for (let i = 0, l = this.children.length; i < l; i++) { const child = this.children[i]; const object = child.getObjectByProperty(name, value); if (object !== undefined) { return object; } } return undefined; } getObjectsByProperty(name, value) { let result = []; if (this[name] === value) result.push(this); for (let i = 0, l = this.children.length; i < l; i++) { const childResult = this.children[i].getObjectsByProperty(name, value); if (childResult.length > 0) { result = result.concat(childResult); } } return result; } getWorldPosition(target) { this.updateWorldMatrix(true, false); return target.setFromMatrixPosition(this.matrixWorld); } getWorldQuaternion(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, target, _scale$2); return target; } getWorldScale(target) { this.updateWorldMatrix(true, false); this.matrixWorld.decompose(_position$3, _quaternion$2, target); return target; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(e[8], e[9], e[10]).normalize(); } raycast( /* raycaster, intersects */) {} traverse(callback) { callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverse(callback); } } traverseVisible(callback) { if (this.visible === false) return; callback(this); const children = this.children; for (let i = 0, l = children.length; i < l; i++) { children[i].traverseVisible(callback); } } traverseAncestors(callback) { const parent = this.parent; if (parent !== null) { callback(parent); parent.traverseAncestors(callback); } } updateMatrix() { this.matrix.compose(this.position, this.quaternion, this.scale); this.matrixWorldNeedsUpdate = true; } updateMatrixWorld(force) { if (this.matrixAutoUpdate) this.updateMatrix(); if (this.matrixWorldNeedsUpdate || force) { if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } this.matrixWorldNeedsUpdate = false; force = true; } // update children const children = this.children; for (let i = 0, l = children.length; i < l; i++) { const child = children[i]; if (child.matrixWorldAutoUpdate === true || force === true) { child.updateMatrixWorld(force); } } } updateWorldMatrix(updateParents, updateChildren) { const parent = this.parent; if (updateParents === true && parent !== null && parent.matrixWorldAutoUpdate === true) { parent.updateWorldMatrix(true, false); } if (this.matrixAutoUpdate) this.updateMatrix(); if (this.parent === null) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } // update children if (updateChildren === true) { const children = this.children; for (let i = 0, l = children.length; i < l; i++) { const child = children[i]; if (child.matrixWorldAutoUpdate === true) { child.updateWorldMatrix(false, true); } } } } toJSON(meta) { // meta is a string when called from JSON.stringify const isRootObject = meta === undefined || typeof meta === 'string'; const output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if (isRootObject) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {}, shapes: {}, skeletons: {}, animations: {}, nodes: {} }; output.metadata = { version: 4.6, type: 'Object', generator: 'Object3D.toJSON' }; } // standard Object3D serialization const object = {}; object.uuid = this.uuid; object.type = this.type; if (this.name !== '') object.name = this.name; if (this.castShadow === true) object.castShadow = true; if (this.receiveShadow === true) object.receiveShadow = true; if (this.visible === false) object.visible = false; if (this.frustumCulled === false) object.frustumCulled = false; if (this.renderOrder !== 0) object.renderOrder = this.renderOrder; if (Object.keys(this.userData).length > 0) object.userData = this.userData; object.layers = this.layers.mask; object.matrix = this.matrix.toArray(); object.up = this.up.toArray(); if (this.matrixAutoUpdate === false) object.matrixAutoUpdate = false; // object specific properties if (this.isInstancedMesh) { object.type = 'InstancedMesh'; object.count = this.count; object.instanceMatrix = this.instanceMatrix.toJSON(); if (this.instanceColor !== null) object.instanceColor = this.instanceColor.toJSON(); } // function serialize(library, element) { if (library[element.uuid] === undefined) { library[element.uuid] = element.toJSON(meta); } return element.uuid; } if (this.isScene) { if (this.background) { if (this.background.isColor) { object.background = this.background.toJSON(); } else if (this.background.isTexture) { object.background = this.background.toJSON(meta).uuid; } } if (this.environment && this.environment.isTexture && this.environment.isRenderTargetTexture !== true) { object.environment = this.environment.toJSON(meta).uuid; } } else if (this.isMesh || this.isLine || this.isPoints) { object.geometry = serialize(meta.geometries, this.geometry); const parameters = this.geometry.parameters; if (parameters !== undefined && parameters.shapes !== undefined) { const shapes = parameters.shapes; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; serialize(meta.shapes, shape); } } else { serialize(meta.shapes, shapes); } } } if (this.isSkinnedMesh) { object.bindMode = this.bindMode; object.bindMatrix = this.bindMatrix.toArray(); if (this.skeleton !== undefined) { serialize(meta.skeletons, this.skeleton); object.skeleton = this.skeleton.uuid; } } if (this.material !== undefined) { if (Array.isArray(this.material)) { const uuids = []; for (let i = 0, l = this.material.length; i < l; i++) { uuids.push(serialize(meta.materials, this.material[i])); } object.material = uuids; } else { object.material = serialize(meta.materials, this.material); } } // if (this.children.length > 0) { object.children = []; for (let i = 0; i < this.children.length; i++) { object.children.push(this.children[i].toJSON(meta).object); } } // if (this.animations.length > 0) { object.animations = []; for (let i = 0; i < this.animations.length; i++) { const animation = this.animations[i]; object.animations.push(serialize(meta.animations, animation)); } } if (isRootObject) { const geometries = extractFromCache(meta.geometries); const materials = extractFromCache(meta.materials); const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); const shapes = extractFromCache(meta.shapes); const skeletons = extractFromCache(meta.skeletons); const animations = extractFromCache(meta.animations); const nodes = extractFromCache(meta.nodes); if (geometries.length > 0) output.geometries = geometries; if (materials.length > 0) output.materials = materials; if (textures.length > 0) output.textures = textures; if (images.length > 0) output.images = images; if (shapes.length > 0) output.shapes = shapes; if (skeletons.length > 0) output.skeletons = skeletons; if (animations.length > 0) output.animations = animations; if (nodes.length > 0) output.nodes = nodes; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } } clone(recursive) { return new this.constructor().copy(this, recursive); } copy(source, recursive = true) { this.name = source.name; this.up.copy(source.up); this.position.copy(source.position); this.rotation.order = source.rotation.order; this.quaternion.copy(source.quaternion); this.scale.copy(source.scale); this.matrix.copy(source.matrix); this.matrixWorld.copy(source.matrixWorld); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.matrixWorldAutoUpdate = source.matrixWorldAutoUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.animations = source.animations; this.userData = JSON.parse(JSON.stringify(source.userData)); if (recursive === true) { for (let i = 0; i < source.children.length; i++) { const child = source.children[i]; this.add(child.clone()); } } return this; } } Object3D.DEFAULT_UP = /*@__PURE__*/new Vector3(0, 1, 0); Object3D.DEFAULT_MATRIX_AUTO_UPDATE = true; Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE = true; const _v0$1 = /*@__PURE__*/new Vector3(); const _v1$3 = /*@__PURE__*/new Vector3(); const _v2$2 = /*@__PURE__*/new Vector3(); const _v3$1 = /*@__PURE__*/new Vector3(); const _vab = /*@__PURE__*/new Vector3(); const _vac = /*@__PURE__*/new Vector3(); const _vbc = /*@__PURE__*/new Vector3(); const _vap = /*@__PURE__*/new Vector3(); const _vbp = /*@__PURE__*/new Vector3(); const _vcp = /*@__PURE__*/new Vector3(); let warnedGetUV = false; class Triangle { constructor(a = new Vector3(), b = new Vector3(), c = new Vector3()) { this.a = a; this.b = b; this.c = c; } static getNormal(a, b, c, target) { target.subVectors(c, b); _v0$1.subVectors(a, b); target.cross(_v0$1); const targetLengthSq = target.lengthSq(); if (targetLengthSq > 0) { return target.multiplyScalar(1 / Math.sqrt(targetLengthSq)); } return target.set(0, 0, 0); } // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html static getBarycoord(point, a, b, c, target) { _v0$1.subVectors(c, a); _v1$3.subVectors(b, a); _v2$2.subVectors(point, a); const dot00 = _v0$1.dot(_v0$1); const dot01 = _v0$1.dot(_v1$3); const dot02 = _v0$1.dot(_v2$2); const dot11 = _v1$3.dot(_v1$3); const dot12 = _v1$3.dot(_v2$2); const denom = dot00 * dot11 - dot01 * dot01; // collinear or singular triangle if (denom === 0) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return target.set(-2, -1, -1); } const invDenom = 1 / denom; const u = (dot11 * dot02 - dot01 * dot12) * invDenom; const v = (dot00 * dot12 - dot01 * dot02) * invDenom; // barycentric coordinates must always sum to 1 return target.set(1 - u - v, v, u); } static containsPoint(point, a, b, c) { this.getBarycoord(point, a, b, c, _v3$1); return _v3$1.x >= 0 && _v3$1.y >= 0 && _v3$1.x + _v3$1.y <= 1; } static getUV(point, p1, p2, p3, uv1, uv2, uv3, target) { // @deprecated, r151 if (warnedGetUV === false) { console.warn('THREE.Triangle.getUV() has been renamed to THREE.Triangle.getInterpolation().'); warnedGetUV = true; } return this.getInterpolation(point, p1, p2, p3, uv1, uv2, uv3, target); } static getInterpolation(point, p1, p2, p3, v1, v2, v3, target) { this.getBarycoord(point, p1, p2, p3, _v3$1); target.setScalar(0); target.addScaledVector(v1, _v3$1.x); target.addScaledVector(v2, _v3$1.y); target.addScaledVector(v3, _v3$1.z); return target; } static isFrontFacing(a, b, c, direction) { _v0$1.subVectors(c, b); _v1$3.subVectors(a, b); // strictly front facing return _v0$1.cross(_v1$3).dot(direction) < 0 ? true : false; } set(a, b, c) { this.a.copy(a); this.b.copy(b); this.c.copy(c); return this; } setFromPointsAndIndices(points, i0, i1, i2) { this.a.copy(points[i0]); this.b.copy(points[i1]); this.c.copy(points[i2]); return this; } setFromAttributeAndIndices(attribute, i0, i1, i2) { this.a.fromBufferAttribute(attribute, i0); this.b.fromBufferAttribute(attribute, i1); this.c.fromBufferAttribute(attribute, i2); return this; } clone() { return new this.constructor().copy(this); } copy(triangle) { this.a.copy(triangle.a); this.b.copy(triangle.b); this.c.copy(triangle.c); return this; } getArea() { _v0$1.subVectors(this.c, this.b); _v1$3.subVectors(this.a, this.b); return _v0$1.cross(_v1$3).length() * 0.5; } getMidpoint(target) { return target.addVectors(this.a, this.b).add(this.c).multiplyScalar(1 / 3); } getNormal(target) { return Triangle.getNormal(this.a, this.b, this.c, target); } getPlane(target) { return target.setFromCoplanarPoints(this.a, this.b, this.c); } getBarycoord(point, target) { return Triangle.getBarycoord(point, this.a, this.b, this.c, target); } getUV(point, uv1, uv2, uv3, target) { // @deprecated, r151 if (warnedGetUV === false) { console.warn('THREE.Triangle.getUV() has been renamed to THREE.Triangle.getInterpolation().'); warnedGetUV = true; } return Triangle.getInterpolation(point, this.a, this.b, this.c, uv1, uv2, uv3, target); } getInterpolation(point, v1, v2, v3, target) { return Triangle.getInterpolation(point, this.a, this.b, this.c, v1, v2, v3, target); } containsPoint(point) { return Triangle.containsPoint(point, this.a, this.b, this.c); } isFrontFacing(direction) { return Triangle.isFrontFacing(this.a, this.b, this.c, direction); } intersectsBox(box) { return box.intersectsTriangle(this); } closestPointToPoint(p, target) { const a = this.a, b = this.b, c = this.c; let v, w; // algorithm thanks to Real-Time Collision Detection by Christer Ericson, // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., // under the accompanying license; see chapter 5.1.5 for detailed explanation. // basically, we're distinguishing which of the voronoi regions of the triangle // the point lies in with the minimum amount of redundant computation. _vab.subVectors(b, a); _vac.subVectors(c, a); _vap.subVectors(p, a); const d1 = _vab.dot(_vap); const d2 = _vac.dot(_vap); if (d1 <= 0 && d2 <= 0) { // vertex region of A; barycentric coords (1, 0, 0) return target.copy(a); } _vbp.subVectors(p, b); const d3 = _vab.dot(_vbp); const d4 = _vac.dot(_vbp); if (d3 >= 0 && d4 <= d3) { // vertex region of B; barycentric coords (0, 1, 0) return target.copy(b); } const vc = d1 * d4 - d3 * d2; if (vc <= 0 && d1 >= 0 && d3 <= 0) { v = d1 / (d1 - d3); // edge region of AB; barycentric coords (1-v, v, 0) return target.copy(a).addScaledVector(_vab, v); } _vcp.subVectors(p, c); const d5 = _vab.dot(_vcp); const d6 = _vac.dot(_vcp); if (d6 >= 0 && d5 <= d6) { // vertex region of C; barycentric coords (0, 0, 1) return target.copy(c); } const vb = d5 * d2 - d1 * d6; if (vb <= 0 && d2 >= 0 && d6 <= 0) { w = d2 / (d2 - d6); // edge region of AC; barycentric coords (1-w, 0, w) return target.copy(a).addScaledVector(_vac, w); } const va = d3 * d6 - d5 * d4; if (va <= 0 && d4 - d3 >= 0 && d5 - d6 >= 0) { _vbc.subVectors(c, b); w = (d4 - d3) / (d4 - d3 + (d5 - d6)); // edge region of BC; barycentric coords (0, 1-w, w) return target.copy(b).addScaledVector(_vbc, w); // edge region of BC } // face region const denom = 1 / (va + vb + vc); // u = va * denom v = vb * denom; w = vc * denom; return target.copy(a).addScaledVector(_vab, v).addScaledVector(_vac, w); } equals(triangle) { return triangle.a.equals(this.a) && triangle.b.equals(this.b) && triangle.c.equals(this.c); } } let materialId = 0; class Material extends EventDispatcher { constructor() { super(); this.isMaterial = true; Object.defineProperty(this, 'id', { value: materialId++ }); this.uuid = generateUUID(); this.name = ''; this.type = 'Material'; this.blending = NormalBlending; this.side = FrontSide; this.vertexColors = false; this.opacity = 1; this.transparent = false; this.alphaHash = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.stencilWriteMask = 0xff; this.stencilFunc = AlwaysStencilFunc; this.stencilRef = 0; this.stencilFuncMask = 0xff; this.stencilFail = KeepStencilOp; this.stencilZFail = KeepStencilOp; this.stencilZPass = KeepStencilOp; this.stencilWrite = false; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.shadowSide = null; this.colorWrite = true; this.precision = null; // override the renderer's default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.dithering = false; this.alphaToCoverage = false; this.premultipliedAlpha = false; this.forceSinglePass = false; this.visible = true; this.toneMapped = true; this.userData = {}; this.version = 0; this._alphaTest = 0; } get alphaTest() { return this._alphaTest; } set alphaTest(value) { if (this._alphaTest > 0 !== value > 0) { this.version++; } this._alphaTest = value; } onBuild( /* shaderobject, renderer */) {} onBeforeRender( /* renderer, scene, camera, geometry, object, group */) {} onBeforeCompile( /* shaderobject, renderer */) {} customProgramCacheKey() { return this.onBeforeCompile.toString(); } setValues(values) { if (values === undefined) return; for (const key in values) { const newValue = values[key]; if (newValue === undefined) { console.warn(`THREE.Material: parameter '${key}' has value of undefined.`); continue; } const currentValue = this[key]; if (currentValue === undefined) { console.warn(`THREE.Material: '${key}' is not a property of THREE.${this.type}.`); continue; } if (currentValue && currentValue.isColor) { currentValue.set(newValue); } else if (currentValue && currentValue.isVector3 && newValue && newValue.isVector3) { currentValue.copy(newValue); } else { this[key] = newValue; } } } toJSON(meta) { const isRootObject = meta === undefined || typeof meta === 'string'; if (isRootObject) { meta = { textures: {}, images: {} }; } const data = { metadata: { version: 4.6, type: 'Material', generator: 'Material.toJSON' } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== '') data.name = this.name; if (this.color && this.color.isColor) data.color = this.color.getHex(); if (this.roughness !== undefined) data.roughness = this.roughness; if (this.metalness !== undefined) data.metalness = this.metalness; if (this.sheen !== undefined) data.sheen = this.sheen; if (this.sheenColor && this.sheenColor.isColor) data.sheenColor = this.sheenColor.getHex(); if (this.sheenRoughness !== undefined) data.sheenRoughness = this.sheenRoughness; if (this.emissive && this.emissive.isColor) data.emissive = this.emissive.getHex(); if (this.emissiveIntensity && this.emissiveIntensity !== 1) data.emissiveIntensity = this.emissiveIntensity; if (this.specular && this.specular.isColor) data.specular = this.specular.getHex(); if (this.specularIntensity !== undefined) data.specularIntensity = this.specularIntensity; if (this.specularColor && this.specularColor.isColor) data.specularColor = this.specularColor.getHex(); if (this.shininess !== undefined) data.shininess = this.shininess; if (this.clearcoat !== undefined) data.clearcoat = this.clearcoat; if (this.clearcoatRoughness !== undefined) data.clearcoatRoughness = this.clearcoatRoughness; if (this.clearcoatMap && this.clearcoatMap.isTexture) { data.clearcoatMap = this.clearcoatMap.toJSON(meta).uuid; } if (this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture) { data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON(meta).uuid; } if (this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture) { data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON(meta).uuid; data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); } if (this.iridescence !== undefined) data.iridescence = this.iridescence; if (this.iridescenceIOR !== undefined) data.iridescenceIOR = this.iridescenceIOR; if (this.iridescenceThicknessRange !== undefined) data.iridescenceThicknessRange = this.iridescenceThicknessRange; if (this.iridescenceMap && this.iridescenceMap.isTexture) { data.iridescenceMap = this.iridescenceMap.toJSON(meta).uuid; } if (this.iridescenceThicknessMap && this.iridescenceThicknessMap.isTexture) { data.iridescenceThicknessMap = this.iridescenceThicknessMap.toJSON(meta).uuid; } if (this.anisotropy !== undefined) data.anisotropy = this.anisotropy; if (this.anisotropyRotation !== undefined) data.anisotropyRotation = this.anisotropyRotation; if (this.anisotropyMap && this.anisotropyMap.isTexture) { data.anisotropyMap = this.anisotropyMap.toJSON(meta).uuid; } if (this.map && this.map.isTexture) data.map = this.map.toJSON(meta).uuid; if (this.matcap && this.matcap.isTexture) data.matcap = this.matcap.toJSON(meta).uuid; if (this.alphaMap && this.alphaMap.isTexture) data.alphaMap = this.alphaMap.toJSON(meta).uuid; if (this.lightMap && this.lightMap.isTexture) { data.lightMap = this.lightMap.toJSON(meta).uuid; data.lightMapIntensity = this.lightMapIntensity; } if (this.aoMap && this.aoMap.isTexture) { data.aoMap = this.aoMap.toJSON(meta).uuid; data.aoMapIntensity = this.aoMapIntensity; } if (this.bumpMap && this.bumpMap.isTexture) { data.bumpMap = this.bumpMap.toJSON(meta).uuid; data.bumpScale = this.bumpScale; } if (this.normalMap && this.normalMap.isTexture) { data.normalMap = this.normalMap.toJSON(meta).uuid; data.normalMapType = this.normalMapType; data.normalScale = this.normalScale.toArray(); } if (this.displacementMap && this.displacementMap.isTexture) { data.displacementMap = this.displacementMap.toJSON(meta).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if (this.roughnessMap && this.roughnessMap.isTexture) data.roughnessMap = this.roughnessMap.toJSON(meta).uuid; if (this.metalnessMap && this.metalnessMap.isTexture) data.metalnessMap = this.metalnessMap.toJSON(meta).uuid; if (this.emissiveMap && this.emissiveMap.isTexture) data.emissiveMap = this.emissiveMap.toJSON(meta).uuid; if (this.specularMap && this.specularMap.isTexture) data.specularMap = this.specularMap.toJSON(meta).uuid; if (this.specularIntensityMap && this.specularIntensityMap.isTexture) data.specularIntensityMap = this.specularIntensityMap.toJSON(meta).uuid; if (this.specularColorMap && this.specularColorMap.isTexture) data.specularColorMap = this.specularColorMap.toJSON(meta).uuid; if (this.envMap && this.envMap.isTexture) { data.envMap = this.envMap.toJSON(meta).uuid; if (this.combine !== undefined) data.combine = this.combine; } if (this.envMapIntensity !== undefined) data.envMapIntensity = this.envMapIntensity; if (this.reflectivity !== undefined) data.reflectivity = this.reflectivity; if (this.refractionRatio !== undefined) data.refractionRatio = this.refractionRatio; if (this.gradientMap && this.gradientMap.isTexture) { data.gradientMap = this.gradientMap.toJSON(meta).uuid; } if (this.transmission !== undefined) data.transmission = this.transmission; if (this.transmissionMap && this.transmissionMap.isTexture) data.transmissionMap = this.transmissionMap.toJSON(meta).uuid; if (this.thickness !== undefined) data.thickness = this.thickness; if (this.thicknessMap && this.thicknessMap.isTexture) data.thicknessMap = this.thicknessMap.toJSON(meta).uuid; if (this.attenuationDistance !== undefined && this.attenuationDistance !== Infinity) data.attenuationDistance = this.attenuationDistance; if (this.attenuationColor !== undefined) data.attenuationColor = this.attenuationColor.getHex(); if (this.size !== undefined) data.size = this.size; if (this.shadowSide !== null) data.shadowSide = this.shadowSide; if (this.sizeAttenuation !== undefined) data.sizeAttenuation = this.sizeAttenuation; if (this.blending !== NormalBlending) data.blending = this.blending; if (this.side !== FrontSide) data.side = this.side; if (this.vertexColors) data.vertexColors = true; if (this.opacity < 1) data.opacity = this.opacity; if (this.transparent === true) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; data.colorWrite = this.colorWrite; data.stencilWrite = this.stencilWrite; data.stencilWriteMask = this.stencilWriteMask; data.stencilFunc = this.stencilFunc; data.stencilRef = this.stencilRef; data.stencilFuncMask = this.stencilFuncMask; data.stencilFail = this.stencilFail; data.stencilZFail = this.stencilZFail; data.stencilZPass = this.stencilZPass; // rotation (SpriteMaterial) if (this.rotation !== undefined && this.rotation !== 0) data.rotation = this.rotation; if (this.polygonOffset === true) data.polygonOffset = true; if (this.polygonOffsetFactor !== 0) data.polygonOffsetFactor = this.polygonOffsetFactor; if (this.polygonOffsetUnits !== 0) data.polygonOffsetUnits = this.polygonOffsetUnits; if (this.linewidth !== undefined && this.linewidth !== 1) data.linewidth = this.linewidth; if (this.dashSize !== undefined) data.dashSize = this.dashSize; if (this.gapSize !== undefined) data.gapSize = this.gapSize; if (this.scale !== undefined) data.scale = this.scale; if (this.dithering === true) data.dithering = true; if (this.alphaTest > 0) data.alphaTest = this.alphaTest; if (this.alphaHash === true) data.alphaHash = this.alphaHash; if (this.alphaToCoverage === true) data.alphaToCoverage = this.alphaToCoverage; if (this.premultipliedAlpha === true) data.premultipliedAlpha = this.premultipliedAlpha; if (this.forceSinglePass === true) data.forceSinglePass = this.forceSinglePass; if (this.wireframe === true) data.wireframe = this.wireframe; if (this.wireframeLinewidth > 1) data.wireframeLinewidth = this.wireframeLinewidth; if (this.wireframeLinecap !== 'round') data.wireframeLinecap = this.wireframeLinecap; if (this.wireframeLinejoin !== 'round') data.wireframeLinejoin = this.wireframeLinejoin; if (this.flatShading === true) data.flatShading = this.flatShading; if (this.visible === false) data.visible = false; if (this.toneMapped === false) data.toneMapped = false; if (this.fog === false) data.fog = false; if (Object.keys(this.userData).length > 0) data.userData = this.userData; // TODO: Copied from Object3D.toJSON function extractFromCache(cache) { const values = []; for (const key in cache) { const data = cache[key]; delete data.metadata; values.push(data); } return values; } if (isRootObject) { const textures = extractFromCache(meta.textures); const images = extractFromCache(meta.images); if (textures.length > 0) data.textures = textures; if (images.length > 0) data.images = images; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { this.name = source.name; this.blending = source.blending; this.side = source.side; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.stencilWriteMask = source.stencilWriteMask; this.stencilFunc = source.stencilFunc; this.stencilRef = source.stencilRef; this.stencilFuncMask = source.stencilFuncMask; this.stencilFail = source.stencilFail; this.stencilZFail = source.stencilZFail; this.stencilZPass = source.stencilZPass; this.stencilWrite = source.stencilWrite; const srcPlanes = source.clippingPlanes; let dstPlanes = null; if (srcPlanes !== null) { const n = srcPlanes.length; dstPlanes = new Array(n); for (let i = 0; i !== n; ++i) { dstPlanes[i] = srcPlanes[i].clone(); } } this.clippingPlanes = dstPlanes; this.clipIntersection = source.clipIntersection; this.clipShadows = source.clipShadows; this.shadowSide = source.shadowSide; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.dithering = source.dithering; this.alphaTest = source.alphaTest; this.alphaHash = source.alphaHash; this.alphaToCoverage = source.alphaToCoverage; this.premultipliedAlpha = source.premultipliedAlpha; this.forceSinglePass = source.forceSinglePass; this.visible = source.visible; this.toneMapped = source.toneMapped; this.userData = JSON.parse(JSON.stringify(source.userData)); return this; } dispose() { this.dispatchEvent({ type: 'dispose' }); } set needsUpdate(value) { if (value === true) this.version++; } } const _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; const _hslA = { h: 0, s: 0, l: 0 }; const _hslB = { h: 0, s: 0, l: 0 }; function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t); return p; } class Color { constructor(r, g, b) { this.isColor = true; this.r = 1; this.g = 1; this.b = 1; return this.set(r, g, b); } set(r, g, b) { if (g === undefined && b === undefined) { // r is THREE.Color, hex or string const value = r; if (value && value.isColor) { this.copy(value); } else if (typeof value === 'number') { this.setHex(value); } else if (typeof value === 'string') { this.setStyle(value); } } else { this.setRGB(r, g, b); } return this; } setScalar(scalar) { this.r = scalar; this.g = scalar; this.b = scalar; return this; } setHex(hex, colorSpace = SRGBColorSpace) { hex = Math.floor(hex); this.r = (hex >> 16 & 255) / 255; this.g = (hex >> 8 & 255) / 255; this.b = (hex & 255) / 255; ColorManagement.toWorkingColorSpace(this, colorSpace); return this; } setRGB(r, g, b, colorSpace = ColorManagement.workingColorSpace) { this.r = r; this.g = g; this.b = b; ColorManagement.toWorkingColorSpace(this, colorSpace); return this; } setHSL(h, s, l, colorSpace = ColorManagement.workingColorSpace) { // h,s,l ranges are in 0.0 - 1.0 h = euclideanModulo(h, 1); s = clamp(s, 0, 1); l = clamp(l, 0, 1); if (s === 0) { this.r = this.g = this.b = l; } else { const p = l <= 0.5 ? l * (1 + s) : l + s - l * s; const q = 2 * l - p; this.r = hue2rgb(q, p, h + 1 / 3); this.g = hue2rgb(q, p, h); this.b = hue2rgb(q, p, h - 1 / 3); } ColorManagement.toWorkingColorSpace(this, colorSpace); return this; } setStyle(style, colorSpace = SRGBColorSpace) { function handleAlpha(string) { if (string === undefined) return; if (parseFloat(string) < 1) { console.warn('THREE.Color: Alpha component of ' + style + ' will be ignored.'); } } let m; if (m = /^(\w+)\(([^\)]*)\)/.exec(style)) { // rgb / hsl let color; const name = m[1]; const components = m[2]; switch (name) { case 'rgb': case 'rgba': if (color = /^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(components)) { // rgb(255,0,0) rgba(255,0,0,0.5) handleAlpha(color[4]); return this.setRGB(Math.min(255, parseInt(color[1], 10)) / 255, Math.min(255, parseInt(color[2], 10)) / 255, Math.min(255, parseInt(color[3], 10)) / 255, colorSpace); } if (color = /^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(components)) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) handleAlpha(color[4]); return this.setRGB(Math.min(100, parseInt(color[1], 10)) / 100, Math.min(100, parseInt(color[2], 10)) / 100, Math.min(100, parseInt(color[3], 10)) / 100, colorSpace); } break; case 'hsl': case 'hsla': if (color = /^\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\%\s*,\s*(\d*\.?\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(components)) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) handleAlpha(color[4]); return this.setHSL(parseFloat(color[1]) / 360, parseFloat(color[2]) / 100, parseFloat(color[3]) / 100, colorSpace); } break; default: console.warn('THREE.Color: Unknown color model ' + style); } } else if (m = /^\#([A-Fa-f\d]+)$/.exec(style)) { // hex color const hex = m[1]; const size = hex.length; if (size === 3) { // #ff0 return this.setRGB(parseInt(hex.charAt(0), 16) / 15, parseInt(hex.charAt(1), 16) / 15, parseInt(hex.charAt(2), 16) / 15, colorSpace); } else if (size === 6) { // #ff0000 return this.setHex(parseInt(hex, 16), colorSpace); } else { console.warn('THREE.Color: Invalid hex color ' + style); } } else if (style && style.length > 0) { return this.setColorName(style, colorSpace); } return this; } setColorName(style, colorSpace = SRGBColorSpace) { // color keywords const hex = _colorKeywords[style.toLowerCase()]; if (hex !== undefined) { // red this.setHex(hex, colorSpace); } else { // unknown color console.warn('THREE.Color: Unknown color ' + style); } return this; } clone() { return new this.constructor(this.r, this.g, this.b); } copy(color) { this.r = color.r; this.g = color.g; this.b = color.b; return this; } copySRGBToLinear(color) { this.r = SRGBToLinear(color.r); this.g = SRGBToLinear(color.g); this.b = SRGBToLinear(color.b); return this; } copyLinearToSRGB(color) { this.r = LinearToSRGB(color.r); this.g = LinearToSRGB(color.g); this.b = LinearToSRGB(color.b); return this; } convertSRGBToLinear() { this.copySRGBToLinear(this); return this; } convertLinearToSRGB() { this.copyLinearToSRGB(this); return this; } getHex(colorSpace = SRGBColorSpace) { ColorManagement.fromWorkingColorSpace(_color.copy(this), colorSpace); return Math.round(clamp(_color.r * 255, 0, 255)) * 65536 + Math.round(clamp(_color.g * 255, 0, 255)) * 256 + Math.round(clamp(_color.b * 255, 0, 255)); } getHexString(colorSpace = SRGBColorSpace) { return ('000000' + this.getHex(colorSpace).toString(16)).slice(-6); } getHSL(target, colorSpace = ColorManagement.workingColorSpace) { // h,s,l ranges are in 0.0 - 1.0 ColorManagement.fromWorkingColorSpace(_color.copy(this), colorSpace); const r = _color.r, g = _color.g, b = _color.b; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let hue, saturation; const lightness = (min + max) / 2.0; if (min === max) { hue = 0; saturation = 0; } else { const delta = max - min; saturation = lightness <= 0.5 ? delta / (max + min) : delta / (2 - max - min); switch (max) { case r: hue = (g - b) / delta + (g < b ? 6 : 0); break; case g: hue = (b - r) / delta + 2; break; case b: hue = (r - g) / delta + 4; break; } hue /= 6; } target.h = hue; target.s = saturation; target.l = lightness; return target; } getRGB(target, colorSpace = ColorManagement.workingColorSpace) { ColorManagement.fromWorkingColorSpace(_color.copy(this), colorSpace); target.r = _color.r; target.g = _color.g; target.b = _color.b; return target; } getStyle(colorSpace = SRGBColorSpace) { ColorManagement.fromWorkingColorSpace(_color.copy(this), colorSpace); const r = _color.r, g = _color.g, b = _color.b; if (colorSpace !== SRGBColorSpace) { // Requires CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/). return `color(${colorSpace} ${r.toFixed(3)} ${g.toFixed(3)} ${b.toFixed(3)})`; } return `rgb(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)})`; } offsetHSL(h, s, l) { this.getHSL(_hslA); _hslA.h += h; _hslA.s += s; _hslA.l += l; this.setHSL(_hslA.h, _hslA.s, _hslA.l); return this; } add(color) { this.r += color.r; this.g += color.g; this.b += color.b; return this; } addColors(color1, color2) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; } addScalar(s) { this.r += s; this.g += s; this.b += s; return this; } sub(color) { this.r = Math.max(0, this.r - color.r); this.g = Math.max(0, this.g - color.g); this.b = Math.max(0, this.b - color.b); return this; } multiply(color) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; } multiplyScalar(s) { this.r *= s; this.g *= s; this.b *= s; return this; } lerp(color, alpha) { this.r += (color.r - this.r) * alpha; this.g += (color.g - this.g) * alpha; this.b += (color.b - this.b) * alpha; return this; } lerpColors(color1, color2, alpha) { this.r = color1.r + (color2.r - color1.r) * alpha; this.g = color1.g + (color2.g - color1.g) * alpha; this.b = color1.b + (color2.b - color1.b) * alpha; return this; } lerpHSL(color, alpha) { this.getHSL(_hslA); color.getHSL(_hslB); const h = lerp(_hslA.h, _hslB.h, alpha); const s = lerp(_hslA.s, _hslB.s, alpha); const l = lerp(_hslA.l, _hslB.l, alpha); this.setHSL(h, s, l); return this; } setFromVector3(v) { this.r = v.x; this.g = v.y; this.b = v.z; return this; } applyMatrix3(m) { const r = this.r, g = this.g, b = this.b; const e = m.elements; this.r = e[0] * r + e[3] * g + e[6] * b; this.g = e[1] * r + e[4] * g + e[7] * b; this.b = e[2] * r + e[5] * g + e[8] * b; return this; } equals(c) { return c.r === this.r && c.g === this.g && c.b === this.b; } fromArray(array, offset = 0) { this.r = array[offset]; this.g = array[offset + 1]; this.b = array[offset + 2]; return this; } toArray(array = [], offset = 0) { array[offset] = this.r; array[offset + 1] = this.g; array[offset + 2] = this.b; return array; } fromBufferAttribute(attribute, index) { this.r = attribute.getX(index); this.g = attribute.getY(index); this.b = attribute.getZ(index); return this; } toJSON() { return this.getHex(); } *[Symbol.iterator]() { yield this.r; yield this.g; yield this.b; } } const _color = /*@__PURE__*/new Color(); Color.NAMES = _colorKeywords; class MeshBasicMaterial extends Material { constructor(parameters) { super(); this.isMeshBasicMaterial = true; this.type = 'MeshBasicMaterial'; this.color = new Color(0xffffff); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = 'round'; this.wireframeLinejoin = 'round'; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.fog = source.fog; return this; } } // Fast Half Float Conversions, http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf const _tables = /*@__PURE__*/_generateTables(); function _generateTables() { // float32 to float16 helpers const buffer = new ArrayBuffer(4); const floatView = new Float32Array(buffer); const uint32View = new Uint32Array(buffer); const baseTable = new Uint32Array(512); const shiftTable = new Uint32Array(512); for (let i = 0; i < 256; ++i) { const e = i - 127; // very small number (0, -0) if (e < -27) { baseTable[i] = 0x0000; baseTable[i | 0x100] = 0x8000; shiftTable[i] = 24; shiftTable[i | 0x100] = 24; // small number (denorm) } else if (e < -14) { baseTable[i] = 0x0400 >> -e - 14; baseTable[i | 0x100] = 0x0400 >> -e - 14 | 0x8000; shiftTable[i] = -e - 1; shiftTable[i | 0x100] = -e - 1; // normal number } else if (e <= 15) { baseTable[i] = e + 15 << 10; baseTable[i | 0x100] = e + 15 << 10 | 0x8000; shiftTable[i] = 13; shiftTable[i | 0x100] = 13; // large number (Infinity, -Infinity) } else if (e < 128) { baseTable[i] = 0x7c00; baseTable[i | 0x100] = 0xfc00; shiftTable[i] = 24; shiftTable[i | 0x100] = 24; // stay (NaN, Infinity, -Infinity) } else { baseTable[i] = 0x7c00; baseTable[i | 0x100] = 0xfc00; shiftTable[i] = 13; shiftTable[i | 0x100] = 13; } } // float16 to float32 helpers const mantissaTable = new Uint32Array(2048); const exponentTable = new Uint32Array(64); const offsetTable = new Uint32Array(64); for (let i = 1; i < 1024; ++i) { let m = i << 13; // zero pad mantissa bits let e = 0; // zero exponent // normalized while ((m & 0x00800000) === 0) { m <<= 1; e -= 0x00800000; // decrement exponent } m &= ~0x00800000; // clear leading 1 bit e += 0x38800000; // adjust bias mantissaTable[i] = m | e; } for (let i = 1024; i < 2048; ++i) { mantissaTable[i] = 0x38000000 + (i - 1024 << 13); } for (let i = 1; i < 31; ++i) { exponentTable[i] = i << 23; } exponentTable[31] = 0x47800000; exponentTable[32] = 0x80000000; for (let i = 33; i < 63; ++i) { exponentTable[i] = 0x80000000 + (i - 32 << 23); } exponentTable[63] = 0xc7800000; for (let i = 1; i < 64; ++i) { if (i !== 32) { offsetTable[i] = 1024; } } return { floatView: floatView, uint32View: uint32View, baseTable: baseTable, shiftTable: shiftTable, mantissaTable: mantissaTable, exponentTable: exponentTable, offsetTable: offsetTable }; } // float32 to float16 function toHalfFloat(val) { if (Math.abs(val) > 65504) console.warn('THREE.DataUtils.toHalfFloat(): Value out of range.'); val = clamp(val, -65504, 65504); _tables.floatView[0] = val; const f = _tables.uint32View[0]; const e = f >> 23 & 0x1ff; return _tables.baseTable[e] + ((f & 0x007fffff) >> _tables.shiftTable[e]); } // float16 to float32 function fromHalfFloat(val) { const m = val >> 10; _tables.uint32View[0] = _tables.mantissaTable[_tables.offsetTable[m] + (val & 0x3ff)] + _tables.exponentTable[m]; return _tables.floatView[0]; } const DataUtils = { toHalfFloat: toHalfFloat, fromHalfFloat: fromHalfFloat }; const _vector$8 = /*@__PURE__*/new Vector3(); const _vector2$1 = /*@__PURE__*/new Vector2(); class BufferAttribute { constructor(array, itemSize, normalized = false) { if (Array.isArray(array)) { throw new TypeError('THREE.BufferAttribute: array should be a Typed Array.'); } this.isBufferAttribute = true; this.name = ''; this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: -1 }; this.gpuType = FloatType; this.version = 0; } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } setUsage(value) { this.usage = value; return this; } copy(source) { this.name = source.name; this.array = new source.array.constructor(source.array); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.usage = source.usage; this.gpuType = source.gpuType; return this; } copyAt(index1, attribute, index2) { index1 *= this.itemSize; index2 *= attribute.itemSize; for (let i = 0, l = this.itemSize; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } copyArray(array) { this.array.set(array); return this; } applyMatrix3(m) { if (this.itemSize === 2) { for (let i = 0, l = this.count; i < l; i++) { _vector2$1.fromBufferAttribute(this, i); _vector2$1.applyMatrix3(m); this.setXY(i, _vector2$1.x, _vector2$1.y); } } else if (this.itemSize === 3) { for (let i = 0, l = this.count; i < l; i++) { _vector$8.fromBufferAttribute(this, i); _vector$8.applyMatrix3(m); this.setXYZ(i, _vector$8.x, _vector$8.y, _vector$8.z); } } return this; } applyMatrix4(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$8.fromBufferAttribute(this, i); _vector$8.applyMatrix4(m); this.setXYZ(i, _vector$8.x, _vector$8.y, _vector$8.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$8.fromBufferAttribute(this, i); _vector$8.applyNormalMatrix(m); this.setXYZ(i, _vector$8.x, _vector$8.y, _vector$8.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$8.fromBufferAttribute(this, i); _vector$8.transformDirection(m); this.setXYZ(i, _vector$8.x, _vector$8.y, _vector$8.z); } return this; } set(value, offset = 0) { // Matching BufferAttribute constructor, do not normalize the array. this.array.set(value, offset); return this; } getX(index) { let x = this.array[index * this.itemSize]; if (this.normalized) x = denormalize(x, this.array); return x; } setX(index, x) { if (this.normalized) x = normalize(x, this.array); this.array[index * this.itemSize] = x; return this; } getY(index) { let y = this.array[index * this.itemSize + 1]; if (this.normalized) y = denormalize(y, this.array); return y; } setY(index, y) { if (this.normalized) y = normalize(y, this.array); this.array[index * this.itemSize + 1] = y; return this; } getZ(index) { let z = this.array[index * this.itemSize + 2]; if (this.normalized) z = denormalize(z, this.array); return z; } setZ(index, z) { if (this.normalized) z = normalize(z, this.array); this.array[index * this.itemSize + 2] = z; return this; } getW(index) { let w = this.array[index * this.itemSize + 3]; if (this.normalized) w = denormalize(w, this.array); return w; } setW(index, w) { if (this.normalized) w = normalize(w, this.array); this.array[index * this.itemSize + 3] = w; return this; } setXY(index, x, y) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); } this.array[index + 0] = x; this.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); } this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); w = normalize(w, this.array); } this.array[index + 0] = x; this.array[index + 1] = y; this.array[index + 2] = z; this.array[index + 3] = w; return this; } onUpload(callback) { this.onUploadCallback = callback; return this; } clone() { return new this.constructor(this.array, this.itemSize).copy(this); } toJSON() { const data = { itemSize: this.itemSize, type: this.array.constructor.name, array: Array.from(this.array), normalized: this.normalized }; if (this.name !== '') data.name = this.name; if (this.usage !== StaticDrawUsage) data.usage = this.usage; if (this.updateRange.offset !== 0 || this.updateRange.count !== -1) data.updateRange = this.updateRange; return data; } } // class Int8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int8Array(array), itemSize, normalized); } } class Uint8BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8Array(array), itemSize, normalized); } } class Uint8ClampedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint8ClampedArray(array), itemSize, normalized); } } class Int16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int16Array(array), itemSize, normalized); } } class Uint16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); } } class Int32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Int32Array(array), itemSize, normalized); } } class Uint32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint32Array(array), itemSize, normalized); } } class Float16BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Uint16Array(array), itemSize, normalized); this.isFloat16BufferAttribute = true; } getX(index) { let x = fromHalfFloat(this.array[index * this.itemSize]); if (this.normalized) x = denormalize(x, this.array); return x; } setX(index, x) { if (this.normalized) x = normalize(x, this.array); this.array[index * this.itemSize] = toHalfFloat(x); return this; } getY(index) { let y = fromHalfFloat(this.array[index * this.itemSize + 1]); if (this.normalized) y = denormalize(y, this.array); return y; } setY(index, y) { if (this.normalized) y = normalize(y, this.array); this.array[index * this.itemSize + 1] = toHalfFloat(y); return this; } getZ(index) { let z = fromHalfFloat(this.array[index * this.itemSize + 2]); if (this.normalized) z = denormalize(z, this.array); return z; } setZ(index, z) { if (this.normalized) z = normalize(z, this.array); this.array[index * this.itemSize + 2] = toHalfFloat(z); return this; } getW(index) { let w = fromHalfFloat(this.array[index * this.itemSize + 3]); if (this.normalized) w = denormalize(w, this.array); return w; } setW(index, w) { if (this.normalized) w = normalize(w, this.array); this.array[index * this.itemSize + 3] = toHalfFloat(w); return this; } setXY(index, x, y) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); } this.array[index + 0] = toHalfFloat(x); this.array[index + 1] = toHalfFloat(y); return this; } setXYZ(index, x, y, z) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); } this.array[index + 0] = toHalfFloat(x); this.array[index + 1] = toHalfFloat(y); this.array[index + 2] = toHalfFloat(z); return this; } setXYZW(index, x, y, z, w) { index *= this.itemSize; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); w = normalize(w, this.array); } this.array[index + 0] = toHalfFloat(x); this.array[index + 1] = toHalfFloat(y); this.array[index + 2] = toHalfFloat(z); this.array[index + 3] = toHalfFloat(w); return this; } } class Float32BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float32Array(array), itemSize, normalized); } } class Float64BufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized) { super(new Float64Array(array), itemSize, normalized); } } let _id$1 = 0; const _m1 = /*@__PURE__*/new Matrix4(); const _obj = /*@__PURE__*/new Object3D(); const _offset = /*@__PURE__*/new Vector3(); const _box$1 = /*@__PURE__*/new Box3(); const _boxMorphTargets = /*@__PURE__*/new Box3(); const _vector$7 = /*@__PURE__*/new Vector3(); class BufferGeometry extends EventDispatcher { constructor() { super(); this.isBufferGeometry = true; Object.defineProperty(this, 'id', { value: _id$1++ }); this.uuid = generateUUID(); this.name = ''; this.type = 'BufferGeometry'; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.morphTargetsRelative = false; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; this.userData = {}; } getIndex() { return this.index; } setIndex(index) { if (Array.isArray(index)) { this.index = new (arrayNeedsUint32(index) ? Uint32BufferAttribute : Uint16BufferAttribute)(index, 1); } else { this.index = index; } return this; } getAttribute(name) { return this.attributes[name]; } setAttribute(name, attribute) { this.attributes[name] = attribute; return this; } deleteAttribute(name) { delete this.attributes[name]; return this; } hasAttribute(name) { return this.attributes[name] !== undefined; } addGroup(start, count, materialIndex = 0) { this.groups.push({ start: start, count: count, materialIndex: materialIndex }); } clearGroups() { this.groups = []; } setDrawRange(start, count) { this.drawRange.start = start; this.drawRange.count = count; } applyMatrix4(matrix) { const position = this.attributes.position; if (position !== undefined) { position.applyMatrix4(matrix); position.needsUpdate = true; } const normal = this.attributes.normal; if (normal !== undefined) { const normalMatrix = new Matrix3().getNormalMatrix(matrix); normal.applyNormalMatrix(normalMatrix); normal.needsUpdate = true; } const tangent = this.attributes.tangent; if (tangent !== undefined) { tangent.transformDirection(matrix); tangent.needsUpdate = true; } if (this.boundingBox !== null) { this.computeBoundingBox(); } if (this.boundingSphere !== null) { this.computeBoundingSphere(); } return this; } applyQuaternion(q) { _m1.makeRotationFromQuaternion(q); this.applyMatrix4(_m1); return this; } rotateX(angle) { // rotate geometry around world x-axis _m1.makeRotationX(angle); this.applyMatrix4(_m1); return this; } rotateY(angle) { // rotate geometry around world y-axis _m1.makeRotationY(angle); this.applyMatrix4(_m1); return this; } rotateZ(angle) { // rotate geometry around world z-axis _m1.makeRotationZ(angle); this.applyMatrix4(_m1); return this; } translate(x, y, z) { // translate geometry _m1.makeTranslation(x, y, z); this.applyMatrix4(_m1); return this; } scale(x, y, z) { // scale geometry _m1.makeScale(x, y, z); this.applyMatrix4(_m1); return this; } lookAt(vector) { _obj.lookAt(vector); _obj.updateMatrix(); this.applyMatrix4(_obj.matrix); return this; } center() { this.computeBoundingBox(); this.boundingBox.getCenter(_offset).negate(); this.translate(_offset.x, _offset.y, _offset.z); return this; } setFromPoints(points) { const position = []; for (let i = 0, l = points.length; i < l; i++) { const point = points[i]; position.push(point.x, point.y, point.z || 0); } this.setAttribute('position', new Float32BufferAttribute(position, 3)); return this; } computeBoundingBox() { if (this.boundingBox === null) { this.boundingBox = new Box3(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error('THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".', this); this.boundingBox.set(new Vector3(-Infinity, -Infinity, -Infinity), new Vector3(+Infinity, +Infinity, +Infinity)); return; } if (position !== undefined) { this.boundingBox.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _box$1.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$7.addVectors(this.boundingBox.min, _box$1.min); this.boundingBox.expandByPoint(_vector$7); _vector$7.addVectors(this.boundingBox.max, _box$1.max); this.boundingBox.expandByPoint(_vector$7); } else { this.boundingBox.expandByPoint(_box$1.min); this.boundingBox.expandByPoint(_box$1.max); } } } } else { this.boundingBox.makeEmpty(); } if (isNaN(this.boundingBox.min.x) || isNaN(this.boundingBox.min.y) || isNaN(this.boundingBox.min.z)) { console.error('THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this); } } computeBoundingSphere() { if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } const position = this.attributes.position; const morphAttributesPosition = this.morphAttributes.position; if (position && position.isGLBufferAttribute) { console.error('THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".', this); this.boundingSphere.set(new Vector3(), Infinity); return; } if (position) { // first, find the center of the bounding sphere const center = this.boundingSphere.center; _box$1.setFromBufferAttribute(position); // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; _boxMorphTargets.setFromBufferAttribute(morphAttribute); if (this.morphTargetsRelative) { _vector$7.addVectors(_box$1.min, _boxMorphTargets.min); _box$1.expandByPoint(_vector$7); _vector$7.addVectors(_box$1.max, _boxMorphTargets.max); _box$1.expandByPoint(_vector$7); } else { _box$1.expandByPoint(_boxMorphTargets.min); _box$1.expandByPoint(_boxMorphTargets.max); } } } _box$1.getCenter(center); // second, try to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case let maxRadiusSq = 0; for (let i = 0, il = position.count; i < il; i++) { _vector$7.fromBufferAttribute(position, i); maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$7)); } // process morph attributes if present if (morphAttributesPosition) { for (let i = 0, il = morphAttributesPosition.length; i < il; i++) { const morphAttribute = morphAttributesPosition[i]; const morphTargetsRelative = this.morphTargetsRelative; for (let j = 0, jl = morphAttribute.count; j < jl; j++) { _vector$7.fromBufferAttribute(morphAttribute, j); if (morphTargetsRelative) { _offset.fromBufferAttribute(position, j); _vector$7.add(_offset); } maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$7)); } } } this.boundingSphere.radius = Math.sqrt(maxRadiusSq); if (isNaN(this.boundingSphere.radius)) { console.error('THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this); } } } computeTangents() { const index = this.index; const attributes = this.attributes; // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) if (index === null || attributes.position === undefined || attributes.normal === undefined || attributes.uv === undefined) { console.error('THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)'); return; } const indices = index.array; const positions = attributes.position.array; const normals = attributes.normal.array; const uvs = attributes.uv.array; const nVertices = positions.length / 3; if (this.hasAttribute('tangent') === false) { this.setAttribute('tangent', new BufferAttribute(new Float32Array(4 * nVertices), 4)); } const tangents = this.getAttribute('tangent').array; const tan1 = [], tan2 = []; for (let i = 0; i < nVertices; i++) { tan1[i] = new Vector3(); tan2[i] = new Vector3(); } const vA = new Vector3(), vB = new Vector3(), vC = new Vector3(), uvA = new Vector2(), uvB = new Vector2(), uvC = new Vector2(), sdir = new Vector3(), tdir = new Vector3(); function handleTriangle(a, b, c) { vA.fromArray(positions, a * 3); vB.fromArray(positions, b * 3); vC.fromArray(positions, c * 3); uvA.fromArray(uvs, a * 2); uvB.fromArray(uvs, b * 2); uvC.fromArray(uvs, c * 2); vB.sub(vA); vC.sub(vA); uvB.sub(uvA); uvC.sub(uvA); const r = 1.0 / (uvB.x * uvC.y - uvC.x * uvB.y); // silently ignore degenerate uv triangles having coincident or colinear vertices if (!isFinite(r)) return; sdir.copy(vB).multiplyScalar(uvC.y).addScaledVector(vC, -uvB.y).multiplyScalar(r); tdir.copy(vC).multiplyScalar(uvB.x).addScaledVector(vB, -uvC.x).multiplyScalar(r); tan1[a].add(sdir); tan1[b].add(sdir); tan1[c].add(sdir); tan2[a].add(tdir); tan2[b].add(tdir); tan2[c].add(tdir); } let groups = this.groups; if (groups.length === 0) { groups = [{ start: 0, count: indices.length }]; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleTriangle(indices[j + 0], indices[j + 1], indices[j + 2]); } } const tmp = new Vector3(), tmp2 = new Vector3(); const n = new Vector3(), n2 = new Vector3(); function handleVertex(v) { n.fromArray(normals, v * 3); n2.copy(n); const t = tan1[v]; // Gram-Schmidt orthogonalize tmp.copy(t); tmp.sub(n.multiplyScalar(n.dot(t))).normalize(); // Calculate handedness tmp2.crossVectors(n2, t); const test = tmp2.dot(tan2[v]); const w = test < 0.0 ? -1.0 : 1.0; tangents[v * 4] = tmp.x; tangents[v * 4 + 1] = tmp.y; tangents[v * 4 + 2] = tmp.z; tangents[v * 4 + 3] = w; } for (let i = 0, il = groups.length; i < il; ++i) { const group = groups[i]; const start = group.start; const count = group.count; for (let j = start, jl = start + count; j < jl; j += 3) { handleVertex(indices[j + 0]); handleVertex(indices[j + 1]); handleVertex(indices[j + 2]); } } } computeVertexNormals() { const index = this.index; const positionAttribute = this.getAttribute('position'); if (positionAttribute !== undefined) { let normalAttribute = this.getAttribute('normal'); if (normalAttribute === undefined) { normalAttribute = new BufferAttribute(new Float32Array(positionAttribute.count * 3), 3); this.setAttribute('normal', normalAttribute); } else { // reset existing normals to zero for (let i = 0, il = normalAttribute.count; i < il; i++) { normalAttribute.setXYZ(i, 0, 0, 0); } } const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); const cb = new Vector3(), ab = new Vector3(); // indexed elements if (index) { for (let i = 0, il = index.count; i < il; i += 3) { const vA = index.getX(i + 0); const vB = index.getX(i + 1); const vC = index.getX(i + 2); pA.fromBufferAttribute(positionAttribute, vA); pB.fromBufferAttribute(positionAttribute, vB); pC.fromBufferAttribute(positionAttribute, vC); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); nA.fromBufferAttribute(normalAttribute, vA); nB.fromBufferAttribute(normalAttribute, vB); nC.fromBufferAttribute(normalAttribute, vC); nA.add(cb); nB.add(cb); nC.add(cb); normalAttribute.setXYZ(vA, nA.x, nA.y, nA.z); normalAttribute.setXYZ(vB, nB.x, nB.y, nB.z); normalAttribute.setXYZ(vC, nC.x, nC.y, nC.z); } } else { // non-indexed elements (unconnected triangle soup) for (let i = 0, il = positionAttribute.count; i < il; i += 3) { pA.fromBufferAttribute(positionAttribute, i + 0); pB.fromBufferAttribute(positionAttribute, i + 1); pC.fromBufferAttribute(positionAttribute, i + 2); cb.subVectors(pC, pB); ab.subVectors(pA, pB); cb.cross(ab); normalAttribute.setXYZ(i + 0, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 1, cb.x, cb.y, cb.z); normalAttribute.setXYZ(i + 2, cb.x, cb.y, cb.z); } } this.normalizeNormals(); normalAttribute.needsUpdate = true; } } normalizeNormals() { const normals = this.attributes.normal; for (let i = 0, il = normals.count; i < il; i++) { _vector$7.fromBufferAttribute(normals, i); _vector$7.normalize(); normals.setXYZ(i, _vector$7.x, _vector$7.y, _vector$7.z); } } toNonIndexed() { function convertBufferAttribute(attribute, indices) { const array = attribute.array; const itemSize = attribute.itemSize; const normalized = attribute.normalized; const array2 = new array.constructor(indices.length * itemSize); let index = 0, index2 = 0; for (let i = 0, l = indices.length; i < l; i++) { if (attribute.isInterleavedBufferAttribute) { index = indices[i] * attribute.data.stride + attribute.offset; } else { index = indices[i] * itemSize; } for (let j = 0; j < itemSize; j++) { array2[index2++] = array[index++]; } } return new BufferAttribute(array2, itemSize, normalized); } // if (this.index === null) { console.warn('THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed.'); return this; } const geometry2 = new BufferGeometry(); const indices = this.index.array; const attributes = this.attributes; // attributes for (const name in attributes) { const attribute = attributes[name]; const newAttribute = convertBufferAttribute(attribute, indices); geometry2.setAttribute(name, newAttribute); } // morph attributes const morphAttributes = this.morphAttributes; for (const name in morphAttributes) { const morphArray = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, il = morphAttribute.length; i < il; i++) { const attribute = morphAttribute[i]; const newAttribute = convertBufferAttribute(attribute, indices); morphArray.push(newAttribute); } geometry2.morphAttributes[name] = morphArray; } geometry2.morphTargetsRelative = this.morphTargetsRelative; // groups const groups = this.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; geometry2.addGroup(group.start, group.count, group.materialIndex); } return geometry2; } toJSON() { const data = { metadata: { version: 4.6, type: 'BufferGeometry', generator: 'BufferGeometry.toJSON' } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if (this.name !== '') data.name = this.name; if (Object.keys(this.userData).length > 0) data.userData = this.userData; if (this.parameters !== undefined) { const parameters = this.parameters; for (const key in parameters) { if (parameters[key] !== undefined) data[key] = parameters[key]; } return data; } // for simplicity the code assumes attributes are not shared across geometries, see #15811 data.data = { attributes: {} }; const index = this.index; if (index !== null) { data.data.index = { type: index.array.constructor.name, array: Array.prototype.slice.call(index.array) }; } const attributes = this.attributes; for (const key in attributes) { const attribute = attributes[key]; data.data.attributes[key] = attribute.toJSON(data.data); } const morphAttributes = {}; let hasMorphAttributes = false; for (const key in this.morphAttributes) { const attributeArray = this.morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; array.push(attribute.toJSON(data.data)); } if (array.length > 0) { morphAttributes[key] = array; hasMorphAttributes = true; } } if (hasMorphAttributes) { data.data.morphAttributes = morphAttributes; data.data.morphTargetsRelative = this.morphTargetsRelative; } const groups = this.groups; if (groups.length > 0) { data.data.groups = JSON.parse(JSON.stringify(groups)); } const boundingSphere = this.boundingSphere; if (boundingSphere !== null) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; } clone() { return new this.constructor().copy(this); } copy(source) { // reset this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; // used for storing cloned, shared data const data = {}; // name this.name = source.name; // index const index = source.index; if (index !== null) { this.setIndex(index.clone(data)); } // attributes const attributes = source.attributes; for (const name in attributes) { const attribute = attributes[name]; this.setAttribute(name, attribute.clone(data)); } // morph attributes const morphAttributes = source.morphAttributes; for (const name in morphAttributes) { const array = []; const morphAttribute = morphAttributes[name]; // morphAttribute: array of Float32BufferAttributes for (let i = 0, l = morphAttribute.length; i < l; i++) { array.push(morphAttribute[i].clone(data)); } this.morphAttributes[name] = array; } this.morphTargetsRelative = source.morphTargetsRelative; // groups const groups = source.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; this.addGroup(group.start, group.count, group.materialIndex); } // bounding box const boundingBox = source.boundingBox; if (boundingBox !== null) { this.boundingBox = boundingBox.clone(); } // bounding sphere const boundingSphere = source.boundingSphere; if (boundingSphere !== null) { this.boundingSphere = boundingSphere.clone(); } // draw range this.drawRange.start = source.drawRange.start; this.drawRange.count = source.drawRange.count; // user data this.userData = source.userData; return this; } dispose() { this.dispatchEvent({ type: 'dispose' }); } } const _inverseMatrix$3 = /*@__PURE__*/new Matrix4(); const _ray$3 = /*@__PURE__*/new Ray(); const _sphere$5 = /*@__PURE__*/new Sphere(); const _sphereHitAt = /*@__PURE__*/new Vector3(); const _vA$1 = /*@__PURE__*/new Vector3(); const _vB$1 = /*@__PURE__*/new Vector3(); const _vC$1 = /*@__PURE__*/new Vector3(); const _tempA = /*@__PURE__*/new Vector3(); const _morphA = /*@__PURE__*/new Vector3(); const _uvA$1 = /*@__PURE__*/new Vector2(); const _uvB$1 = /*@__PURE__*/new Vector2(); const _uvC$1 = /*@__PURE__*/new Vector2(); const _normalA = /*@__PURE__*/new Vector3(); const _normalB = /*@__PURE__*/new Vector3(); const _normalC = /*@__PURE__*/new Vector3(); const _intersectionPoint = /*@__PURE__*/new Vector3(); const _intersectionPointWorld = /*@__PURE__*/new Vector3(); class Mesh extends Object3D { constructor(geometry = new BufferGeometry(), material = new MeshBasicMaterial()) { super(); this.isMesh = true; this.type = 'Mesh'; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source, recursive) { super.copy(source, recursive); if (source.morphTargetInfluences !== undefined) { this.morphTargetInfluences = source.morphTargetInfluences.slice(); } if (source.morphTargetDictionary !== undefined) { this.morphTargetDictionary = Object.assign({}, source.morphTargetDictionary); } this.material = source.material; this.geometry = source.geometry; return this; } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } getVertexPosition(index, target) { const geometry = this.geometry; const position = geometry.attributes.position; const morphPosition = geometry.morphAttributes.position; const morphTargetsRelative = geometry.morphTargetsRelative; target.fromBufferAttribute(position, index); const morphInfluences = this.morphTargetInfluences; if (morphPosition && morphInfluences) { _morphA.set(0, 0, 0); for (let i = 0, il = morphPosition.length; i < il; i++) { const influence = morphInfluences[i]; const morphAttribute = morphPosition[i]; if (influence === 0) continue; _tempA.fromBufferAttribute(morphAttribute, index); if (morphTargetsRelative) { _morphA.addScaledVector(_tempA, influence); } else { _morphA.addScaledVector(_tempA.sub(target), influence); } } target.add(_morphA); } return target; } raycast(raycaster, intersects) { const geometry = this.geometry; const material = this.material; const matrixWorld = this.matrixWorld; if (material === undefined) return; // test with bounding sphere in world space if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$5.copy(geometry.boundingSphere); _sphere$5.applyMatrix4(matrixWorld); // check distance from ray origin to bounding sphere _ray$3.copy(raycaster.ray).recast(raycaster.near); if (_sphere$5.containsPoint(_ray$3.origin) === false) { if (_ray$3.intersectSphere(_sphere$5, _sphereHitAt) === null) return; if (_ray$3.origin.distanceToSquared(_sphereHitAt) > (raycaster.far - raycaster.near) ** 2) return; } // convert ray to local space of mesh _inverseMatrix$3.copy(matrixWorld).invert(); _ray$3.copy(raycaster.ray).applyMatrix4(_inverseMatrix$3); // test with bounding box in local space if (geometry.boundingBox !== null) { if (_ray$3.intersectsBox(geometry.boundingBox) === false) return; } // test for intersections with geometry this._computeIntersections(raycaster, intersects, _ray$3); } _computeIntersections(raycaster, intersects, rayLocalSpace) { let intersection; const geometry = this.geometry; const material = this.material; const index = geometry.index; const position = geometry.attributes.position; const uv = geometry.attributes.uv; const uv1 = geometry.attributes.uv1; const normal = geometry.attributes.normal; const groups = geometry.groups; const drawRange = geometry.drawRange; if (index !== null) { // indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min(index.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (let j = start, jl = end; j < jl; j += 3) { const a = index.getX(j); const b = index.getX(j + 1); const c = index.getX(j + 2); intersection = checkGeometryIntersection(this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = index.getX(i); const b = index.getX(i + 1); const c = index.getX(i + 2); intersection = checkGeometryIntersection(this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in indexed buffer semantics intersects.push(intersection); } } } } else if (position !== undefined) { // non-indexed buffer geometry if (Array.isArray(material)) { for (let i = 0, il = groups.length; i < il; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; const start = Math.max(group.start, drawRange.start); const end = Math.min(position.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (let j = start, jl = end; j < jl; j += 3) { const a = j; const b = j + 1; const c = j + 2; intersection = checkGeometryIntersection(this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(j / 3); // triangle number in non-indexed buffer semantics intersection.face.materialIndex = group.materialIndex; intersects.push(intersection); } } } } else { const start = Math.max(0, drawRange.start); const end = Math.min(position.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i += 3) { const a = i; const b = i + 1; const c = i + 2; intersection = checkGeometryIntersection(this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c); if (intersection) { intersection.faceIndex = Math.floor(i / 3); // triangle number in non-indexed buffer semantics intersects.push(intersection); } } } } } } function checkIntersection(object, material, raycaster, ray, pA, pB, pC, point) { let intersect; if (material.side === BackSide) { intersect = ray.intersectTriangle(pC, pB, pA, true, point); } else { intersect = ray.intersectTriangle(pA, pB, pC, material.side === FrontSide, point); } if (intersect === null) return null; _intersectionPointWorld.copy(point); _intersectionPointWorld.applyMatrix4(object.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_intersectionPointWorld); if (distance < raycaster.near || distance > raycaster.far) return null; return { distance: distance, point: _intersectionPointWorld.clone(), object: object }; } function checkGeometryIntersection(object, material, raycaster, ray, uv, uv1, normal, a, b, c) { object.getVertexPosition(a, _vA$1); object.getVertexPosition(b, _vB$1); object.getVertexPosition(c, _vC$1); const intersection = checkIntersection(object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint); if (intersection) { if (uv) { _uvA$1.fromBufferAttribute(uv, a); _uvB$1.fromBufferAttribute(uv, b); _uvC$1.fromBufferAttribute(uv, c); intersection.uv = Triangle.getInterpolation(_intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()); } if (uv1) { _uvA$1.fromBufferAttribute(uv1, a); _uvB$1.fromBufferAttribute(uv1, b); _uvC$1.fromBufferAttribute(uv1, c); intersection.uv1 = Triangle.getInterpolation(_intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2()); intersection.uv2 = intersection.uv1; // @deprecated, r152 } if (normal) { _normalA.fromBufferAttribute(normal, a); _normalB.fromBufferAttribute(normal, b); _normalC.fromBufferAttribute(normal, c); intersection.normal = Triangle.getInterpolation(_intersectionPoint, _vA$1, _vB$1, _vC$1, _normalA, _normalB, _normalC, new Vector3()); if (intersection.normal.dot(ray.direction) > 0) { intersection.normal.multiplyScalar(-1); } } const face = { a: a, b: b, c: c, normal: new Vector3(), materialIndex: 0 }; Triangle.getNormal(_vA$1, _vB$1, _vC$1, face.normal); intersection.face = face; } return intersection; } class BoxGeometry extends BufferGeometry { constructor(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) { super(); this.type = 'BoxGeometry'; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; const scope = this; // segments widthSegments = Math.floor(widthSegments); heightSegments = Math.floor(heightSegments); depthSegments = Math.floor(depthSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let numberOfVertices = 0; let groupStart = 0; // build each side of the box geometry buildPlane('z', 'y', 'x', -1, -1, depth, height, width, depthSegments, heightSegments, 0); // px buildPlane('z', 'y', 'x', 1, -1, depth, height, -width, depthSegments, heightSegments, 1); // nx buildPlane('x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2); // py buildPlane('x', 'z', 'y', 1, -1, width, depth, -height, widthSegments, depthSegments, 3); // ny buildPlane('x', 'y', 'z', 1, -1, width, height, depth, widthSegments, heightSegments, 4); // pz buildPlane('x', 'y', 'z', -1, -1, width, height, -depth, widthSegments, heightSegments, 5); // nz // build geometry this.setIndex(indices); this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); function buildPlane(u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex) { const segmentWidth = width / gridX; const segmentHeight = height / gridY; const widthHalf = width / 2; const heightHalf = height / 2; const depthHalf = depth / 2; const gridX1 = gridX + 1; const gridY1 = gridY + 1; let vertexCounter = 0; let groupCount = 0; const vector = new Vector3(); // generate vertices, normals and uvs for (let iy = 0; iy < gridY1; iy++) { const y = iy * segmentHeight - heightHalf; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[u] = x * udir; vector[v] = y * vdir; vector[w] = depthHalf; // now apply vector to vertex buffer vertices.push(vector.x, vector.y, vector.z); // set values to correct vector component vector[u] = 0; vector[v] = 0; vector[w] = depth > 0 ? 1 : -1; // now apply vector to normal buffer normals.push(vector.x, vector.y, vector.z); // uvs uvs.push(ix / gridX); uvs.push(1 - iy / gridY); // counters vertexCounter += 1; } } // indices // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = numberOfVertices + ix + gridX1 * iy; const b = numberOfVertices + ix + gridX1 * (iy + 1); const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1); const d = numberOfVertices + (ix + 1) + gridX1 * iy; // faces indices.push(a, b, d); indices.push(b, c, d); // increase counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, materialIndex); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new BoxGeometry(data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments); } } /** * Uniform Utilities */ function cloneUniforms(src) { const dst = {}; for (const u in src) { dst[u] = {}; for (const p in src[u]) { const property = src[u][p]; if (property && (property.isColor || property.isMatrix3 || property.isMatrix4 || property.isVector2 || property.isVector3 || property.isVector4 || property.isTexture || property.isQuaternion)) { if (property.isRenderTargetTexture) { console.warn('UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms().'); dst[u][p] = null; } else { dst[u][p] = property.clone(); } } else if (Array.isArray(property)) { dst[u][p] = property.slice(); } else { dst[u][p] = property; } } } return dst; } function mergeUniforms(uniforms) { const merged = {}; for (let u = 0; u < uniforms.length; u++) { const tmp = cloneUniforms(uniforms[u]); for (const p in tmp) { merged[p] = tmp[p]; } } return merged; } function cloneUniformsGroups(src) { const dst = []; for (let u = 0; u < src.length; u++) { dst.push(src[u].clone()); } return dst; } function getUnlitUniformColorSpace(renderer) { if (renderer.getRenderTarget() === null) { // https://github.com/mrdoob/three.js/pull/23937#issuecomment-1111067398 return renderer.outputColorSpace; } return LinearSRGBColorSpace; } // Legacy const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; var default_vertex = "void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}"; var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}"; class ShaderMaterial extends Material { constructor(parameters) { super(); this.isShaderMaterial = true; this.type = 'ShaderMaterial'; this.defines = {}; this.uniforms = {}; this.uniformsGroups = []; this.vertexShader = default_vertex; this.fragmentShader = default_fragment; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.forceSinglePass = true; this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn't include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { 'color': [1, 1, 1], 'uv': [0, 0], 'uv1': [0, 0] }; this.index0AttributeName = undefined; this.uniformsNeedUpdate = false; this.glslVersion = null; if (parameters !== undefined) { this.setValues(parameters); } } copy(source) { super.copy(source); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = cloneUniforms(source.uniforms); this.uniformsGroups = cloneUniformsGroups(source.uniformsGroups); this.defines = Object.assign({}, source.defines); this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.fog = source.fog; this.lights = source.lights; this.clipping = source.clipping; this.extensions = Object.assign({}, source.extensions); this.glslVersion = source.glslVersion; return this; } toJSON(meta) { const data = super.toJSON(meta); data.glslVersion = this.glslVersion; data.uniforms = {}; for (const name in this.uniforms) { const uniform = this.uniforms[name]; const value = uniform.value; if (value && value.isTexture) { data.uniforms[name] = { type: 't', value: value.toJSON(meta).uuid }; } else if (value && value.isColor) { data.uniforms[name] = { type: 'c', value: value.getHex() }; } else if (value && value.isVector2) { data.uniforms[name] = { type: 'v2', value: value.toArray() }; } else if (value && value.isVector3) { data.uniforms[name] = { type: 'v3', value: value.toArray() }; } else if (value && value.isVector4) { data.uniforms[name] = { type: 'v4', value: value.toArray() }; } else if (value && value.isMatrix3) { data.uniforms[name] = { type: 'm3', value: value.toArray() }; } else if (value && value.isMatrix4) { data.uniforms[name] = { type: 'm4', value: value.toArray() }; } else { data.uniforms[name] = { value: value }; // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far } } if (Object.keys(this.defines).length > 0) data.defines = this.defines; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; data.lights = this.lights; data.clipping = this.clipping; const extensions = {}; for (const key in this.extensions) { if (this.extensions[key] === true) extensions[key] = true; } if (Object.keys(extensions).length > 0) data.extensions = extensions; return data; } } class Camera extends Object3D { constructor() { super(); this.isCamera = true; this.type = 'Camera'; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); this.projectionMatrixInverse = new Matrix4(); this.coordinateSystem = WebGLCoordinateSystem; } copy(source, recursive) { super.copy(source, recursive); this.matrixWorldInverse.copy(source.matrixWorldInverse); this.projectionMatrix.copy(source.projectionMatrix); this.projectionMatrixInverse.copy(source.projectionMatrixInverse); this.coordinateSystem = source.coordinateSystem; return this; } getWorldDirection(target) { this.updateWorldMatrix(true, false); const e = this.matrixWorld.elements; return target.set(-e[8], -e[9], -e[10]).normalize(); } updateMatrixWorld(force) { super.updateMatrixWorld(force); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } updateWorldMatrix(updateParents, updateChildren) { super.updateWorldMatrix(updateParents, updateChildren); this.matrixWorldInverse.copy(this.matrixWorld).invert(); } clone() { return new this.constructor().copy(this); } } class PerspectiveCamera extends Camera { constructor(fov = 50, aspect = 1, near = 0.1, far = 2000) { super(); this.isPerspectiveCamera = true; this.type = 'PerspectiveCamera'; this.fov = fov; this.zoom = 1; this.near = near; this.far = far; this.focus = 10; this.aspect = aspect; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign({}, source.view); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; } /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength(focalLength) { /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = RAD2DEG * 2 * Math.atan(vExtentSlope); this.updateProjectionMatrix(); } /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength() { const vExtentSlope = Math.tan(DEG2RAD * 0.5 * this.fov); return 0.5 * this.getFilmHeight() / vExtentSlope; } getEffectiveFOV() { return RAD2DEG * 2 * Math.atan(Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom); } getFilmWidth() { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min(this.aspect, 1); } getFilmHeight() { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max(this.aspect, 1); } /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * const w = 1920; * const h = 1080; * const fullWidth = w * 3; * const fullHeight = h * 2; * * --A-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset(fullWidth, fullHeight, x, y, width, height) { this.aspect = fullWidth / fullHeight; if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const near = this.near; let top = near * Math.tan(DEG2RAD * 0.5 * this.fov) / this.zoom; let height = 2 * top; let width = this.aspect * height; let left = -0.5 * width; const view = this.view; if (this.view !== null && this.view.enabled) { const fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } const skew = this.filmOffset; if (skew !== 0) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makePerspective(left, left + width, top, top - height, near, this.far, this.coordinateSystem); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if (this.view !== null) data.object.view = Object.assign({}, this.view); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } const fov = -90; // negative fov is not an error const aspect = 1; class CubeCamera extends Object3D { constructor(near, far, renderTarget) { super(); this.type = 'CubeCamera'; this.renderTarget = renderTarget; this.coordinateSystem = null; const cameraPX = new PerspectiveCamera(fov, aspect, near, far); cameraPX.layers = this.layers; this.add(cameraPX); const cameraNX = new PerspectiveCamera(fov, aspect, near, far); cameraNX.layers = this.layers; this.add(cameraNX); const cameraPY = new PerspectiveCamera(fov, aspect, near, far); cameraPY.layers = this.layers; this.add(cameraPY); const cameraNY = new PerspectiveCamera(fov, aspect, near, far); cameraNY.layers = this.layers; this.add(cameraNY); const cameraPZ = new PerspectiveCamera(fov, aspect, near, far); cameraPZ.layers = this.layers; this.add(cameraPZ); const cameraNZ = new PerspectiveCamera(fov, aspect, near, far); cameraNZ.layers = this.layers; this.add(cameraNZ); } updateCoordinateSystem() { const coordinateSystem = this.coordinateSystem; const cameras = this.children.concat(); const [cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ] = cameras; for (const camera of cameras) this.remove(camera); if (coordinateSystem === WebGLCoordinateSystem) { cameraPX.up.set(0, 1, 0); cameraPX.lookAt(1, 0, 0); cameraNX.up.set(0, 1, 0); cameraNX.lookAt(-1, 0, 0); cameraPY.up.set(0, 0, -1); cameraPY.lookAt(0, 1, 0); cameraNY.up.set(0, 0, 1); cameraNY.lookAt(0, -1, 0); cameraPZ.up.set(0, 1, 0); cameraPZ.lookAt(0, 0, 1); cameraNZ.up.set(0, 1, 0); cameraNZ.lookAt(0, 0, -1); } else if (coordinateSystem === WebGPUCoordinateSystem) { cameraPX.up.set(0, -1, 0); cameraPX.lookAt(-1, 0, 0); cameraNX.up.set(0, -1, 0); cameraNX.lookAt(1, 0, 0); cameraPY.up.set(0, 0, 1); cameraPY.lookAt(0, 1, 0); cameraNY.up.set(0, 0, -1); cameraNY.lookAt(0, -1, 0); cameraPZ.up.set(0, -1, 0); cameraPZ.lookAt(0, 0, 1); cameraNZ.up.set(0, -1, 0); cameraNZ.lookAt(0, 0, -1); } else { throw new Error('THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: ' + coordinateSystem); } for (const camera of cameras) { this.add(camera); camera.updateMatrixWorld(); } } update(renderer, scene) { if (this.parent === null) this.updateMatrixWorld(); const renderTarget = this.renderTarget; if (this.coordinateSystem !== renderer.coordinateSystem) { this.coordinateSystem = renderer.coordinateSystem; this.updateCoordinateSystem(); } const [cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ] = this.children; const currentRenderTarget = renderer.getRenderTarget(); const currentToneMapping = renderer.toneMapping; const currentXrEnabled = renderer.xr.enabled; renderer.toneMapping = NoToneMapping; renderer.xr.enabled = false; const generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderer.setRenderTarget(renderTarget, 0); renderer.render(scene, cameraPX); renderer.setRenderTarget(renderTarget, 1); renderer.render(scene, cameraNX); renderer.setRenderTarget(renderTarget, 2); renderer.render(scene, cameraPY); renderer.setRenderTarget(renderTarget, 3); renderer.render(scene, cameraNY); renderer.setRenderTarget(renderTarget, 4); renderer.render(scene, cameraPZ); renderTarget.texture.generateMipmaps = generateMipmaps; renderer.setRenderTarget(renderTarget, 5); renderer.render(scene, cameraNZ); renderer.setRenderTarget(currentRenderTarget); renderer.toneMapping = currentToneMapping; renderer.xr.enabled = currentXrEnabled; renderTarget.texture.needsPMREMUpdate = true; } } class CubeTexture extends Texture { constructor(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; super(images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace); this.isCubeTexture = true; this.flipY = false; } get images() { return this.image; } set images(value) { this.image = value; } } class WebGLCubeRenderTarget extends WebGLRenderTarget { constructor(size = 1, options = {}) { super(size, size, options); this.isWebGLCubeRenderTarget = true; const image = { width: size, height: size, depth: 1 }; const images = [image, image, image, image, image, image]; if (options.encoding !== undefined) { // @deprecated, r152 warnOnce('THREE.WebGLCubeRenderTarget: option.encoding has been replaced by option.colorSpace.'); options.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; } this.texture = new CubeTexture(images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace); // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js) // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). this.texture.isRenderTargetTexture = true; this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; } fromEquirectangularTexture(renderer, texture) { this.texture.type = texture.type; this.texture.colorSpace = texture.colorSpace; this.texture.generateMipmaps = texture.generateMipmaps; this.texture.minFilter = texture.minFilter; this.texture.magFilter = texture.magFilter; const shader = { uniforms: { tEquirect: { value: null } }, vertexShader: /* glsl */` varying vec3 vWorldDirection; vec3 transformDirection( in vec3 dir, in mat4 matrix ) { return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); } void main() { vWorldDirection = transformDirection( position, modelMatrix ); #include <begin_vertex> #include <project_vertex> } `, fragmentShader: /* glsl */` uniform sampler2D tEquirect; varying vec3 vWorldDirection; #include <common> void main() { vec3 direction = normalize( vWorldDirection ); vec2 sampleUV = equirectUv( direction ); gl_FragColor = texture2D( tEquirect, sampleUV ); } ` }; const geometry = new BoxGeometry(5, 5, 5); const material = new ShaderMaterial({ name: 'CubemapFromEquirect', uniforms: cloneUniforms(shader.uniforms), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, side: BackSide, blending: NoBlending }); material.uniforms.tEquirect.value = texture; const mesh = new Mesh(geometry, material); const currentMinFilter = texture.minFilter; // Avoid blurred poles if (texture.minFilter === LinearMipmapLinearFilter) texture.minFilter = LinearFilter; const camera = new CubeCamera(1, 10, this); camera.update(renderer, mesh); texture.minFilter = currentMinFilter; mesh.geometry.dispose(); mesh.material.dispose(); return this; } clear(renderer, color, depth, stencil) { const currentRenderTarget = renderer.getRenderTarget(); for (let i = 0; i < 6; i++) { renderer.setRenderTarget(this, i); renderer.clear(color, depth, stencil); } renderer.setRenderTarget(currentRenderTarget); } } const _vector1 = /*@__PURE__*/new Vector3(); const _vector2 = /*@__PURE__*/new Vector3(); const _normalMatrix = /*@__PURE__*/new Matrix3(); class Plane { constructor(normal = new Vector3(1, 0, 0), constant = 0) { this.isPlane = true; // normal is assumed to be normalized this.normal = normal; this.constant = constant; } set(normal, constant) { this.normal.copy(normal); this.constant = constant; return this; } setComponents(x, y, z, w) { this.normal.set(x, y, z); this.constant = w; return this; } setFromNormalAndCoplanarPoint(normal, point) { this.normal.copy(normal); this.constant = -point.dot(this.normal); return this; } setFromCoplanarPoints(a, b, c) { const normal = _vector1.subVectors(c, b).cross(_vector2.subVectors(a, b)).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint(normal, a); return this; } copy(plane) { this.normal.copy(plane.normal); this.constant = plane.constant; return this; } normalize() { // Note: will lead to a divide by zero if the plane is invalid. const inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar(inverseNormalLength); this.constant *= inverseNormalLength; return this; } negate() { this.constant *= -1; this.normal.negate(); return this; } distanceToPoint(point) { return this.normal.dot(point) + this.constant; } distanceToSphere(sphere) { return this.distanceToPoint(sphere.center) - sphere.radius; } projectPoint(point, target) { return target.copy(point).addScaledVector(this.normal, -this.distanceToPoint(point)); } intersectLine(line, target) { const direction = line.delta(_vector1); const denominator = this.normal.dot(direction); if (denominator === 0) { // line is coplanar, return origin if (this.distanceToPoint(line.start) === 0) { return target.copy(line.start); } // Unsure if this is the correct method to handle this case. return null; } const t = -(line.start.dot(this.normal) + this.constant) / denominator; if (t < 0 || t > 1) { return null; } return target.copy(line.start).addScaledVector(direction, t); } intersectsLine(line) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. const startSign = this.distanceToPoint(line.start); const endSign = this.distanceToPoint(line.end); return startSign < 0 && endSign > 0 || endSign < 0 && startSign > 0; } intersectsBox(box) { return box.intersectsPlane(this); } intersectsSphere(sphere) { return sphere.intersectsPlane(this); } coplanarPoint(target) { return target.copy(this.normal).multiplyScalar(-this.constant); } applyMatrix4(matrix, optionalNormalMatrix) { const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix(matrix); const referencePoint = this.coplanarPoint(_vector1).applyMatrix4(matrix); const normal = this.normal.applyMatrix3(normalMatrix).normalize(); this.constant = -referencePoint.dot(normal); return this; } translate(offset) { this.constant -= offset.dot(this.normal); return this; } equals(plane) { return plane.normal.equals(this.normal) && plane.constant === this.constant; } clone() { return new this.constructor().copy(this); } } const _sphere$4 = /*@__PURE__*/new Sphere(); const _vector$6 = /*@__PURE__*/new Vector3(); class Frustum { constructor(p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane()) { this.planes = [p0, p1, p2, p3, p4, p5]; } set(p0, p1, p2, p3, p4, p5) { const planes = this.planes; planes[0].copy(p0); planes[1].copy(p1); planes[2].copy(p2); planes[3].copy(p3); planes[4].copy(p4); planes[5].copy(p5); return this; } copy(frustum) { const planes = this.planes; for (let i = 0; i < 6; i++) { planes[i].copy(frustum.planes[i]); } return this; } setFromProjectionMatrix(m, coordinateSystem = WebGLCoordinateSystem) { const planes = this.planes; const me = m.elements; const me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3]; const me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7]; const me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11]; const me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15]; planes[0].setComponents(me3 - me0, me7 - me4, me11 - me8, me15 - me12).normalize(); planes[1].setComponents(me3 + me0, me7 + me4, me11 + me8, me15 + me12).normalize(); planes[2].setComponents(me3 + me1, me7 + me5, me11 + me9, me15 + me13).normalize(); planes[3].setComponents(me3 - me1, me7 - me5, me11 - me9, me15 - me13).normalize(); planes[4].setComponents(me3 - me2, me7 - me6, me11 - me10, me15 - me14).normalize(); if (coordinateSystem === WebGLCoordinateSystem) { planes[5].setComponents(me3 + me2, me7 + me6, me11 + me10, me15 + me14).normalize(); } else if (coordinateSystem === WebGPUCoordinateSystem) { planes[5].setComponents(me2, me6, me10, me14).normalize(); } else { throw new Error('THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: ' + coordinateSystem); } return this; } intersectsObject(object) { if (object.boundingSphere !== undefined) { if (object.boundingSphere === null) object.computeBoundingSphere(); _sphere$4.copy(object.boundingSphere).applyMatrix4(object.matrixWorld); } else { const geometry = object.geometry; if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$4.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld); } return this.intersectsSphere(_sphere$4); } intersectsSprite(sprite) { _sphere$4.center.set(0, 0, 0); _sphere$4.radius = 0.7071067811865476; _sphere$4.applyMatrix4(sprite.matrixWorld); return this.intersectsSphere(_sphere$4); } intersectsSphere(sphere) { const planes = this.planes; const center = sphere.center; const negRadius = -sphere.radius; for (let i = 0; i < 6; i++) { const distance = planes[i].distanceToPoint(center); if (distance < negRadius) { return false; } } return true; } intersectsBox(box) { const planes = this.planes; for (let i = 0; i < 6; i++) { const plane = planes[i]; // corner at max distance _vector$6.x = plane.normal.x > 0 ? box.max.x : box.min.x; _vector$6.y = plane.normal.y > 0 ? box.max.y : box.min.y; _vector$6.z = plane.normal.z > 0 ? box.max.z : box.min.z; if (plane.distanceToPoint(_vector$6) < 0) { return false; } } return true; } containsPoint(point) { const planes = this.planes; for (let i = 0; i < 6; i++) { if (planes[i].distanceToPoint(point) < 0) { return false; } } return true; } clone() { return new this.constructor().copy(this); } } function WebGLAnimation() { let context = null; let isAnimating = false; let animationLoop = null; let requestId = null; function onAnimationFrame(time, frame) { animationLoop(time, frame); requestId = context.requestAnimationFrame(onAnimationFrame); } return { start: function () { if (isAnimating === true) return; if (animationLoop === null) return; requestId = context.requestAnimationFrame(onAnimationFrame); isAnimating = true; }, stop: function () { context.cancelAnimationFrame(requestId); isAnimating = false; }, setAnimationLoop: function (callback) { animationLoop = callback; }, setContext: function (value) { context = value; } }; } function WebGLAttributes(gl, capabilities) { const isWebGL2 = capabilities.isWebGL2; const buffers = new WeakMap(); function createBuffer(attribute, bufferType) { const array = attribute.array; const usage = attribute.usage; const buffer = gl.createBuffer(); gl.bindBuffer(bufferType, buffer); gl.bufferData(bufferType, array, usage); attribute.onUploadCallback(); let type; if (array instanceof Float32Array) { type = gl.FLOAT; } else if (array instanceof Uint16Array) { if (attribute.isFloat16BufferAttribute) { if (isWebGL2) { type = gl.HALF_FLOAT; } else { throw new Error('THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.'); } } else { type = gl.UNSIGNED_SHORT; } } else if (array instanceof Int16Array) { type = gl.SHORT; } else if (array instanceof Uint32Array) { type = gl.UNSIGNED_INT; } else if (array instanceof Int32Array) { type = gl.INT; } else if (array instanceof Int8Array) { type = gl.BYTE; } else if (array instanceof Uint8Array) { type = gl.UNSIGNED_BYTE; } else if (array instanceof Uint8ClampedArray) { type = gl.UNSIGNED_BYTE; } else { throw new Error('THREE.WebGLAttributes: Unsupported buffer data format: ' + array); } return { buffer: buffer, type: type, bytesPerElement: array.BYTES_PER_ELEMENT, version: attribute.version }; } function updateBuffer(buffer, attribute, bufferType) { const array = attribute.array; const updateRange = attribute.updateRange; gl.bindBuffer(bufferType, buffer); if (updateRange.count === -1) { // Not using update ranges gl.bufferSubData(bufferType, 0, array); } else { if (isWebGL2) { gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array, updateRange.offset, updateRange.count); } else { gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, array.subarray(updateRange.offset, updateRange.offset + updateRange.count)); } updateRange.count = -1; // reset range } attribute.onUploadCallback(); } // function get(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; return buffers.get(attribute); } function remove(attribute) { if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data) { gl.deleteBuffer(data.buffer); buffers.delete(attribute); } } function update(attribute, bufferType) { if (attribute.isGLBufferAttribute) { const cached = buffers.get(attribute); if (!cached || cached.version < attribute.version) { buffers.set(attribute, { buffer: attribute.buffer, type: attribute.type, bytesPerElement: attribute.elementSize, version: attribute.version }); } return; } if (attribute.isInterleavedBufferAttribute) attribute = attribute.data; const data = buffers.get(attribute); if (data === undefined) { buffers.set(attribute, createBuffer(attribute, bufferType)); } else if (data.version < attribute.version) { updateBuffer(data.buffer, attribute, bufferType); data.version = attribute.version; } } return { get: get, remove: remove, update: update }; } class PlaneGeometry extends BufferGeometry { constructor(width = 1, height = 1, widthSegments = 1, heightSegments = 1) { super(); this.type = 'PlaneGeometry'; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; const width_half = width / 2; const height_half = height / 2; const gridX = Math.floor(widthSegments); const gridY = Math.floor(heightSegments); const gridX1 = gridX + 1; const gridY1 = gridY + 1; const segment_width = width / gridX; const segment_height = height / gridY; // const indices = []; const vertices = []; const normals = []; const uvs = []; for (let iy = 0; iy < gridY1; iy++) { const y = iy * segment_height - height_half; for (let ix = 0; ix < gridX1; ix++) { const x = ix * segment_width - width_half; vertices.push(x, -y, 0); normals.push(0, 0, 1); uvs.push(ix / gridX); uvs.push(1 - iy / gridY); } } for (let iy = 0; iy < gridY; iy++) { for (let ix = 0; ix < gridX; ix++) { const a = ix + gridX1 * iy; const b = ix + gridX1 * (iy + 1); const c = ix + 1 + gridX1 * (iy + 1); const d = ix + 1 + gridX1 * iy; indices.push(a, b, d); indices.push(b, c, d); } } this.setIndex(indices); this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new PlaneGeometry(data.width, data.height, data.widthSegments, data.heightSegments); } } var alphahash_fragment = "#ifdef USE_ALPHAHASH\n\tif ( diffuseColor.a < getAlphaHashThreshold( vPosition ) ) discard;\n#endif"; var alphahash_pars_fragment = "#ifdef USE_ALPHAHASH\n\tconst float ALPHA_HASH_SCALE = 0.05;\n\tfloat hash2D( vec2 value ) {\n\t\treturn fract( 1.0e4 * sin( 17.0 * value.x + 0.1 * value.y ) * ( 0.1 + abs( sin( 13.0 * value.y + value.x ) ) ) );\n\t}\n\tfloat hash3D( vec3 value ) {\n\t\treturn hash2D( vec2( hash2D( value.xy ), value.z ) );\n\t}\n\tfloat getAlphaHashThreshold( vec3 position ) {\n\t\tfloat maxDeriv = max(\n\t\t\tlength( dFdx( position.xyz ) ),\n\t\t\tlength( dFdy( position.xyz ) )\n\t\t);\n\t\tfloat pixScale = 1.0 / ( ALPHA_HASH_SCALE * maxDeriv );\n\t\tvec2 pixScales = vec2(\n\t\t\texp2( floor( log2( pixScale ) ) ),\n\t\t\texp2( ceil( log2( pixScale ) ) )\n\t\t);\n\t\tvec2 alpha = vec2(\n\t\t\thash3D( floor( pixScales.x * position.xyz ) ),\n\t\t\thash3D( floor( pixScales.y * position.xyz ) )\n\t\t);\n\t\tfloat lerpFactor = fract( log2( pixScale ) );\n\t\tfloat x = ( 1.0 - lerpFactor ) * alpha.x + lerpFactor * alpha.y;\n\t\tfloat a = min( lerpFactor, 1.0 - lerpFactor );\n\t\tvec3 cases = vec3(\n\t\t\tx * x / ( 2.0 * a * ( 1.0 - a ) ),\n\t\t\t( x - 0.5 * a ) / ( 1.0 - a ),\n\t\t\t1.0 - ( ( 1.0 - x ) * ( 1.0 - x ) / ( 2.0 * a * ( 1.0 - a ) ) )\n\t\t);\n\t\tfloat threshold = ( x < ( 1.0 - a ) )\n\t\t\t? ( ( x < a ) ? cases.x : cases.y )\n\t\t\t: cases.z;\n\t\treturn clamp( threshold , 1.0e-6, 1.0 );\n\t}\n#endif"; var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g;\n#endif"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; var alphatest_fragment = "#ifdef USE_ALPHATEST\n\tif ( diffuseColor.a < alphaTest ) discard;\n#endif"; var alphatest_pars_fragment = "#ifdef USE_ALPHATEST\n\tuniform float alphaTest;\n#endif"; var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vAoMapUv ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness );\n\t#endif\n#endif"; var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; var begin_vertex = "vec3 transformed = vec3( position );\n#ifdef USE_ALPHAHASH\n\tvPosition = vec3( position );\n#endif"; var beginnormal_vertex = "vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif"; var bsdfs = "float G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, 1.0, dotVH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n} // validated"; var iridescence_fragment = "#ifdef USE_IRIDESCENCE\n\tconst mat3 XYZ_TO_REC709 = mat3(\n\t\t 3.2404542, -0.9692660, 0.0556434,\n\t\t-1.5371385, 1.8760108, -0.2040259,\n\t\t-0.4985314, 0.0415560, 1.0572252\n\t);\n\tvec3 Fresnel0ToIor( vec3 fresnel0 ) {\n\t\tvec3 sqrtF0 = sqrt( fresnel0 );\n\t\treturn ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 );\n\t}\n\tvec3 IorToFresnel0( vec3 transmittedIor, float incidentIor ) {\n\t\treturn pow2( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) );\n\t}\n\tfloat IorToFresnel0( float transmittedIor, float incidentIor ) {\n\t\treturn pow2( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor ));\n\t}\n\tvec3 evalSensitivity( float OPD, vec3 shift ) {\n\t\tfloat phase = 2.0 * PI * OPD * 1.0e-9;\n\t\tvec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 );\n\t\tvec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 );\n\t\tvec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 );\n\t\tvec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - pow2( phase ) * var );\n\t\txyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * pow2( phase ) );\n\t\txyz /= 1.0685e-7;\n\t\tvec3 rgb = XYZ_TO_REC709 * xyz;\n\t\treturn rgb;\n\t}\n\tvec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) {\n\t\tvec3 I;\n\t\tfloat iridescenceIOR = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) );\n\t\tfloat sinTheta2Sq = pow2( outsideIOR / iridescenceIOR ) * ( 1.0 - pow2( cosTheta1 ) );\n\t\tfloat cosTheta2Sq = 1.0 - sinTheta2Sq;\n\t\tif ( cosTheta2Sq < 0.0 ) {\n\t\t\t return vec3( 1.0 );\n\t\t}\n\t\tfloat cosTheta2 = sqrt( cosTheta2Sq );\n\t\tfloat R0 = IorToFresnel0( iridescenceIOR, outsideIOR );\n\t\tfloat R12 = F_Schlick( R0, 1.0, cosTheta1 );\n\t\tfloat R21 = R12;\n\t\tfloat T121 = 1.0 - R12;\n\t\tfloat phi12 = 0.0;\n\t\tif ( iridescenceIOR < outsideIOR ) phi12 = PI;\n\t\tfloat phi21 = PI - phi12;\n\t\tvec3 baseIOR = Fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) );\t\tvec3 R1 = IorToFresnel0( baseIOR, iridescenceIOR );\n\t\tvec3 R23 = F_Schlick( R1, 1.0, cosTheta2 );\n\t\tvec3 phi23 = vec3( 0.0 );\n\t\tif ( baseIOR[ 0 ] < iridescenceIOR ) phi23[ 0 ] = PI;\n\t\tif ( baseIOR[ 1 ] < iridescenceIOR ) phi23[ 1 ] = PI;\n\t\tif ( baseIOR[ 2 ] < iridescenceIOR ) phi23[ 2 ] = PI;\n\t\tfloat OPD = 2.0 * iridescenceIOR * thinFilmThickness * cosTheta2;\n\t\tvec3 phi = vec3( phi21 ) + phi23;\n\t\tvec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 );\n\t\tvec3 r123 = sqrt( R123 );\n\t\tvec3 Rs = pow2( T121 ) * R23 / ( vec3( 1.0 ) - R123 );\n\t\tvec3 C0 = R12 + Rs;\n\t\tI = C0;\n\t\tvec3 Cm = Rs - T121;\n\t\tfor ( int m = 1; m <= 2; ++ m ) {\n\t\t\tCm *= r123;\n\t\t\tvec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi );\n\t\t\tI += Cm * Sm;\n\t\t}\n\t\treturn max( I, vec3( 0.0 ) );\n\t}\n#endif"; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vBumpMapUv );\n\t\tvec2 dSTdy = dFdy( vBumpMapUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vBumpMapUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) {\n\t\tvec3 vSigmaX = dFdx( surf_pos.xyz );\n\t\tvec3 vSigmaY = dFdy( surf_pos.xyz );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 ) * faceDirection;\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif"; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#pragma unroll_loop_end\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t\tif ( clipped ) discard;\n\t#endif\n#endif"; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif"; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n#endif"; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvClipPosition = - mvPosition.xyz;\n#endif"; var color_fragment = "#if defined( USE_COLOR_ALPHA )\n\tdiffuseColor *= vColor;\n#elif defined( USE_COLOR )\n\tdiffuseColor.rgb *= vColor;\n#endif"; var color_pars_fragment = "#if defined( USE_COLOR_ALPHA )\n\tvarying vec4 vColor;\n#elif defined( USE_COLOR )\n\tvarying vec3 vColor;\n#endif"; var color_pars_vertex = "#if defined( USE_COLOR_ALPHA )\n\tvarying vec4 vColor;\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvarying vec3 vColor;\n#endif"; var color_vertex = "#if defined( USE_COLOR_ALPHA )\n\tvColor = vec4( 1.0 );\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor *= color;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif"; var common = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement( a ) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nvec3 pow2( const in vec3 x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); }\nfloat average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract( sin( sn ) * c );\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n#ifdef USE_CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\n};\n#ifdef USE_ALPHAHASH\n\tvarying vec3 vPosition;\n#endif\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat luminance( const in vec3 rgb ) {\n\tconst vec3 weights = vec3( 0.2126729, 0.7151522, 0.0721750 );\n\treturn dot( weights, rgb );\n}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}\nvec3 BRDF_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) {\n\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\n\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\n}\nfloat F_Schlick( const in float f0, const in float f90, const in float dotVH ) {\n\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\n\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\n} // validated"; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\thighp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tuv.x += filterInt * 3.0 * cubeUV_minTileSize;\n\t\tuv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize );\n\t\tuv.x *= CUBEUV_TEXEL_WIDTH;\n\t\tuv.y *= CUBEUV_TEXEL_HEIGHT;\n\t\t#ifdef texture2DGradEXT\n\t\t\treturn texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb;\n\t\t#else\n\t\t\treturn texture2D( envMap, uv ).rgb;\n\t\t#endif\n\t}\n\t#define cubeUV_r0 1.0\n\t#define cubeUV_v0 0.339\n\t#define cubeUV_m0 - 2.0\n\t#define cubeUV_r1 0.8\n\t#define cubeUV_v1 0.276\n\t#define cubeUV_m1 - 1.0\n\t#define cubeUV_r4 0.4\n\t#define cubeUV_v4 0.046\n\t#define cubeUV_m4 2.0\n\t#define cubeUV_r5 0.305\n\t#define cubeUV_v5 0.016\n\t#define cubeUV_m5 3.0\n\t#define cubeUV_r6 0.21\n\t#define cubeUV_v6 0.0038\n\t#define cubeUV_m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= cubeUV_r1 ) {\n\t\t\tmip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0;\n\t\t} else if ( roughness >= cubeUV_r4 ) {\n\t\t\tmip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1;\n\t\t} else if ( roughness >= cubeUV_r5 ) {\n\t\t\tmip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4;\n\t\t} else if ( roughness >= cubeUV_r6 ) {\n\t\t\tmip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif"; var defaultnormal_vertex = "vec3 transformedNormal = objectNormal;\n#ifdef USE_INSTANCING\n\tmat3 m = mat3( instanceMatrix );\n\ttransformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );\n\ttransformedNormal = m * transformedNormal;\n#endif\ntransformedNormal = normalMatrix * transformedNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif"; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif"; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias );\n#endif"; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv );\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif"; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif"; var colorspace_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; var colorspace_pars_fragment = "vec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}"; var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif"; var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif"; var envmap_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif"; var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\t\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif"; var envmap_vertex = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif"; var fog_vertex = "#ifdef USE_FOG\n\tvFogDepth = - mvPosition.z;\n#endif"; var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float vFogDepth;\n#endif"; var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, vFogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif"; var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float vFogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif"; var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn vec3( texture2D( gradientMap, coord ).r );\n\t#else\n\t\tvec2 fw = fwidth( coord ) * 0.5;\n\t\treturn mix( vec3( 0.7 ), vec3( 1.0 ), smoothstep( 0.7 - fw.x, 0.7 + fw.x, coord.x ) );\n\t#endif\n}"; var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel = texture2D( lightMap, vLightMapUv );\n\tvec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;\n\treflectedLight.indirectDiffuse += lightMapIrradiance;\n#endif"; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif"; var lights_lambert_fragment = "LambertMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularStrength = specularStrength;"; var lights_lambert_pars_fragment = "varying vec3 vViewPosition;\nstruct LambertMaterial {\n\tvec3 diffuseColor;\n\tfloat specularStrength;\n};\nvoid RE_Direct_Lambert( const in IncidentLight directLight, const in GeometricContext geometry, const in LambertMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Lambert( const in vec3 irradiance, const in GeometricContext geometry, const in LambertMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Lambert\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Lambert"; var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) {\n\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\treturn irradiance;\n}\nfloat getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\t#if defined ( LEGACY_LIGHTS )\n\t\tif ( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\t\treturn pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t\t}\n\t\treturn 1.0;\n\t#else\n\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\tif ( cutoffDistance > 0.0 ) {\n\t\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\t}\n\t\treturn distanceFalloff;\n\t#endif\n}\nfloat getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {\n\treturn smoothstep( coneCosine, penumbraCosine, angleCosine );\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalLightInfo( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tlight.color = directionalLight.color;\n\t\tlight.direction = directionalLight.direction;\n\t\tlight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointLightInfo( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tlight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tlight.color = pointLight.color;\n\t\tlight.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay );\n\t\tlight.visible = ( light.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotLightInfo( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight light ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tlight.direction = normalize( lVector );\n\t\tfloat angleCos = dot( light.direction, spotLight.direction );\n\t\tfloat spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\tif ( spotAttenuation > 0.0 ) {\n\t\t\tfloat lightDistance = length( lVector );\n\t\t\tlight.color = spotLight.color * spotAttenuation;\n\t\t\tlight.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tlight.visible = ( light.color != vec3( 0.0 ) );\n\t\t} else {\n\t\t\tlight.color = vec3( 0.0 );\n\t\t\tlight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) {\n\t\tfloat dotNL = dot( normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\treturn irradiance;\n\t}\n#endif"; var envmap_physical_pars_fragment = "#ifdef USE_ENVMAP\n\tvec3 getIBLIrradiance( const in vec3 normal ) {\n\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\t\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n\tvec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) {\n\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\t\t\tvec3 reflectVec = reflect( - viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t\treturn envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n\t#ifdef USE_ANISOTROPY\n\t\tvec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) {\n\t\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\t\t\t\tvec3 bentNormal = cross( bitangent, viewDir );\n\t\t\t\tbentNormal = normalize( cross( bentNormal, bitangent ) );\n\t\t\t\tbentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) );\n\t\t\t\treturn getIBLRadiance( viewDir, bentNormal, roughness );\n\t\t\t#else\n\t\t\t\treturn vec3( 0.0 );\n\t\t\t#endif\n\t\t}\n\t#endif\n#endif"; var lights_toon_fragment = "ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;"; var lights_toon_pars_fragment = "varying vec3 vViewPosition;\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon"; var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;"; var lights_phong_pars_fragment = "varying vec3 vViewPosition;\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometry.viewDir, geometry.normal, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong"; var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness;\nmaterial.roughness = min( material.roughness, 1.0 );\n#ifdef IOR\n\tmaterial.ior = ior;\n\t#ifdef USE_SPECULAR\n\t\tfloat specularIntensityFactor = specularIntensity;\n\t\tvec3 specularColorFactor = specularColor;\n\t\t#ifdef USE_SPECULAR_COLORMAP\n\t\t\tspecularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb;\n\t\t#endif\n\t\t#ifdef USE_SPECULAR_INTENSITYMAP\n\t\t\tspecularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a;\n\t\t#endif\n\t\tmaterial.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor );\n\t#else\n\t\tfloat specularIntensityFactor = 1.0;\n\t\tvec3 specularColorFactor = vec3( 1.0 );\n\t\tmaterial.specularF90 = 1.0;\n\t#endif\n\tmaterial.specularColor = mix( min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.specularF90 = 1.0;\n#endif\n#ifdef USE_CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\tmaterial.clearcoatF0 = vec3( 0.04 );\n\tmaterial.clearcoatF90 = 1.0;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_IRIDESCENCE\n\tmaterial.iridescence = iridescence;\n\tmaterial.iridescenceIOR = iridescenceIOR;\n\t#ifdef USE_IRIDESCENCEMAP\n\t\tmaterial.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r;\n\t#endif\n\t#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\t\tmaterial.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum;\n\t#else\n\t\tmaterial.iridescenceThickness = iridescenceThicknessMaximum;\n\t#endif\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheenColor;\n\t#ifdef USE_SHEEN_COLORMAP\n\t\tmaterial.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb;\n\t#endif\n\tmaterial.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 );\n\t#ifdef USE_SHEEN_ROUGHNESSMAP\n\t\tmaterial.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a;\n\t#endif\n#endif\n#ifdef USE_ANISOTROPY\n\t#ifdef USE_ANISOTROPYMAP\n\t\tmat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x );\n\t\tvec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb;\n\t\tvec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b;\n\t#else\n\t\tvec2 anisotropyV = anisotropyVector;\n\t#endif\n\tmaterial.anisotropy = length( anisotropyV );\n\tanisotropyV /= material.anisotropy;\n\tmaterial.anisotropy = saturate( material.anisotropy );\n\tmaterial.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) );\n\tmaterial.anisotropyT = tbn[ 0 ] * anisotropyV.x - tbn[ 1 ] * anisotropyV.y;\n\tmaterial.anisotropyB = tbn[ 1 ] * anisotropyV.x + tbn[ 0 ] * anisotropyV.y;\n#endif"; var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat roughness;\n\tvec3 specularColor;\n\tfloat specularF90;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat clearcoat;\n\t\tfloat clearcoatRoughness;\n\t\tvec3 clearcoatF0;\n\t\tfloat clearcoatF90;\n\t#endif\n\t#ifdef USE_IRIDESCENCE\n\t\tfloat iridescence;\n\t\tfloat iridescenceIOR;\n\t\tfloat iridescenceThickness;\n\t\tvec3 iridescenceFresnel;\n\t\tvec3 iridescenceF0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tvec3 sheenColor;\n\t\tfloat sheenRoughness;\n\t#endif\n\t#ifdef IOR\n\t\tfloat ior;\n\t#endif\n\t#ifdef USE_TRANSMISSION\n\t\tfloat transmission;\n\t\tfloat transmissionAlpha;\n\t\tfloat thickness;\n\t\tfloat attenuationDistance;\n\t\tvec3 attenuationColor;\n\t#endif\n\t#ifdef USE_ANISOTROPY\n\t\tfloat anisotropy;\n\t\tfloat alphaT;\n\t\tvec3 anisotropyT;\n\t\tvec3 anisotropyB;\n\t#endif\n};\nvec3 clearcoatSpecular = vec3( 0.0 );\nvec3 sheenSpecular = vec3( 0.0 );\nvec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) {\n float x = clamp( 1.0 - dotVH, 0.0, 1.0 );\n float x2 = x * x;\n float x5 = clamp( x * x2 * x2, 0.0, 0.9999 );\n return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 );\n}\nfloat V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\n#ifdef USE_ANISOTROPY\n\tfloat V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) {\n\t\tfloat gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) );\n\t\tfloat gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) );\n\t\tfloat v = 0.5 / ( gv + gl );\n\t\treturn saturate(v);\n\t}\n\tfloat D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) {\n\t\tfloat a2 = alphaT * alphaB;\n\t\thighp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH );\n\t\thighp float v2 = dot( v, v );\n\t\tfloat w2 = a2 / v2;\n\t\treturn RECIPROCAL_PI * a2 * pow2 ( w2 );\n\t}\n#endif\n#ifdef USE_CLEARCOAT\n\tvec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) {\n\t\tvec3 f0 = material.clearcoatF0;\n\t\tfloat f90 = material.clearcoatF90;\n\t\tfloat roughness = material.clearcoatRoughness;\n\t\tfloat alpha = pow2( roughness );\n\t\tvec3 halfDir = normalize( lightDir + viewDir );\n\t\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\t\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\t\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\t\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\t\tvec3 F = F_Schlick( f0, f90, dotVH );\n\t\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\t\tfloat D = D_GGX( alpha, dotNH );\n\t\treturn F * ( V * D );\n\t}\n#endif\nvec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) {\n\tvec3 f0 = material.specularColor;\n\tfloat f90 = material.specularF90;\n\tfloat roughness = material.roughness;\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\tvec3 F = F_Schlick( f0, f90, dotVH );\n\t#ifdef USE_IRIDESCENCE\n\t\tF = mix( F, material.iridescenceFresnel, material.iridescence );\n\t#endif\n\t#ifdef USE_ANISOTROPY\n\t\tfloat dotTL = dot( material.anisotropyT, lightDir );\n\t\tfloat dotTV = dot( material.anisotropyT, viewDir );\n\t\tfloat dotTH = dot( material.anisotropyT, halfDir );\n\t\tfloat dotBL = dot( material.anisotropyB, lightDir );\n\t\tfloat dotBV = dot( material.anisotropyB, viewDir );\n\t\tfloat dotBH = dot( material.anisotropyB, halfDir );\n\t\tfloat V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL );\n\t\tfloat D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH );\n\t#else\n\t\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\t\tfloat D = D_GGX( alpha, dotNH );\n\t#endif\n\treturn F * ( V * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie( float roughness, float dotNH ) {\n\tfloat alpha = pow2( roughness );\n\tfloat invAlpha = 1.0 / alpha;\n\tfloat cos2h = dotNH * dotNH;\n\tfloat sin2h = max( 1.0 - cos2h, 0.0078125 );\n\treturn ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI );\n}\nfloat V_Neubelt( float dotNV, float dotNL ) {\n\treturn saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) );\n}\nvec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) {\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat D = D_Charlie( sheenRoughness, dotNH );\n\tfloat V = V_Neubelt( dotNV, dotNL );\n\treturn sheenColor * ( D * V );\n}\n#endif\nfloat IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat r2 = roughness * roughness;\n\tfloat a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95;\n\tfloat b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72;\n\tfloat DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) );\n\treturn saturate( DG * RECIPROCAL_PI );\n}\nvec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\tvec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw;\n\treturn fab;\n}\nvec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) {\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\treturn specularColor * fab.x + specularF90 * fab.y;\n}\n#ifdef USE_IRIDESCENCE\nvoid computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n#else\nvoid computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n#endif\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\t#ifdef USE_IRIDESCENCE\n\t\tvec3 Fr = mix( specularColor, iridescenceF0, iridescence );\n\t#else\n\t\tvec3 Fr = specularColor;\n\t#endif\n\tvec3 FssEss = Fr * fab.x + specularF90 * fab.y;\n\tfloat Ess = fab.x + fab.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.roughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat dotNLcc = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = dotNLcc * directLight.color;\n\t\tclearcoatSpecular += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometry.viewDir, geometry.clearcoatNormal, material );\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tsheenSpecular += irradiance * BRDF_Sheen( directLight.direction, geometry.viewDir, geometry.normal, material.sheenColor, material.sheenRoughness );\n\t#endif\n\treflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometry.viewDir, geometry.normal, material );\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometry.clearcoatNormal, geometry.viewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tsheenSpecular += irradiance * material.sheenColor * IBLSheenBRDF( geometry.normal, geometry.viewDir, material.sheenRoughness );\n\t#endif\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\t#ifdef USE_IRIDESCENCE\n\t\tcomputeMultiscatteringIridescence( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness, singleScattering, multiScattering );\n\t#else\n\t\tcomputeMultiscattering( geometry.normal, geometry.viewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering );\n\t#endif\n\tvec3 totalScattering = singleScattering + multiScattering;\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - max( max( totalScattering.r, totalScattering.g ), totalScattering.b ) );\n\treflectedLight.indirectSpecular += radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}"; var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef USE_CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\n#ifdef USE_IRIDESCENCE\n\tfloat dotNVi = saturate( dot( normal, geometry.viewDir ) );\n\tif ( material.iridescenceThickness == 0.0 ) {\n\t\tmaterial.iridescence = 0.0;\n\t} else {\n\t\tmaterial.iridescence = saturate( material.iridescence );\n\t}\n\tif ( material.iridescence > 0.0 ) {\n\t\tmaterial.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor );\n\t\tmaterial.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi );\n\t}\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointLightInfo( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\tvec4 spotColor;\n\tvec3 spotLightCoord;\n\tbool inSpotLightMap;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotLightInfo( spotLight, geometry, directLight );\n\t\t#if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )\n\t\t#define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX\n\t\t#elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\t#define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS\n\t\t#else\n\t\t#define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )\n\t\t#endif\n\t\t#if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS )\n\t\t\tspotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w;\n\t\t\tinSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) );\n\t\t\tspotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy );\n\t\t\tdirectLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color;\n\t\t#endif\n\t\t#undef SPOT_LIGHT_MAP_INDEX\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalLightInfo( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry.normal );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel = texture2D( lightMap, vLightMapUv );\n\t\tvec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getIBLIrradiance( geometry.normal );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\t#ifdef USE_ANISOTROPY\n\t\tradiance += getIBLAnisotropyRadiance( geometry.viewDir, geometry.normal, material.roughness, material.anisotropyB, material.anisotropy );\n\t#else\n\t\tradiance += getIBLRadiance( geometry.viewDir, geometry.normal, material.roughness );\n\t#endif\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatRadiance += getIBLRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness );\n\t#endif\n#endif"; var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );\n#endif"; var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif"; var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n\tvarying float vIsPerspective;\n#endif"; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t\tvarying float vIsPerspective;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\n\t#else\n\t\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\n\t\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\t\tgl_Position.z *= gl_Position.w;\n\t\t}\n\t#endif\n#endif"; var map_fragment = "#ifdef USE_MAP\n\tdiffuseColor *= texture2D( map, vMapUv );\n#endif"; var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif"; var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\t#if defined( USE_POINTS_UV )\n\t\tvec2 uv = vUv;\n\t#else\n\t\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n\t#endif\n#endif\n#ifdef USE_MAP\n\tdiffuseColor *= texture2D( map, uv );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif"; var map_particle_pars_fragment = "#if defined( USE_POINTS_UV )\n\tvarying vec2 vUv;\n#else\n\t#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\t\tuniform mat3 uvTransform;\n\t#endif\n#endif\n#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif"; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; var morphcolor_vertex = "#if defined( USE_MORPHCOLORS ) && defined( MORPHTARGETS_TEXTURE )\n\tvColor *= morphTargetBaseInfluence;\n\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\t\t#if defined( USE_COLOR_ALPHA )\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ];\n\t\t#elif defined( USE_COLOR )\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ];\n\t\t#endif\n\t}\n#endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ];\n\t\t}\n\t#else\n\t\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\t\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\t\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\t\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n\t#endif\n#endif"; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tuniform float morphTargetInfluences[ MORPHTARGETS_COUNT ];\n\t\tuniform sampler2DArray morphTargetsTexture;\n\t\tuniform ivec2 morphTargetsTextureSize;\n\t\tvec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) {\n\t\t\tint texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset;\n\t\t\tint y = texelIndex / morphTargetsTextureSize.x;\n\t\t\tint x = texelIndex - y * morphTargetsTextureSize.x;\n\t\t\tivec3 morphUV = ivec3( x, y, morphTargetIndex );\n\t\t\treturn texelFetch( morphTargetsTexture, morphUV, 0 );\n\t\t}\n\t#else\n\t\t#ifndef USE_MORPHNORMALS\n\t\t\tuniform float morphTargetInfluences[ 8 ];\n\t\t#else\n\t\t\tuniform float morphTargetInfluences[ 4 ];\n\t\t#endif\n\t#endif\n#endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ];\n\t\t}\n\t#else\n\t\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\t\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\t\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\t\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t\t#ifndef USE_MORPHNORMALS\n\t\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t\t#endif\n\t#endif\n#endif"; var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;\n#ifdef FLAT_SHADED\n\tvec3 fdx = dFdx( vViewPosition );\n\tvec3 fdy = dFdy( vViewPosition );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal *= faceDirection;\n\t#endif\n#endif\n#if defined( USE_NORMALMAP_TANGENTSPACE ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY )\n\t#ifdef USE_TANGENT\n\t\tmat3 tbn = mat3( normalize( vTangent ), normalize( vBitangent ), normal );\n\t#else\n\t\tmat3 tbn = getTangentFrame( - vViewPosition, normal,\n\t\t#if defined( USE_NORMALMAP )\n\t\t\tvNormalMapUv\n\t\t#elif defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tvClearcoatNormalMapUv\n\t\t#else\n\t\t\tvUv\n\t\t#endif\n\t\t);\n\t#endif\n\t#if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED )\n\t\ttbn[0] *= faceDirection;\n\t\ttbn[1] *= faceDirection;\n\t#endif\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\t#ifdef USE_TANGENT\n\t\tmat3 tbn2 = mat3( normalize( vTangent ), normalize( vBitangent ), normal );\n\t#else\n\t\tmat3 tbn2 = getTangentFrame( - vViewPosition, normal, vClearcoatNormalMapUv );\n\t#endif\n\t#if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED )\n\t\ttbn2[0] *= faceDirection;\n\t\ttbn2[1] *= faceDirection;\n\t#endif\n#endif\nvec3 geometryNormal = normal;"; var normal_fragment_maps = "#ifdef USE_NORMALMAP_OBJECTSPACE\n\tnormal = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( USE_NORMALMAP_TANGENTSPACE )\n\tvec3 mapN = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\tnormal = normalize( tbn * mapN );\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection );\n#endif"; var normal_pars_fragment = "#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif"; var normal_pars_vertex = "#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif"; var normal_vertex = "#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif"; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef USE_NORMALMAP_OBJECTSPACE\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) )\n\tmat3 getTangentFrame( vec3 eye_pos, vec3 surf_norm, vec2 uv ) {\n\t\tvec3 q0 = dFdx( eye_pos.xyz );\n\t\tvec3 q1 = dFdy( eye_pos.xyz );\n\t\tvec2 st0 = dFdx( uv.st );\n\t\tvec2 st1 = dFdy( uv.st );\n\t\tvec3 N = surf_norm;\n\t\tvec3 q1perp = cross( q1, N );\n\t\tvec3 q0perp = cross( N, q0 );\n\t\tvec3 T = q1perp * st0.x + q0perp * st1.x;\n\t\tvec3 B = q1perp * st0.y + q0perp * st1.y;\n\t\tfloat det = max( dot( T, T ), dot( B, B ) );\n\t\tfloat scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det );\n\t\treturn mat3( T * scale, B * scale, N );\n\t}\n#endif"; var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif"; var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\tclearcoatNormal = normalize( tbn2 * clearcoatMapN );\n#endif"; var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP\n\tuniform sampler2D clearcoatMap;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform sampler2D clearcoatRoughnessMap;\n#endif"; var iridescence_pars_fragment = "#ifdef USE_IRIDESCENCEMAP\n\tuniform sampler2D iridescenceMap;\n#endif\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\tuniform sampler2D iridescenceThicknessMap;\n#endif"; var opaque_fragment = "#ifdef OPAQUE\ndiffuseColor.a = 1.0;\n#endif\n#ifdef USE_TRANSMISSION\ndiffuseColor.a *= material.transmissionAlpha;\n#endif\ngl_FragColor = vec4( outgoingLight, diffuseColor.a );"; var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nvec2 packDepthToRG( in highp float v ) {\n\treturn packDepthToRGBA( v ).yx;\n}\nfloat unpackRGToDepth( const in highp vec2 v ) {\n\treturn unpackRGBAToDepth( vec4( v.xy, 0.0, 0.0 ) );\n}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) );\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w );\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) {\n\treturn depth * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * depth - far );\n}"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif"; var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 );\n#ifdef USE_INSTANCING\n\tmvPosition = instanceMatrix * mvPosition;\n#endif\nmvPosition = modelViewMatrix * mvPosition;\ngl_Position = projectionMatrix * mvPosition;"; var dithering_fragment = "#ifdef DITHERING\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif"; var dithering_pars_fragment = "#ifdef DITHERING\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif"; var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv );\n\troughnessFactor *= texelRoughness.g;\n#endif"; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif"; var shadowmap_pars_fragment = "#if NUM_SPOT_LIGHT_COORDS > 0\n\tvarying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ];\n#endif\n#if NUM_SPOT_LIGHT_MAPS > 0\n\tuniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ];\n#endif\n#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\n\t\treturn unpackRGBATo2Half( texture2D( shadow, uv ) );\n\t}\n\tfloat VSMShadow (sampler2D shadow, vec2 uv, float compare ){\n\t\tfloat occlusion = 1.0;\n\t\tvec2 distribution = texture2DDistribution( shadow, uv );\n\t\tfloat hard_shadow = step( compare , distribution.x );\n\t\tif (hard_shadow != 1.0 ) {\n\t\t\tfloat distance = compare - distribution.x ;\n\t\t\tfloat variance = max( 0.00000, distribution.y * distribution.y );\n\t\t\tfloat softness_probability = variance / (variance + distance * distance );\t\t\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 );\t\t\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\n\t\t}\n\t\treturn occlusion;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;\n\t\tbool frustumTest = inFrustum && shadowCoord.z <= 1.0;\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tfloat dx2 = dx0 / 2.0;\n\t\t\tfloat dy2 = dy0 / 2.0;\n\t\t\tfloat dx3 = dx1 / 2.0;\n\t\t\tfloat dy3 = dy1 / 2.0;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 17.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx = texelSize.x;\n\t\t\tfloat dy = texelSize.y;\n\t\t\tvec2 uv = shadowCoord.xy;\n\t\t\tvec2 f = fract( uv * shadowMapSize + 0.5 );\n\t\t\tuv -= f * texelSize;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, uv, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t f.y )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_VSM )\n\t\t\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif"; var shadowmap_pars_vertex = "#if NUM_SPOT_LIGHT_COORDS > 0\n\tuniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ];\n\tvarying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ];\n#endif\n#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n#endif"; var shadowmap_vertex = "#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 )\n\tvec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\tvec4 shadowWorldPosition;\n#endif\n#if defined( USE_SHADOWMAP )\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );\n\t\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 );\n\t\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if NUM_SPOT_LIGHT_COORDS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_COORDS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition;\n\t\t#if ( defined( USE_SHADOWMAP ) && UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\t\tshadowWorldPosition.xyz += shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias;\n\t\t#endif\n\t\tvSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n#endif"; var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tdirectionalLight = directionalLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tspotLight = spotLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tpointLight = pointLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#endif\n\treturn shadow;\n}"; var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\tuniform highp sampler2D boneTexture;\n\tuniform int boneTextureSize;\n\tmat4 getBoneMatrix( const in float i ) {\n\t\tfloat j = i * 4.0;\n\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\ty = dy * ( y + 0.5 );\n\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\treturn bone;\n\t}\n#endif"; var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif"; var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\t#ifdef USE_TANGENT\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#endif\n#endif"; var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vSpecularMapUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif"; var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn saturate( toneMappingExposure * color );\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }"; var transmission_fragment = "#ifdef USE_TRANSMISSION\n\tmaterial.transmission = transmission;\n\tmaterial.transmissionAlpha = 1.0;\n\tmaterial.thickness = thickness;\n\tmaterial.attenuationDistance = attenuationDistance;\n\tmaterial.attenuationColor = attenuationColor;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\tmaterial.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tmaterial.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g;\n\t#endif\n\tvec3 pos = vWorldPosition;\n\tvec3 v = normalize( cameraPosition - pos );\n\tvec3 n = inverseTransformDirection( normal, viewMatrix );\n\tvec4 transmitted = getIBLVolumeRefraction(\n\t\tn, v, material.roughness, material.diffuseColor, material.specularColor, material.specularF90,\n\t\tpos, modelMatrix, viewMatrix, projectionMatrix, material.ior, material.thickness,\n\t\tmaterial.attenuationColor, material.attenuationDistance );\n\tmaterial.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission );\n\ttotalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission );\n#endif"; var transmission_pars_fragment = "#ifdef USE_TRANSMISSION\n\tuniform float transmission;\n\tuniform float thickness;\n\tuniform float attenuationDistance;\n\tuniform vec3 attenuationColor;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\tuniform sampler2D transmissionMap;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tuniform sampler2D thicknessMap;\n\t#endif\n\tuniform vec2 transmissionSamplerSize;\n\tuniform sampler2D transmissionSamplerMap;\n\tuniform mat4 modelMatrix;\n\tuniform mat4 projectionMatrix;\n\tvarying vec3 vWorldPosition;\n\tfloat w0( float a ) {\n\t\treturn ( 1.0 / 6.0 ) * ( a * ( a * ( - a + 3.0 ) - 3.0 ) + 1.0 );\n\t}\n\tfloat w1( float a ) {\n\t\treturn ( 1.0 / 6.0 ) * ( a * a * ( 3.0 * a - 6.0 ) + 4.0 );\n\t}\n\tfloat w2( float a ){\n\t\treturn ( 1.0 / 6.0 ) * ( a * ( a * ( - 3.0 * a + 3.0 ) + 3.0 ) + 1.0 );\n\t}\n\tfloat w3( float a ) {\n\t\treturn ( 1.0 / 6.0 ) * ( a * a * a );\n\t}\n\tfloat g0( float a ) {\n\t\treturn w0( a ) + w1( a );\n\t}\n\tfloat g1( float a ) {\n\t\treturn w2( a ) + w3( a );\n\t}\n\tfloat h0( float a ) {\n\t\treturn - 1.0 + w1( a ) / ( w0( a ) + w1( a ) );\n\t}\n\tfloat h1( float a ) {\n\t\treturn 1.0 + w3( a ) / ( w2( a ) + w3( a ) );\n\t}\n\tvec4 bicubic( sampler2D tex, vec2 uv, vec4 texelSize, float lod ) {\n\t\tuv = uv * texelSize.zw + 0.5;\n\t\tvec2 iuv = floor( uv );\n\t\tvec2 fuv = fract( uv );\n\t\tfloat g0x = g0( fuv.x );\n\t\tfloat g1x = g1( fuv.x );\n\t\tfloat h0x = h0( fuv.x );\n\t\tfloat h1x = h1( fuv.x );\n\t\tfloat h0y = h0( fuv.y );\n\t\tfloat h1y = h1( fuv.y );\n\t\tvec2 p0 = ( vec2( iuv.x + h0x, iuv.y + h0y ) - 0.5 ) * texelSize.xy;\n\t\tvec2 p1 = ( vec2( iuv.x + h1x, iuv.y + h0y ) - 0.5 ) * texelSize.xy;\n\t\tvec2 p2 = ( vec2( iuv.x + h0x, iuv.y + h1y ) - 0.5 ) * texelSize.xy;\n\t\tvec2 p3 = ( vec2( iuv.x + h1x, iuv.y + h1y ) - 0.5 ) * texelSize.xy;\n\t\treturn g0( fuv.y ) * ( g0x * textureLod( tex, p0, lod ) + g1x * textureLod( tex, p1, lod ) ) +\n\t\t\tg1( fuv.y ) * ( g0x * textureLod( tex, p2, lod ) + g1x * textureLod( tex, p3, lod ) );\n\t}\n\tvec4 textureBicubic( sampler2D sampler, vec2 uv, float lod ) {\n\t\tvec2 fLodSize = vec2( textureSize( sampler, int( lod ) ) );\n\t\tvec2 cLodSize = vec2( textureSize( sampler, int( lod + 1.0 ) ) );\n\t\tvec2 fLodSizeInv = 1.0 / fLodSize;\n\t\tvec2 cLodSizeInv = 1.0 / cLodSize;\n\t\tvec4 fSample = bicubic( sampler, uv, vec4( fLodSizeInv, fLodSize ), floor( lod ) );\n\t\tvec4 cSample = bicubic( sampler, uv, vec4( cLodSizeInv, cLodSize ), ceil( lod ) );\n\t\treturn mix( fSample, cSample, fract( lod ) );\n\t}\n\tvec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) {\n\t\tvec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior );\n\t\tvec3 modelScale;\n\t\tmodelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) );\n\t\tmodelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) );\n\t\tmodelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) );\n\t\treturn normalize( refractionVector ) * thickness * modelScale;\n\t}\n\tfloat applyIorToRoughness( const in float roughness, const in float ior ) {\n\t\treturn roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 );\n\t}\n\tvec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) {\n\t\tfloat lod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior );\n\t\treturn textureBicubic( transmissionSamplerMap, fragCoord.xy, lod );\n\t}\n\tvec3 volumeAttenuation( const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) {\n\t\tif ( isinf( attenuationDistance ) ) {\n\t\t\treturn vec3( 1.0 );\n\t\t} else {\n\t\t\tvec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance;\n\t\t\tvec3 transmittance = exp( - attenuationCoefficient * transmissionDistance );\t\t\treturn transmittance;\n\t\t}\n\t}\n\tvec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor,\n\t\tconst in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix,\n\t\tconst in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness,\n\t\tconst in vec3 attenuationColor, const in float attenuationDistance ) {\n\t\tvec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix );\n\t\tvec3 refractedRayExit = position + transmissionRay;\n\t\tvec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 );\n\t\tvec2 refractionCoords = ndcPos.xy / ndcPos.w;\n\t\trefractionCoords += 1.0;\n\t\trefractionCoords /= 2.0;\n\t\tvec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior );\n\t\tvec3 transmittance = diffuseColor * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance );\n\t\tvec3 attenuatedColor = transmittance * transmittedLight.rgb;\n\t\tvec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness );\n\t\tfloat transmittanceFactor = ( transmittance.r + transmittance.g + transmittance.b ) / 3.0;\n\t\treturn vec4( ( 1.0 - F ) * attenuatedColor, 1.0 - ( 1.0 - transmittedLight.a ) * transmittanceFactor );\n\t}\n#endif"; var uv_pars_fragment = "#if defined( USE_UV ) || defined( USE_ANISOTROPY )\n\tvarying vec2 vUv;\n#endif\n#ifdef USE_MAP\n\tvarying vec2 vMapUv;\n#endif\n#ifdef USE_ALPHAMAP\n\tvarying vec2 vAlphaMapUv;\n#endif\n#ifdef USE_LIGHTMAP\n\tvarying vec2 vLightMapUv;\n#endif\n#ifdef USE_AOMAP\n\tvarying vec2 vAoMapUv;\n#endif\n#ifdef USE_BUMPMAP\n\tvarying vec2 vBumpMapUv;\n#endif\n#ifdef USE_NORMALMAP\n\tvarying vec2 vNormalMapUv;\n#endif\n#ifdef USE_EMISSIVEMAP\n\tvarying vec2 vEmissiveMapUv;\n#endif\n#ifdef USE_METALNESSMAP\n\tvarying vec2 vMetalnessMapUv;\n#endif\n#ifdef USE_ROUGHNESSMAP\n\tvarying vec2 vRoughnessMapUv;\n#endif\n#ifdef USE_ANISOTROPYMAP\n\tvarying vec2 vAnisotropyMapUv;\n#endif\n#ifdef USE_CLEARCOATMAP\n\tvarying vec2 vClearcoatMapUv;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tvarying vec2 vClearcoatNormalMapUv;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tvarying vec2 vClearcoatRoughnessMapUv;\n#endif\n#ifdef USE_IRIDESCENCEMAP\n\tvarying vec2 vIridescenceMapUv;\n#endif\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\tvarying vec2 vIridescenceThicknessMapUv;\n#endif\n#ifdef USE_SHEEN_COLORMAP\n\tvarying vec2 vSheenColorMapUv;\n#endif\n#ifdef USE_SHEEN_ROUGHNESSMAP\n\tvarying vec2 vSheenRoughnessMapUv;\n#endif\n#ifdef USE_SPECULARMAP\n\tvarying vec2 vSpecularMapUv;\n#endif\n#ifdef USE_SPECULAR_COLORMAP\n\tvarying vec2 vSpecularColorMapUv;\n#endif\n#ifdef USE_SPECULAR_INTENSITYMAP\n\tvarying vec2 vSpecularIntensityMapUv;\n#endif\n#ifdef USE_TRANSMISSIONMAP\n\tuniform mat3 transmissionMapTransform;\n\tvarying vec2 vTransmissionMapUv;\n#endif\n#ifdef USE_THICKNESSMAP\n\tuniform mat3 thicknessMapTransform;\n\tvarying vec2 vThicknessMapUv;\n#endif"; var uv_pars_vertex = "#if defined( USE_UV ) || defined( USE_ANISOTROPY )\n\tvarying vec2 vUv;\n#endif\n#ifdef USE_MAP\n\tuniform mat3 mapTransform;\n\tvarying vec2 vMapUv;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform mat3 alphaMapTransform;\n\tvarying vec2 vAlphaMapUv;\n#endif\n#ifdef USE_LIGHTMAP\n\tuniform mat3 lightMapTransform;\n\tvarying vec2 vLightMapUv;\n#endif\n#ifdef USE_AOMAP\n\tuniform mat3 aoMapTransform;\n\tvarying vec2 vAoMapUv;\n#endif\n#ifdef USE_BUMPMAP\n\tuniform mat3 bumpMapTransform;\n\tvarying vec2 vBumpMapUv;\n#endif\n#ifdef USE_NORMALMAP\n\tuniform mat3 normalMapTransform;\n\tvarying vec2 vNormalMapUv;\n#endif\n#ifdef USE_DISPLACEMENTMAP\n\tuniform mat3 displacementMapTransform;\n\tvarying vec2 vDisplacementMapUv;\n#endif\n#ifdef USE_EMISSIVEMAP\n\tuniform mat3 emissiveMapTransform;\n\tvarying vec2 vEmissiveMapUv;\n#endif\n#ifdef USE_METALNESSMAP\n\tuniform mat3 metalnessMapTransform;\n\tvarying vec2 vMetalnessMapUv;\n#endif\n#ifdef USE_ROUGHNESSMAP\n\tuniform mat3 roughnessMapTransform;\n\tvarying vec2 vRoughnessMapUv;\n#endif\n#ifdef USE_ANISOTROPYMAP\n\tuniform mat3 anisotropyMapTransform;\n\tvarying vec2 vAnisotropyMapUv;\n#endif\n#ifdef USE_CLEARCOATMAP\n\tuniform mat3 clearcoatMapTransform;\n\tvarying vec2 vClearcoatMapUv;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform mat3 clearcoatNormalMapTransform;\n\tvarying vec2 vClearcoatNormalMapUv;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform mat3 clearcoatRoughnessMapTransform;\n\tvarying vec2 vClearcoatRoughnessMapUv;\n#endif\n#ifdef USE_SHEEN_COLORMAP\n\tuniform mat3 sheenColorMapTransform;\n\tvarying vec2 vSheenColorMapUv;\n#endif\n#ifdef USE_SHEEN_ROUGHNESSMAP\n\tuniform mat3 sheenRoughnessMapTransform;\n\tvarying vec2 vSheenRoughnessMapUv;\n#endif\n#ifdef USE_IRIDESCENCEMAP\n\tuniform mat3 iridescenceMapTransform;\n\tvarying vec2 vIridescenceMapUv;\n#endif\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\tuniform mat3 iridescenceThicknessMapTransform;\n\tvarying vec2 vIridescenceThicknessMapUv;\n#endif\n#ifdef USE_SPECULARMAP\n\tuniform mat3 specularMapTransform;\n\tvarying vec2 vSpecularMapUv;\n#endif\n#ifdef USE_SPECULAR_COLORMAP\n\tuniform mat3 specularColorMapTransform;\n\tvarying vec2 vSpecularColorMapUv;\n#endif\n#ifdef USE_SPECULAR_INTENSITYMAP\n\tuniform mat3 specularIntensityMapTransform;\n\tvarying vec2 vSpecularIntensityMapUv;\n#endif\n#ifdef USE_TRANSMISSIONMAP\n\tuniform mat3 transmissionMapTransform;\n\tvarying vec2 vTransmissionMapUv;\n#endif\n#ifdef USE_THICKNESSMAP\n\tuniform mat3 thicknessMapTransform;\n\tvarying vec2 vThicknessMapUv;\n#endif"; var uv_vertex = "#if defined( USE_UV ) || defined( USE_ANISOTROPY )\n\tvUv = vec3( uv, 1 ).xy;\n#endif\n#ifdef USE_MAP\n\tvMapUv = ( mapTransform * vec3( MAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_ALPHAMAP\n\tvAlphaMapUv = ( alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_LIGHTMAP\n\tvLightMapUv = ( lightMapTransform * vec3( LIGHTMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_AOMAP\n\tvAoMapUv = ( aoMapTransform * vec3( AOMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_BUMPMAP\n\tvBumpMapUv = ( bumpMapTransform * vec3( BUMPMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_NORMALMAP\n\tvNormalMapUv = ( normalMapTransform * vec3( NORMALMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_DISPLACEMENTMAP\n\tvDisplacementMapUv = ( displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_EMISSIVEMAP\n\tvEmissiveMapUv = ( emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_METALNESSMAP\n\tvMetalnessMapUv = ( metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_ROUGHNESSMAP\n\tvRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_ANISOTROPYMAP\n\tvAnisotropyMapUv = ( anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_CLEARCOATMAP\n\tvClearcoatMapUv = ( clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tvClearcoatNormalMapUv = ( clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tvClearcoatRoughnessMapUv = ( clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_IRIDESCENCEMAP\n\tvIridescenceMapUv = ( iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\tvIridescenceThicknessMapUv = ( iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_SHEEN_COLORMAP\n\tvSheenColorMapUv = ( sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_SHEEN_ROUGHNESSMAP\n\tvSheenRoughnessMapUv = ( sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_SPECULARMAP\n\tvSpecularMapUv = ( specularMapTransform * vec3( SPECULARMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_SPECULAR_COLORMAP\n\tvSpecularColorMapUv = ( specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_SPECULAR_INTENSITYMAP\n\tvSpecularIntensityMapUv = ( specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_TRANSMISSIONMAP\n\tvTransmissionMapUv = ( transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_THICKNESSMAP\n\tvThicknessMapUv = ( thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) ).xy;\n#endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif"; const vertex$h = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}"; const fragment$h = "uniform sampler2D t2D;\nuniform float backgroundIntensity;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\ttexColor.rgb *= backgroundIntensity;\n\tgl_FragColor = texColor;\n\t#include <tonemapping_fragment>\n\t#include <colorspace_fragment>\n}"; const vertex$g = "varying vec3 vWorldDirection;\n#include <common>\nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include <begin_vertex>\n\t#include <project_vertex>\n\tgl_Position.z = gl_Position.w;\n}"; const fragment$g = "#ifdef ENVMAP_TYPE_CUBE\n\tuniform samplerCube envMap;\n#elif defined( ENVMAP_TYPE_CUBE_UV )\n\tuniform sampler2D envMap;\n#endif\nuniform float flipEnvMap;\nuniform float backgroundBlurriness;\nuniform float backgroundIntensity;\nvarying vec3 vWorldDirection;\n#include <cube_uv_reflection_fragment>\nvoid main() {\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 texColor = textureCube( envMap, vec3( flipEnvMap * vWorldDirection.x, vWorldDirection.yz ) );\n\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec4 texColor = textureCubeUV( envMap, vWorldDirection, backgroundBlurriness );\n\t#else\n\t\tvec4 texColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\t#endif\n\ttexColor.rgb *= backgroundIntensity;\n\tgl_FragColor = texColor;\n\t#include <tonemapping_fragment>\n\t#include <colorspace_fragment>\n}"; const vertex$f = "varying vec3 vWorldDirection;\n#include <common>\nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include <begin_vertex>\n\t#include <project_vertex>\n\tgl_Position.z = gl_Position.w;\n}"; const fragment$f = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldDirection;\nvoid main() {\n\tvec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) );\n\tgl_FragColor = texColor;\n\tgl_FragColor.a *= opacity;\n\t#include <tonemapping_fragment>\n\t#include <colorspace_fragment>\n}"; const vertex$e = "#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include <uv_vertex>\n\t#include <skinbase_vertex>\n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include <beginnormal_vertex>\n\t\t#include <morphnormal_vertex>\n\t\t#include <skinnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\tvHighPrecisionZW = gl_Position.zw;\n}"; const fragment$e = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include <common>\n#include <packing>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <alphatest_pars_fragment>\n#include <alphahash_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include <map_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <alphahash_fragment>\n\t#include <logdepthbuf_fragment>\n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}"; const vertex$d = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <skinbase_vertex>\n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include <beginnormal_vertex>\n\t\t#include <morphnormal_vertex>\n\t\t#include <skinnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <worldpos_vertex>\n\t#include <clipping_planes_vertex>\n\tvWorldPosition = worldPosition.xyz;\n}"; const fragment$d = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include <common>\n#include <packing>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <alphatest_pars_fragment>\n#include <alphahash_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main () {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include <map_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <alphahash_fragment>\n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; const vertex$c = "varying vec3 vWorldDirection;\n#include <common>\nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include <begin_vertex>\n\t#include <project_vertex>\n}"; const fragment$c = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include <common>\nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n\t#include <tonemapping_fragment>\n\t#include <colorspace_fragment>\n}"; const vertex$b = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include <common>\n#include <uv_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include <uv_vertex>\n\t#include <color_vertex>\n\t#include <morphcolor_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <fog_vertex>\n}"; const fragment$b = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include <common>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <fog_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\toutgoingLight = diffuseColor.rgb;\n\t#include <opaque_fragment>\n\t#include <tonemapping_fragment>\n\t#include <colorspace_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n}"; const vertex$a = "#include <common>\n#include <uv_pars_vertex>\n#include <envmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <color_vertex>\n\t#include <morphcolor_vertex>\n\t#if defined ( USE_ENVMAP ) || defined ( USE_SKINNING )\n\t\t#include <beginnormal_vertex>\n\t\t#include <morphnormal_vertex>\n\t\t#include <skinbase_vertex>\n\t\t#include <skinnormal_vertex>\n\t\t#include <defaultnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <worldpos_vertex>\n\t#include <envmap_vertex>\n\t#include <fog_vertex>\n}"; const fragment$a = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include <common>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <alphatest_pars_fragment>\n#include <alphahash_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <envmap_common_pars_fragment>\n#include <envmap_pars_fragment>\n#include <fog_pars_fragment>\n#include <specularmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <alphahash_fragment>\n\t#include <specularmap_fragment>\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel = texture2D( lightMap, vLightMapUv );\n\t\treflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include <aomap_fragment>\n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include <envmap_fragment>\n\t#include <opaque_fragment>\n\t#include <tonemapping_fragment>\n\t#include <colorspace_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}"; const vertex$9 = "#define LAMBERT\nvarying vec3 vViewPosition;\n#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <envmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <normal_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <color_vertex>\n\t#include <morphcolor_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#include <normal_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\tvViewPosition = - mvPosition.xyz;\n\t#include <worldpos_vertex>\n\t#include <envmap_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}"; const fragment$9 = "#define LAMBERT\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <alphatest_pars_fragment>\n#include <alphahash_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n#include <envmap_common_pars_fragment>\n#include <envmap_pars_fragment>\n#include <fog_pars_fragment>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <normal_pars_fragment>\n#include <lights_lambert_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <specularmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <alphahash_fragment>\n\t#include <specularmap_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\t#include <emissivemap_fragment>\n\t#include <lights_lambert_fragment>\n\t#include <lights_fragment_begin>\n\t#include <lights_fragment_maps>\n\t#include <lights_fragment_end>\n\t#include <aomap_fragment>\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include <envmap_fragment>\n\t#include <opaque_fragment>\n\t#include <tonemapping_fragment>\n\t#include <colorspace_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}"; const vertex$8 = "#define MATCAP\nvarying vec3 vViewPosition;\n#include <common>\n#include <uv_pars_vertex>\n#include <color_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <fog_pars_vertex>\n#include <normal_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <color_vertex>\n\t#include <morphcolor_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#include <normal_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <fog_vertex>\n\tvViewPosition = - mvPosition.xyz;\n}"; const fragment$8 = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#include <common>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <alphatest_pars_fragment>\n#include <alphahash_pars_fragment>\n#include <fog_pars_fragment>\n#include <normal_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <alphahash_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t#else\n\t\tvec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\t#include <opaque_fragment>\n\t#include <tonemapping_fragment>\n\t#include <colorspace_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}"; const vertex$7 = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\n\tvarying vec3 vViewPosition;\n#endif\n#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <normal_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#include <normal_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; const fragment$7 = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\n\tvarying vec3 vViewPosition;\n#endif\n#include <packing>\n#include <uv_pars_fragment>\n#include <normal_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\t#include <logdepthbuf_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n\t#ifdef OPAQUE\n\t\tgl_FragColor.a = 1.0;\n\t#endif\n}"; const vertex$6 = "#define PHONG\nvarying vec3 vViewPosition;\n#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <envmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <normal_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <color_vertex>\n\t#include <morphcolor_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#include <normal_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\tvViewPosition = - mvPosition.xyz;\n\t#include <worldpos_vertex>\n\t#include <envmap_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}"; const fragment$6 = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <alphatest_pars_fragment>\n#include <alphahash_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n#include <envmap_common_pars_fragment>\n#include <envmap_pars_fragment>\n#include <fog_pars_fragment>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <normal_pars_fragment>\n#include <lights_phong_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <specularmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <alphahash_fragment>\n\t#include <specularmap_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\t#include <emissivemap_fragment>\n\t#include <lights_phong_fragment>\n\t#include <lights_fragment_begin>\n\t#include <lights_fragment_maps>\n\t#include <lights_fragment_end>\n\t#include <aomap_fragment>\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include <envmap_fragment>\n\t#include <opaque_fragment>\n\t#include <tonemapping_fragment>\n\t#include <colorspace_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}"; const vertex$5 = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifdef USE_TRANSMISSION\n\tvarying vec3 vWorldPosition;\n#endif\n#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <normal_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <color_vertex>\n\t#include <morphcolor_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#include <normal_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\tvViewPosition = - mvPosition.xyz;\n\t#include <worldpos_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n#ifdef USE_TRANSMISSION\n\tvWorldPosition = worldPosition.xyz;\n#endif\n}"; const fragment$5 = "#define STANDARD\n#ifdef PHYSICAL\n\t#define IOR\n\t#define USE_SPECULAR\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef IOR\n\tuniform float ior;\n#endif\n#ifdef USE_SPECULAR\n\tuniform float specularIntensity;\n\tuniform vec3 specularColor;\n\t#ifdef USE_SPECULAR_COLORMAP\n\t\tuniform sampler2D specularColorMap;\n\t#endif\n\t#ifdef USE_SPECULAR_INTENSITYMAP\n\t\tuniform sampler2D specularIntensityMap;\n\t#endif\n#endif\n#ifdef USE_CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_IRIDESCENCE\n\tuniform float iridescence;\n\tuniform float iridescenceIOR;\n\tuniform float iridescenceThicknessMinimum;\n\tuniform float iridescenceThicknessMaximum;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheenColor;\n\tuniform float sheenRoughness;\n\t#ifdef USE_SHEEN_COLORMAP\n\t\tuniform sampler2D sheenColorMap;\n\t#endif\n\t#ifdef USE_SHEEN_ROUGHNESSMAP\n\t\tuniform sampler2D sheenRoughnessMap;\n\t#endif\n#endif\n#ifdef USE_ANISOTROPY\n\tuniform vec2 anisotropyVector;\n\t#ifdef USE_ANISOTROPYMAP\n\t\tuniform sampler2D anisotropyMap;\n\t#endif\n#endif\nvarying vec3 vViewPosition;\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <alphatest_pars_fragment>\n#include <alphahash_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n#include <iridescence_fragment>\n#include <cube_uv_reflection_fragment>\n#include <envmap_common_pars_fragment>\n#include <envmap_physical_pars_fragment>\n#include <fog_pars_fragment>\n#include <lights_pars_begin>\n#include <normal_pars_fragment>\n#include <lights_physical_pars_fragment>\n#include <transmission_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <clearcoat_pars_fragment>\n#include <iridescence_pars_fragment>\n#include <roughnessmap_pars_fragment>\n#include <metalnessmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <alphahash_fragment>\n\t#include <roughnessmap_fragment>\n\t#include <metalnessmap_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\t#include <clearcoat_normal_fragment_begin>\n\t#include <clearcoat_normal_fragment_maps>\n\t#include <emissivemap_fragment>\n\t#include <lights_physical_fragment>\n\t#include <lights_fragment_begin>\n\t#include <lights_fragment_maps>\n\t#include <lights_fragment_end>\n\t#include <aomap_fragment>\n\tvec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;\n\tvec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;\n\t#include <transmission_fragment>\n\tvec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;\n\t#ifdef USE_SHEEN\n\t\tfloat sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );\n\t\toutgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular;\n\t#endif\n\t#ifdef USE_CLEARCOAT\n\t\tfloat dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\tvec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );\n\t\toutgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat;\n\t#endif\n\t#include <opaque_fragment>\n\t#include <tonemapping_fragment>\n\t#include <colorspace_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}"; const vertex$4 = "#define TOON\nvarying vec3 vViewPosition;\n#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <normal_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <shadowmap_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <color_vertex>\n\t#include <morphcolor_vertex>\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#include <normal_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\tvViewPosition = - mvPosition.xyz;\n\t#include <worldpos_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}"; const fragment$4 = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include <common>\n#include <packing>\n#include <dithering_pars_fragment>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <alphatest_pars_fragment>\n#include <alphahash_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <emissivemap_pars_fragment>\n#include <gradientmap_pars_fragment>\n#include <fog_pars_fragment>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <normal_pars_fragment>\n#include <lights_toon_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <bumpmap_pars_fragment>\n#include <normalmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <color_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <alphahash_fragment>\n\t#include <normal_fragment_begin>\n\t#include <normal_fragment_maps>\n\t#include <emissivemap_fragment>\n\t#include <lights_toon_fragment>\n\t#include <lights_fragment_begin>\n\t#include <lights_fragment_maps>\n\t#include <lights_fragment_end>\n\t#include <aomap_fragment>\n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include <opaque_fragment>\n\t#include <tonemapping_fragment>\n\t#include <colorspace_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n\t#include <dithering_fragment>\n}"; const vertex$3 = "uniform float size;\nuniform float scale;\n#include <common>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\n#ifdef USE_POINTS_UV\n\tvarying vec2 vUv;\n\tuniform mat3 uvTransform;\n#endif\nvoid main() {\n\t#ifdef USE_POINTS_UV\n\t\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\t#endif\n\t#include <color_vertex>\n\t#include <morphcolor_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <project_vertex>\n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <worldpos_vertex>\n\t#include <fog_vertex>\n}"; const fragment$3 = "uniform vec3 diffuse;\nuniform float opacity;\n#include <common>\n#include <color_pars_fragment>\n#include <map_particle_pars_fragment>\n#include <alphatest_pars_fragment>\n#include <alphahash_pars_fragment>\n#include <fog_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <map_particle_fragment>\n\t#include <color_fragment>\n\t#include <alphatest_fragment>\n\t#include <alphahash_fragment>\n\toutgoingLight = diffuseColor.rgb;\n\t#include <opaque_fragment>\n\t#include <tonemapping_fragment>\n\t#include <colorspace_fragment>\n\t#include <fog_fragment>\n\t#include <premultiplied_alpha_fragment>\n}"; const vertex$2 = "#include <common>\n#include <fog_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <shadowmap_pars_vertex>\nvoid main() {\n\t#include <beginnormal_vertex>\n\t#include <morphnormal_vertex>\n\t#include <skinbase_vertex>\n\t#include <skinnormal_vertex>\n\t#include <defaultnormal_vertex>\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <project_vertex>\n\t#include <logdepthbuf_vertex>\n\t#include <worldpos_vertex>\n\t#include <shadowmap_vertex>\n\t#include <fog_vertex>\n}"; const fragment$2 = "uniform vec3 color;\nuniform float opacity;\n#include <common>\n#include <packing>\n#include <fog_pars_fragment>\n#include <bsdfs>\n#include <lights_pars_begin>\n#include <logdepthbuf_pars_fragment>\n#include <shadowmap_pars_fragment>\n#include <shadowmask_pars_fragment>\nvoid main() {\n\t#include <logdepthbuf_fragment>\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include <tonemapping_fragment>\n\t#include <colorspace_fragment>\n\t#include <fog_fragment>\n}"; const vertex$1 = "uniform float rotation;\nuniform vec2 center;\n#include <common>\n#include <uv_pars_vertex>\n#include <fog_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include <logdepthbuf_vertex>\n\t#include <clipping_planes_vertex>\n\t#include <fog_vertex>\n}"; const fragment$1 = "uniform vec3 diffuse;\nuniform float opacity;\n#include <common>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <alphatest_pars_fragment>\n#include <alphahash_pars_fragment>\n#include <fog_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include <logdepthbuf_fragment>\n\t#include <map_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <alphahash_fragment>\n\toutgoingLight = diffuseColor.rgb;\n\t#include <opaque_fragment>\n\t#include <tonemapping_fragment>\n\t#include <colorspace_fragment>\n\t#include <fog_fragment>\n}"; const ShaderChunk = { alphahash_fragment: alphahash_fragment, alphahash_pars_fragment: alphahash_pars_fragment, alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, alphatest_pars_fragment: alphatest_pars_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, iridescence_fragment: iridescence_fragment, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, colorspace_fragment: colorspace_fragment, colorspace_pars_fragment: colorspace_pars_fragment, envmap_fragment: envmap_fragment, envmap_common_pars_fragment: envmap_common_pars_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_physical_pars_fragment: envmap_physical_pars_fragment, envmap_vertex: envmap_vertex, fog_vertex: fog_vertex, fog_pars_vertex: fog_pars_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_fragment: lightmap_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_fragment: lights_lambert_fragment, lights_lambert_pars_fragment: lights_lambert_pars_fragment, lights_pars_begin: lights_pars_begin, lights_toon_fragment: lights_toon_fragment, lights_toon_pars_fragment: lights_toon_pars_fragment, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_fragment_begin: lights_fragment_begin, lights_fragment_maps: lights_fragment_maps, lights_fragment_end: lights_fragment_end, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphcolor_vertex: morphcolor_vertex, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_fragment_begin: normal_fragment_begin, normal_fragment_maps: normal_fragment_maps, normal_pars_fragment: normal_pars_fragment, normal_pars_vertex: normal_pars_vertex, normal_vertex: normal_vertex, normalmap_pars_fragment: normalmap_pars_fragment, clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, clearcoat_pars_fragment: clearcoat_pars_fragment, iridescence_pars_fragment: iridescence_pars_fragment, opaque_fragment: opaque_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, dithering_fragment: dithering_fragment, dithering_pars_fragment: dithering_pars_fragment, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, transmission_fragment: transmission_fragment, transmission_pars_fragment: transmission_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, worldpos_vertex: worldpos_vertex, background_vert: vertex$h, background_frag: fragment$h, backgroundCube_vert: vertex$g, backgroundCube_frag: fragment$g, cube_vert: vertex$f, cube_frag: fragment$f, depth_vert: vertex$e, depth_frag: fragment$e, distanceRGBA_vert: vertex$d, distanceRGBA_frag: fragment$d, equirect_vert: vertex$c, equirect_frag: fragment$c, linedashed_vert: vertex$b, linedashed_frag: fragment$b, meshbasic_vert: vertex$a, meshbasic_frag: fragment$a, meshlambert_vert: vertex$9, meshlambert_frag: fragment$9, meshmatcap_vert: vertex$8, meshmatcap_frag: fragment$8, meshnormal_vert: vertex$7, meshnormal_frag: fragment$7, meshphong_vert: vertex$6, meshphong_frag: fragment$6, meshphysical_vert: vertex$5, meshphysical_frag: fragment$5, meshtoon_vert: vertex$4, meshtoon_frag: fragment$4, points_vert: vertex$3, points_frag: fragment$3, shadow_vert: vertex$2, shadow_frag: fragment$2, sprite_vert: vertex$1, sprite_frag: fragment$1 }; /** * Uniforms library for shared webgl shaders */ const UniformsLib = { common: { diffuse: { value: /*@__PURE__*/new Color(0xffffff) }, opacity: { value: 1.0 }, map: { value: null }, mapTransform: { value: /*@__PURE__*/new Matrix3() }, alphaMap: { value: null }, alphaMapTransform: { value: /*@__PURE__*/new Matrix3() }, alphaTest: { value: 0 } }, specularmap: { specularMap: { value: null }, specularMapTransform: { value: /*@__PURE__*/new Matrix3() } }, envmap: { envMap: { value: null }, flipEnvMap: { value: -1 }, reflectivity: { value: 1.0 }, // basic, lambert, phong ior: { value: 1.5 }, // physical refractionRatio: { value: 0.98 } // basic, lambert, phong }, aomap: { aoMap: { value: null }, aoMapIntensity: { value: 1 }, aoMapTransform: { value: /*@__PURE__*/new Matrix3() } }, lightmap: { lightMap: { value: null }, lightMapIntensity: { value: 1 }, lightMapTransform: { value: /*@__PURE__*/new Matrix3() } }, bumpmap: { bumpMap: { value: null }, bumpMapTransform: { value: /*@__PURE__*/new Matrix3() }, bumpScale: { value: 1 } }, normalmap: { normalMap: { value: null }, normalMapTransform: { value: /*@__PURE__*/new Matrix3() }, normalScale: { value: /*@__PURE__*/new Vector2(1, 1) } }, displacementmap: { displacementMap: { value: null }, displacementMapTransform: { value: /*@__PURE__*/new Matrix3() }, displacementScale: { value: 1 }, displacementBias: { value: 0 } }, emissivemap: { emissiveMap: { value: null }, emissiveMapTransform: { value: /*@__PURE__*/new Matrix3() } }, metalnessmap: { metalnessMap: { value: null }, metalnessMapTransform: { value: /*@__PURE__*/new Matrix3() } }, roughnessmap: { roughnessMap: { value: null }, roughnessMapTransform: { value: /*@__PURE__*/new Matrix3() } }, gradientmap: { gradientMap: { value: null } }, fog: { fogDensity: { value: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: /*@__PURE__*/new Color(0xffffff) } }, lights: { ambientLightColor: { value: [] }, lightProbe: { value: [] }, directionalLights: { value: [], properties: { direction: {}, color: {} } }, directionalLightShadows: { value: [], properties: { shadowBias: {}, shadowNormalBias: {}, shadowRadius: {}, shadowMapSize: {} } }, directionalShadowMap: { value: [] }, directionalShadowMatrix: { value: [] }, spotLights: { value: [], properties: { color: {}, position: {}, direction: {}, distance: {}, coneCos: {}, penumbraCos: {}, decay: {} } }, spotLightShadows: { value: [], properties: { shadowBias: {}, shadowNormalBias: {}, shadowRadius: {}, shadowMapSize: {} } }, spotLightMap: { value: [] }, spotShadowMap: { value: [] }, spotLightMatrix: { value: [] }, pointLights: { value: [], properties: { color: {}, position: {}, decay: {}, distance: {} } }, pointLightShadows: { value: [], properties: { shadowBias: {}, shadowNormalBias: {}, shadowRadius: {}, shadowMapSize: {}, shadowCameraNear: {}, shadowCameraFar: {} } }, pointShadowMap: { value: [] }, pointShadowMatrix: { value: [] }, hemisphereLights: { value: [], properties: { direction: {}, skyColor: {}, groundColor: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {} } }, ltc_1: { value: null }, ltc_2: { value: null } }, points: { diffuse: { value: /*@__PURE__*/new Color(0xffffff) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, alphaMap: { value: null }, alphaMapTransform: { value: /*@__PURE__*/new Matrix3() }, alphaTest: { value: 0 }, uvTransform: { value: /*@__PURE__*/new Matrix3() } }, sprite: { diffuse: { value: /*@__PURE__*/new Color(0xffffff) }, opacity: { value: 1.0 }, center: { value: /*@__PURE__*/new Vector2(0.5, 0.5) }, rotation: { value: 0.0 }, map: { value: null }, mapTransform: { value: /*@__PURE__*/new Matrix3() }, alphaMap: { value: null }, alphaMapTransform: { value: /*@__PURE__*/new Matrix3() }, alphaTest: { value: 0 } } }; const ShaderLib = { basic: { uniforms: /*@__PURE__*/mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog]), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag }, lambert: { uniforms: /*@__PURE__*/mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/new Color(0x000000) } }]), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag }, phong: { uniforms: /*@__PURE__*/mergeUniforms([UniformsLib.common, UniformsLib.specularmap, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/new Color(0x000000) }, specular: { value: /*@__PURE__*/new Color(0x111111) }, shininess: { value: 30 } }]), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag }, standard: { uniforms: /*@__PURE__*/mergeUniforms([UniformsLib.common, UniformsLib.envmap, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/new Color(0x000000) }, roughness: { value: 1.0 }, metalness: { value: 0.0 }, envMapIntensity: { value: 1 } // temporary }]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }, toon: { uniforms: /*@__PURE__*/mergeUniforms([UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: /*@__PURE__*/new Color(0x000000) } }]), vertexShader: ShaderChunk.meshtoon_vert, fragmentShader: ShaderChunk.meshtoon_frag }, matcap: { uniforms: /*@__PURE__*/mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.fog, { matcap: { value: null } }]), vertexShader: ShaderChunk.meshmatcap_vert, fragmentShader: ShaderChunk.meshmatcap_frag }, points: { uniforms: /*@__PURE__*/mergeUniforms([UniformsLib.points, UniformsLib.fog]), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag }, dashed: { uniforms: /*@__PURE__*/mergeUniforms([UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } }]), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag }, depth: { uniforms: /*@__PURE__*/mergeUniforms([UniformsLib.common, UniformsLib.displacementmap]), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag }, normal: { uniforms: /*@__PURE__*/mergeUniforms([UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.meshnormal_vert, fragmentShader: ShaderChunk.meshnormal_frag }, sprite: { uniforms: /*@__PURE__*/mergeUniforms([UniformsLib.sprite, UniformsLib.fog]), vertexShader: ShaderChunk.sprite_vert, fragmentShader: ShaderChunk.sprite_frag }, background: { uniforms: { uvTransform: { value: /*@__PURE__*/new Matrix3() }, t2D: { value: null }, backgroundIntensity: { value: 1 } }, vertexShader: ShaderChunk.background_vert, fragmentShader: ShaderChunk.background_frag }, backgroundCube: { uniforms: { envMap: { value: null }, flipEnvMap: { value: -1 }, backgroundBlurriness: { value: 0 }, backgroundIntensity: { value: 1 } }, vertexShader: ShaderChunk.backgroundCube_vert, fragmentShader: ShaderChunk.backgroundCube_frag }, cube: { uniforms: { tCube: { value: null }, tFlip: { value: -1 }, opacity: { value: 1.0 } }, vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag }, equirect: { uniforms: { tEquirect: { value: null } }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag }, distanceRGBA: { uniforms: /*@__PURE__*/mergeUniforms([UniformsLib.common, UniformsLib.displacementmap, { referencePosition: { value: /*@__PURE__*/new Vector3() }, nearDistance: { value: 1 }, farDistance: { value: 1000 } }]), vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag }, shadow: { uniforms: /*@__PURE__*/mergeUniforms([UniformsLib.lights, UniformsLib.fog, { color: { value: /*@__PURE__*/new Color(0x00000) }, opacity: { value: 1.0 } }]), vertexShader: ShaderChunk.shadow_vert, fragmentShader: ShaderChunk.shadow_frag } }; ShaderLib.physical = { uniforms: /*@__PURE__*/mergeUniforms([ShaderLib.standard.uniforms, { clearcoat: { value: 0 }, clearcoatMap: { value: null }, clearcoatMapTransform: { value: /*@__PURE__*/new Matrix3() }, clearcoatNormalMap: { value: null }, clearcoatNormalMapTransform: { value: /*@__PURE__*/new Matrix3() }, clearcoatNormalScale: { value: /*@__PURE__*/new Vector2(1, 1) }, clearcoatRoughness: { value: 0 }, clearcoatRoughnessMap: { value: null }, clearcoatRoughnessMapTransform: { value: /*@__PURE__*/new Matrix3() }, iridescence: { value: 0 }, iridescenceMap: { value: null }, iridescenceMapTransform: { value: /*@__PURE__*/new Matrix3() }, iridescenceIOR: { value: 1.3 }, iridescenceThicknessMinimum: { value: 100 }, iridescenceThicknessMaximum: { value: 400 }, iridescenceThicknessMap: { value: null }, iridescenceThicknessMapTransform: { value: /*@__PURE__*/new Matrix3() }, sheen: { value: 0 }, sheenColor: { value: /*@__PURE__*/new Color(0x000000) }, sheenColorMap: { value: null }, sheenColorMapTransform: { value: /*@__PURE__*/new Matrix3() }, sheenRoughness: { value: 1 }, sheenRoughnessMap: { value: null }, sheenRoughnessMapTransform: { value: /*@__PURE__*/new Matrix3() }, transmission: { value: 0 }, transmissionMap: { value: null }, transmissionMapTransform: { value: /*@__PURE__*/new Matrix3() }, transmissionSamplerSize: { value: /*@__PURE__*/new Vector2() }, transmissionSamplerMap: { value: null }, thickness: { value: 0 }, thicknessMap: { value: null }, thicknessMapTransform: { value: /*@__PURE__*/new Matrix3() }, attenuationDistance: { value: 0 }, attenuationColor: { value: /*@__PURE__*/new Color(0x000000) }, specularColor: { value: /*@__PURE__*/new Color(1, 1, 1) }, specularColorMap: { value: null }, specularColorMapTransform: { value: /*@__PURE__*/new Matrix3() }, specularIntensity: { value: 1 }, specularIntensityMap: { value: null }, specularIntensityMapTransform: { value: /*@__PURE__*/new Matrix3() }, anisotropyVector: { value: /*@__PURE__*/new Vector2() }, anisotropyMap: { value: null }, anisotropyMapTransform: { value: /*@__PURE__*/new Matrix3() } }]), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }; const _rgb = { r: 0, b: 0, g: 0 }; function WebGLBackground(renderer, cubemaps, cubeuvmaps, state, objects, alpha, premultipliedAlpha) { const clearColor = new Color(0x000000); let clearAlpha = alpha === true ? 0 : 1; let planeMesh; let boxMesh; let currentBackground = null; let currentBackgroundVersion = 0; let currentTonemapping = null; function render(renderList, scene) { let forceClear = false; let background = scene.isScene === true ? scene.background : null; if (background && background.isTexture) { const usePMREM = scene.backgroundBlurriness > 0; // use PMREM if the user wants to blur the background background = (usePMREM ? cubeuvmaps : cubemaps).get(background); } if (background === null) { setClear(clearColor, clearAlpha); } else if (background && background.isColor) { setClear(background, 1); forceClear = true; } const xr = renderer.xr; const environmentBlendMode = xr.getEnvironmentBlendMode(); switch (environmentBlendMode) { case 'opaque': forceClear = true; break; case 'additive': state.buffers.color.setClear(0, 0, 0, 1, premultipliedAlpha); forceClear = true; break; case 'alpha-blend': state.buffers.color.setClear(0, 0, 0, 0, premultipliedAlpha); forceClear = true; break; } if (renderer.autoClear || forceClear) { renderer.clear(renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil); } if (background && (background.isCubeTexture || background.mapping === CubeUVReflectionMapping)) { if (boxMesh === undefined) { boxMesh = new Mesh(new BoxGeometry(1, 1, 1), new ShaderMaterial({ name: 'BackgroundCubeMaterial', uniforms: cloneUniforms(ShaderLib.backgroundCube.uniforms), vertexShader: ShaderLib.backgroundCube.vertexShader, fragmentShader: ShaderLib.backgroundCube.fragmentShader, side: BackSide, depthTest: false, depthWrite: false, fog: false })); boxMesh.geometry.deleteAttribute('normal'); boxMesh.geometry.deleteAttribute('uv'); boxMesh.onBeforeRender = function (renderer, scene, camera) { this.matrixWorld.copyPosition(camera.matrixWorld); }; // add "envMap" material property so the renderer can evaluate it like for built-in materials Object.defineProperty(boxMesh.material, 'envMap', { get: function () { return this.uniforms.envMap.value; } }); objects.update(boxMesh); } boxMesh.material.uniforms.envMap.value = background; boxMesh.material.uniforms.flipEnvMap.value = background.isCubeTexture && background.isRenderTargetTexture === false ? -1 : 1; boxMesh.material.uniforms.backgroundBlurriness.value = scene.backgroundBlurriness; boxMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity; boxMesh.material.toneMapped = background.colorSpace === SRGBColorSpace ? false : true; if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { boxMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } boxMesh.layers.enableAll(); // push to the pre-sorted opaque render list renderList.unshift(boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null); } else if (background && background.isTexture) { if (planeMesh === undefined) { planeMesh = new Mesh(new PlaneGeometry(2, 2), new ShaderMaterial({ name: 'BackgroundMaterial', uniforms: cloneUniforms(ShaderLib.background.uniforms), vertexShader: ShaderLib.background.vertexShader, fragmentShader: ShaderLib.background.fragmentShader, side: FrontSide, depthTest: false, depthWrite: false, fog: false })); planeMesh.geometry.deleteAttribute('normal'); // add "map" material property so the renderer can evaluate it like for built-in materials Object.defineProperty(planeMesh.material, 'map', { get: function () { return this.uniforms.t2D.value; } }); objects.update(planeMesh); } planeMesh.material.uniforms.t2D.value = background; planeMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity; planeMesh.material.toneMapped = background.colorSpace === SRGBColorSpace ? false : true; if (background.matrixAutoUpdate === true) { background.updateMatrix(); } planeMesh.material.uniforms.uvTransform.value.copy(background.matrix); if (currentBackground !== background || currentBackgroundVersion !== background.version || currentTonemapping !== renderer.toneMapping) { planeMesh.material.needsUpdate = true; currentBackground = background; currentBackgroundVersion = background.version; currentTonemapping = renderer.toneMapping; } planeMesh.layers.enableAll(); // push to the pre-sorted opaque render list renderList.unshift(planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null); } } function setClear(color, alpha) { color.getRGB(_rgb, getUnlitUniformColorSpace(renderer)); state.buffers.color.setClear(_rgb.r, _rgb.g, _rgb.b, alpha, premultipliedAlpha); } return { getClearColor: function () { return clearColor; }, setClearColor: function (color, alpha = 1) { clearColor.set(color); clearAlpha = alpha; setClear(clearColor, clearAlpha); }, getClearAlpha: function () { return clearAlpha; }, setClearAlpha: function (alpha) { clearAlpha = alpha; setClear(clearColor, clearAlpha); }, render: render }; } function WebGLBindingStates(gl, extensions, attributes, capabilities) { const maxVertexAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const extension = capabilities.isWebGL2 ? null : extensions.get('OES_vertex_array_object'); const vaoAvailable = capabilities.isWebGL2 || extension !== null; const bindingStates = {}; const defaultState = createBindingState(null); let currentState = defaultState; let forceUpdate = false; function setup(object, material, program, geometry, index) { let updateBuffers = false; if (vaoAvailable) { const state = getBindingState(geometry, program, material); if (currentState !== state) { currentState = state; bindVertexArrayObject(currentState.object); } updateBuffers = needsUpdate(object, geometry, program, index); if (updateBuffers) saveCache(object, geometry, program, index); } else { const wireframe = material.wireframe === true; if (currentState.geometry !== geometry.id || currentState.program !== program.id || currentState.wireframe !== wireframe) { currentState.geometry = geometry.id; currentState.program = program.id; currentState.wireframe = wireframe; updateBuffers = true; } } if (index !== null) { attributes.update(index, gl.ELEMENT_ARRAY_BUFFER); } if (updateBuffers || forceUpdate) { forceUpdate = false; setupVertexAttributes(object, material, program, geometry); if (index !== null) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, attributes.get(index).buffer); } } } function createVertexArrayObject() { if (capabilities.isWebGL2) return gl.createVertexArray(); return extension.createVertexArrayOES(); } function bindVertexArrayObject(vao) { if (capabilities.isWebGL2) return gl.bindVertexArray(vao); return extension.bindVertexArrayOES(vao); } function deleteVertexArrayObject(vao) { if (capabilities.isWebGL2) return gl.deleteVertexArray(vao); return extension.deleteVertexArrayOES(vao); } function getBindingState(geometry, program, material) { const wireframe = material.wireframe === true; let programMap = bindingStates[geometry.id]; if (programMap === undefined) { programMap = {}; bindingStates[geometry.id] = programMap; } let stateMap = programMap[program.id]; if (stateMap === undefined) { stateMap = {}; programMap[program.id] = stateMap; } let state = stateMap[wireframe]; if (state === undefined) { state = createBindingState(createVertexArrayObject()); stateMap[wireframe] = state; } return state; } function createBindingState(vao) { const newAttributes = []; const enabledAttributes = []; const attributeDivisors = []; for (let i = 0; i < maxVertexAttributes; i++) { newAttributes[i] = 0; enabledAttributes[i] = 0; attributeDivisors[i] = 0; } return { // for backward compatibility on non-VAO support browser geometry: null, program: null, wireframe: false, newAttributes: newAttributes, enabledAttributes: enabledAttributes, attributeDivisors: attributeDivisors, object: vao, attributes: {}, index: null }; } function needsUpdate(object, geometry, program, index) { const cachedAttributes = currentState.attributes; const geometryAttributes = geometry.attributes; let attributesNum = 0; const programAttributes = program.getAttributes(); for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { const cachedAttribute = cachedAttributes[name]; let geometryAttribute = geometryAttributes[name]; if (geometryAttribute === undefined) { if (name === 'instanceMatrix' && object.instanceMatrix) geometryAttribute = object.instanceMatrix; if (name === 'instanceColor' && object.instanceColor) geometryAttribute = object.instanceColor; } if (cachedAttribute === undefined) return true; if (cachedAttribute.attribute !== geometryAttribute) return true; if (geometryAttribute && cachedAttribute.data !== geometryAttribute.data) return true; attributesNum++; } } if (currentState.attributesNum !== attributesNum) return true; if (currentState.index !== index) return true; return false; } function saveCache(object, geometry, program, index) { const cache = {}; const attributes = geometry.attributes; let attributesNum = 0; const programAttributes = program.getAttributes(); for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { let attribute = attributes[name]; if (attribute === undefined) { if (name === 'instanceMatrix' && object.instanceMatrix) attribute = object.instanceMatrix; if (name === 'instanceColor' && object.instanceColor) attribute = object.instanceColor; } const data = {}; data.attribute = attribute; if (attribute && attribute.data) { data.data = attribute.data; } cache[name] = data; attributesNum++; } } currentState.attributes = cache; currentState.attributesNum = attributesNum; currentState.index = index; } function initAttributes() { const newAttributes = currentState.newAttributes; for (let i = 0, il = newAttributes.length; i < il; i++) { newAttributes[i] = 0; } } function enableAttribute(attribute) { enableAttributeAndDivisor(attribute, 0); } function enableAttributeAndDivisor(attribute, meshPerAttribute) { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; const attributeDivisors = currentState.attributeDivisors; newAttributes[attribute] = 1; if (enabledAttributes[attribute] === 0) { gl.enableVertexAttribArray(attribute); enabledAttributes[attribute] = 1; } if (attributeDivisors[attribute] !== meshPerAttribute) { const extension = capabilities.isWebGL2 ? gl : extensions.get('ANGLE_instanced_arrays'); extension[capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE'](attribute, meshPerAttribute); attributeDivisors[attribute] = meshPerAttribute; } } function disableUnusedAttributes() { const newAttributes = currentState.newAttributes; const enabledAttributes = currentState.enabledAttributes; for (let i = 0, il = enabledAttributes.length; i < il; i++) { if (enabledAttributes[i] !== newAttributes[i]) { gl.disableVertexAttribArray(i); enabledAttributes[i] = 0; } } } function vertexAttribPointer(index, size, type, normalized, stride, offset, integer) { if (integer === true) { gl.vertexAttribIPointer(index, size, type, stride, offset); } else { gl.vertexAttribPointer(index, size, type, normalized, stride, offset); } } function setupVertexAttributes(object, material, program, geometry) { if (capabilities.isWebGL2 === false && (object.isInstancedMesh || geometry.isInstancedBufferGeometry)) { if (extensions.get('ANGLE_instanced_arrays') === null) return; } initAttributes(); const geometryAttributes = geometry.attributes; const programAttributes = program.getAttributes(); const materialDefaultAttributeValues = material.defaultAttributeValues; for (const name in programAttributes) { const programAttribute = programAttributes[name]; if (programAttribute.location >= 0) { let geometryAttribute = geometryAttributes[name]; if (geometryAttribute === undefined) { if (name === 'instanceMatrix' && object.instanceMatrix) geometryAttribute = object.instanceMatrix; if (name === 'instanceColor' && object.instanceColor) geometryAttribute = object.instanceColor; } if (geometryAttribute !== undefined) { const normalized = geometryAttribute.normalized; const size = geometryAttribute.itemSize; const attribute = attributes.get(geometryAttribute); // TODO Attribute may not be available on context restore if (attribute === undefined) continue; const buffer = attribute.buffer; const type = attribute.type; const bytesPerElement = attribute.bytesPerElement; // check for integer attributes (WebGL 2 only) const integer = capabilities.isWebGL2 === true && (type === gl.INT || type === gl.UNSIGNED_INT || geometryAttribute.gpuType === IntType); if (geometryAttribute.isInterleavedBufferAttribute) { const data = geometryAttribute.data; const stride = data.stride; const offset = geometryAttribute.offset; if (data.isInstancedInterleavedBuffer) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor(programAttribute.location + i, data.meshPerAttribute); } if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { geometry._maxInstanceCount = data.meshPerAttribute * data.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer(programAttribute.location + i, size / programAttribute.locationSize, type, normalized, stride * bytesPerElement, (offset + size / programAttribute.locationSize * i) * bytesPerElement, integer); } } else { if (geometryAttribute.isInstancedBufferAttribute) { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttributeAndDivisor(programAttribute.location + i, geometryAttribute.meshPerAttribute); } if (object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined) { geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { for (let i = 0; i < programAttribute.locationSize; i++) { enableAttribute(programAttribute.location + i); } } gl.bindBuffer(gl.ARRAY_BUFFER, buffer); for (let i = 0; i < programAttribute.locationSize; i++) { vertexAttribPointer(programAttribute.location + i, size / programAttribute.locationSize, type, normalized, size * bytesPerElement, size / programAttribute.locationSize * i * bytesPerElement, integer); } } } else if (materialDefaultAttributeValues !== undefined) { const value = materialDefaultAttributeValues[name]; if (value !== undefined) { switch (value.length) { case 2: gl.vertexAttrib2fv(programAttribute.location, value); break; case 3: gl.vertexAttrib3fv(programAttribute.location, value); break; case 4: gl.vertexAttrib4fv(programAttribute.location, value); break; default: gl.vertexAttrib1fv(programAttribute.location, value); } } } } } disableUnusedAttributes(); } function dispose() { reset(); for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometryId]; } } function releaseStatesOfGeometry(geometry) { if (bindingStates[geometry.id] === undefined) return; const programMap = bindingStates[geometry.id]; for (const programId in programMap) { const stateMap = programMap[programId]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[programId]; } delete bindingStates[geometry.id]; } function releaseStatesOfProgram(program) { for (const geometryId in bindingStates) { const programMap = bindingStates[geometryId]; if (programMap[program.id] === undefined) continue; const stateMap = programMap[program.id]; for (const wireframe in stateMap) { deleteVertexArrayObject(stateMap[wireframe].object); delete stateMap[wireframe]; } delete programMap[program.id]; } } function reset() { resetDefaultState(); forceUpdate = true; if (currentState === defaultState) return; currentState = defaultState; bindVertexArrayObject(currentState.object); } // for backward-compatibility function resetDefaultState() { defaultState.geometry = null; defaultState.program = null; defaultState.wireframe = false; } return { setup: setup, reset: reset, resetDefaultState: resetDefaultState, dispose: dispose, releaseStatesOfGeometry: releaseStatesOfGeometry, releaseStatesOfProgram: releaseStatesOfProgram, initAttributes: initAttributes, enableAttribute: enableAttribute, disableUnusedAttributes: disableUnusedAttributes }; } function WebGLBufferRenderer(gl, extensions, info, capabilities) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode(value) { mode = value; } function render(start, count) { gl.drawArrays(mode, start, count); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; let extension, methodName; if (isWebGL2) { extension = gl; methodName = 'drawArraysInstanced'; } else { extension = extensions.get('ANGLE_instanced_arrays'); methodName = 'drawArraysInstancedANGLE'; if (extension === null) { console.error('THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.'); return; } } extension[methodName](mode, start, count, primcount); info.update(count, mode, primcount); } // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; } function WebGLCapabilities(gl, extensions, parameters) { let maxAnisotropy; function getMaxAnisotropy() { if (maxAnisotropy !== undefined) return maxAnisotropy; if (extensions.has('EXT_texture_filter_anisotropic') === true) { const extension = extensions.get('EXT_texture_filter_anisotropic'); maxAnisotropy = gl.getParameter(extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision(precision) { if (precision === 'highp') { if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) { return 'highp'; } precision = 'mediump'; } if (precision === 'mediump') { if (gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT).precision > 0) { return 'mediump'; } } return 'lowp'; } const isWebGL2 = typeof WebGL2RenderingContext !== 'undefined' && gl.constructor.name === 'WebGL2RenderingContext'; let precision = parameters.precision !== undefined ? parameters.precision : 'highp'; const maxPrecision = getMaxPrecision(precision); if (maxPrecision !== precision) { console.warn('THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.'); precision = maxPrecision; } const drawBuffers = isWebGL2 || extensions.has('WEBGL_draw_buffers'); const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); const maxVertexTextures = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS); const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); const maxCubemapSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE); const maxAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); const maxVertexUniforms = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS); const maxVaryings = gl.getParameter(gl.MAX_VARYING_VECTORS); const maxFragmentUniforms = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); const vertexTextures = maxVertexTextures > 0; const floatFragmentTextures = isWebGL2 || extensions.has('OES_texture_float'); const floatVertexTextures = vertexTextures && floatFragmentTextures; const maxSamples = isWebGL2 ? gl.getParameter(gl.MAX_SAMPLES) : 0; return { isWebGL2: isWebGL2, drawBuffers: drawBuffers, getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, floatVertexTextures: floatVertexTextures, maxSamples: maxSamples }; } function WebGLClipping(properties) { const scope = this; let globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false; const plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function (planes, enableLocalClipping) { const enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function () { renderingShadows = true; projectPlanes(null); }; this.endShadows = function () { renderingShadows = false; }; this.setGlobalState = function (planes, camera) { globalState = projectPlanes(planes, camera, 0); }; this.setState = function (material, camera, useCache) { const planes = material.clippingPlanes, clipIntersection = material.clipIntersection, clipShadows = material.clipShadows; const materialProperties = properties.get(material); if (!localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && !clipShadows) { // there's no local clipping if (renderingShadows) { // there's no global clipping projectPlanes(null); } else { resetGlobalState(); } } else { const nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4; let dstArray = materialProperties.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes(planes, camera, lGlobal, useCache); for (let i = 0; i !== lGlobal; ++i) { dstArray[i] = globalState[i]; } materialProperties.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if (uniform.value !== globalState) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes(planes, camera, dstOffset, skipTransform) { const nPlanes = planes !== null ? planes.length : 0; let dstArray = null; if (nPlanes !== 0) { dstArray = uniform.value; if (skipTransform !== true || dstArray === null) { const flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix(viewMatrix); if (dstArray === null || dstArray.length < flatSize) { dstArray = new Float32Array(flatSize); } for (let i = 0, i4 = dstOffset; i !== nPlanes; ++i, i4 += 4) { plane.copy(planes[i]).applyMatrix4(viewMatrix, viewNormalMatrix); plane.normal.toArray(dstArray, i4); dstArray[i4 + 3] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; scope.numIntersection = 0; return dstArray; } } function WebGLCubeMaps(renderer) { let cubemaps = new WeakMap(); function mapTextureMapping(texture, mapping) { if (mapping === EquirectangularReflectionMapping) { texture.mapping = CubeReflectionMapping; } else if (mapping === EquirectangularRefractionMapping) { texture.mapping = CubeRefractionMapping; } return texture; } function get(texture) { if (texture && texture.isTexture && texture.isRenderTargetTexture === false) { const mapping = texture.mapping; if (mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping) { if (cubemaps.has(texture)) { const cubemap = cubemaps.get(texture).texture; return mapTextureMapping(cubemap, texture.mapping); } else { const image = texture.image; if (image && image.height > 0) { const renderTarget = new WebGLCubeRenderTarget(image.height / 2); renderTarget.fromEquirectangularTexture(renderer, texture); cubemaps.set(texture, renderTarget); texture.addEventListener('dispose', onTextureDispose); return mapTextureMapping(renderTarget.texture, texture.mapping); } else { // image not yet ready. try the conversion next frame return null; } } } } return texture; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener('dispose', onTextureDispose); const cubemap = cubemaps.get(texture); if (cubemap !== undefined) { cubemaps.delete(texture); cubemap.dispose(); } } function dispose() { cubemaps = new WeakMap(); } return { get: get, dispose: dispose }; } class OrthographicCamera extends Camera { constructor(left = -1, right = 1, top = 1, bottom = -1, near = 0.1, far = 2000) { super(); this.isOrthographicCamera = true; this.type = 'OrthographicCamera'; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = near; this.far = far; this.updateProjectionMatrix(); } copy(source, recursive) { super.copy(source, recursive); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign({}, source.view); return this; } setViewOffset(fullWidth, fullHeight, x, y, width, height) { if (this.view === null) { this.view = { enabled: true, fullWidth: 1, fullHeight: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 }; } this.view.enabled = true; this.view.fullWidth = fullWidth; this.view.fullHeight = fullHeight; this.view.offsetX = x; this.view.offsetY = y; this.view.width = width; this.view.height = height; this.updateProjectionMatrix(); } clearViewOffset() { if (this.view !== null) { this.view.enabled = false; } this.updateProjectionMatrix(); } updateProjectionMatrix() { const dx = (this.right - this.left) / (2 * this.zoom); const dy = (this.top - this.bottom) / (2 * this.zoom); const cx = (this.right + this.left) / 2; const cy = (this.top + this.bottom) / 2; let left = cx - dx; let right = cx + dx; let top = cy + dy; let bottom = cy - dy; if (this.view !== null && this.view.enabled) { const scaleW = (this.right - this.left) / this.view.fullWidth / this.zoom; const scaleH = (this.top - this.bottom) / this.view.fullHeight / this.zoom; left += scaleW * this.view.offsetX; right = left + scaleW * this.view.width; top -= scaleH * this.view.offsetY; bottom = top - scaleH * this.view.height; } this.projectionMatrix.makeOrthographic(left, right, top, bottom, this.near, this.far, this.coordinateSystem); this.projectionMatrixInverse.copy(this.projectionMatrix).invert(); } toJSON(meta) { const data = super.toJSON(meta); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if (this.view !== null) data.object.view = Object.assign({}, this.view); return data; } } const LOD_MIN = 4; // The standard deviations (radians) associated with the extra mips. These are // chosen to approximate a Trowbridge-Reitz distribution function times the // geometric shadowing function. These sigma values squared must match the // variance #defines in cube_uv_reflection_fragment.glsl.js. const EXTRA_LOD_SIGMA = [0.125, 0.215, 0.35, 0.446, 0.526, 0.582]; // The maximum length of the blur for loop. Smaller sigmas will use fewer // samples and exit early, but not recompile the shader. const MAX_SAMPLES = 20; const _flatCamera = /*@__PURE__*/new OrthographicCamera(); const _clearColor = /*@__PURE__*/new Color(); let _oldTarget = null; // Golden Ratio const PHI = (1 + Math.sqrt(5)) / 2; const INV_PHI = 1 / PHI; // Vertices of a dodecahedron (except the opposites, which represent the // same axis), used as axis directions evenly spread on a sphere. const _axisDirections = [/*@__PURE__*/new Vector3(1, 1, 1), /*@__PURE__*/new Vector3(-1, 1, 1), /*@__PURE__*/new Vector3(1, 1, -1), /*@__PURE__*/new Vector3(-1, 1, -1), /*@__PURE__*/new Vector3(0, PHI, INV_PHI), /*@__PURE__*/new Vector3(0, PHI, -INV_PHI), /*@__PURE__*/new Vector3(INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(-INV_PHI, 0, PHI), /*@__PURE__*/new Vector3(PHI, INV_PHI, 0), /*@__PURE__*/new Vector3(-PHI, INV_PHI, 0)]; /** * This class generates a Prefiltered, Mipmapped Radiance Environment Map * (PMREM) from a cubeMap environment texture. This allows different levels of * blur to be quickly accessed based on material roughness. It is packed into a * special CubeUV format that allows us to perform custom interpolation so that * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap * chain, it only goes down to the LOD_MIN level (above), and then creates extra * even more filtered 'mips' at the same LOD_MIN resolution, associated with * higher roughness levels. In this way we maintain resolution to smoothly * interpolate diffuse lighting while limiting sampling computation. * * Paper: Fast, Accurate Image-Based Lighting * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view */ class PMREMGenerator { constructor(renderer) { this._renderer = renderer; this._pingPongRenderTarget = null; this._lodMax = 0; this._cubeSize = 0; this._lodPlanes = []; this._sizeLods = []; this._sigmas = []; this._blurMaterial = null; this._cubemapMaterial = null; this._equirectMaterial = null; this._compileMaterial(this._blurMaterial); } /** * Generates a PMREM from a supplied Scene, which can be faster than using an * image if networking bandwidth is low. Optional sigma specifies a blur radius * in radians to be applied to the scene before PMREM generation. Optional near * and far planes ensure the scene is rendered in its entirety (the cubeCamera * is placed at the origin). */ fromScene(scene, sigma = 0, near = 0.1, far = 100) { _oldTarget = this._renderer.getRenderTarget(); this._setSize(256); const cubeUVRenderTarget = this._allocateTargets(); cubeUVRenderTarget.depthBuffer = true; this._sceneToCubeUV(scene, near, far, cubeUVRenderTarget); if (sigma > 0) { this._blur(cubeUVRenderTarget, 0, 0, sigma); } this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } /** * Generates a PMREM from an equirectangular texture, which can be either LDR * or HDR. The ideal input image size is 1k (1024 x 512), * as this matches best with the 256 x 256 cubemap output. */ fromEquirectangular(equirectangular, renderTarget = null) { return this._fromTexture(equirectangular, renderTarget); } /** * Generates a PMREM from an cubemap texture, which can be either LDR * or HDR. The ideal input cube size is 256 x 256, * as this matches best with the 256 x 256 cubemap output. */ fromCubemap(cubemap, renderTarget = null) { return this._fromTexture(cubemap, renderTarget); } /** * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during * your texture's network fetch for increased concurrency. */ compileCubemapShader() { if (this._cubemapMaterial === null) { this._cubemapMaterial = _getCubemapMaterial(); this._compileMaterial(this._cubemapMaterial); } } /** * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during * your texture's network fetch for increased concurrency. */ compileEquirectangularShader() { if (this._equirectMaterial === null) { this._equirectMaterial = _getEquirectMaterial(); this._compileMaterial(this._equirectMaterial); } } /** * Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class, * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on * one of them will cause any others to also become unusable. */ dispose() { this._dispose(); if (this._cubemapMaterial !== null) this._cubemapMaterial.dispose(); if (this._equirectMaterial !== null) this._equirectMaterial.dispose(); } // private interface _setSize(cubeSize) { this._lodMax = Math.floor(Math.log2(cubeSize)); this._cubeSize = Math.pow(2, this._lodMax); } _dispose() { if (this._blurMaterial !== null) this._blurMaterial.dispose(); if (this._pingPongRenderTarget !== null) this._pingPongRenderTarget.dispose(); for (let i = 0; i < this._lodPlanes.length; i++) { this._lodPlanes[i].dispose(); } } _cleanup(outputTarget) { this._renderer.setRenderTarget(_oldTarget); outputTarget.scissorTest = false; _setViewport(outputTarget, 0, 0, outputTarget.width, outputTarget.height); } _fromTexture(texture, renderTarget) { if (texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping) { this._setSize(texture.image.length === 0 ? 16 : texture.image[0].width || texture.image[0].image.width); } else { // Equirectangular this._setSize(texture.image.width / 4); } _oldTarget = this._renderer.getRenderTarget(); const cubeUVRenderTarget = renderTarget || this._allocateTargets(); this._textureToCubeUV(texture, cubeUVRenderTarget); this._applyPMREM(cubeUVRenderTarget); this._cleanup(cubeUVRenderTarget); return cubeUVRenderTarget; } _allocateTargets() { const width = 3 * Math.max(this._cubeSize, 16 * 7); const height = 4 * this._cubeSize; const params = { magFilter: LinearFilter, minFilter: LinearFilter, generateMipmaps: false, type: HalfFloatType, format: RGBAFormat, colorSpace: LinearSRGBColorSpace, depthBuffer: false }; const cubeUVRenderTarget = _createRenderTarget(width, height, params); if (this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width || this._pingPongRenderTarget.height !== height) { if (this._pingPongRenderTarget !== null) { this._dispose(); } this._pingPongRenderTarget = _createRenderTarget(width, height, params); const { _lodMax } = this; ({ sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes(_lodMax)); this._blurMaterial = _getBlurShader(_lodMax, width, height); } return cubeUVRenderTarget; } _compileMaterial(material) { const tmpMesh = new Mesh(this._lodPlanes[0], material); this._renderer.compile(tmpMesh, _flatCamera); } _sceneToCubeUV(scene, near, far, cubeUVRenderTarget) { const fov = 90; const aspect = 1; const cubeCamera = new PerspectiveCamera(fov, aspect, near, far); const upSign = [1, -1, 1, 1, 1, 1]; const forwardSign = [1, 1, 1, -1, -1, -1]; const renderer = this._renderer; const originalAutoClear = renderer.autoClear; const toneMapping = renderer.toneMapping; renderer.getClearColor(_clearColor); renderer.toneMapping = NoToneMapping; renderer.autoClear = false; const backgroundMaterial = new MeshBasicMaterial({ name: 'PMREM.Background', side: BackSide, depthWrite: false, depthTest: false }); const backgroundBox = new Mesh(new BoxGeometry(), backgroundMaterial); let useSolidColor = false; const background = scene.background; if (background) { if (background.isColor) { backgroundMaterial.color.copy(background); scene.background = null; useSolidColor = true; } } else { backgroundMaterial.color.copy(_clearColor); useSolidColor = true; } for (let i = 0; i < 6; i++) { const col = i % 3; if (col === 0) { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(forwardSign[i], 0, 0); } else if (col === 1) { cubeCamera.up.set(0, 0, upSign[i]); cubeCamera.lookAt(0, forwardSign[i], 0); } else { cubeCamera.up.set(0, upSign[i], 0); cubeCamera.lookAt(0, 0, forwardSign[i]); } const size = this._cubeSize; _setViewport(cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size); renderer.setRenderTarget(cubeUVRenderTarget); if (useSolidColor) { renderer.render(backgroundBox, cubeCamera); } renderer.render(scene, cubeCamera); } backgroundBox.geometry.dispose(); backgroundBox.material.dispose(); renderer.toneMapping = toneMapping; renderer.autoClear = originalAutoClear; scene.background = background; } _textureToCubeUV(texture, cubeUVRenderTarget) { const renderer = this._renderer; const isCubeTexture = texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping; if (isCubeTexture) { if (this._cubemapMaterial === null) { this._cubemapMaterial = _getCubemapMaterial(); } this._cubemapMaterial.uniforms.flipEnvMap.value = texture.isRenderTargetTexture === false ? -1 : 1; } else { if (this._equirectMaterial === null) { this._equirectMaterial = _getEquirectMaterial(); } } const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial; const mesh = new Mesh(this._lodPlanes[0], material); const uniforms = material.uniforms; uniforms['envMap'].value = texture; const size = this._cubeSize; _setViewport(cubeUVRenderTarget, 0, 0, 3 * size, 2 * size); renderer.setRenderTarget(cubeUVRenderTarget); renderer.render(mesh, _flatCamera); } _applyPMREM(cubeUVRenderTarget) { const renderer = this._renderer; const autoClear = renderer.autoClear; renderer.autoClear = false; for (let i = 1; i < this._lodPlanes.length; i++) { const sigma = Math.sqrt(this._sigmas[i] * this._sigmas[i] - this._sigmas[i - 1] * this._sigmas[i - 1]); const poleAxis = _axisDirections[(i - 1) % _axisDirections.length]; this._blur(cubeUVRenderTarget, i - 1, i, sigma, poleAxis); } renderer.autoClear = autoClear; } /** * This is a two-pass Gaussian blur for a cubemap. Normally this is done * vertically and horizontally, but this breaks down on a cube. Here we apply * the blur latitudinally (around the poles), and then longitudinally (towards * the poles) to approximate the orthogonally-separable blur. It is least * accurate at the poles, but still does a decent job. */ _blur(cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis) { const pingPongRenderTarget = this._pingPongRenderTarget; this._halfBlur(cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, sigma, 'latitudinal', poleAxis); this._halfBlur(pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, sigma, 'longitudinal', poleAxis); } _halfBlur(targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis) { const renderer = this._renderer; const blurMaterial = this._blurMaterial; if (direction !== 'latitudinal' && direction !== 'longitudinal') { console.error('blur direction must be either latitudinal or longitudinal!'); } // Number of standard deviations at which to cut off the discrete approximation. const STANDARD_DEVIATIONS = 3; const blurMesh = new Mesh(this._lodPlanes[lodOut], blurMaterial); const blurUniforms = blurMaterial.uniforms; const pixels = this._sizeLods[lodIn] - 1; const radiansPerPixel = isFinite(sigmaRadians) ? Math.PI / (2 * pixels) : 2 * Math.PI / (2 * MAX_SAMPLES - 1); const sigmaPixels = sigmaRadians / radiansPerPixel; const samples = isFinite(sigmaRadians) ? 1 + Math.floor(STANDARD_DEVIATIONS * sigmaPixels) : MAX_SAMPLES; if (samples > MAX_SAMPLES) { console.warn(`sigmaRadians, ${sigmaRadians}, is too large and will clip, as it requested ${samples} samples when the maximum is set to ${MAX_SAMPLES}`); } const weights = []; let sum = 0; for (let i = 0; i < MAX_SAMPLES; ++i) { const x = i / sigmaPixels; const weight = Math.exp(-x * x / 2); weights.push(weight); if (i === 0) { sum += weight; } else if (i < samples) { sum += 2 * weight; } } for (let i = 0; i < weights.length; i++) { weights[i] = weights[i] / sum; } blurUniforms['envMap'].value = targetIn.texture; blurUniforms['samples'].value = samples; blurUniforms['weights'].value = weights; blurUniforms['latitudinal'].value = direction === 'latitudinal'; if (poleAxis) { blurUniforms['poleAxis'].value = poleAxis; } const { _lodMax } = this; blurUniforms['dTheta'].value = radiansPerPixel; blurUniforms['mipInt'].value = _lodMax - lodIn; const outputSize = this._sizeLods[lodOut]; const x = 3 * outputSize * (lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0); const y = 4 * (this._cubeSize - outputSize); _setViewport(targetOut, x, y, 3 * outputSize, 2 * outputSize); renderer.setRenderTarget(targetOut); renderer.render(blurMesh, _flatCamera); } } function _createPlanes(lodMax) { const lodPlanes = []; const sizeLods = []; const sigmas = []; let lod = lodMax; const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; for (let i = 0; i < totalLods; i++) { const sizeLod = Math.pow(2, lod); sizeLods.push(sizeLod); let sigma = 1.0 / sizeLod; if (i > lodMax - LOD_MIN) { sigma = EXTRA_LOD_SIGMA[i - lodMax + LOD_MIN - 1]; } else if (i === 0) { sigma = 0; } sigmas.push(sigma); const texelSize = 1.0 / (sizeLod - 2); const min = -texelSize; const max = 1 + texelSize; const uv1 = [min, min, max, min, max, max, min, min, max, max, min, max]; const cubeFaces = 6; const vertices = 6; const positionSize = 3; const uvSize = 2; const faceIndexSize = 1; const position = new Float32Array(positionSize * vertices * cubeFaces); const uv = new Float32Array(uvSize * vertices * cubeFaces); const faceIndex = new Float32Array(faceIndexSize * vertices * cubeFaces); for (let face = 0; face < cubeFaces; face++) { const x = face % 3 * 2 / 3 - 1; const y = face > 2 ? 0 : -1; const coordinates = [x, y, 0, x + 2 / 3, y, 0, x + 2 / 3, y + 1, 0, x, y, 0, x + 2 / 3, y + 1, 0, x, y + 1, 0]; position.set(coordinates, positionSize * vertices * face); uv.set(uv1, uvSize * vertices * face); const fill = [face, face, face, face, face, face]; faceIndex.set(fill, faceIndexSize * vertices * face); } const planes = new BufferGeometry(); planes.setAttribute('position', new BufferAttribute(position, positionSize)); planes.setAttribute('uv', new BufferAttribute(uv, uvSize)); planes.setAttribute('faceIndex', new BufferAttribute(faceIndex, faceIndexSize)); lodPlanes.push(planes); if (lod > LOD_MIN) { lod--; } } return { lodPlanes, sizeLods, sigmas }; } function _createRenderTarget(width, height, params) { const cubeUVRenderTarget = new WebGLRenderTarget(width, height, params); cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; cubeUVRenderTarget.texture.name = 'PMREM.cubeUv'; cubeUVRenderTarget.scissorTest = true; return cubeUVRenderTarget; } function _setViewport(target, x, y, width, height) { target.viewport.set(x, y, width, height); target.scissor.set(x, y, width, height); } function _getBlurShader(lodMax, width, height) { const weights = new Float32Array(MAX_SAMPLES); const poleAxis = new Vector3(0, 1, 0); const shaderMaterial = new ShaderMaterial({ name: 'SphericalGaussianBlur', defines: { 'n': MAX_SAMPLES, 'CUBEUV_TEXEL_WIDTH': 1.0 / width, 'CUBEUV_TEXEL_HEIGHT': 1.0 / height, 'CUBEUV_MAX_MIP': `${lodMax}.0` }, uniforms: { 'envMap': { value: null }, 'samples': { value: 1 }, 'weights': { value: weights }, 'latitudinal': { value: false }, 'dTheta': { value: 0 }, 'mipInt': { value: 0 }, 'poleAxis': { value: poleAxis } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; uniform int samples; uniform float weights[ n ]; uniform bool latitudinal; uniform float dTheta; uniform float mipInt; uniform vec3 poleAxis; #define ENVMAP_TYPE_CUBE_UV #include <cube_uv_reflection_fragment> vec3 getSample( float theta, vec3 axis ) { float cosTheta = cos( theta ); // Rodrigues' axis-angle rotation vec3 sampleDirection = vOutputDirection * cosTheta + cross( axis, vOutputDirection ) * sin( theta ) + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); return bilinearCubeUV( envMap, sampleDirection, mipInt ); } void main() { vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); if ( all( equal( axis, vec3( 0.0 ) ) ) ) { axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); } axis = normalize( axis ); gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); for ( int i = 1; i < n; i++ ) { if ( i >= samples ) { break; } float theta = dTheta * float( i ); gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); } } `, blending: NoBlending, depthTest: false, depthWrite: false }); return shaderMaterial; } function _getEquirectMaterial() { return new ShaderMaterial({ name: 'EquirectangularToCubeUV', uniforms: { 'envMap': { value: null } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; varying vec3 vOutputDirection; uniform sampler2D envMap; #include <common> void main() { vec3 outputDirection = normalize( vOutputDirection ); vec2 uv = equirectUv( outputDirection ); gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); } `, blending: NoBlending, depthTest: false, depthWrite: false }); } function _getCubemapMaterial() { return new ShaderMaterial({ name: 'CubemapToCubeUV', uniforms: { 'envMap': { value: null }, 'flipEnvMap': { value: -1 } }, vertexShader: _getCommonVertexShader(), fragmentShader: /* glsl */` precision mediump float; precision mediump int; uniform float flipEnvMap; varying vec3 vOutputDirection; uniform samplerCube envMap; void main() { gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } `, blending: NoBlending, depthTest: false, depthWrite: false }); } function _getCommonVertexShader() { return (/* glsl */` precision mediump float; precision mediump int; attribute float faceIndex; varying vec3 vOutputDirection; // RH coordinate system; PMREM face-indexing convention vec3 getDirection( vec2 uv, float face ) { uv = 2.0 * uv - 1.0; vec3 direction = vec3( uv, 1.0 ); if ( face == 0.0 ) { direction = direction.zyx; // ( 1, v, u ) pos x } else if ( face == 1.0 ) { direction = direction.xzy; direction.xz *= -1.0; // ( -u, 1, -v ) pos y } else if ( face == 2.0 ) { direction.x *= -1.0; // ( -u, v, 1 ) pos z } else if ( face == 3.0 ) { direction = direction.zyx; direction.xz *= -1.0; // ( -1, v, -u ) neg x } else if ( face == 4.0 ) { direction = direction.xzy; direction.xy *= -1.0; // ( -u, -1, v ) neg y } else if ( face == 5.0 ) { direction.z *= -1.0; // ( u, v, -1 ) neg z } return direction; } void main() { vOutputDirection = getDirection( uv, faceIndex ); gl_Position = vec4( position, 1.0 ); } ` ); } function WebGLCubeUVMaps(renderer) { let cubeUVmaps = new WeakMap(); let pmremGenerator = null; function get(texture) { if (texture && texture.isTexture) { const mapping = texture.mapping; const isEquirectMap = mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping; const isCubeMap = mapping === CubeReflectionMapping || mapping === CubeRefractionMapping; // equirect/cube map to cubeUV conversion if (isEquirectMap || isCubeMap) { if (texture.isRenderTargetTexture && texture.needsPMREMUpdate === true) { texture.needsPMREMUpdate = false; let renderTarget = cubeUVmaps.get(texture); if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture, renderTarget) : pmremGenerator.fromCubemap(texture, renderTarget); cubeUVmaps.set(texture, renderTarget); return renderTarget.texture; } else { if (cubeUVmaps.has(texture)) { return cubeUVmaps.get(texture).texture; } else { const image = texture.image; if (isEquirectMap && image && image.height > 0 || isCubeMap && image && isCubeTextureComplete(image)) { if (pmremGenerator === null) pmremGenerator = new PMREMGenerator(renderer); const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular(texture) : pmremGenerator.fromCubemap(texture); cubeUVmaps.set(texture, renderTarget); texture.addEventListener('dispose', onTextureDispose); return renderTarget.texture; } else { // image not yet ready. try the conversion next frame return null; } } } } } return texture; } function isCubeTextureComplete(image) { let count = 0; const length = 6; for (let i = 0; i < length; i++) { if (image[i] !== undefined) count++; } return count === length; } function onTextureDispose(event) { const texture = event.target; texture.removeEventListener('dispose', onTextureDispose); const cubemapUV = cubeUVmaps.get(texture); if (cubemapUV !== undefined) { cubeUVmaps.delete(texture); cubemapUV.dispose(); } } function dispose() { cubeUVmaps = new WeakMap(); if (pmremGenerator !== null) { pmremGenerator.dispose(); pmremGenerator = null; } } return { get: get, dispose: dispose }; } function WebGLExtensions(gl) { const extensions = {}; function getExtension(name) { if (extensions[name] !== undefined) { return extensions[name]; } let extension; switch (name) { case 'WEBGL_depth_texture': extension = gl.getExtension('WEBGL_depth_texture') || gl.getExtension('MOZ_WEBGL_depth_texture') || gl.getExtension('WEBKIT_WEBGL_depth_texture'); break; case 'EXT_texture_filter_anisotropic': extension = gl.getExtension('EXT_texture_filter_anisotropic') || gl.getExtension('MOZ_EXT_texture_filter_anisotropic') || gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic'); break; case 'WEBGL_compressed_texture_s3tc': extension = gl.getExtension('WEBGL_compressed_texture_s3tc') || gl.getExtension('MOZ_WEBGL_compressed_texture_s3tc') || gl.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc'); break; case 'WEBGL_compressed_texture_pvrtc': extension = gl.getExtension('WEBGL_compressed_texture_pvrtc') || gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc'); break; default: extension = gl.getExtension(name); } extensions[name] = extension; return extension; } return { has: function (name) { return getExtension(name) !== null; }, init: function (capabilities) { if (capabilities.isWebGL2) { getExtension('EXT_color_buffer_float'); } else { getExtension('WEBGL_depth_texture'); getExtension('OES_texture_float'); getExtension('OES_texture_half_float'); getExtension('OES_texture_half_float_linear'); getExtension('OES_standard_derivatives'); getExtension('OES_element_index_uint'); getExtension('OES_vertex_array_object'); getExtension('ANGLE_instanced_arrays'); } getExtension('OES_texture_float_linear'); getExtension('EXT_color_buffer_half_float'); getExtension('WEBGL_multisampled_render_to_texture'); }, get: function (name) { const extension = getExtension(name); if (extension === null) { console.warn('THREE.WebGLRenderer: ' + name + ' extension not supported.'); } return extension; } }; } function WebGLGeometries(gl, attributes, info, bindingStates) { const geometries = {}; const wireframeAttributes = new WeakMap(); function onGeometryDispose(event) { const geometry = event.target; if (geometry.index !== null) { attributes.remove(geometry.index); } for (const name in geometry.attributes) { attributes.remove(geometry.attributes[name]); } for (const name in geometry.morphAttributes) { const array = geometry.morphAttributes[name]; for (let i = 0, l = array.length; i < l; i++) { attributes.remove(array[i]); } } geometry.removeEventListener('dispose', onGeometryDispose); delete geometries[geometry.id]; const attribute = wireframeAttributes.get(geometry); if (attribute) { attributes.remove(attribute); wireframeAttributes.delete(geometry); } bindingStates.releaseStatesOfGeometry(geometry); if (geometry.isInstancedBufferGeometry === true) { delete geometry._maxInstanceCount; } // info.memory.geometries--; } function get(object, geometry) { if (geometries[geometry.id] === true) return geometry; geometry.addEventListener('dispose', onGeometryDispose); geometries[geometry.id] = true; info.memory.geometries++; return geometry; } function update(geometry) { const geometryAttributes = geometry.attributes; // Updating index buffer in VAO now. See WebGLBindingStates. for (const name in geometryAttributes) { attributes.update(geometryAttributes[name], gl.ARRAY_BUFFER); } // morph targets const morphAttributes = geometry.morphAttributes; for (const name in morphAttributes) { const array = morphAttributes[name]; for (let i = 0, l = array.length; i < l; i++) { attributes.update(array[i], gl.ARRAY_BUFFER); } } } function updateWireframeAttribute(geometry) { const indices = []; const geometryIndex = geometry.index; const geometryPosition = geometry.attributes.position; let version = 0; if (geometryIndex !== null) { const array = geometryIndex.array; version = geometryIndex.version; for (let i = 0, l = array.length; i < l; i += 3) { const a = array[i + 0]; const b = array[i + 1]; const c = array[i + 2]; indices.push(a, b, b, c, c, a); } } else { const array = geometryPosition.array; version = geometryPosition.version; for (let i = 0, l = array.length / 3 - 1; i < l; i += 3) { const a = i + 0; const b = i + 1; const c = i + 2; indices.push(a, b, b, c, c, a); } } const attribute = new (arrayNeedsUint32(indices) ? Uint32BufferAttribute : Uint16BufferAttribute)(indices, 1); attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates // const previousAttribute = wireframeAttributes.get(geometry); if (previousAttribute) attributes.remove(previousAttribute); // wireframeAttributes.set(geometry, attribute); } function getWireframeAttribute(geometry) { const currentAttribute = wireframeAttributes.get(geometry); if (currentAttribute) { const geometryIndex = geometry.index; if (geometryIndex !== null) { // if the attribute is obsolete, create a new one if (currentAttribute.version < geometryIndex.version) { updateWireframeAttribute(geometry); } } } else { updateWireframeAttribute(geometry); } return wireframeAttributes.get(geometry); } return { get: get, update: update, getWireframeAttribute: getWireframeAttribute }; } function WebGLIndexedBufferRenderer(gl, extensions, info, capabilities) { const isWebGL2 = capabilities.isWebGL2; let mode; function setMode(value) { mode = value; } let type, bytesPerElement; function setIndex(value) { type = value.type; bytesPerElement = value.bytesPerElement; } function render(start, count) { gl.drawElements(mode, count, type, start * bytesPerElement); info.update(count, mode, 1); } function renderInstances(start, count, primcount) { if (primcount === 0) return; let extension, methodName; if (isWebGL2) { extension = gl; methodName = 'drawElementsInstanced'; } else { extension = extensions.get('ANGLE_instanced_arrays'); methodName = 'drawElementsInstancedANGLE'; if (extension === null) { console.error('THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.'); return; } } extension[methodName](mode, count, type, start * bytesPerElement, primcount); info.update(count, mode, primcount); } // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; } function WebGLInfo(gl) { const memory = { geometries: 0, textures: 0 }; const render = { frame: 0, calls: 0, triangles: 0, points: 0, lines: 0 }; function update(count, mode, instanceCount) { render.calls++; switch (mode) { case gl.TRIANGLES: render.triangles += instanceCount * (count / 3); break; case gl.LINES: render.lines += instanceCount * (count / 2); break; case gl.LINE_STRIP: render.lines += instanceCount * (count - 1); break; case gl.LINE_LOOP: render.lines += instanceCount * count; break; case gl.POINTS: render.points += instanceCount * count; break; default: console.error('THREE.WebGLInfo: Unknown draw mode:', mode); break; } } function reset() { render.calls = 0; render.triangles = 0; render.points = 0; render.lines = 0; } return { memory: memory, render: render, programs: null, autoReset: true, reset: reset, update: update }; } function numericalSort(a, b) { return a[0] - b[0]; } function absNumericalSort(a, b) { return Math.abs(b[1]) - Math.abs(a[1]); } function WebGLMorphtargets(gl, capabilities, textures) { const influencesList = {}; const morphInfluences = new Float32Array(8); const morphTextures = new WeakMap(); const morph = new Vector4(); const workInfluences = []; for (let i = 0; i < 8; i++) { workInfluences[i] = [i, 0]; } function update(object, geometry, program) { const objectInfluences = object.morphTargetInfluences; if (capabilities.isWebGL2 === true) { // instead of using attributes, the WebGL 2 code path encodes morph targets // into an array of data textures. Each layer represents a single morph target. const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; let entry = morphTextures.get(geometry); if (entry === undefined || entry.count !== morphTargetsCount) { if (entry !== undefined) entry.texture.dispose(); const hasMorphPosition = geometry.morphAttributes.position !== undefined; const hasMorphNormals = geometry.morphAttributes.normal !== undefined; const hasMorphColors = geometry.morphAttributes.color !== undefined; const morphTargets = geometry.morphAttributes.position || []; const morphNormals = geometry.morphAttributes.normal || []; const morphColors = geometry.morphAttributes.color || []; let vertexDataCount = 0; if (hasMorphPosition === true) vertexDataCount = 1; if (hasMorphNormals === true) vertexDataCount = 2; if (hasMorphColors === true) vertexDataCount = 3; let width = geometry.attributes.position.count * vertexDataCount; let height = 1; if (width > capabilities.maxTextureSize) { height = Math.ceil(width / capabilities.maxTextureSize); width = capabilities.maxTextureSize; } const buffer = new Float32Array(width * height * 4 * morphTargetsCount); const texture = new DataArrayTexture(buffer, width, height, morphTargetsCount); texture.type = FloatType; texture.needsUpdate = true; // fill buffer const vertexDataStride = vertexDataCount * 4; for (let i = 0; i < morphTargetsCount; i++) { const morphTarget = morphTargets[i]; const morphNormal = morphNormals[i]; const morphColor = morphColors[i]; const offset = width * height * 4 * i; for (let j = 0; j < morphTarget.count; j++) { const stride = j * vertexDataStride; if (hasMorphPosition === true) { morph.fromBufferAttribute(morphTarget, j); buffer[offset + stride + 0] = morph.x; buffer[offset + stride + 1] = morph.y; buffer[offset + stride + 2] = morph.z; buffer[offset + stride + 3] = 0; } if (hasMorphNormals === true) { morph.fromBufferAttribute(morphNormal, j); buffer[offset + stride + 4] = morph.x; buffer[offset + stride + 5] = morph.y; buffer[offset + stride + 6] = morph.z; buffer[offset + stride + 7] = 0; } if (hasMorphColors === true) { morph.fromBufferAttribute(morphColor, j); buffer[offset + stride + 8] = morph.x; buffer[offset + stride + 9] = morph.y; buffer[offset + stride + 10] = morph.z; buffer[offset + stride + 11] = morphColor.itemSize === 4 ? morph.w : 1; } } } entry = { count: morphTargetsCount, texture: texture, size: new Vector2(width, height) }; morphTextures.set(geometry, entry); function disposeTexture() { texture.dispose(); morphTextures.delete(geometry); geometry.removeEventListener('dispose', disposeTexture); } geometry.addEventListener('dispose', disposeTexture); } // let morphInfluencesSum = 0; for (let i = 0; i < objectInfluences.length; i++) { morphInfluencesSum += objectInfluences[i]; } const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue(gl, 'morphTargetBaseInfluence', morphBaseInfluence); program.getUniforms().setValue(gl, 'morphTargetInfluences', objectInfluences); program.getUniforms().setValue(gl, 'morphTargetsTexture', entry.texture, textures); program.getUniforms().setValue(gl, 'morphTargetsTextureSize', entry.size); } else { // When object doesn't have morph target influences defined, we treat it as a 0-length array // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences const length = objectInfluences === undefined ? 0 : objectInfluences.length; let influences = influencesList[geometry.id]; if (influences === undefined || influences.length !== length) { // initialise list influences = []; for (let i = 0; i < length; i++) { influences[i] = [i, 0]; } influencesList[geometry.id] = influences; } // Collect influences for (let i = 0; i < length; i++) { const influence = influences[i]; influence[0] = i; influence[1] = objectInfluences[i]; } influences.sort(absNumericalSort); for (let i = 0; i < 8; i++) { if (i < length && influences[i][1]) { workInfluences[i][0] = influences[i][0]; workInfluences[i][1] = influences[i][1]; } else { workInfluences[i][0] = Number.MAX_SAFE_INTEGER; workInfluences[i][1] = 0; } } workInfluences.sort(numericalSort); const morphTargets = geometry.morphAttributes.position; const morphNormals = geometry.morphAttributes.normal; let morphInfluencesSum = 0; for (let i = 0; i < 8; i++) { const influence = workInfluences[i]; const index = influence[0]; const value = influence[1]; if (index !== Number.MAX_SAFE_INTEGER && value) { if (morphTargets && geometry.getAttribute('morphTarget' + i) !== morphTargets[index]) { geometry.setAttribute('morphTarget' + i, morphTargets[index]); } if (morphNormals && geometry.getAttribute('morphNormal' + i) !== morphNormals[index]) { geometry.setAttribute('morphNormal' + i, morphNormals[index]); } morphInfluences[i] = value; morphInfluencesSum += value; } else { if (morphTargets && geometry.hasAttribute('morphTarget' + i) === true) { geometry.deleteAttribute('morphTarget' + i); } if (morphNormals && geometry.hasAttribute('morphNormal' + i) === true) { geometry.deleteAttribute('morphNormal' + i); } morphInfluences[i] = 0; } } // GLSL shader uses formula baseinfluence * base + sum(target * influence) // This allows us to switch between absolute morphs and relative morphs without changing shader code // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; program.getUniforms().setValue(gl, 'morphTargetBaseInfluence', morphBaseInfluence); program.getUniforms().setValue(gl, 'morphTargetInfluences', morphInfluences); } } return { update: update }; } function WebGLObjects(gl, geometries, attributes, info) { let updateMap = new WeakMap(); function update(object) { const frame = info.render.frame; const geometry = object.geometry; const buffergeometry = geometries.get(object, geometry); // Update once per frame if (updateMap.get(buffergeometry) !== frame) { geometries.update(buffergeometry); updateMap.set(buffergeometry, frame); } if (object.isInstancedMesh) { if (object.hasEventListener('dispose', onInstancedMeshDispose) === false) { object.addEventListener('dispose', onInstancedMeshDispose); } if (updateMap.get(object) !== frame) { attributes.update(object.instanceMatrix, gl.ARRAY_BUFFER); if (object.instanceColor !== null) { attributes.update(object.instanceColor, gl.ARRAY_BUFFER); } updateMap.set(object, frame); } } if (object.isSkinnedMesh) { const skeleton = object.skeleton; if (updateMap.get(skeleton) !== frame) { skeleton.update(); updateMap.set(skeleton, frame); } } return buffergeometry; } function dispose() { updateMap = new WeakMap(); } function onInstancedMeshDispose(event) { const instancedMesh = event.target; instancedMesh.removeEventListener('dispose', onInstancedMeshDispose); attributes.remove(instancedMesh.instanceMatrix); if (instancedMesh.instanceColor !== null) attributes.remove(instancedMesh.instanceColor); } return { update: update, dispose: dispose }; } /** * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling 'new WebGLUniforms( gl, program )'. * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [textures] ) * * uploads a uniform value(s) * the 'textures' parameter is needed for sampler uniforms * * * Static methods of the top-level container (textures factorizations): * * .upload( gl, seq, values, textures ) * * sets uniforms in 'seq' to 'values[id].value' * * .seqWithValue( seq, values ) : filteredSeq * * filters 'seq' entries with corresponding entry in values * * * Methods of the top-level container (textures factorizations): * * .setValue( gl, name, value, textures ) * * sets uniform with name 'name' to 'value' * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ const emptyTexture = /*@__PURE__*/new Texture(); const emptyArrayTexture = /*@__PURE__*/new DataArrayTexture(); const empty3dTexture = /*@__PURE__*/new Data3DTexture(); const emptyCubeTexture = /*@__PURE__*/new CubeTexture(); // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) const arrayCacheF32 = []; const arrayCacheI32 = []; // Float32Array caches used for uploading Matrix uniforms const mat4array = new Float32Array(16); const mat3array = new Float32Array(9); const mat2array = new Float32Array(4); // Flattening for arrays of vectors and matrices function flatten(array, nBlocks, blockSize) { const firstElem = array[0]; if (firstElem <= 0 || firstElem > 0) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 const n = nBlocks * blockSize; let r = arrayCacheF32[n]; if (r === undefined) { r = new Float32Array(n); arrayCacheF32[n] = r; } if (nBlocks !== 0) { firstElem.toArray(r, 0); for (let i = 1, offset = 0; i !== nBlocks; ++i) { offset += blockSize; array[i].toArray(r, offset); } } return r; } function arraysEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0, l = a.length; i < l; i++) { if (a[i] !== b[i]) return false; } return true; } function copyArray(a, b) { for (let i = 0, l = b.length; i < l; i++) { a[i] = b[i]; } } // Texture unit allocation function allocTexUnits(textures, n) { let r = arrayCacheI32[n]; if (r === undefined) { r = new Int32Array(n); arrayCacheI32[n] = r; } for (let i = 0; i !== n; ++i) { r[i] = textures.allocateTextureUnit(); } return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValueV1f(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1f(this.addr, v); cache[0] = v; } // Single float vector (from flat array or THREE.VectorN) function setValueV2f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2f(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2fv(this.addr, v); copyArray(cache, v); } } function setValueV3f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3f(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else if (v.r !== undefined) { if (cache[0] !== v.r || cache[1] !== v.g || cache[2] !== v.b) { gl.uniform3f(this.addr, v.r, v.g, v.b); cache[0] = v.r; cache[1] = v.g; cache[2] = v.b; } } else { if (arraysEqual(cache, v)) return; gl.uniform3fv(this.addr, v); copyArray(cache, v); } } function setValueV4f(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) { gl.uniform4f(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4fv(this.addr, v); copyArray(cache, v); } } // Single matrix (from flat array or THREE.MatrixN) function setValueM2(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix2fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat2array.set(elements); gl.uniformMatrix2fv(this.addr, false, mat2array); copyArray(cache, elements); } } function setValueM3(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix3fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat3array.set(elements); gl.uniformMatrix3fv(this.addr, false, mat3array); copyArray(cache, elements); } } function setValueM4(gl, v) { const cache = this.cache; const elements = v.elements; if (elements === undefined) { if (arraysEqual(cache, v)) return; gl.uniformMatrix4fv(this.addr, false, v); copyArray(cache, v); } else { if (arraysEqual(cache, elements)) return; mat4array.set(elements); gl.uniformMatrix4fv(this.addr, false, mat4array); copyArray(cache, elements); } } // Single integer / boolean function setValueV1i(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1i(this.addr, v); cache[0] = v; } // Single integer / boolean vector (from flat array or THREE.VectorN) function setValueV2i(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2i(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2iv(this.addr, v); copyArray(cache, v); } } function setValueV3i(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3i(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else { if (arraysEqual(cache, v)) return; gl.uniform3iv(this.addr, v); copyArray(cache, v); } } function setValueV4i(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) { gl.uniform4i(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4iv(this.addr, v); copyArray(cache, v); } } // Single unsigned integer function setValueV1ui(gl, v) { const cache = this.cache; if (cache[0] === v) return; gl.uniform1ui(this.addr, v); cache[0] = v; } // Single unsigned integer vector (from flat array or THREE.VectorN) function setValueV2ui(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y) { gl.uniform2ui(this.addr, v.x, v.y); cache[0] = v.x; cache[1] = v.y; } } else { if (arraysEqual(cache, v)) return; gl.uniform2uiv(this.addr, v); copyArray(cache, v); } } function setValueV3ui(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z) { gl.uniform3ui(this.addr, v.x, v.y, v.z); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; } } else { if (arraysEqual(cache, v)) return; gl.uniform3uiv(this.addr, v); copyArray(cache, v); } } function setValueV4ui(gl, v) { const cache = this.cache; if (v.x !== undefined) { if (cache[0] !== v.x || cache[1] !== v.y || cache[2] !== v.z || cache[3] !== v.w) { gl.uniform4ui(this.addr, v.x, v.y, v.z, v.w); cache[0] = v.x; cache[1] = v.y; cache[2] = v.z; cache[3] = v.w; } } else { if (arraysEqual(cache, v)) return; gl.uniform4uiv(this.addr, v); copyArray(cache, v); } } // Single texture (2D / Cube) function setValueT1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture2D(v || emptyTexture, unit); } function setValueT3D1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture3D(v || empty3dTexture, unit); } function setValueT6(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTextureCube(v || emptyCubeTexture, unit); } function setValueT2DArray1(gl, v, textures) { const cache = this.cache; const unit = textures.allocateTextureUnit(); if (cache[0] !== unit) { gl.uniform1i(this.addr, unit); cache[0] = unit; } textures.setTexture2DArray(v || emptyArrayTexture, unit); } // Helper to pick the right setter for the singular case function getSingularSetter(type) { switch (type) { case 0x1406: return setValueV1f; // FLOAT case 0x8b50: return setValueV2f; // _VEC2 case 0x8b51: return setValueV3f; // _VEC3 case 0x8b52: return setValueV4f; // _VEC4 case 0x8b5a: return setValueM2; // _MAT2 case 0x8b5b: return setValueM3; // _MAT3 case 0x8b5c: return setValueM4; // _MAT4 case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 case 0x1405: return setValueV1ui; // UINT case 0x8dc6: return setValueV2ui; // _VEC2 case 0x8dc7: return setValueV3ui; // _VEC3 case 0x8dc8: return setValueV4ui; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3D1; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArray1; } } // Array of scalars function setValueV1fArray(gl, v) { gl.uniform1fv(this.addr, v); } // Array of vectors (from flat array or array of THREE.VectorN) function setValueV2fArray(gl, v) { const data = flatten(v, this.size, 2); gl.uniform2fv(this.addr, data); } function setValueV3fArray(gl, v) { const data = flatten(v, this.size, 3); gl.uniform3fv(this.addr, data); } function setValueV4fArray(gl, v) { const data = flatten(v, this.size, 4); gl.uniform4fv(this.addr, data); } // Array of matrices (from flat array or array of THREE.MatrixN) function setValueM2Array(gl, v) { const data = flatten(v, this.size, 4); gl.uniformMatrix2fv(this.addr, false, data); } function setValueM3Array(gl, v) { const data = flatten(v, this.size, 9); gl.uniformMatrix3fv(this.addr, false, data); } function setValueM4Array(gl, v) { const data = flatten(v, this.size, 16); gl.uniformMatrix4fv(this.addr, false, data); } // Array of integer / boolean function setValueV1iArray(gl, v) { gl.uniform1iv(this.addr, v); } // Array of integer / boolean vectors (from flat array) function setValueV2iArray(gl, v) { gl.uniform2iv(this.addr, v); } function setValueV3iArray(gl, v) { gl.uniform3iv(this.addr, v); } function setValueV4iArray(gl, v) { gl.uniform4iv(this.addr, v); } // Array of unsigned integer function setValueV1uiArray(gl, v) { gl.uniform1uiv(this.addr, v); } // Array of unsigned integer vectors (from flat array) function setValueV2uiArray(gl, v) { gl.uniform2uiv(this.addr, v); } function setValueV3uiArray(gl, v) { gl.uniform3uiv(this.addr, v); } function setValueV4uiArray(gl, v) { gl.uniform4uiv(this.addr, v); } // Array of textures (2D / 3D / Cube / 2DArray) function setValueT1Array(gl, v, textures) { const cache = this.cache; const n = v.length; const units = allocTexUnits(textures, n); if (!arraysEqual(cache, units)) { gl.uniform1iv(this.addr, units); copyArray(cache, units); } for (let i = 0; i !== n; ++i) { textures.setTexture2D(v[i] || emptyTexture, units[i]); } } function setValueT3DArray(gl, v, textures) { const cache = this.cache; const n = v.length; const units = allocTexUnits(textures, n); if (!arraysEqual(cache, units)) { gl.uniform1iv(this.addr, units); copyArray(cache, units); } for (let i = 0; i !== n; ++i) { textures.setTexture3D(v[i] || empty3dTexture, units[i]); } } function setValueT6Array(gl, v, textures) { const cache = this.cache; const n = v.length; const units = allocTexUnits(textures, n); if (!arraysEqual(cache, units)) { gl.uniform1iv(this.addr, units); copyArray(cache, units); } for (let i = 0; i !== n; ++i) { textures.setTextureCube(v[i] || emptyCubeTexture, units[i]); } } function setValueT2DArrayArray(gl, v, textures) { const cache = this.cache; const n = v.length; const units = allocTexUnits(textures, n); if (!arraysEqual(cache, units)) { gl.uniform1iv(this.addr, units); copyArray(cache, units); } for (let i = 0; i !== n; ++i) { textures.setTexture2DArray(v[i] || emptyArrayTexture, units[i]); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter(type) { switch (type) { case 0x1406: return setValueV1fArray; // FLOAT case 0x8b50: return setValueV2fArray; // _VEC2 case 0x8b51: return setValueV3fArray; // _VEC3 case 0x8b52: return setValueV4fArray; // _VEC4 case 0x8b5a: return setValueM2Array; // _MAT2 case 0x8b5b: return setValueM3Array; // _MAT3 case 0x8b5c: return setValueM4Array; // _MAT4 case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 case 0x1405: return setValueV1uiArray; // UINT case 0x8dc6: return setValueV2uiArray; // _VEC2 case 0x8dc7: return setValueV3uiArray; // _VEC3 case 0x8dc8: return setValueV4uiArray; // _VEC4 case 0x8b5e: // SAMPLER_2D case 0x8d66: // SAMPLER_EXTERNAL_OES case 0x8dca: // INT_SAMPLER_2D case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D case 0x8b62: // SAMPLER_2D_SHADOW return setValueT1Array; case 0x8b5f: // SAMPLER_3D case 0x8dcb: // INT_SAMPLER_3D case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D return setValueT3DArray; case 0x8b60: // SAMPLER_CUBE case 0x8dcc: // INT_SAMPLER_CUBE case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE case 0x8dc5: // SAMPLER_CUBE_SHADOW return setValueT6Array; case 0x8dc1: // SAMPLER_2D_ARRAY case 0x8dcf: // INT_SAMPLER_2D_ARRAY case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW return setValueT2DArrayArray; } } // --- Uniform Classes --- class SingleUniform { constructor(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.setValue = getSingularSetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } } class PureArrayUniform { constructor(id, activeInfo, addr) { this.id = id; this.addr = addr; this.cache = []; this.size = activeInfo.size; this.setValue = getPureArraySetter(activeInfo.type); // this.path = activeInfo.name; // DEBUG } } class StructuredUniform { constructor(id) { this.id = id; this.seq = []; this.map = {}; } setValue(gl, value, textures) { const seq = this.seq; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; u.setValue(gl, value[u.id], textures); } } } // --- Top-level --- // Parser - builds up the property tree from the path strings const RePathPart = /(\w+)(\])?(\[|\.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform(container, uniformObject) { container.seq.push(uniformObject); container.map[uniformObject.id] = uniformObject; } function parseUniform(activeInfo, addr, container) { const path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; while (true) { const match = RePathPart.exec(path), matchEnd = RePathPart.lastIndex; let id = match[1]; const idIsIndex = match[2] === ']', subscript = match[3]; if (idIsIndex) id = id | 0; // convert to integer if (subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength) { // bare name or "pure" bottom-level array "[0]" suffix addUniform(container, subscript === undefined ? new SingleUniform(id, activeInfo, addr) : new PureArrayUniform(id, activeInfo, addr)); break; } else { // step into inner node / create it in case it doesn't exist const map = container.map; let next = map[id]; if (next === undefined) { next = new StructuredUniform(id); addUniform(container, next); } container = next; } } } // Root Container class WebGLUniforms { constructor(gl, program) { this.seq = []; this.map = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < n; ++i) { const info = gl.getActiveUniform(program, i), addr = gl.getUniformLocation(program, info.name); parseUniform(info, addr, this); } } setValue(gl, name, value, textures) { const u = this.map[name]; if (u !== undefined) u.setValue(gl, value, textures); } setOptional(gl, object, name) { const v = object[name]; if (v !== undefined) this.setValue(gl, name, v); } static upload(gl, seq, values, textures) { for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i], v = values[u.id]; if (v.needsUpdate !== false) { // note: always updating when .needsUpdate is undefined u.setValue(gl, v.value, textures); } } } static seqWithValue(seq, values) { const r = []; for (let i = 0, n = seq.length; i !== n; ++i) { const u = seq[i]; if (u.id in values) r.push(u); } return r; } } function WebGLShader(gl, type, string) { const shader = gl.createShader(type); gl.shaderSource(shader, string); gl.compileShader(shader); return shader; } let programIdCount = 0; function handleSource(string, errorLine) { const lines = string.split('\n'); const lines2 = []; const from = Math.max(errorLine - 6, 0); const to = Math.min(errorLine + 6, lines.length); for (let i = from; i < to; i++) { const line = i + 1; lines2.push(`${line === errorLine ? '>' : ' '} ${line}: ${lines[i]}`); } return lines2.join('\n'); } function getEncodingComponents(colorSpace) { switch (colorSpace) { case LinearSRGBColorSpace: return ['Linear', '( value )']; case SRGBColorSpace: return ['sRGB', '( value )']; default: console.warn('THREE.WebGLProgram: Unsupported color space:', colorSpace); return ['Linear', '( value )']; } } function getShaderErrors(gl, shader, type) { const status = gl.getShaderParameter(shader, gl.COMPILE_STATUS); const errors = gl.getShaderInfoLog(shader).trim(); if (status && errors === '') return ''; const errorMatches = /ERROR: 0:(\d+)/.exec(errors); if (errorMatches) { // --enable-privileged-webgl-extension // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); const errorLine = parseInt(errorMatches[1]); return type.toUpperCase() + '\n\n' + errors + '\n\n' + handleSource(gl.getShaderSource(shader), errorLine); } else { return errors; } } function getTexelEncodingFunction(functionName, colorSpace) { const components = getEncodingComponents(colorSpace); return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[0] + components[1] + '; }'; } function getToneMappingFunction(functionName, toneMapping) { let toneMappingName; switch (toneMapping) { case LinearToneMapping: toneMappingName = 'Linear'; break; case ReinhardToneMapping: toneMappingName = 'Reinhard'; break; case CineonToneMapping: toneMappingName = 'OptimizedCineon'; break; case ACESFilmicToneMapping: toneMappingName = 'ACESFilmic'; break; case CustomToneMapping: toneMappingName = 'Custom'; break; default: console.warn('THREE.WebGLProgram: Unsupported toneMapping:', toneMapping); toneMappingName = 'Linear'; } return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; } function generateExtensions(parameters) { const chunks = [parameters.extensionDerivatives || !!parameters.envMapCubeUVHeight || parameters.bumpMap || parameters.normalMapTangentSpace || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ? '#extension GL_OES_standard_derivatives : enable' : '', (parameters.extensionFragDepth || parameters.logarithmicDepthBuffer) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '', parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ? '#extension GL_EXT_draw_buffers : require' : '', (parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : '']; return chunks.filter(filterEmptyLine).join('\n'); } function generateDefines(defines) { const chunks = []; for (const name in defines) { const value = defines[name]; if (value === false) continue; chunks.push('#define ' + name + ' ' + value); } return chunks.join('\n'); } function fetchAttributeLocations(gl, program) { const attributes = {}; const n = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); for (let i = 0; i < n; i++) { const info = gl.getActiveAttrib(program, i); const name = info.name; let locationSize = 1; if (info.type === gl.FLOAT_MAT2) locationSize = 2; if (info.type === gl.FLOAT_MAT3) locationSize = 3; if (info.type === gl.FLOAT_MAT4) locationSize = 4; // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); attributes[name] = { type: info.type, location: gl.getAttribLocation(program, name), locationSize: locationSize }; } return attributes; } function filterEmptyLine(string) { return string !== ''; } function replaceLightNums(string, parameters) { const numSpotLightCoords = parameters.numSpotLightShadows + parameters.numSpotLightMaps - parameters.numSpotLightShadowsWithMaps; return string.replace(/NUM_DIR_LIGHTS/g, parameters.numDirLights).replace(/NUM_SPOT_LIGHTS/g, parameters.numSpotLights).replace(/NUM_SPOT_LIGHT_MAPS/g, parameters.numSpotLightMaps).replace(/NUM_SPOT_LIGHT_COORDS/g, numSpotLightCoords).replace(/NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights).replace(/NUM_POINT_LIGHTS/g, parameters.numPointLights).replace(/NUM_HEMI_LIGHTS/g, parameters.numHemiLights).replace(/NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows).replace(/NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS/g, parameters.numSpotLightShadowsWithMaps).replace(/NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows).replace(/NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows); } function replaceClippingPlaneNums(string, parameters) { return string.replace(/NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes).replace(/UNION_CLIPPING_PLANES/g, parameters.numClippingPlanes - parameters.numClipIntersection); } // Resolve Includes const includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm; function resolveIncludes(string) { return string.replace(includePattern, includeReplacer); } const shaderChunkMap = new Map([['encodings_fragment', 'colorspace_fragment'], // @deprecated, r154 ['encodings_pars_fragment', 'colorspace_pars_fragment'], // @deprecated, r154 ['output_fragment', 'opaque_fragment'] // @deprecated, r154 ]); function includeReplacer(match, include) { let string = ShaderChunk[include]; if (string === undefined) { const newInclude = shaderChunkMap.get(include); if (newInclude !== undefined) { string = ShaderChunk[newInclude]; console.warn('THREE.WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.', include, newInclude); } else { throw new Error('Can not resolve #include <' + include + '>'); } } return resolveIncludes(string); } // Unroll Loops const unrollLoopPattern = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g; function unrollLoops(string) { return string.replace(unrollLoopPattern, loopReplacer); } function loopReplacer(match, start, end, snippet) { let string = ''; for (let i = parseInt(start); i < parseInt(end); i++) { string += snippet.replace(/\[\s*i\s*\]/g, '[ ' + i + ' ]').replace(/UNROLLED_LOOP_INDEX/g, i); } return string; } // function generatePrecision(parameters) { let precisionstring = 'precision ' + parameters.precision + ' float;\nprecision ' + parameters.precision + ' int;'; if (parameters.precision === 'highp') { precisionstring += '\n#define HIGH_PRECISION'; } else if (parameters.precision === 'mediump') { precisionstring += '\n#define MEDIUM_PRECISION'; } else if (parameters.precision === 'lowp') { precisionstring += '\n#define LOW_PRECISION'; } return precisionstring; } function generateShadowMapTypeDefine(parameters) { let shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; if (parameters.shadowMapType === PCFShadowMap) { shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; } else if (parameters.shadowMapType === PCFSoftShadowMap) { shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; } else if (parameters.shadowMapType === VSMShadowMap) { shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; } return shadowMapTypeDefine; } function generateEnvMapTypeDefine(parameters) { let envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; if (parameters.envMap) { switch (parameters.envMapMode) { case CubeReflectionMapping: case CubeRefractionMapping: envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; break; case CubeUVReflectionMapping: envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; break; } } return envMapTypeDefine; } function generateEnvMapModeDefine(parameters) { let envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; if (parameters.envMap) { switch (parameters.envMapMode) { case CubeRefractionMapping: envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; break; } } return envMapModeDefine; } function generateEnvMapBlendingDefine(parameters) { let envMapBlendingDefine = 'ENVMAP_BLENDING_NONE'; if (parameters.envMap) { switch (parameters.combine) { case MultiplyOperation: envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; break; case MixOperation: envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; break; case AddOperation: envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; break; } } return envMapBlendingDefine; } function generateCubeUVSize(parameters) { const imageHeight = parameters.envMapCubeUVHeight; if (imageHeight === null) return null; const maxMip = Math.log2(imageHeight) - 2; const texelHeight = 1.0 / imageHeight; const texelWidth = 1.0 / (3 * Math.max(Math.pow(2, maxMip), 7 * 16)); return { texelWidth, texelHeight, maxMip }; } function WebGLProgram(renderer, cacheKey, parameters, bindingStates) { // TODO Send this event to Three.js DevTools // console.log( 'WebGLProgram', cacheKey ); const gl = renderer.getContext(); const defines = parameters.defines; let vertexShader = parameters.vertexShader; let fragmentShader = parameters.fragmentShader; const shadowMapTypeDefine = generateShadowMapTypeDefine(parameters); const envMapTypeDefine = generateEnvMapTypeDefine(parameters); const envMapModeDefine = generateEnvMapModeDefine(parameters); const envMapBlendingDefine = generateEnvMapBlendingDefine(parameters); const envMapCubeUVSize = generateCubeUVSize(parameters); const customExtensions = parameters.isWebGL2 ? '' : generateExtensions(parameters); const customDefines = generateDefines(defines); const program = gl.createProgram(); let prefixVertex, prefixFragment; let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : ''; if (parameters.isRawShaderMaterial) { prefixVertex = ['#define SHADER_TYPE ' + parameters.shaderType, '#define SHADER_NAME ' + parameters.shaderName, customDefines].filter(filterEmptyLine).join('\n'); if (prefixVertex.length > 0) { prefixVertex += '\n'; } prefixFragment = [customExtensions, '#define SHADER_TYPE ' + parameters.shaderType, '#define SHADER_NAME ' + parameters.shaderName, customDefines].filter(filterEmptyLine).join('\n'); if (prefixFragment.length > 0) { prefixFragment += '\n'; } } else { prefixVertex = [generatePrecision(parameters), '#define SHADER_TYPE ' + parameters.shaderType, '#define SHADER_NAME ' + parameters.shaderName, customDefines, parameters.instancing ? '#define USE_INSTANCING' : '', parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '', parameters.useFog && parameters.fog ? '#define USE_FOG' : '', parameters.useFog && parameters.fogExp2 ? '#define FOG_EXP2' : '', parameters.map ? '#define USE_MAP' : '', parameters.envMap ? '#define USE_ENVMAP' : '', parameters.envMap ? '#define ' + envMapModeDefine : '', parameters.lightMap ? '#define USE_LIGHTMAP' : '', parameters.aoMap ? '#define USE_AOMAP' : '', parameters.bumpMap ? '#define USE_BUMPMAP' : '', parameters.normalMap ? '#define USE_NORMALMAP' : '', parameters.normalMapObjectSpace ? '#define USE_NORMALMAP_OBJECTSPACE' : '', parameters.normalMapTangentSpace ? '#define USE_NORMALMAP_TANGENTSPACE' : '', parameters.displacementMap ? '#define USE_DISPLACEMENTMAP' : '', parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', parameters.anisotropyMap ? '#define USE_ANISOTROPYMAP' : '', parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', parameters.iridescenceMap ? '#define USE_IRIDESCENCEMAP' : '', parameters.iridescenceThicknessMap ? '#define USE_IRIDESCENCE_THICKNESSMAP' : '', parameters.specularMap ? '#define USE_SPECULARMAP' : '', parameters.specularColorMap ? '#define USE_SPECULAR_COLORMAP' : '', parameters.specularIntensityMap ? '#define USE_SPECULAR_INTENSITYMAP' : '', parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', parameters.alphaMap ? '#define USE_ALPHAMAP' : '', parameters.alphaHash ? '#define USE_ALPHAHASH' : '', parameters.transmission ? '#define USE_TRANSMISSION' : '', parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', parameters.sheenColorMap ? '#define USE_SHEEN_COLORMAP' : '', parameters.sheenRoughnessMap ? '#define USE_SHEEN_ROUGHNESSMAP' : '', // parameters.mapUv ? '#define MAP_UV ' + parameters.mapUv : '', parameters.alphaMapUv ? '#define ALPHAMAP_UV ' + parameters.alphaMapUv : '', parameters.lightMapUv ? '#define LIGHTMAP_UV ' + parameters.lightMapUv : '', parameters.aoMapUv ? '#define AOMAP_UV ' + parameters.aoMapUv : '', parameters.emissiveMapUv ? '#define EMISSIVEMAP_UV ' + parameters.emissiveMapUv : '', parameters.bumpMapUv ? '#define BUMPMAP_UV ' + parameters.bumpMapUv : '', parameters.normalMapUv ? '#define NORMALMAP_UV ' + parameters.normalMapUv : '', parameters.displacementMapUv ? '#define DISPLACEMENTMAP_UV ' + parameters.displacementMapUv : '', parameters.metalnessMapUv ? '#define METALNESSMAP_UV ' + parameters.metalnessMapUv : '', parameters.roughnessMapUv ? '#define ROUGHNESSMAP_UV ' + parameters.roughnessMapUv : '', parameters.anisotropyMapUv ? '#define ANISOTROPYMAP_UV ' + parameters.anisotropyMapUv : '', parameters.clearcoatMapUv ? '#define CLEARCOATMAP_UV ' + parameters.clearcoatMapUv : '', parameters.clearcoatNormalMapUv ? '#define CLEARCOAT_NORMALMAP_UV ' + parameters.clearcoatNormalMapUv : '', parameters.clearcoatRoughnessMapUv ? '#define CLEARCOAT_ROUGHNESSMAP_UV ' + parameters.clearcoatRoughnessMapUv : '', parameters.iridescenceMapUv ? '#define IRIDESCENCEMAP_UV ' + parameters.iridescenceMapUv : '', parameters.iridescenceThicknessMapUv ? '#define IRIDESCENCE_THICKNESSMAP_UV ' + parameters.iridescenceThicknessMapUv : '', parameters.sheenColorMapUv ? '#define SHEEN_COLORMAP_UV ' + parameters.sheenColorMapUv : '', parameters.sheenRoughnessMapUv ? '#define SHEEN_ROUGHNESSMAP_UV ' + parameters.sheenRoughnessMapUv : '', parameters.specularMapUv ? '#define SPECULARMAP_UV ' + parameters.specularMapUv : '', parameters.specularColorMapUv ? '#define SPECULAR_COLORMAP_UV ' + parameters.specularColorMapUv : '', parameters.specularIntensityMapUv ? '#define SPECULAR_INTENSITYMAP_UV ' + parameters.specularIntensityMapUv : '', parameters.transmissionMapUv ? '#define TRANSMISSIONMAP_UV ' + parameters.transmissionMapUv : '', parameters.thicknessMapUv ? '#define THICKNESSMAP_UV ' + parameters.thicknessMapUv : '', // parameters.vertexTangents && parameters.flatShading === false ? '#define USE_TANGENT' : '', parameters.vertexColors ? '#define USE_COLOR' : '', parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', parameters.vertexUv1s ? '#define USE_UV1' : '', parameters.vertexUv2s ? '#define USE_UV2' : '', parameters.vertexUv3s ? '#define USE_UV3' : '', parameters.pointsUvs ? '#define USE_POINTS_UV' : '', parameters.flatShading ? '#define FLAT_SHADED' : '', parameters.skinning ? '#define USE_SKINNING' : '', parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', parameters.morphColors && parameters.isWebGL2 ? '#define USE_MORPHCOLORS' : '', parameters.morphTargetsCount > 0 && parameters.isWebGL2 ? '#define MORPHTARGETS_TEXTURE' : '', parameters.morphTargetsCount > 0 && parameters.isWebGL2 ? '#define MORPHTARGETS_TEXTURE_STRIDE ' + parameters.morphTextureStride : '', parameters.morphTargetsCount > 0 && parameters.isWebGL2 ? '#define MORPHTARGETS_COUNT ' + parameters.morphTargetsCount : '', parameters.doubleSided ? '#define DOUBLE_SIDED' : '', parameters.flipSided ? '#define FLIP_SIDED' : '', parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', parameters.useLegacyLights ? '#define LEGACY_LIGHTS' : '', parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ? '#define USE_LOGDEPTHBUF_EXT' : '', 'uniform mat4 modelMatrix;', 'uniform mat4 modelViewMatrix;', 'uniform mat4 projectionMatrix;', 'uniform mat4 viewMatrix;', 'uniform mat3 normalMatrix;', 'uniform vec3 cameraPosition;', 'uniform bool isOrthographic;', '#ifdef USE_INSTANCING', ' attribute mat4 instanceMatrix;', '#endif', '#ifdef USE_INSTANCING_COLOR', ' attribute vec3 instanceColor;', '#endif', 'attribute vec3 position;', 'attribute vec3 normal;', 'attribute vec2 uv;', '#ifdef USE_UV1', ' attribute vec2 uv1;', '#endif', '#ifdef USE_UV2', ' attribute vec2 uv2;', '#endif', '#ifdef USE_UV3', ' attribute vec2 uv3;', '#endif', '#ifdef USE_TANGENT', ' attribute vec4 tangent;', '#endif', '#if defined( USE_COLOR_ALPHA )', ' attribute vec4 color;', '#elif defined( USE_COLOR )', ' attribute vec3 color;', '#endif', '#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )', ' attribute vec3 morphTarget0;', ' attribute vec3 morphTarget1;', ' attribute vec3 morphTarget2;', ' attribute vec3 morphTarget3;', ' #ifdef USE_MORPHNORMALS', ' attribute vec3 morphNormal0;', ' attribute vec3 morphNormal1;', ' attribute vec3 morphNormal2;', ' attribute vec3 morphNormal3;', ' #else', ' attribute vec3 morphTarget4;', ' attribute vec3 morphTarget5;', ' attribute vec3 morphTarget6;', ' attribute vec3 morphTarget7;', ' #endif', '#endif', '#ifdef USE_SKINNING', ' attribute vec4 skinIndex;', ' attribute vec4 skinWeight;', '#endif', '\n'].filter(filterEmptyLine).join('\n'); prefixFragment = [customExtensions, generatePrecision(parameters), '#define SHADER_TYPE ' + parameters.shaderType, '#define SHADER_NAME ' + parameters.shaderName, customDefines, parameters.useFog && parameters.fog ? '#define USE_FOG' : '', parameters.useFog && parameters.fogExp2 ? '#define FOG_EXP2' : '', parameters.map ? '#define USE_MAP' : '', parameters.matcap ? '#define USE_MATCAP' : '', parameters.envMap ? '#define USE_ENVMAP' : '', parameters.envMap ? '#define ' + envMapTypeDefine : '', parameters.envMap ? '#define ' + envMapModeDefine : '', parameters.envMap ? '#define ' + envMapBlendingDefine : '', envMapCubeUVSize ? '#define CUBEUV_TEXEL_WIDTH ' + envMapCubeUVSize.texelWidth : '', envMapCubeUVSize ? '#define CUBEUV_TEXEL_HEIGHT ' + envMapCubeUVSize.texelHeight : '', envMapCubeUVSize ? '#define CUBEUV_MAX_MIP ' + envMapCubeUVSize.maxMip + '.0' : '', parameters.lightMap ? '#define USE_LIGHTMAP' : '', parameters.aoMap ? '#define USE_AOMAP' : '', parameters.bumpMap ? '#define USE_BUMPMAP' : '', parameters.normalMap ? '#define USE_NORMALMAP' : '', parameters.normalMapObjectSpace ? '#define USE_NORMALMAP_OBJECTSPACE' : '', parameters.normalMapTangentSpace ? '#define USE_NORMALMAP_TANGENTSPACE' : '', parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', parameters.anisotropy ? '#define USE_ANISOTROPY' : '', parameters.anisotropyMap ? '#define USE_ANISOTROPYMAP' : '', parameters.clearcoat ? '#define USE_CLEARCOAT' : '', parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', parameters.iridescence ? '#define USE_IRIDESCENCE' : '', parameters.iridescenceMap ? '#define USE_IRIDESCENCEMAP' : '', parameters.iridescenceThicknessMap ? '#define USE_IRIDESCENCE_THICKNESSMAP' : '', parameters.specularMap ? '#define USE_SPECULARMAP' : '', parameters.specularColorMap ? '#define USE_SPECULAR_COLORMAP' : '', parameters.specularIntensityMap ? '#define USE_SPECULAR_INTENSITYMAP' : '', parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', parameters.alphaMap ? '#define USE_ALPHAMAP' : '', parameters.alphaTest ? '#define USE_ALPHATEST' : '', parameters.alphaHash ? '#define USE_ALPHAHASH' : '', parameters.sheen ? '#define USE_SHEEN' : '', parameters.sheenColorMap ? '#define USE_SHEEN_COLORMAP' : '', parameters.sheenRoughnessMap ? '#define USE_SHEEN_ROUGHNESSMAP' : '', parameters.transmission ? '#define USE_TRANSMISSION' : '', parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', parameters.vertexTangents && parameters.flatShading === false ? '#define USE_TANGENT' : '', parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', parameters.vertexUv1s ? '#define USE_UV1' : '', parameters.vertexUv2s ? '#define USE_UV2' : '', parameters.vertexUv3s ? '#define USE_UV3' : '', parameters.pointsUvs ? '#define USE_POINTS_UV' : '', parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', parameters.flatShading ? '#define FLAT_SHADED' : '', parameters.doubleSided ? '#define DOUBLE_SIDED' : '', parameters.flipSided ? '#define FLIP_SIDED' : '', parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', parameters.useLegacyLights ? '#define LEGACY_LIGHTS' : '', parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ? '#define USE_LOGDEPTHBUF_EXT' : '', 'uniform mat4 viewMatrix;', 'uniform vec3 cameraPosition;', 'uniform bool isOrthographic;', parameters.toneMapping !== NoToneMapping ? '#define TONE_MAPPING' : '', parameters.toneMapping !== NoToneMapping ? ShaderChunk['tonemapping_pars_fragment'] : '', // this code is required here because it is used by the toneMapping() function defined below parameters.toneMapping !== NoToneMapping ? getToneMappingFunction('toneMapping', parameters.toneMapping) : '', parameters.dithering ? '#define DITHERING' : '', parameters.opaque ? '#define OPAQUE' : '', ShaderChunk['colorspace_pars_fragment'], // this code is required here because it is used by the various encoding/decoding function defined below getTexelEncodingFunction('linearToOutputTexel', parameters.outputColorSpace), parameters.useDepthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '', '\n'].filter(filterEmptyLine).join('\n'); } vertexShader = resolveIncludes(vertexShader); vertexShader = replaceLightNums(vertexShader, parameters); vertexShader = replaceClippingPlaneNums(vertexShader, parameters); fragmentShader = resolveIncludes(fragmentShader); fragmentShader = replaceLightNums(fragmentShader, parameters); fragmentShader = replaceClippingPlaneNums(fragmentShader, parameters); vertexShader = unrollLoops(vertexShader); fragmentShader = unrollLoops(fragmentShader); if (parameters.isWebGL2 && parameters.isRawShaderMaterial !== true) { // GLSL 3.0 conversion for built-in materials and ShaderMaterial versionString = '#version 300 es\n'; prefixVertex = ['precision mediump sampler2DArray;', '#define attribute in', '#define varying out', '#define texture2D texture'].join('\n') + '\n' + prefixVertex; prefixFragment = ['#define varying in', parameters.glslVersion === GLSL3 ? '' : 'layout(location = 0) out highp vec4 pc_fragColor;', parameters.glslVersion === GLSL3 ? '' : '#define gl_FragColor pc_fragColor', '#define gl_FragDepthEXT gl_FragDepth', '#define texture2D texture', '#define textureCube texture', '#define texture2DProj textureProj', '#define texture2DLodEXT textureLod', '#define texture2DProjLodEXT textureProjLod', '#define textureCubeLodEXT textureLod', '#define texture2DGradEXT textureGrad', '#define texture2DProjGradEXT textureProjGrad', '#define textureCubeGradEXT textureGrad'].join('\n') + '\n' + prefixFragment; } const vertexGlsl = versionString + prefixVertex + vertexShader; const fragmentGlsl = versionString + prefixFragment + fragmentShader; // console.log( '*VERTEX*', vertexGlsl ); // console.log( '*FRAGMENT*', fragmentGlsl ); const glVertexShader = WebGLShader(gl, gl.VERTEX_SHADER, vertexGlsl); const glFragmentShader = WebGLShader(gl, gl.FRAGMENT_SHADER, fragmentGlsl); gl.attachShader(program, glVertexShader); gl.attachShader(program, glFragmentShader); // Force a particular attribute to index 0. if (parameters.index0AttributeName !== undefined) { gl.bindAttribLocation(program, 0, parameters.index0AttributeName); } else if (parameters.morphTargets === true) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation(program, 0, 'position'); } gl.linkProgram(program); // check for link errors if (renderer.debug.checkShaderErrors) { const programLog = gl.getProgramInfoLog(program).trim(); const vertexLog = gl.getShaderInfoLog(glVertexShader).trim(); const fragmentLog = gl.getShaderInfoLog(glFragmentShader).trim(); let runnable = true; let haveDiagnostics = true; if (gl.getProgramParameter(program, gl.LINK_STATUS) === false) { runnable = false; if (typeof renderer.debug.onShaderError === 'function') { renderer.debug.onShaderError(gl, program, glVertexShader, glFragmentShader); } else { // default error reporting const vertexErrors = getShaderErrors(gl, glVertexShader, 'vertex'); const fragmentErrors = getShaderErrors(gl, glFragmentShader, 'fragment'); console.error('THREE.WebGLProgram: Shader Error ' + gl.getError() + ' - ' + 'VALIDATE_STATUS ' + gl.getProgramParameter(program, gl.VALIDATE_STATUS) + '\n\n' + 'Program Info Log: ' + programLog + '\n' + vertexErrors + '\n' + fragmentErrors); } } else if (programLog !== '') { console.warn('THREE.WebGLProgram: Program Info Log:', programLog); } else if (vertexLog === '' || fragmentLog === '') { haveDiagnostics = false; } if (haveDiagnostics) { this.diagnostics = { runnable: runnable, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } } // Clean up // Crashes in iOS9 and iOS10. #18402 // gl.detachShader( program, glVertexShader ); // gl.detachShader( program, glFragmentShader ); gl.deleteShader(glVertexShader); gl.deleteShader(glFragmentShader); // set up caching for uniform locations let cachedUniforms; this.getUniforms = function () { if (cachedUniforms === undefined) { cachedUniforms = new WebGLUniforms(gl, program); } return cachedUniforms; }; // set up caching for attribute locations let cachedAttributes; this.getAttributes = function () { if (cachedAttributes === undefined) { cachedAttributes = fetchAttributeLocations(gl, program); } return cachedAttributes; }; // free resource this.destroy = function () { bindingStates.releaseStatesOfProgram(this); gl.deleteProgram(program); this.program = undefined; }; // this.type = parameters.shaderType; this.name = parameters.shaderName; this.id = programIdCount++; this.cacheKey = cacheKey; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } let _id = 0; class WebGLShaderCache { constructor() { this.shaderCache = new Map(); this.materialCache = new Map(); } update(material) { const vertexShader = material.vertexShader; const fragmentShader = material.fragmentShader; const vertexShaderStage = this._getShaderStage(vertexShader); const fragmentShaderStage = this._getShaderStage(fragmentShader); const materialShaders = this._getShaderCacheForMaterial(material); if (materialShaders.has(vertexShaderStage) === false) { materialShaders.add(vertexShaderStage); vertexShaderStage.usedTimes++; } if (materialShaders.has(fragmentShaderStage) === false) { materialShaders.add(fragmentShaderStage); fragmentShaderStage.usedTimes++; } return this; } remove(material) { const materialShaders = this.materialCache.get(material); for (const shaderStage of materialShaders) { shaderStage.usedTimes--; if (shaderStage.usedTimes === 0) this.shaderCache.delete(shaderStage.code); } this.materialCache.delete(material); return this; } getVertexShaderID(material) { return this._getShaderStage(material.vertexShader).id; } getFragmentShaderID(material) { return this._getShaderStage(material.fragmentShader).id; } dispose() { this.shaderCache.clear(); this.materialCache.clear(); } _getShaderCacheForMaterial(material) { const cache = this.materialCache; let set = cache.get(material); if (set === undefined) { set = new Set(); cache.set(material, set); } return set; } _getShaderStage(code) { const cache = this.shaderCache; let stage = cache.get(code); if (stage === undefined) { stage = new WebGLShaderStage(code); cache.set(code, stage); } return stage; } } class WebGLShaderStage { constructor(code) { this.id = _id++; this.code = code; this.usedTimes = 0; } } function WebGLPrograms(renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping) { const _programLayers = new Layers(); const _customShaders = new WebGLShaderCache(); const programs = []; const IS_WEBGL2 = capabilities.isWebGL2; const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; const SUPPORTS_VERTEX_TEXTURES = capabilities.vertexTextures; let precision = capabilities.precision; const shaderIDs = { MeshDepthMaterial: 'depth', MeshDistanceMaterial: 'distanceRGBA', MeshNormalMaterial: 'normal', MeshBasicMaterial: 'basic', MeshLambertMaterial: 'lambert', MeshPhongMaterial: 'phong', MeshToonMaterial: 'toon', MeshStandardMaterial: 'physical', MeshPhysicalMaterial: 'physical', MeshMatcapMaterial: 'matcap', LineBasicMaterial: 'basic', LineDashedMaterial: 'dashed', PointsMaterial: 'points', ShadowMaterial: 'shadow', SpriteMaterial: 'sprite' }; function getChannel(value) { if (value === 0) return 'uv'; return `uv${value}`; } function getParameters(material, lights, shadows, scene, object) { const fog = scene.fog; const geometry = object.geometry; const environment = material.isMeshStandardMaterial ? scene.environment : null; const envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || environment); const envMapCubeUVHeight = !!envMap && envMap.mapping === CubeUVReflectionMapping ? envMap.image.height : null; const shaderID = shaderIDs[material.type]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) if (material.precision !== null) { precision = capabilities.getMaxPrecision(material.precision); if (precision !== material.precision) { console.warn('THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.'); } } // const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; let morphTextureStride = 0; if (geometry.morphAttributes.position !== undefined) morphTextureStride = 1; if (geometry.morphAttributes.normal !== undefined) morphTextureStride = 2; if (geometry.morphAttributes.color !== undefined) morphTextureStride = 3; // let vertexShader, fragmentShader; let customVertexShaderID, customFragmentShaderID; if (shaderID) { const shader = ShaderLib[shaderID]; vertexShader = shader.vertexShader; fragmentShader = shader.fragmentShader; } else { vertexShader = material.vertexShader; fragmentShader = material.fragmentShader; _customShaders.update(material); customVertexShaderID = _customShaders.getVertexShaderID(material); customFragmentShaderID = _customShaders.getFragmentShaderID(material); } const currentRenderTarget = renderer.getRenderTarget(); const IS_INSTANCEDMESH = object.isInstancedMesh === true; const HAS_MAP = !!material.map; const HAS_MATCAP = !!material.matcap; const HAS_ENVMAP = !!envMap; const HAS_AOMAP = !!material.aoMap; const HAS_LIGHTMAP = !!material.lightMap; const HAS_BUMPMAP = !!material.bumpMap; const HAS_NORMALMAP = !!material.normalMap; const HAS_DISPLACEMENTMAP = !!material.displacementMap; const HAS_EMISSIVEMAP = !!material.emissiveMap; const HAS_METALNESSMAP = !!material.metalnessMap; const HAS_ROUGHNESSMAP = !!material.roughnessMap; const HAS_ANISOTROPY = material.anisotropy > 0; const HAS_CLEARCOAT = material.clearcoat > 0; const HAS_IRIDESCENCE = material.iridescence > 0; const HAS_SHEEN = material.sheen > 0; const HAS_TRANSMISSION = material.transmission > 0; const HAS_ANISOTROPYMAP = HAS_ANISOTROPY && !!material.anisotropyMap; const HAS_CLEARCOATMAP = HAS_CLEARCOAT && !!material.clearcoatMap; const HAS_CLEARCOAT_NORMALMAP = HAS_CLEARCOAT && !!material.clearcoatNormalMap; const HAS_CLEARCOAT_ROUGHNESSMAP = HAS_CLEARCOAT && !!material.clearcoatRoughnessMap; const HAS_IRIDESCENCEMAP = HAS_IRIDESCENCE && !!material.iridescenceMap; const HAS_IRIDESCENCE_THICKNESSMAP = HAS_IRIDESCENCE && !!material.iridescenceThicknessMap; const HAS_SHEEN_COLORMAP = HAS_SHEEN && !!material.sheenColorMap; const HAS_SHEEN_ROUGHNESSMAP = HAS_SHEEN && !!material.sheenRoughnessMap; const HAS_SPECULARMAP = !!material.specularMap; const HAS_SPECULAR_COLORMAP = !!material.specularColorMap; const HAS_SPECULAR_INTENSITYMAP = !!material.specularIntensityMap; const HAS_TRANSMISSIONMAP = HAS_TRANSMISSION && !!material.transmissionMap; const HAS_THICKNESSMAP = HAS_TRANSMISSION && !!material.thicknessMap; const HAS_GRADIENTMAP = !!material.gradientMap; const HAS_ALPHAMAP = !!material.alphaMap; const HAS_ALPHATEST = material.alphaTest > 0; const HAS_ALPHAHASH = !!material.alphaHash; const HAS_EXTENSIONS = !!material.extensions; const HAS_ATTRIBUTE_UV1 = !!geometry.attributes.uv1; const HAS_ATTRIBUTE_UV2 = !!geometry.attributes.uv2; const HAS_ATTRIBUTE_UV3 = !!geometry.attributes.uv3; const parameters = { isWebGL2: IS_WEBGL2, shaderID: shaderID, shaderType: material.type, shaderName: material.name, vertexShader: vertexShader, fragmentShader: fragmentShader, defines: material.defines, customVertexShaderID: customVertexShaderID, customFragmentShaderID: customFragmentShaderID, isRawShaderMaterial: material.isRawShaderMaterial === true, glslVersion: material.glslVersion, precision: precision, instancing: IS_INSTANCEDMESH, instancingColor: IS_INSTANCEDMESH && object.instanceColor !== null, supportsVertexTextures: SUPPORTS_VERTEX_TEXTURES, outputColorSpace: currentRenderTarget === null ? renderer.outputColorSpace : currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace, map: HAS_MAP, matcap: HAS_MATCAP, envMap: HAS_ENVMAP, envMapMode: HAS_ENVMAP && envMap.mapping, envMapCubeUVHeight: envMapCubeUVHeight, aoMap: HAS_AOMAP, lightMap: HAS_LIGHTMAP, bumpMap: HAS_BUMPMAP, normalMap: HAS_NORMALMAP, displacementMap: SUPPORTS_VERTEX_TEXTURES && HAS_DISPLACEMENTMAP, emissiveMap: HAS_EMISSIVEMAP, normalMapObjectSpace: HAS_NORMALMAP && material.normalMapType === ObjectSpaceNormalMap, normalMapTangentSpace: HAS_NORMALMAP && material.normalMapType === TangentSpaceNormalMap, metalnessMap: HAS_METALNESSMAP, roughnessMap: HAS_ROUGHNESSMAP, anisotropy: HAS_ANISOTROPY, anisotropyMap: HAS_ANISOTROPYMAP, clearcoat: HAS_CLEARCOAT, clearcoatMap: HAS_CLEARCOATMAP, clearcoatNormalMap: HAS_CLEARCOAT_NORMALMAP, clearcoatRoughnessMap: HAS_CLEARCOAT_ROUGHNESSMAP, iridescence: HAS_IRIDESCENCE, iridescenceMap: HAS_IRIDESCENCEMAP, iridescenceThicknessMap: HAS_IRIDESCENCE_THICKNESSMAP, sheen: HAS_SHEEN, sheenColorMap: HAS_SHEEN_COLORMAP, sheenRoughnessMap: HAS_SHEEN_ROUGHNESSMAP, specularMap: HAS_SPECULARMAP, specularColorMap: HAS_SPECULAR_COLORMAP, specularIntensityMap: HAS_SPECULAR_INTENSITYMAP, transmission: HAS_TRANSMISSION, transmissionMap: HAS_TRANSMISSIONMAP, thicknessMap: HAS_THICKNESSMAP, gradientMap: HAS_GRADIENTMAP, opaque: material.transparent === false && material.blending === NormalBlending, alphaMap: HAS_ALPHAMAP, alphaTest: HAS_ALPHATEST, alphaHash: HAS_ALPHAHASH, combine: material.combine, // mapUv: HAS_MAP && getChannel(material.map.channel), aoMapUv: HAS_AOMAP && getChannel(material.aoMap.channel), lightMapUv: HAS_LIGHTMAP && getChannel(material.lightMap.channel), bumpMapUv: HAS_BUMPMAP && getChannel(material.bumpMap.channel), normalMapUv: HAS_NORMALMAP && getChannel(material.normalMap.channel), displacementMapUv: HAS_DISPLACEMENTMAP && getChannel(material.displacementMap.channel), emissiveMapUv: HAS_EMISSIVEMAP && getChannel(material.emissiveMap.channel), metalnessMapUv: HAS_METALNESSMAP && getChannel(material.metalnessMap.channel), roughnessMapUv: HAS_ROUGHNESSMAP && getChannel(material.roughnessMap.channel), anisotropyMapUv: HAS_ANISOTROPYMAP && getChannel(material.anisotropyMap.channel), clearcoatMapUv: HAS_CLEARCOATMAP && getChannel(material.clearcoatMap.channel), clearcoatNormalMapUv: HAS_CLEARCOAT_NORMALMAP && getChannel(material.clearcoatNormalMap.channel), clearcoatRoughnessMapUv: HAS_CLEARCOAT_ROUGHNESSMAP && getChannel(material.clearcoatRoughnessMap.channel), iridescenceMapUv: HAS_IRIDESCENCEMAP && getChannel(material.iridescenceMap.channel), iridescenceThicknessMapUv: HAS_IRIDESCENCE_THICKNESSMAP && getChannel(material.iridescenceThicknessMap.channel), sheenColorMapUv: HAS_SHEEN_COLORMAP && getChannel(material.sheenColorMap.channel), sheenRoughnessMapUv: HAS_SHEEN_ROUGHNESSMAP && getChannel(material.sheenRoughnessMap.channel), specularMapUv: HAS_SPECULARMAP && getChannel(material.specularMap.channel), specularColorMapUv: HAS_SPECULAR_COLORMAP && getChannel(material.specularColorMap.channel), specularIntensityMapUv: HAS_SPECULAR_INTENSITYMAP && getChannel(material.specularIntensityMap.channel), transmissionMapUv: HAS_TRANSMISSIONMAP && getChannel(material.transmissionMap.channel), thicknessMapUv: HAS_THICKNESSMAP && getChannel(material.thicknessMap.channel), alphaMapUv: HAS_ALPHAMAP && getChannel(material.alphaMap.channel), // vertexTangents: !!geometry.attributes.tangent && (HAS_NORMALMAP || HAS_ANISOTROPY), vertexColors: material.vertexColors, vertexAlphas: material.vertexColors === true && !!geometry.attributes.color && geometry.attributes.color.itemSize === 4, vertexUv1s: HAS_ATTRIBUTE_UV1, vertexUv2s: HAS_ATTRIBUTE_UV2, vertexUv3s: HAS_ATTRIBUTE_UV3, pointsUvs: object.isPoints === true && !!geometry.attributes.uv && (HAS_MAP || HAS_ALPHAMAP), fog: !!fog, useFog: material.fog === true, fogExp2: fog && fog.isFogExp2, flatShading: material.flatShading === true, sizeAttenuation: material.sizeAttenuation === true, logarithmicDepthBuffer: logarithmicDepthBuffer, skinning: object.isSkinnedMesh === true, morphTargets: geometry.morphAttributes.position !== undefined, morphNormals: geometry.morphAttributes.normal !== undefined, morphColors: geometry.morphAttributes.color !== undefined, morphTargetsCount: morphTargetsCount, morphTextureStride: morphTextureStride, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numSpotLightMaps: lights.spotLightMap.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numDirLightShadows: lights.directionalShadowMap.length, numPointLightShadows: lights.pointShadowMap.length, numSpotLightShadows: lights.spotShadowMap.length, numSpotLightShadowsWithMaps: lights.numSpotLightShadowsWithMaps, numClippingPlanes: clipping.numPlanes, numClipIntersection: clipping.numIntersection, dithering: material.dithering, shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, useLegacyLights: renderer.useLegacyLights, premultipliedAlpha: material.premultipliedAlpha, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, useDepthPacking: material.depthPacking >= 0, depthPacking: material.depthPacking || 0, index0AttributeName: material.index0AttributeName, extensionDerivatives: HAS_EXTENSIONS && material.extensions.derivatives === true, extensionFragDepth: HAS_EXTENSIONS && material.extensions.fragDepth === true, extensionDrawBuffers: HAS_EXTENSIONS && material.extensions.drawBuffers === true, extensionShaderTextureLOD: HAS_EXTENSIONS && material.extensions.shaderTextureLOD === true, rendererExtensionFragDepth: IS_WEBGL2 || extensions.has('EXT_frag_depth'), rendererExtensionDrawBuffers: IS_WEBGL2 || extensions.has('WEBGL_draw_buffers'), rendererExtensionShaderTextureLod: IS_WEBGL2 || extensions.has('EXT_shader_texture_lod'), customProgramCacheKey: material.customProgramCacheKey() }; return parameters; } function getProgramCacheKey(parameters) { const array = []; if (parameters.shaderID) { array.push(parameters.shaderID); } else { array.push(parameters.customVertexShaderID); array.push(parameters.customFragmentShaderID); } if (parameters.defines !== undefined) { for (const name in parameters.defines) { array.push(name); array.push(parameters.defines[name]); } } if (parameters.isRawShaderMaterial === false) { getProgramCacheKeyParameters(array, parameters); getProgramCacheKeyBooleans(array, parameters); array.push(renderer.outputColorSpace); } array.push(parameters.customProgramCacheKey); return array.join(); } function getProgramCacheKeyParameters(array, parameters) { array.push(parameters.precision); array.push(parameters.outputColorSpace); array.push(parameters.envMapMode); array.push(parameters.envMapCubeUVHeight); array.push(parameters.mapUv); array.push(parameters.alphaMapUv); array.push(parameters.lightMapUv); array.push(parameters.aoMapUv); array.push(parameters.bumpMapUv); array.push(parameters.normalMapUv); array.push(parameters.displacementMapUv); array.push(parameters.emissiveMapUv); array.push(parameters.metalnessMapUv); array.push(parameters.roughnessMapUv); array.push(parameters.anisotropyMapUv); array.push(parameters.clearcoatMapUv); array.push(parameters.clearcoatNormalMapUv); array.push(parameters.clearcoatRoughnessMapUv); array.push(parameters.iridescenceMapUv); array.push(parameters.iridescenceThicknessMapUv); array.push(parameters.sheenColorMapUv); array.push(parameters.sheenRoughnessMapUv); array.push(parameters.specularMapUv); array.push(parameters.specularColorMapUv); array.push(parameters.specularIntensityMapUv); array.push(parameters.transmissionMapUv); array.push(parameters.thicknessMapUv); array.push(parameters.combine); array.push(parameters.fogExp2); array.push(parameters.sizeAttenuation); array.push(parameters.morphTargetsCount); array.push(parameters.morphAttributeCount); array.push(parameters.numDirLights); array.push(parameters.numPointLights); array.push(parameters.numSpotLights); array.push(parameters.numSpotLightMaps); array.push(parameters.numHemiLights); array.push(parameters.numRectAreaLights); array.push(parameters.numDirLightShadows); array.push(parameters.numPointLightShadows); array.push(parameters.numSpotLightShadows); array.push(parameters.numSpotLightShadowsWithMaps); array.push(parameters.shadowMapType); array.push(parameters.toneMapping); array.push(parameters.numClippingPlanes); array.push(parameters.numClipIntersection); array.push(parameters.depthPacking); } function getProgramCacheKeyBooleans(array, parameters) { _programLayers.disableAll(); if (parameters.isWebGL2) _programLayers.enable(0); if (parameters.supportsVertexTextures) _programLayers.enable(1); if (parameters.instancing) _programLayers.enable(2); if (parameters.instancingColor) _programLayers.enable(3); if (parameters.matcap) _programLayers.enable(4); if (parameters.envMap) _programLayers.enable(5); if (parameters.normalMapObjectSpace) _programLayers.enable(6); if (parameters.normalMapTangentSpace) _programLayers.enable(7); if (parameters.clearcoat) _programLayers.enable(8); if (parameters.iridescence) _programLayers.enable(9); if (parameters.alphaTest) _programLayers.enable(10); if (parameters.vertexColors) _programLayers.enable(11); if (parameters.vertexAlphas) _programLayers.enable(12); if (parameters.vertexUv1s) _programLayers.enable(13); if (parameters.vertexUv2s) _programLayers.enable(14); if (parameters.vertexUv3s) _programLayers.enable(15); if (parameters.vertexTangents) _programLayers.enable(16); if (parameters.anisotropy) _programLayers.enable(17); array.push(_programLayers.mask); _programLayers.disableAll(); if (parameters.fog) _programLayers.enable(0); if (parameters.useFog) _programLayers.enable(1); if (parameters.flatShading) _programLayers.enable(2); if (parameters.logarithmicDepthBuffer) _programLayers.enable(3); if (parameters.skinning) _programLayers.enable(4); if (parameters.morphTargets) _programLayers.enable(5); if (parameters.morphNormals) _programLayers.enable(6); if (parameters.morphColors) _programLayers.enable(7); if (parameters.premultipliedAlpha) _programLayers.enable(8); if (parameters.shadowMapEnabled) _programLayers.enable(9); if (parameters.useLegacyLights) _programLayers.enable(10); if (parameters.doubleSided) _programLayers.enable(11); if (parameters.flipSided) _programLayers.enable(12); if (parameters.useDepthPacking) _programLayers.enable(13); if (parameters.dithering) _programLayers.enable(14); if (parameters.transmission) _programLayers.enable(15); if (parameters.sheen) _programLayers.enable(16); if (parameters.opaque) _programLayers.enable(17); if (parameters.pointsUvs) _programLayers.enable(18); array.push(_programLayers.mask); } function getUniforms(material) { const shaderID = shaderIDs[material.type]; let uniforms; if (shaderID) { const shader = ShaderLib[shaderID]; uniforms = UniformsUtils.clone(shader.uniforms); } else { uniforms = material.uniforms; } return uniforms; } function acquireProgram(parameters, cacheKey) { let program; // Check if code has been already compiled for (let p = 0, pl = programs.length; p < pl; p++) { const preexistingProgram = programs[p]; if (preexistingProgram.cacheKey === cacheKey) { program = preexistingProgram; ++program.usedTimes; break; } } if (program === undefined) { program = new WebGLProgram(renderer, cacheKey, parameters, bindingStates); programs.push(program); } return program; } function releaseProgram(program) { if (--program.usedTimes === 0) { // Remove from unordered set const i = programs.indexOf(program); programs[i] = programs[programs.length - 1]; programs.pop(); // Free WebGL resources program.destroy(); } } function releaseShaderCache(material) { _customShaders.remove(material); } function dispose() { _customShaders.dispose(); } return { getParameters: getParameters, getProgramCacheKey: getProgramCacheKey, getUniforms: getUniforms, acquireProgram: acquireProgram, releaseProgram: releaseProgram, releaseShaderCache: releaseShaderCache, // Exposed for resource monitoring & error feedback via renderer.info: programs: programs, dispose: dispose }; } function WebGLProperties() { let properties = new WeakMap(); function get(object) { let map = properties.get(object); if (map === undefined) { map = {}; properties.set(object, map); } return map; } function remove(object) { properties.delete(object); } function update(object, key, value) { properties.get(object)[key] = value; } function dispose() { properties = new WeakMap(); } return { get: get, remove: remove, update: update, dispose: dispose }; } function painterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.material.id !== b.material.id) { return a.material.id - b.material.id; } else if (a.z !== b.z) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable(a, b) { if (a.groupOrder !== b.groupOrder) { return a.groupOrder - b.groupOrder; } else if (a.renderOrder !== b.renderOrder) { return a.renderOrder - b.renderOrder; } else if (a.z !== b.z) { return b.z - a.z; } else { return a.id - b.id; } } function WebGLRenderList() { const renderItems = []; let renderItemsIndex = 0; const opaque = []; const transmissive = []; const transparent = []; function init() { renderItemsIndex = 0; opaque.length = 0; transmissive.length = 0; transparent.length = 0; } function getNextRenderItem(object, geometry, material, groupOrder, z, group) { let renderItem = renderItems[renderItemsIndex]; if (renderItem === undefined) { renderItem = { id: object.id, object: object, geometry: geometry, material: material, groupOrder: groupOrder, renderOrder: object.renderOrder, z: z, group: group }; renderItems[renderItemsIndex] = renderItem; } else { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; renderItem.group = group; } renderItemsIndex++; return renderItem; } function push(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); if (material.transmission > 0.0) { transmissive.push(renderItem); } else if (material.transparent === true) { transparent.push(renderItem); } else { opaque.push(renderItem); } } function unshift(object, geometry, material, groupOrder, z, group) { const renderItem = getNextRenderItem(object, geometry, material, groupOrder, z, group); if (material.transmission > 0.0) { transmissive.unshift(renderItem); } else if (material.transparent === true) { transparent.unshift(renderItem); } else { opaque.unshift(renderItem); } } function sort(customOpaqueSort, customTransparentSort) { if (opaque.length > 1) opaque.sort(customOpaqueSort || painterSortStable); if (transmissive.length > 1) transmissive.sort(customTransparentSort || reversePainterSortStable); if (transparent.length > 1) transparent.sort(customTransparentSort || reversePainterSortStable); } function finish() { // Clear references from inactive renderItems in the list for (let i = renderItemsIndex, il = renderItems.length; i < il; i++) { const renderItem = renderItems[i]; if (renderItem.id === null) break; renderItem.id = null; renderItem.object = null; renderItem.geometry = null; renderItem.material = null; renderItem.group = null; } } return { opaque: opaque, transmissive: transmissive, transparent: transparent, init: init, push: push, unshift: unshift, finish: finish, sort: sort }; } function WebGLRenderLists() { let lists = new WeakMap(); function get(scene, renderCallDepth) { const listArray = lists.get(scene); let list; if (listArray === undefined) { list = new WebGLRenderList(); lists.set(scene, [list]); } else { if (renderCallDepth >= listArray.length) { list = new WebGLRenderList(); listArray.push(list); } else { list = listArray[renderCallDepth]; } } return list; } function dispose() { lists = new WeakMap(); } return { get: get, dispose: dispose }; } function UniformsCache() { const lights = {}; return { get: function (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case 'DirectionalLight': uniforms = { direction: new Vector3(), color: new Color() }; break; case 'SpotLight': uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0 }; break; case 'PointLight': uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0 }; break; case 'HemisphereLight': uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case 'RectAreaLight': uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() }; break; } lights[light.id] = uniforms; return uniforms; } }; } function ShadowUniformsCache() { const lights = {}; return { get: function (light) { if (lights[light.id] !== undefined) { return lights[light.id]; } let uniforms; switch (light.type) { case 'DirectionalLight': uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case 'SpotLight': uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case 'PointLight': uniforms = { shadowBias: 0, shadowNormalBias: 0, shadowRadius: 1, shadowMapSize: new Vector2(), shadowCameraNear: 1, shadowCameraFar: 1000 }; break; // TODO (abelnation): set RectAreaLight shadow uniforms } lights[light.id] = uniforms; return uniforms; } }; } let nextVersion = 0; function shadowCastingAndTexturingLightsFirst(lightA, lightB) { return (lightB.castShadow ? 2 : 0) - (lightA.castShadow ? 2 : 0) + (lightB.map ? 1 : 0) - (lightA.map ? 1 : 0); } function WebGLLights(extensions, capabilities) { const cache = new UniformsCache(); const shadowCache = ShadowUniformsCache(); const state = { version: 0, hash: { directionalLength: -1, pointLength: -1, spotLength: -1, rectAreaLength: -1, hemiLength: -1, numDirectionalShadows: -1, numPointShadows: -1, numSpotShadows: -1, numSpotMaps: -1 }, ambient: [0, 0, 0], probe: [], directional: [], directionalShadow: [], directionalShadowMap: [], directionalShadowMatrix: [], spot: [], spotLightMap: [], spotShadow: [], spotShadowMap: [], spotLightMatrix: [], rectArea: [], rectAreaLTC1: null, rectAreaLTC2: null, point: [], pointShadow: [], pointShadowMap: [], pointShadowMatrix: [], hemi: [], numSpotLightShadowsWithMaps: 0 }; for (let i = 0; i < 9; i++) state.probe.push(new Vector3()); const vector3 = new Vector3(); const matrix4 = new Matrix4(); const matrix42 = new Matrix4(); function setup(lights, useLegacyLights) { let r = 0, g = 0, b = 0; for (let i = 0; i < 9; i++) state.probe[i].set(0, 0, 0); let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; let numDirectionalShadows = 0; let numPointShadows = 0; let numSpotShadows = 0; let numSpotMaps = 0; let numSpotShadowsWithMaps = 0; // ordering : [shadow casting + map texturing, map texturing, shadow casting, none ] lights.sort(shadowCastingAndTexturingLightsFirst); // artist-friendly light intensity scaling factor const scaleFactor = useLegacyLights === true ? Math.PI : 1; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; const color = light.color; const intensity = light.intensity; const distance = light.distance; const shadowMap = light.shadow && light.shadow.map ? light.shadow.map.texture : null; if (light.isAmbientLight) { r += color.r * intensity * scaleFactor; g += color.g * intensity * scaleFactor; b += color.b * intensity * scaleFactor; } else if (light.isLightProbe) { for (let j = 0; j < 9; j++) { state.probe[j].addScaledVector(light.sh.coefficients[j], intensity); } } else if (light.isDirectionalLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity * scaleFactor); if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.directionalShadow[directionalLength] = shadowUniforms; state.directionalShadowMap[directionalLength] = shadowMap; state.directionalShadowMatrix[directionalLength] = light.shadow.matrix; numDirectionalShadows++; } state.directional[directionalLength] = uniforms; directionalLength++; } else if (light.isSpotLight) { const uniforms = cache.get(light); uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.color.copy(color).multiplyScalar(intensity * scaleFactor); uniforms.distance = distance; uniforms.coneCos = Math.cos(light.angle); uniforms.penumbraCos = Math.cos(light.angle * (1 - light.penumbra)); uniforms.decay = light.decay; state.spot[spotLength] = uniforms; const shadow = light.shadow; if (light.map) { state.spotLightMap[numSpotMaps] = light.map; numSpotMaps++; // make sure the lightMatrix is up to date // TODO : do it if required only shadow.updateMatrices(light); if (light.castShadow) numSpotShadowsWithMaps++; } state.spotLightMatrix[spotLength] = shadow.matrix; if (light.castShadow) { const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; state.spotShadow[spotLength] = shadowUniforms; state.spotShadowMap[spotLength] = shadowMap; numSpotShadows++; } spotLength++; } else if (light.isRectAreaLight) { const uniforms = cache.get(light); uniforms.color.copy(color).multiplyScalar(intensity); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); state.rectArea[rectAreaLength] = uniforms; rectAreaLength++; } else if (light.isPointLight) { const uniforms = cache.get(light); uniforms.color.copy(light.color).multiplyScalar(light.intensity * scaleFactor); uniforms.distance = light.distance; uniforms.decay = light.decay; if (light.castShadow) { const shadow = light.shadow; const shadowUniforms = shadowCache.get(light); shadowUniforms.shadowBias = shadow.bias; shadowUniforms.shadowNormalBias = shadow.normalBias; shadowUniforms.shadowRadius = shadow.radius; shadowUniforms.shadowMapSize = shadow.mapSize; shadowUniforms.shadowCameraNear = shadow.camera.near; shadowUniforms.shadowCameraFar = shadow.camera.far; state.pointShadow[pointLength] = shadowUniforms; state.pointShadowMap[pointLength] = shadowMap; state.pointShadowMatrix[pointLength] = light.shadow.matrix; numPointShadows++; } state.point[pointLength] = uniforms; pointLength++; } else if (light.isHemisphereLight) { const uniforms = cache.get(light); uniforms.skyColor.copy(light.color).multiplyScalar(intensity * scaleFactor); uniforms.groundColor.copy(light.groundColor).multiplyScalar(intensity * scaleFactor); state.hemi[hemiLength] = uniforms; hemiLength++; } } if (rectAreaLength > 0) { if (capabilities.isWebGL2) { // WebGL 2 state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else { // WebGL 1 if (extensions.has('OES_texture_float_linear') === true) { state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; } else if (extensions.has('OES_texture_half_float_linear') === true) { state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; } else { console.error('THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.'); } } } state.ambient[0] = r; state.ambient[1] = g; state.ambient[2] = b; const hash = state.hash; if (hash.directionalLength !== directionalLength || hash.pointLength !== pointLength || hash.spotLength !== spotLength || hash.rectAreaLength !== rectAreaLength || hash.hemiLength !== hemiLength || hash.numDirectionalShadows !== numDirectionalShadows || hash.numPointShadows !== numPointShadows || hash.numSpotShadows !== numSpotShadows || hash.numSpotMaps !== numSpotMaps) { state.directional.length = directionalLength; state.spot.length = spotLength; state.rectArea.length = rectAreaLength; state.point.length = pointLength; state.hemi.length = hemiLength; state.directionalShadow.length = numDirectionalShadows; state.directionalShadowMap.length = numDirectionalShadows; state.pointShadow.length = numPointShadows; state.pointShadowMap.length = numPointShadows; state.spotShadow.length = numSpotShadows; state.spotShadowMap.length = numSpotShadows; state.directionalShadowMatrix.length = numDirectionalShadows; state.pointShadowMatrix.length = numPointShadows; state.spotLightMatrix.length = numSpotShadows + numSpotMaps - numSpotShadowsWithMaps; state.spotLightMap.length = numSpotMaps; state.numSpotLightShadowsWithMaps = numSpotShadowsWithMaps; hash.directionalLength = directionalLength; hash.pointLength = pointLength; hash.spotLength = spotLength; hash.rectAreaLength = rectAreaLength; hash.hemiLength = hemiLength; hash.numDirectionalShadows = numDirectionalShadows; hash.numPointShadows = numPointShadows; hash.numSpotShadows = numSpotShadows; hash.numSpotMaps = numSpotMaps; state.version = nextVersion++; } } function setupView(lights, camera) { let directionalLength = 0; let pointLength = 0; let spotLength = 0; let rectAreaLength = 0; let hemiLength = 0; const viewMatrix = camera.matrixWorldInverse; for (let i = 0, l = lights.length; i < l; i++) { const light = lights[i]; if (light.isDirectionalLight) { const uniforms = state.directional[directionalLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); directionalLength++; } else if (light.isSpotLight) { const uniforms = state.spot[spotLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); uniforms.direction.setFromMatrixPosition(light.matrixWorld); vector3.setFromMatrixPosition(light.target.matrixWorld); uniforms.direction.sub(vector3); uniforms.direction.transformDirection(viewMatrix); spotLength++; } else if (light.isRectAreaLight) { const uniforms = state.rectArea[rectAreaLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); // extract local rotation of light to derive width/height half vectors matrix42.identity(); matrix4.copy(light.matrixWorld); matrix4.premultiply(viewMatrix); matrix42.extractRotation(matrix4); uniforms.halfWidth.set(light.width * 0.5, 0.0, 0.0); uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0); uniforms.halfWidth.applyMatrix4(matrix42); uniforms.halfHeight.applyMatrix4(matrix42); rectAreaLength++; } else if (light.isPointLight) { const uniforms = state.point[pointLength]; uniforms.position.setFromMatrixPosition(light.matrixWorld); uniforms.position.applyMatrix4(viewMatrix); pointLength++; } else if (light.isHemisphereLight) { const uniforms = state.hemi[hemiLength]; uniforms.direction.setFromMatrixPosition(light.matrixWorld); uniforms.direction.transformDirection(viewMatrix); hemiLength++; } } } return { setup: setup, setupView: setupView, state: state }; } function WebGLRenderState(extensions, capabilities) { const lights = new WebGLLights(extensions, capabilities); const lightsArray = []; const shadowsArray = []; function init() { lightsArray.length = 0; shadowsArray.length = 0; } function pushLight(light) { lightsArray.push(light); } function pushShadow(shadowLight) { shadowsArray.push(shadowLight); } function setupLights(useLegacyLights) { lights.setup(lightsArray, useLegacyLights); } function setupLightsView(camera) { lights.setupView(lightsArray, camera); } const state = { lightsArray: lightsArray, shadowsArray: shadowsArray, lights: lights }; return { init: init, state: state, setupLights: setupLights, setupLightsView: setupLightsView, pushLight: pushLight, pushShadow: pushShadow }; } function WebGLRenderStates(extensions, capabilities) { let renderStates = new WeakMap(); function get(scene, renderCallDepth = 0) { const renderStateArray = renderStates.get(scene); let renderState; if (renderStateArray === undefined) { renderState = new WebGLRenderState(extensions, capabilities); renderStates.set(scene, [renderState]); } else { if (renderCallDepth >= renderStateArray.length) { renderState = new WebGLRenderState(extensions, capabilities); renderStateArray.push(renderState); } else { renderState = renderStateArray[renderCallDepth]; } } return renderState; } function dispose() { renderStates = new WeakMap(); } return { get: get, dispose: dispose }; } class MeshDepthMaterial extends Material { constructor(parameters) { super(); this.isMeshDepthMaterial = true; this.type = 'MeshDepthMaterial'; this.depthPacking = BasicDepthPacking; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.setValues(parameters); } copy(source) { super.copy(source); this.depthPacking = source.depthPacking; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; } } class MeshDistanceMaterial extends Material { constructor(parameters) { super(); this.isMeshDistanceMaterial = true; this.type = 'MeshDistanceMaterial'; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.setValues(parameters); } copy(source) { super.copy(source); this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; return this; } } const vertex = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; const fragment = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include <packing>\nvoid main() {\n\tconst float samples = float( VSM_SAMPLES );\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 );\n\tfloat uvStart = samples <= 1.0 ? 0.0 : - 1.0;\n\tfor ( float i = 0.0; i < samples; i ++ ) {\n\t\tfloat uvOffset = uvStart + i * uvStride;\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean / samples;\n\tsquared_mean = squared_mean / samples;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"; function WebGLShadowMap(_renderer, _objects, _capabilities) { let _frustum = new Frustum(); const _shadowMapSize = new Vector2(), _viewportSize = new Vector2(), _viewport = new Vector4(), _depthMaterial = new MeshDepthMaterial({ depthPacking: RGBADepthPacking }), _distanceMaterial = new MeshDistanceMaterial(), _materialCache = {}, _maxTextureSize = _capabilities.maxTextureSize; const shadowSide = { [FrontSide]: BackSide, [BackSide]: FrontSide, [DoubleSide]: DoubleSide }; const shadowMaterialVertical = new ShaderMaterial({ defines: { VSM_SAMPLES: 8 }, uniforms: { shadow_pass: { value: null }, resolution: { value: new Vector2() }, radius: { value: 4.0 } }, vertexShader: vertex, fragmentShader: fragment }); const shadowMaterialHorizontal = shadowMaterialVertical.clone(); shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; const fullScreenTri = new BufferGeometry(); fullScreenTri.setAttribute('position', new BufferAttribute(new Float32Array([-1, -1, 0.5, 3, -1, 0.5, -1, 3, 0.5]), 3)); const fullScreenMesh = new Mesh(fullScreenTri, shadowMaterialVertical); const scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; let _previousType = this.type; this.render = function (lights, scene, camera) { if (scope.enabled === false) return; if (scope.autoUpdate === false && scope.needsUpdate === false) return; if (lights.length === 0) return; const currentRenderTarget = _renderer.getRenderTarget(); const activeCubeFace = _renderer.getActiveCubeFace(); const activeMipmapLevel = _renderer.getActiveMipmapLevel(); const _state = _renderer.state; // Set GL state for depth map. _state.setBlending(NoBlending); _state.buffers.color.setClear(1, 1, 1, 1); _state.buffers.depth.setTest(true); _state.setScissorTest(false); // check for shadow map type changes const toVSM = _previousType !== VSMShadowMap && this.type === VSMShadowMap; const fromVSM = _previousType === VSMShadowMap && this.type !== VSMShadowMap; // render depth map for (let i = 0, il = lights.length; i < il; i++) { const light = lights[i]; const shadow = light.shadow; if (shadow === undefined) { console.warn('THREE.WebGLShadowMap:', light, 'has no shadow.'); continue; } if (shadow.autoUpdate === false && shadow.needsUpdate === false) continue; _shadowMapSize.copy(shadow.mapSize); const shadowFrameExtents = shadow.getFrameExtents(); _shadowMapSize.multiply(shadowFrameExtents); _viewportSize.copy(shadow.mapSize); if (_shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize) { if (_shadowMapSize.x > _maxTextureSize) { _viewportSize.x = Math.floor(_maxTextureSize / shadowFrameExtents.x); _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; shadow.mapSize.x = _viewportSize.x; } if (_shadowMapSize.y > _maxTextureSize) { _viewportSize.y = Math.floor(_maxTextureSize / shadowFrameExtents.y); _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; shadow.mapSize.y = _viewportSize.y; } } if (shadow.map === null || toVSM === true || fromVSM === true) { const pars = this.type !== VSMShadowMap ? { minFilter: NearestFilter, magFilter: NearestFilter } : {}; if (shadow.map !== null) { shadow.map.dispose(); } shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars); shadow.map.texture.name = light.name + '.shadowMap'; shadow.camera.updateProjectionMatrix(); } _renderer.setRenderTarget(shadow.map); _renderer.clear(); const viewportCount = shadow.getViewportCount(); for (let vp = 0; vp < viewportCount; vp++) { const viewport = shadow.getViewport(vp); _viewport.set(_viewportSize.x * viewport.x, _viewportSize.y * viewport.y, _viewportSize.x * viewport.z, _viewportSize.y * viewport.w); _state.viewport(_viewport); shadow.updateMatrices(light, vp); _frustum = shadow.getFrustum(); renderObject(scene, camera, shadow.camera, light, this.type); } // do blur pass for VSM if (shadow.isPointLightShadow !== true && this.type === VSMShadowMap) { VSMPass(shadow, camera); } shadow.needsUpdate = false; } _previousType = this.type; scope.needsUpdate = false; _renderer.setRenderTarget(currentRenderTarget, activeCubeFace, activeMipmapLevel); }; function VSMPass(shadow, camera) { const geometry = _objects.update(fullScreenMesh); if (shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples) { shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; shadowMaterialVertical.needsUpdate = true; shadowMaterialHorizontal.needsUpdate = true; } if (shadow.mapPass === null) { shadow.mapPass = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y); } // vertical pass shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; shadowMaterialVertical.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget(shadow.mapPass); _renderer.clear(); _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null); // horizontal pass shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; _renderer.setRenderTarget(shadow.map); _renderer.clear(); _renderer.renderBufferDirect(camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null); } function getDepthMaterial(object, material, light, type) { let result = null; const customMaterial = light.isPointLight === true ? object.customDistanceMaterial : object.customDepthMaterial; if (customMaterial !== undefined) { result = customMaterial; } else { result = light.isPointLight === true ? _distanceMaterial : _depthMaterial; if (_renderer.localClippingEnabled && material.clipShadows === true && Array.isArray(material.clippingPlanes) && material.clippingPlanes.length !== 0 || material.displacementMap && material.displacementScale !== 0 || material.alphaMap && material.alphaTest > 0 || material.map && material.alphaTest > 0) { // in this case we need a unique material instance reflecting the // appropriate state const keyA = result.uuid, keyB = material.uuid; let materialsForVariant = _materialCache[keyA]; if (materialsForVariant === undefined) { materialsForVariant = {}; _materialCache[keyA] = materialsForVariant; } let cachedMaterial = materialsForVariant[keyB]; if (cachedMaterial === undefined) { cachedMaterial = result.clone(); materialsForVariant[keyB] = cachedMaterial; } result = cachedMaterial; } } result.visible = material.visible; result.wireframe = material.wireframe; if (type === VSMShadowMap) { result.side = material.shadowSide !== null ? material.shadowSide : material.side; } else { result.side = material.shadowSide !== null ? material.shadowSide : shadowSide[material.side]; } result.alphaMap = material.alphaMap; result.alphaTest = material.alphaTest; result.map = material.map; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.clipIntersection = material.clipIntersection; result.displacementMap = material.displacementMap; result.displacementScale = material.displacementScale; result.displacementBias = material.displacementBias; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if (light.isPointLight === true && result.isMeshDistanceMaterial === true) { const materialProperties = _renderer.properties.get(result); materialProperties.light = light; } return result; } function renderObject(object, camera, shadowCamera, light, type) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible && (object.isMesh || object.isLine || object.isPoints)) { if ((object.castShadow || object.receiveShadow && type === VSMShadowMap) && (!object.frustumCulled || _frustum.intersectsObject(object))) { object.modelViewMatrix.multiplyMatrices(shadowCamera.matrixWorldInverse, object.matrixWorld); const geometry = _objects.update(object); const material = object.material; if (Array.isArray(material)) { const groups = geometry.groups; for (let k = 0, kl = groups.length; k < kl; k++) { const group = groups[k]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { const depthMaterial = getDepthMaterial(object, groupMaterial, light, type); _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, group); } } } else if (material.visible) { const depthMaterial = getDepthMaterial(object, material, light, type); _renderer.renderBufferDirect(shadowCamera, null, geometry, depthMaterial, object, null); } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { renderObject(children[i], camera, shadowCamera, light, type); } } } function WebGLState(gl, extensions, capabilities) { const isWebGL2 = capabilities.isWebGL2; function ColorBuffer() { let locked = false; const color = new Vector4(); let currentColorMask = null; const currentColorClear = new Vector4(0, 0, 0, 0); return { setMask: function (colorMask) { if (currentColorMask !== colorMask && !locked) { gl.colorMask(colorMask, colorMask, colorMask, colorMask); currentColorMask = colorMask; } }, setLocked: function (lock) { locked = lock; }, setClear: function (r, g, b, a, premultipliedAlpha) { if (premultipliedAlpha === true) { r *= a; g *= a; b *= a; } color.set(r, g, b, a); if (currentColorClear.equals(color) === false) { gl.clearColor(r, g, b, a); currentColorClear.copy(color); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set(-1, 0, 0, 0); // set to invalid state } }; } function DepthBuffer() { let locked = false; let currentDepthMask = null; let currentDepthFunc = null; let currentDepthClear = null; return { setTest: function (depthTest) { if (depthTest) { enable(gl.DEPTH_TEST); } else { disable(gl.DEPTH_TEST); } }, setMask: function (depthMask) { if (currentDepthMask !== depthMask && !locked) { gl.depthMask(depthMask); currentDepthMask = depthMask; } }, setFunc: function (depthFunc) { if (currentDepthFunc !== depthFunc) { switch (depthFunc) { case NeverDepth: gl.depthFunc(gl.NEVER); break; case AlwaysDepth: gl.depthFunc(gl.ALWAYS); break; case LessDepth: gl.depthFunc(gl.LESS); break; case LessEqualDepth: gl.depthFunc(gl.LEQUAL); break; case EqualDepth: gl.depthFunc(gl.EQUAL); break; case GreaterEqualDepth: gl.depthFunc(gl.GEQUAL); break; case GreaterDepth: gl.depthFunc(gl.GREATER); break; case NotEqualDepth: gl.depthFunc(gl.NOTEQUAL); break; default: gl.depthFunc(gl.LEQUAL); } currentDepthFunc = depthFunc; } }, setLocked: function (lock) { locked = lock; }, setClear: function (depth) { if (currentDepthClear !== depth) { gl.clearDepth(depth); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { let locked = false; let currentStencilMask = null; let currentStencilFunc = null; let currentStencilRef = null; let currentStencilFuncMask = null; let currentStencilFail = null; let currentStencilZFail = null; let currentStencilZPass = null; let currentStencilClear = null; return { setTest: function (stencilTest) { if (!locked) { if (stencilTest) { enable(gl.STENCIL_TEST); } else { disable(gl.STENCIL_TEST); } } }, setMask: function (stencilMask) { if (currentStencilMask !== stencilMask && !locked) { gl.stencilMask(stencilMask); currentStencilMask = stencilMask; } }, setFunc: function (stencilFunc, stencilRef, stencilMask) { if (currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask) { gl.stencilFunc(stencilFunc, stencilRef, stencilMask); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function (stencilFail, stencilZFail, stencilZPass) { if (currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass) { gl.stencilOp(stencilFail, stencilZFail, stencilZPass); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function (lock) { locked = lock; }, setClear: function (stencil) { if (currentStencilClear !== stencil) { gl.clearStencil(stencil); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // const colorBuffer = new ColorBuffer(); const depthBuffer = new DepthBuffer(); const stencilBuffer = new StencilBuffer(); const uboBindings = new WeakMap(); const uboProgramMap = new WeakMap(); let enabledCapabilities = {}; let currentBoundFramebuffers = {}; let currentDrawbuffers = new WeakMap(); let defaultDrawbuffers = []; let currentProgram = null; let currentBlendingEnabled = false; let currentBlending = null; let currentBlendEquation = null; let currentBlendSrc = null; let currentBlendDst = null; let currentBlendEquationAlpha = null; let currentBlendSrcAlpha = null; let currentBlendDstAlpha = null; let currentPremultipledAlpha = false; let currentFlipSided = null; let currentCullFace = null; let currentLineWidth = null; let currentPolygonOffsetFactor = null; let currentPolygonOffsetUnits = null; const maxTextures = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS); let lineWidthAvailable = false; let version = 0; const glVersion = gl.getParameter(gl.VERSION); if (glVersion.indexOf('WebGL') !== -1) { version = parseFloat(/^WebGL (\d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 1.0; } else if (glVersion.indexOf('OpenGL ES') !== -1) { version = parseFloat(/^OpenGL ES (\d)/.exec(glVersion)[1]); lineWidthAvailable = version >= 2.0; } let currentTextureSlot = null; let currentBoundTextures = {}; const scissorParam = gl.getParameter(gl.SCISSOR_BOX); const viewportParam = gl.getParameter(gl.VIEWPORT); const currentScissor = new Vector4().fromArray(scissorParam); const currentViewport = new Vector4().fromArray(viewportParam); function createTexture(type, target, count, dimensions) { const data = new Uint8Array(4); // 4 is required to match default unpack alignment of 4. const texture = gl.createTexture(); gl.bindTexture(type, texture); gl.texParameteri(type, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(type, gl.TEXTURE_MAG_FILTER, gl.NEAREST); for (let i = 0; i < count; i++) { if (isWebGL2 && (type === gl.TEXTURE_3D || type === gl.TEXTURE_2D_ARRAY)) { gl.texImage3D(target, 0, gl.RGBA, 1, 1, dimensions, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); } else { gl.texImage2D(target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); } } return texture; } const emptyTextures = {}; emptyTextures[gl.TEXTURE_2D] = createTexture(gl.TEXTURE_2D, gl.TEXTURE_2D, 1); emptyTextures[gl.TEXTURE_CUBE_MAP] = createTexture(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6); if (isWebGL2) { emptyTextures[gl.TEXTURE_2D_ARRAY] = createTexture(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_2D_ARRAY, 1, 1); emptyTextures[gl.TEXTURE_3D] = createTexture(gl.TEXTURE_3D, gl.TEXTURE_3D, 1, 1); } // init colorBuffer.setClear(0, 0, 0, 1); depthBuffer.setClear(1); stencilBuffer.setClear(0); enable(gl.DEPTH_TEST); depthBuffer.setFunc(LessEqualDepth); setFlipSided(false); setCullFace(CullFaceBack); enable(gl.CULL_FACE); setBlending(NoBlending); // function enable(id) { if (enabledCapabilities[id] !== true) { gl.enable(id); enabledCapabilities[id] = true; } } function disable(id) { if (enabledCapabilities[id] !== false) { gl.disable(id); enabledCapabilities[id] = false; } } function bindFramebuffer(target, framebuffer) { if (currentBoundFramebuffers[target] !== framebuffer) { gl.bindFramebuffer(target, framebuffer); currentBoundFramebuffers[target] = framebuffer; if (isWebGL2) { // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER if (target === gl.DRAW_FRAMEBUFFER) { currentBoundFramebuffers[gl.FRAMEBUFFER] = framebuffer; } if (target === gl.FRAMEBUFFER) { currentBoundFramebuffers[gl.DRAW_FRAMEBUFFER] = framebuffer; } } return true; } return false; } function drawBuffers(renderTarget, framebuffer) { let drawBuffers = defaultDrawbuffers; let needsUpdate = false; if (renderTarget) { drawBuffers = currentDrawbuffers.get(framebuffer); if (drawBuffers === undefined) { drawBuffers = []; currentDrawbuffers.set(framebuffer, drawBuffers); } if (renderTarget.isWebGLMultipleRenderTargets) { const textures = renderTarget.texture; if (drawBuffers.length !== textures.length || drawBuffers[0] !== gl.COLOR_ATTACHMENT0) { for (let i = 0, il = textures.length; i < il; i++) { drawBuffers[i] = gl.COLOR_ATTACHMENT0 + i; } drawBuffers.length = textures.length; needsUpdate = true; } } else { if (drawBuffers[0] !== gl.COLOR_ATTACHMENT0) { drawBuffers[0] = gl.COLOR_ATTACHMENT0; needsUpdate = true; } } } else { if (drawBuffers[0] !== gl.BACK) { drawBuffers[0] = gl.BACK; needsUpdate = true; } } if (needsUpdate) { if (capabilities.isWebGL2) { gl.drawBuffers(drawBuffers); } else { extensions.get('WEBGL_draw_buffers').drawBuffersWEBGL(drawBuffers); } } } function useProgram(program) { if (currentProgram !== program) { gl.useProgram(program); currentProgram = program; return true; } return false; } const equationToGL = { [AddEquation]: gl.FUNC_ADD, [SubtractEquation]: gl.FUNC_SUBTRACT, [ReverseSubtractEquation]: gl.FUNC_REVERSE_SUBTRACT }; if (isWebGL2) { equationToGL[MinEquation] = gl.MIN; equationToGL[MaxEquation] = gl.MAX; } else { const extension = extensions.get('EXT_blend_minmax'); if (extension !== null) { equationToGL[MinEquation] = extension.MIN_EXT; equationToGL[MaxEquation] = extension.MAX_EXT; } } const factorToGL = { [ZeroFactor]: gl.ZERO, [OneFactor]: gl.ONE, [SrcColorFactor]: gl.SRC_COLOR, [SrcAlphaFactor]: gl.SRC_ALPHA, [SrcAlphaSaturateFactor]: gl.SRC_ALPHA_SATURATE, [DstColorFactor]: gl.DST_COLOR, [DstAlphaFactor]: gl.DST_ALPHA, [OneMinusSrcColorFactor]: gl.ONE_MINUS_SRC_COLOR, [OneMinusSrcAlphaFactor]: gl.ONE_MINUS_SRC_ALPHA, [OneMinusDstColorFactor]: gl.ONE_MINUS_DST_COLOR, [OneMinusDstAlphaFactor]: gl.ONE_MINUS_DST_ALPHA }; function setBlending(blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha) { if (blending === NoBlending) { if (currentBlendingEnabled === true) { disable(gl.BLEND); currentBlendingEnabled = false; } return; } if (currentBlendingEnabled === false) { enable(gl.BLEND); currentBlendingEnabled = true; } if (blending !== CustomBlending) { if (blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha) { if (currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation) { gl.blendEquation(gl.FUNC_ADD); currentBlendEquation = AddEquation; currentBlendEquationAlpha = AddEquation; } if (premultipliedAlpha) { switch (blending) { case NormalBlending: gl.blendFuncSeparate(gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); break; case AdditiveBlending: gl.blendFunc(gl.ONE, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate(gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE); break; case MultiplyBlending: gl.blendFuncSeparate(gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA); break; default: console.error('THREE.WebGLState: Invalid blending: ', blending); break; } } else { switch (blending) { case NormalBlending: gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); break; case AdditiveBlending: gl.blendFunc(gl.SRC_ALPHA, gl.ONE); break; case SubtractiveBlending: gl.blendFuncSeparate(gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE); break; case MultiplyBlending: gl.blendFunc(gl.ZERO, gl.SRC_COLOR); break; default: console.error('THREE.WebGLState: Invalid blending: ', blending); break; } } currentBlendSrc = null; currentBlendDst = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } return; } // custom blending blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if (blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha) { gl.blendEquationSeparate(equationToGL[blendEquation], equationToGL[blendEquationAlpha]); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if (blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha) { gl.blendFuncSeparate(factorToGL[blendSrc], factorToGL[blendDst], factorToGL[blendSrcAlpha], factorToGL[blendDstAlpha]); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } currentBlending = blending; currentPremultipledAlpha = false; } function setMaterial(material, frontFaceCW) { material.side === DoubleSide ? disable(gl.CULL_FACE) : enable(gl.CULL_FACE); let flipSided = material.side === BackSide; if (frontFaceCW) flipSided = !flipSided; setFlipSided(flipSided); material.blending === NormalBlending && material.transparent === false ? setBlending(NoBlending) : setBlending(material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha); depthBuffer.setFunc(material.depthFunc); depthBuffer.setTest(material.depthTest); depthBuffer.setMask(material.depthWrite); colorBuffer.setMask(material.colorWrite); const stencilWrite = material.stencilWrite; stencilBuffer.setTest(stencilWrite); if (stencilWrite) { stencilBuffer.setMask(material.stencilWriteMask); stencilBuffer.setFunc(material.stencilFunc, material.stencilRef, material.stencilFuncMask); stencilBuffer.setOp(material.stencilFail, material.stencilZFail, material.stencilZPass); } setPolygonOffset(material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits); material.alphaToCoverage === true ? enable(gl.SAMPLE_ALPHA_TO_COVERAGE) : disable(gl.SAMPLE_ALPHA_TO_COVERAGE); } // function setFlipSided(flipSided) { if (currentFlipSided !== flipSided) { if (flipSided) { gl.frontFace(gl.CW); } else { gl.frontFace(gl.CCW); } currentFlipSided = flipSided; } } function setCullFace(cullFace) { if (cullFace !== CullFaceNone) { enable(gl.CULL_FACE); if (cullFace !== currentCullFace) { if (cullFace === CullFaceBack) { gl.cullFace(gl.BACK); } else if (cullFace === CullFaceFront) { gl.cullFace(gl.FRONT); } else { gl.cullFace(gl.FRONT_AND_BACK); } } } else { disable(gl.CULL_FACE); } currentCullFace = cullFace; } function setLineWidth(width) { if (width !== currentLineWidth) { if (lineWidthAvailable) gl.lineWidth(width); currentLineWidth = width; } } function setPolygonOffset(polygonOffset, factor, units) { if (polygonOffset) { enable(gl.POLYGON_OFFSET_FILL); if (currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units) { gl.polygonOffset(factor, units); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable(gl.POLYGON_OFFSET_FILL); } } function setScissorTest(scissorTest) { if (scissorTest) { enable(gl.SCISSOR_TEST); } else { disable(gl.SCISSOR_TEST); } } // texture function activeTexture(webglSlot) { if (webglSlot === undefined) webglSlot = gl.TEXTURE0 + maxTextures - 1; if (currentTextureSlot !== webglSlot) { gl.activeTexture(webglSlot); currentTextureSlot = webglSlot; } } function bindTexture(webglType, webglTexture, webglSlot) { if (webglSlot === undefined) { if (currentTextureSlot === null) { webglSlot = gl.TEXTURE0 + maxTextures - 1; } else { webglSlot = currentTextureSlot; } } let boundTexture = currentBoundTextures[webglSlot]; if (boundTexture === undefined) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[webglSlot] = boundTexture; } if (boundTexture.type !== webglType || boundTexture.texture !== webglTexture) { if (currentTextureSlot !== webglSlot) { gl.activeTexture(webglSlot); currentTextureSlot = webglSlot; } gl.bindTexture(webglType, webglTexture || emptyTextures[webglType]); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function unbindTexture() { const boundTexture = currentBoundTextures[currentTextureSlot]; if (boundTexture !== undefined && boundTexture.type !== undefined) { gl.bindTexture(boundTexture.type, null); boundTexture.type = undefined; boundTexture.texture = undefined; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply(gl, arguments); } catch (error) { console.error('THREE.WebGLState:', error); } } function compressedTexImage3D() { try { gl.compressedTexImage3D.apply(gl, arguments); } catch (error) { console.error('THREE.WebGLState:', error); } } function texSubImage2D() { try { gl.texSubImage2D.apply(gl, arguments); } catch (error) { console.error('THREE.WebGLState:', error); } } function texSubImage3D() { try { gl.texSubImage3D.apply(gl, arguments); } catch (error) { console.error('THREE.WebGLState:', error); } } function compressedTexSubImage2D() { try { gl.compressedTexSubImage2D.apply(gl, arguments); } catch (error) { console.error('THREE.WebGLState:', error); } } function compressedTexSubImage3D() { try { gl.compressedTexSubImage3D.apply(gl, arguments); } catch (error) { console.error('THREE.WebGLState:', error); } } function texStorage2D() { try { gl.texStorage2D.apply(gl, arguments); } catch (error) { console.error('THREE.WebGLState:', error); } } function texStorage3D() { try { gl.texStorage3D.apply(gl, arguments); } catch (error) { console.error('THREE.WebGLState:', error); } } function texImage2D() { try { gl.texImage2D.apply(gl, arguments); } catch (error) { console.error('THREE.WebGLState:', error); } } function texImage3D() { try { gl.texImage3D.apply(gl, arguments); } catch (error) { console.error('THREE.WebGLState:', error); } } // function scissor(scissor) { if (currentScissor.equals(scissor) === false) { gl.scissor(scissor.x, scissor.y, scissor.z, scissor.w); currentScissor.copy(scissor); } } function viewport(viewport) { if (currentViewport.equals(viewport) === false) { gl.viewport(viewport.x, viewport.y, viewport.z, viewport.w); currentViewport.copy(viewport); } } function updateUBOMapping(uniformsGroup, program) { let mapping = uboProgramMap.get(program); if (mapping === undefined) { mapping = new WeakMap(); uboProgramMap.set(program, mapping); } let blockIndex = mapping.get(uniformsGroup); if (blockIndex === undefined) { blockIndex = gl.getUniformBlockIndex(program, uniformsGroup.name); mapping.set(uniformsGroup, blockIndex); } } function uniformBlockBinding(uniformsGroup, program) { const mapping = uboProgramMap.get(program); const blockIndex = mapping.get(uniformsGroup); if (uboBindings.get(program) !== blockIndex) { // bind shader specific block index to global block point gl.uniformBlockBinding(program, blockIndex, uniformsGroup.__bindingPointIndex); uboBindings.set(program, blockIndex); } } // function reset() { // reset state gl.disable(gl.BLEND); gl.disable(gl.CULL_FACE); gl.disable(gl.DEPTH_TEST); gl.disable(gl.POLYGON_OFFSET_FILL); gl.disable(gl.SCISSOR_TEST); gl.disable(gl.STENCIL_TEST); gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE); gl.blendEquation(gl.FUNC_ADD); gl.blendFunc(gl.ONE, gl.ZERO); gl.blendFuncSeparate(gl.ONE, gl.ZERO, gl.ONE, gl.ZERO); gl.colorMask(true, true, true, true); gl.clearColor(0, 0, 0, 0); gl.depthMask(true); gl.depthFunc(gl.LESS); gl.clearDepth(1); gl.stencilMask(0xffffffff); gl.stencilFunc(gl.ALWAYS, 0, 0xffffffff); gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); gl.clearStencil(0); gl.cullFace(gl.BACK); gl.frontFace(gl.CCW); gl.polygonOffset(0, 0); gl.activeTexture(gl.TEXTURE0); gl.bindFramebuffer(gl.FRAMEBUFFER, null); if (isWebGL2 === true) { gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); } gl.useProgram(null); gl.lineWidth(1); gl.scissor(0, 0, gl.canvas.width, gl.canvas.height); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // reset internals enabledCapabilities = {}; currentTextureSlot = null; currentBoundTextures = {}; currentBoundFramebuffers = {}; currentDrawbuffers = new WeakMap(); defaultDrawbuffers = []; currentProgram = null; currentBlendingEnabled = false; currentBlending = null; currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; currentPremultipledAlpha = false; currentFlipSided = null; currentCullFace = null; currentLineWidth = null; currentPolygonOffsetFactor = null; currentPolygonOffsetUnits = null; currentScissor.set(0, 0, gl.canvas.width, gl.canvas.height); currentViewport.set(0, 0, gl.canvas.width, gl.canvas.height); colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, enable: enable, disable: disable, bindFramebuffer: bindFramebuffer, drawBuffers: drawBuffers, useProgram: useProgram, setBlending: setBlending, setMaterial: setMaterial, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, unbindTexture: unbindTexture, compressedTexImage2D: compressedTexImage2D, compressedTexImage3D: compressedTexImage3D, texImage2D: texImage2D, texImage3D: texImage3D, updateUBOMapping: updateUBOMapping, uniformBlockBinding: uniformBlockBinding, texStorage2D: texStorage2D, texStorage3D: texStorage3D, texSubImage2D: texSubImage2D, texSubImage3D: texSubImage3D, compressedTexSubImage2D: compressedTexSubImage2D, compressedTexSubImage3D: compressedTexSubImage3D, scissor: scissor, viewport: viewport, reset: reset }; } function WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info) { const isWebGL2 = capabilities.isWebGL2; const maxTextures = capabilities.maxTextures; const maxCubemapSize = capabilities.maxCubemapSize; const maxTextureSize = capabilities.maxTextureSize; const maxSamples = capabilities.maxSamples; const multisampledRTTExt = extensions.has('WEBGL_multisampled_render_to_texture') ? extensions.get('WEBGL_multisampled_render_to_texture') : null; const supportsInvalidateFramebuffer = typeof navigator === 'undefined' ? false : /OculusBrowser/g.test(navigator.userAgent); const _videoTextures = new WeakMap(); let _canvas; const _sources = new WeakMap(); // maps WebglTexture objects to instances of Source // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). let useOffscreenCanvas = false; try { useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' // eslint-disable-next-line compat/compat && new OffscreenCanvas(1, 1).getContext('2d') !== null; } catch (err) { // Ignore any errors } function createCanvas(width, height) { // Use OffscreenCanvas when available. Specially needed in web workers return useOffscreenCanvas ? // eslint-disable-next-line compat/compat new OffscreenCanvas(width, height) : createElementNS('canvas'); } function resizeImage(image, needsPowerOfTwo, needsNewCanvas, maxSize) { let scale = 1; // handle case if texture exceeds max size if (image.width > maxSize || image.height > maxSize) { scale = maxSize / Math.max(image.width, image.height); } // only perform resize if necessary if (scale < 1 || needsPowerOfTwo === true) { // only perform resize for certain image types if (typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement || typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap) { const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; const width = floor(scale * image.width); const height = floor(scale * image.height); if (_canvas === undefined) _canvas = createCanvas(width, height); // cube textures can't reuse the same canvas const canvas = needsNewCanvas ? createCanvas(width, height) : _canvas; canvas.width = width; canvas.height = height; const context = canvas.getContext('2d'); context.drawImage(image, 0, 0, width, height); console.warn('THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').'); return canvas; } else { if ('data' in image) { console.warn('THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').'); } return image; } } return image; } function isPowerOfTwo$1(image) { return isPowerOfTwo(image.width) && isPowerOfTwo(image.height); } function textureNeedsPowerOfTwo(texture) { if (isWebGL2) return false; return texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping || texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function textureNeedsGenerateMipmaps(texture, supportsMips) { return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; } function generateMipmap(target) { _gl.generateMipmap(target); } function getInternalFormat(internalFormatName, glFormat, glType, colorSpace, forceLinearTransfer = false) { if (isWebGL2 === false) return glFormat; if (internalFormatName !== null) { if (_gl[internalFormatName] !== undefined) return _gl[internalFormatName]; console.warn('THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\''); } let internalFormat = glFormat; if (glFormat === _gl.RED) { if (glType === _gl.FLOAT) internalFormat = _gl.R32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.R16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.R8; } if (glFormat === _gl.RG) { if (glType === _gl.FLOAT) internalFormat = _gl.RG32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RG16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = _gl.RG8; } if (glFormat === _gl.RGBA) { if (glType === _gl.FLOAT) internalFormat = _gl.RGBA32F; if (glType === _gl.HALF_FLOAT) internalFormat = _gl.RGBA16F; if (glType === _gl.UNSIGNED_BYTE) internalFormat = colorSpace === SRGBColorSpace && forceLinearTransfer === false ? _gl.SRGB8_ALPHA8 : _gl.RGBA8; if (glType === _gl.UNSIGNED_SHORT_4_4_4_4) internalFormat = _gl.RGBA4; if (glType === _gl.UNSIGNED_SHORT_5_5_5_1) internalFormat = _gl.RGB5_A1; } if (internalFormat === _gl.R16F || internalFormat === _gl.R32F || internalFormat === _gl.RG16F || internalFormat === _gl.RG32F || internalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F) { extensions.get('EXT_color_buffer_float'); } return internalFormat; } function getMipLevels(texture, image, supportsMips) { if (textureNeedsGenerateMipmaps(texture, supportsMips) === true || texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { return Math.log2(Math.max(image.width, image.height)) + 1; } else if (texture.mipmaps !== undefined && texture.mipmaps.length > 0) { // user-defined mipmaps return texture.mipmaps.length; } else if (texture.isCompressedTexture && Array.isArray(texture.image)) { return image.mipmaps.length; } else { // texture without mipmaps (only base level) return 1; } } // Fallback filters for non-power-of-2 textures function filterFallback(f) { if (f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter) { return _gl.NEAREST; } return _gl.LINEAR; } // function onTextureDispose(event) { const texture = event.target; texture.removeEventListener('dispose', onTextureDispose); deallocateTexture(texture); if (texture.isVideoTexture) { _videoTextures.delete(texture); } } function onRenderTargetDispose(event) { const renderTarget = event.target; renderTarget.removeEventListener('dispose', onRenderTargetDispose); deallocateRenderTarget(renderTarget); } // function deallocateTexture(texture) { const textureProperties = properties.get(texture); if (textureProperties.__webglInit === undefined) return; // check if it's necessary to remove the WebGLTexture object const source = texture.source; const webglTextures = _sources.get(source); if (webglTextures) { const webglTexture = webglTextures[textureProperties.__cacheKey]; webglTexture.usedTimes--; // the WebGLTexture object is not used anymore, remove it if (webglTexture.usedTimes === 0) { deleteTexture(texture); } // remove the weak map entry if no WebGLTexture uses the source anymore if (Object.keys(webglTextures).length === 0) { _sources.delete(source); } } properties.remove(texture); } function deleteTexture(texture) { const textureProperties = properties.get(texture); _gl.deleteTexture(textureProperties.__webglTexture); const source = texture.source; const webglTextures = _sources.get(source); delete webglTextures[textureProperties.__cacheKey]; info.memory.textures--; } function deallocateRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); if (textureProperties.__webglTexture !== undefined) { _gl.deleteTexture(textureProperties.__webglTexture); info.memory.textures--; } if (renderTarget.depthTexture) { renderTarget.depthTexture.dispose(); } if (renderTarget.isWebGLCubeRenderTarget) { for (let i = 0; i < 6; i++) { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[i]); if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer[i]); } } else { _gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer); if (renderTargetProperties.__webglDepthbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer); if (renderTargetProperties.__webglMultisampledFramebuffer) _gl.deleteFramebuffer(renderTargetProperties.__webglMultisampledFramebuffer); if (renderTargetProperties.__webglColorRenderbuffer) { for (let i = 0; i < renderTargetProperties.__webglColorRenderbuffer.length; i++) { if (renderTargetProperties.__webglColorRenderbuffer[i]) _gl.deleteRenderbuffer(renderTargetProperties.__webglColorRenderbuffer[i]); } } if (renderTargetProperties.__webglDepthRenderbuffer) _gl.deleteRenderbuffer(renderTargetProperties.__webglDepthRenderbuffer); } if (renderTarget.isWebGLMultipleRenderTargets) { for (let i = 0, il = texture.length; i < il; i++) { const attachmentProperties = properties.get(texture[i]); if (attachmentProperties.__webglTexture) { _gl.deleteTexture(attachmentProperties.__webglTexture); info.memory.textures--; } properties.remove(texture[i]); } } properties.remove(texture); properties.remove(renderTarget); } // let textureUnits = 0; function resetTextureUnits() { textureUnits = 0; } function allocateTextureUnit() { const textureUnit = textureUnits; if (textureUnit >= maxTextures) { console.warn('THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures); } textureUnits += 1; return textureUnit; } function getTextureCacheKey(texture) { const array = []; array.push(texture.wrapS); array.push(texture.wrapT); array.push(texture.wrapR || 0); array.push(texture.magFilter); array.push(texture.minFilter); array.push(texture.anisotropy); array.push(texture.internalFormat); array.push(texture.format); array.push(texture.type); array.push(texture.generateMipmaps); array.push(texture.premultiplyAlpha); array.push(texture.flipY); array.push(texture.unpackAlignment); array.push(texture.colorSpace); return array.join(); } // function setTexture2D(texture, slot) { const textureProperties = properties.get(texture); if (texture.isVideoTexture) updateVideoTexture(texture); if (texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version) { const image = texture.image; if (image === null) { console.warn('THREE.WebGLRenderer: Texture marked for update but no image data found.'); } else if (image.complete === false) { console.warn('THREE.WebGLRenderer: Texture marked for update but image is incomplete'); } else { uploadTexture(textureProperties, texture, slot); return; } } state.bindTexture(_gl.TEXTURE_2D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot); } function setTexture2DArray(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadTexture(textureProperties, texture, slot); return; } state.bindTexture(_gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture, _gl.TEXTURE0 + slot); } function setTexture3D(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadTexture(textureProperties, texture, slot); return; } state.bindTexture(_gl.TEXTURE_3D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot); } function setTextureCube(texture, slot) { const textureProperties = properties.get(texture); if (texture.version > 0 && textureProperties.__version !== texture.version) { uploadCubeTexture(textureProperties, texture, slot); return; } state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot); } const wrappingToGL = { [RepeatWrapping]: _gl.REPEAT, [ClampToEdgeWrapping]: _gl.CLAMP_TO_EDGE, [MirroredRepeatWrapping]: _gl.MIRRORED_REPEAT }; const filterToGL = { [NearestFilter]: _gl.NEAREST, [NearestMipmapNearestFilter]: _gl.NEAREST_MIPMAP_NEAREST, [NearestMipmapLinearFilter]: _gl.NEAREST_MIPMAP_LINEAR, [LinearFilter]: _gl.LINEAR, [LinearMipmapNearestFilter]: _gl.LINEAR_MIPMAP_NEAREST, [LinearMipmapLinearFilter]: _gl.LINEAR_MIPMAP_LINEAR }; const compareToGL = { [NeverCompare]: _gl.NEVER, [AlwaysCompare]: _gl.ALWAYS, [LessCompare]: _gl.LESS, [LessEqualCompare]: _gl.LEQUAL, [EqualCompare]: _gl.EQUAL, [GreaterEqualCompare]: _gl.GEQUAL, [GreaterCompare]: _gl.GREATER, [NotEqualCompare]: _gl.NOTEQUAL }; function setTextureParameters(textureType, texture, supportsMips) { if (supportsMips) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[texture.wrapS]); _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[texture.wrapT]); if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[texture.wrapR]); } _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[texture.magFilter]); _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[texture.minFilter]); } else { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE); _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE); if (textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY) { _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_R, _gl.CLAMP_TO_EDGE); } if (texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping) { console.warn('THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.'); } _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER, filterFallback(texture.magFilter)); _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER, filterFallback(texture.minFilter)); if (texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter) { console.warn('THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.'); } } if (texture.compareFunction) { _gl.texParameteri(textureType, _gl.TEXTURE_COMPARE_MODE, _gl.COMPARE_REF_TO_TEXTURE); _gl.texParameteri(textureType, _gl.TEXTURE_COMPARE_FUNC, compareToGL[texture.compareFunction]); } if (extensions.has('EXT_texture_filter_anisotropic') === true) { const extension = extensions.get('EXT_texture_filter_anisotropic'); if (texture.magFilter === NearestFilter) return; if (texture.minFilter !== NearestMipmapLinearFilter && texture.minFilter !== LinearMipmapLinearFilter) return; if (texture.type === FloatType && extensions.has('OES_texture_float_linear') === false) return; // verify extension for WebGL 1 and WebGL 2 if (isWebGL2 === false && texture.type === HalfFloatType && extensions.has('OES_texture_half_float_linear') === false) return; // verify extension for WebGL 1 only if (texture.anisotropy > 1 || properties.get(texture).__currentAnisotropy) { _gl.texParameterf(textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(texture.anisotropy, capabilities.getMaxAnisotropy())); properties.get(texture).__currentAnisotropy = texture.anisotropy; } } } function initTexture(textureProperties, texture) { let forceUpload = false; if (textureProperties.__webglInit === undefined) { textureProperties.__webglInit = true; texture.addEventListener('dispose', onTextureDispose); } // create Source <-> WebGLTextures mapping if necessary const source = texture.source; let webglTextures = _sources.get(source); if (webglTextures === undefined) { webglTextures = {}; _sources.set(source, webglTextures); } // check if there is already a WebGLTexture object for the given texture parameters const textureCacheKey = getTextureCacheKey(texture); if (textureCacheKey !== textureProperties.__cacheKey) { // if not, create a new instance of WebGLTexture if (webglTextures[textureCacheKey] === undefined) { // create new entry webglTextures[textureCacheKey] = { texture: _gl.createTexture(), usedTimes: 0 }; info.memory.textures++; // when a new instance of WebGLTexture was created, a texture upload is required // even if the image contents are identical forceUpload = true; } webglTextures[textureCacheKey].usedTimes++; // every time the texture cache key changes, it's necessary to check if an instance of // WebGLTexture can be deleted in order to avoid a memory leak. const webglTexture = webglTextures[textureProperties.__cacheKey]; if (webglTexture !== undefined) { webglTextures[textureProperties.__cacheKey].usedTimes--; if (webglTexture.usedTimes === 0) { deleteTexture(texture); } } // store references to cache key and WebGLTexture object textureProperties.__cacheKey = textureCacheKey; textureProperties.__webglTexture = webglTextures[textureCacheKey].texture; } return forceUpload; } function uploadTexture(textureProperties, texture, slot) { let textureType = _gl.TEXTURE_2D; if (texture.isDataArrayTexture || texture.isCompressedArrayTexture) textureType = _gl.TEXTURE_2D_ARRAY; if (texture.isData3DTexture) textureType = _gl.TEXTURE_3D; const forceUpload = initTexture(textureProperties, texture); const source = texture.source; state.bindTexture(textureType, textureProperties.__webglTexture, _gl.TEXTURE0 + slot); const sourceProperties = properties.get(source); if (source.version !== sourceProperties.__version || forceUpload === true) { state.activeTexture(_gl.TEXTURE0 + slot); _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE); const needsPowerOfTwo = textureNeedsPowerOfTwo(texture) && isPowerOfTwo$1(texture.image) === false; let image = resizeImage(texture.image, needsPowerOfTwo, false, maxTextureSize); image = verifyColorSpace(texture, image); const supportsMips = isPowerOfTwo$1(image) || isWebGL2, glFormat = utils.convert(texture.format, texture.colorSpace); let glType = utils.convert(texture.type), glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.colorSpace); setTextureParameters(textureType, texture, supportsMips); let mipmap; const mipmaps = texture.mipmaps; const useTexStorage = isWebGL2 && texture.isVideoTexture !== true; const allocateMemory = sourceProperties.__version === undefined || forceUpload === true; const levels = getMipLevels(texture, image, supportsMips); if (texture.isDepthTexture) { // populate depth texture with dummy data glInternalFormat = _gl.DEPTH_COMPONENT; if (isWebGL2) { if (texture.type === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (texture.type === UnsignedIntType) { glInternalFormat = _gl.DEPTH_COMPONENT24; } else if (texture.type === UnsignedInt248Type) { glInternalFormat = _gl.DEPTH24_STENCIL8; } else { glInternalFormat = _gl.DEPTH_COMPONENT16; // WebGL2 requires sized internalformat for glTexImage2D } } else { if (texture.type === FloatType) { console.error('WebGLRenderer: Floating point depth texture requires WebGL2.'); } } // validation checks for WebGL 1 if (texture.format === DepthFormat && glInternalFormat === _gl.DEPTH_COMPONENT) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if (texture.type !== UnsignedShortType && texture.type !== UnsignedIntType) { console.warn('THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.'); texture.type = UnsignedIntType; glType = utils.convert(texture.type); } } if (texture.format === DepthStencilFormat && glInternalFormat === _gl.DEPTH_COMPONENT) { // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) glInternalFormat = _gl.DEPTH_STENCIL; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if (texture.type !== UnsignedInt248Type) { console.warn('THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.'); texture.type = UnsignedInt248Type; glType = utils.convert(texture.type); } } // if (allocateMemory) { if (useTexStorage) { state.texStorage2D(_gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null); } } } else if (texture.isDataTexture) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0 && supportsMips) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data); } } } else if (texture.isCompressedTexture) { if (texture.isCompressedArrayTexture) { if (useTexStorage && allocateMemory) { state.texStorage3D(_gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height, image.depth); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage3D(_gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data, 0, 0); } else { state.compressedTexImage3D(_gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, mipmap.data, 0, 0); } } else { console.warn('THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()'); } } else { if (useTexStorage) { state.texSubImage3D(_gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data); } else { state.texImage3D(_gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, glFormat, glType, mipmap.data); } } } } else { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data); } else { state.compressedTexImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); } } else { console.warn('THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()'); } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } } } } else if (texture.isDataArrayTexture) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D(_gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth); } state.texSubImage3D(_gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data); } else { state.texImage3D(_gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); } } else if (texture.isData3DTexture) { if (useTexStorage) { if (allocateMemory) { state.texStorage3D(_gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth); } state.texSubImage3D(_gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data); } else { state.texImage3D(_gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data); } } else if (texture.isFramebufferTexture) { if (allocateMemory) { if (useTexStorage) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } else { let width = image.width, height = image.height; for (let i = 0; i < levels; i++) { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, width, height, 0, glFormat, glType, null); width >>= 1; height >>= 1; } } } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if (mipmaps.length > 0 && supportsMips) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[0].width, mipmaps[0].height); } for (let i = 0, il = mipmaps.length; i < il; i++) { mipmap = mipmaps[i]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap); } else { state.texImage2D(_gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap); } } texture.generateMipmaps = false; } else { if (useTexStorage) { if (allocateMemory) { state.texStorage2D(_gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height); } state.texSubImage2D(_gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image); } else { state.texImage2D(_gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image); } } } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(textureType); } sourceProperties.__version = source.version; if (texture.onUpdate) texture.onUpdate(texture); } textureProperties.__version = texture.version; } function uploadCubeTexture(textureProperties, texture, slot) { if (texture.image.length !== 6) return; const forceUpload = initTexture(textureProperties, texture); const source = texture.source; state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot); const sourceProperties = properties.get(source); if (source.version !== sourceProperties.__version || forceUpload === true) { state.activeTexture(_gl.TEXTURE0 + slot); _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, texture.unpackAlignment); _gl.pixelStorei(_gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE); const isCompressed = texture.isCompressedTexture || texture.image[0].isCompressedTexture; const isDataTexture = texture.image[0] && texture.image[0].isDataTexture; const cubeImage = []; for (let i = 0; i < 6; i++) { if (!isCompressed && !isDataTexture) { cubeImage[i] = resizeImage(texture.image[i], false, true, maxCubemapSize); } else { cubeImage[i] = isDataTexture ? texture.image[i].image : texture.image[i]; } cubeImage[i] = verifyColorSpace(texture, cubeImage[i]); } const image = cubeImage[0], supportsMips = isPowerOfTwo$1(image) || isWebGL2, glFormat = utils.convert(texture.format, texture.colorSpace), glType = utils.convert(texture.type), glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.colorSpace); const useTexStorage = isWebGL2 && texture.isVideoTexture !== true; const allocateMemory = sourceProperties.__version === undefined || forceUpload === true; let levels = getMipLevels(texture, image, supportsMips); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture, supportsMips); let mipmaps; if (isCompressed) { if (useTexStorage && allocateMemory) { state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height); } for (let i = 0; i < 6; i++) { mipmaps = cubeImage[i].mipmaps; for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (texture.format !== RGBAFormat) { if (glFormat !== null) { if (useTexStorage) { state.compressedTexSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data); } else { state.compressedTexImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data); } } else { console.warn('THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()'); } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data); } } } } } else { mipmaps = texture.mipmaps; if (useTexStorage && allocateMemory) { // TODO: Uniformly handle mipmap definitions // Normal textures and compressed cube textures define base level + mips with their mipmap array // Uncompressed cube textures use their mipmap array only for mips (no base level) if (mipmaps.length > 0) levels++; state.texStorage2D(_gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, cubeImage[0].width, cubeImage[0].height); } for (let i = 0; i < 6; i++) { if (isDataTexture) { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[i].width, cubeImage[i].height, glFormat, glType, cubeImage[i].data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[i].width, cubeImage[i].height, 0, glFormat, glType, cubeImage[i].data); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; const mipmapImage = mipmap.image[i].image; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data); } } } else { if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[i]); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[i]); } for (let j = 0; j < mipmaps.length; j++) { const mipmap = mipmaps[j]; if (useTexStorage) { state.texSubImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[i]); } else { state.texImage2D(_gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[i]); } } } } } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { // We assume images for cube map have the same size. generateMipmap(_gl.TEXTURE_CUBE_MAP); } sourceProperties.__version = source.version; if (texture.onUpdate) texture.onUpdate(texture); } textureProperties.__version = texture.version; } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture(framebuffer, renderTarget, texture, attachment, textureTarget) { const glFormat = utils.convert(texture.format, texture.colorSpace); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.colorSpace); const renderTargetProperties = properties.get(renderTarget); if (!renderTargetProperties.__hasExternalTextures) { if (textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY) { state.texImage3D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, renderTarget.depth, 0, glFormat, glType, null); } else { state.texImage2D(textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null); } } state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0, getRenderTargetSamples(renderTarget)); } else if (textureTarget === _gl.TEXTURE_2D || textureTarget >= _gl.TEXTURE_CUBE_MAP_POSITIVE_X && textureTarget <= _gl.TEXTURE_CUBE_MAP_NEGATIVE_Z) { // see #24753 _gl.framebufferTexture2D(_gl.FRAMEBUFFER, attachment, textureTarget, properties.get(texture).__webglTexture, 0); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage(renderbuffer, renderTarget, isMultisample) { _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderbuffer); if (renderTarget.depthBuffer && !renderTarget.stencilBuffer) { let glInternalFormat = _gl.DEPTH_COMPONENT16; if (isMultisample || useMultisampledRTT(renderTarget)) { const depthTexture = renderTarget.depthTexture; if (depthTexture && depthTexture.isDepthTexture) { if (depthTexture.type === FloatType) { glInternalFormat = _gl.DEPTH_COMPONENT32F; } else if (depthTexture.type === UnsignedIntType) { glInternalFormat = _gl.DEPTH_COMPONENT24; } } const samples = getRenderTargetSamples(renderTarget); if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height); } _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); } else if (renderTarget.depthBuffer && renderTarget.stencilBuffer) { const samples = getRenderTargetSamples(renderTarget); if (isMultisample && useMultisampledRTT(renderTarget) === false) { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height); } else if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height); } _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer); } else { const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [renderTarget.texture]; for (let i = 0; i < textures.length; i++) { const texture = textures[i]; const glFormat = utils.convert(texture.format, texture.colorSpace); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.colorSpace); const samples = getRenderTargetSamples(renderTarget); if (isMultisample && useMultisampledRTT(renderTarget) === false) { _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.renderbufferStorageMultisampleEXT(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); } else { _gl.renderbufferStorage(_gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height); } } } _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture(framebuffer, renderTarget) { const isCube = renderTarget && renderTarget.isWebGLCubeRenderTarget; if (isCube) throw new Error('Depth Texture with cube render targets is not supported'); state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (!(renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture)) { throw new Error('renderTarget.depthTexture must be an instance of THREE.DepthTexture'); } // upload an empty depth texture with framebuffer size if (!properties.get(renderTarget.depthTexture).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D(renderTarget.depthTexture, 0); const webglDepthTexture = properties.get(renderTarget.depthTexture).__webglTexture; const samples = getRenderTargetSamples(renderTarget); if (renderTarget.depthTexture.format === DepthFormat) { if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0); } } else if (renderTarget.depthTexture.format === DepthStencilFormat) { if (useMultisampledRTT(renderTarget)) { multisampledRTTExt.framebufferTexture2DMultisampleEXT(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples); } else { _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0); } } else { throw new Error('Unknown depthTexture format'); } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer(renderTarget) { const renderTargetProperties = properties.get(renderTarget); const isCube = renderTarget.isWebGLCubeRenderTarget === true; if (renderTarget.depthTexture && !renderTargetProperties.__autoAllocateDepthBuffer) { if (isCube) throw new Error('target.depthTexture not supported in Cube render targets'); setupDepthTexture(renderTargetProperties.__webglFramebuffer, renderTarget); } else { if (isCube) { renderTargetProperties.__webglDepthbuffer = []; for (let i = 0; i < 6; i++) { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[i]); renderTargetProperties.__webglDepthbuffer[i] = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer[i], renderTarget, false); } } else { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthbuffer, renderTarget, false); } } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } // rebind framebuffer with external textures function rebindTextures(renderTarget, colorTexture, depthTexture) { const renderTargetProperties = properties.get(renderTarget); if (colorTexture !== undefined) { setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D); } if (depthTexture !== undefined) { setupDepthRenderbuffer(renderTarget); } } // Set up GL resources for the render target function setupRenderTarget(renderTarget) { const texture = renderTarget.texture; const renderTargetProperties = properties.get(renderTarget); const textureProperties = properties.get(texture); renderTarget.addEventListener('dispose', onRenderTargetDispose); if (renderTarget.isWebGLMultipleRenderTargets !== true) { if (textureProperties.__webglTexture === undefined) { textureProperties.__webglTexture = _gl.createTexture(); } textureProperties.__version = texture.version; info.memory.textures++; } const isCube = renderTarget.isWebGLCubeRenderTarget === true; const isMultipleRenderTargets = renderTarget.isWebGLMultipleRenderTargets === true; const supportsMips = isPowerOfTwo$1(renderTarget) || isWebGL2; // Setup framebuffer if (isCube) { renderTargetProperties.__webglFramebuffer = []; for (let i = 0; i < 6; i++) { renderTargetProperties.__webglFramebuffer[i] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); if (isMultipleRenderTargets) { if (capabilities.drawBuffers) { const textures = renderTarget.texture; for (let i = 0, il = textures.length; i < il; i++) { const attachmentProperties = properties.get(textures[i]); if (attachmentProperties.__webglTexture === undefined) { attachmentProperties.__webglTexture = _gl.createTexture(); info.memory.textures++; } } } else { console.warn('THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension.'); } } if (isWebGL2 && renderTarget.samples > 0 && useMultisampledRTT(renderTarget) === false) { const textures = isMultipleRenderTargets ? texture : [texture]; renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); renderTargetProperties.__webglColorRenderbuffer = []; state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); for (let i = 0; i < textures.length; i++) { const texture = textures[i]; renderTargetProperties.__webglColorRenderbuffer[i] = _gl.createRenderbuffer(); _gl.bindRenderbuffer(_gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[i]); const glFormat = utils.convert(texture.format, texture.colorSpace); const glType = utils.convert(texture.type); const glInternalFormat = getInternalFormat(texture.internalFormat, glFormat, glType, texture.colorSpace, renderTarget.isXRRenderTarget === true); const samples = getRenderTargetSamples(renderTarget); _gl.renderbufferStorageMultisample(_gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height); _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[i]); } _gl.bindRenderbuffer(_gl.RENDERBUFFER, null); if (renderTarget.depthBuffer) { renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage(renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true); } state.bindFramebuffer(_gl.FRAMEBUFFER, null); } } // Setup color buffer if (isCube) { state.bindTexture(_gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_CUBE_MAP, texture, supportsMips); for (let i = 0; i < 6; i++) { setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer[i], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i); } if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(_gl.TEXTURE_CUBE_MAP); } state.unbindTexture(); } else if (isMultipleRenderTargets) { const textures = renderTarget.texture; for (let i = 0, il = textures.length; i < il; i++) { const attachment = textures[i]; const attachmentProperties = properties.get(attachment); state.bindTexture(_gl.TEXTURE_2D, attachmentProperties.__webglTexture); setTextureParameters(_gl.TEXTURE_2D, attachment, supportsMips); setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D); if (textureNeedsGenerateMipmaps(attachment, supportsMips)) { generateMipmap(_gl.TEXTURE_2D); } } state.unbindTexture(); } else { let glTextureType = _gl.TEXTURE_2D; if (renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget) { if (isWebGL2) { glTextureType = renderTarget.isWebGL3DRenderTarget ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY; } else { console.error('THREE.WebGLTextures: THREE.Data3DTexture and THREE.DataArrayTexture only supported with WebGL2.'); } } state.bindTexture(glTextureType, textureProperties.__webglTexture); setTextureParameters(glTextureType, texture, supportsMips); setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType); if (textureNeedsGenerateMipmaps(texture, supportsMips)) { generateMipmap(glTextureType); } state.unbindTexture(); } // Setup depth and stencil buffers if (renderTarget.depthBuffer) { setupDepthRenderbuffer(renderTarget); } } function updateRenderTargetMipmap(renderTarget) { const supportsMips = isPowerOfTwo$1(renderTarget) || isWebGL2; const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [renderTarget.texture]; for (let i = 0, il = textures.length; i < il; i++) { const texture = textures[i]; if (textureNeedsGenerateMipmaps(texture, supportsMips)) { const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; const webglTexture = properties.get(texture).__webglTexture; state.bindTexture(target, webglTexture); generateMipmap(target); state.unbindTexture(); } } } function updateMultisampleRenderTarget(renderTarget) { if (isWebGL2 && renderTarget.samples > 0 && useMultisampledRTT(renderTarget) === false) { const textures = renderTarget.isWebGLMultipleRenderTargets ? renderTarget.texture : [renderTarget.texture]; const width = renderTarget.width; const height = renderTarget.height; let mask = _gl.COLOR_BUFFER_BIT; const invalidationArray = []; const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; const renderTargetProperties = properties.get(renderTarget); const isMultipleRenderTargets = renderTarget.isWebGLMultipleRenderTargets === true; // If MRT we need to remove FBO attachments if (isMultipleRenderTargets) { for (let i = 0; i < textures.length; i++) { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, null); state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); _gl.framebufferTexture2D(_gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, null, 0); } } state.bindFramebuffer(_gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); for (let i = 0; i < textures.length; i++) { invalidationArray.push(_gl.COLOR_ATTACHMENT0 + i); if (renderTarget.depthBuffer) { invalidationArray.push(depthStyle); } const ignoreDepthValues = renderTargetProperties.__ignoreDepthValues !== undefined ? renderTargetProperties.__ignoreDepthValues : false; if (ignoreDepthValues === false) { if (renderTarget.depthBuffer) mask |= _gl.DEPTH_BUFFER_BIT; if (renderTarget.stencilBuffer) mask |= _gl.STENCIL_BUFFER_BIT; } if (isMultipleRenderTargets) { _gl.framebufferRenderbuffer(_gl.READ_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[i]); } if (ignoreDepthValues === true) { _gl.invalidateFramebuffer(_gl.READ_FRAMEBUFFER, [depthStyle]); _gl.invalidateFramebuffer(_gl.DRAW_FRAMEBUFFER, [depthStyle]); } if (isMultipleRenderTargets) { const webglTexture = properties.get(textures[i]).__webglTexture; _gl.framebufferTexture2D(_gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, webglTexture, 0); } _gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST); if (supportsInvalidateFramebuffer) { _gl.invalidateFramebuffer(_gl.READ_FRAMEBUFFER, invalidationArray); } } state.bindFramebuffer(_gl.READ_FRAMEBUFFER, null); state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, null); // If MRT since pre-blit we removed the FBO we need to reconstruct the attachments if (isMultipleRenderTargets) { for (let i = 0; i < textures.length; i++) { state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); _gl.framebufferRenderbuffer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[i]); const webglTexture = properties.get(textures[i]).__webglTexture; state.bindFramebuffer(_gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer); _gl.framebufferTexture2D(_gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, webglTexture, 0); } } state.bindFramebuffer(_gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer); } } function getRenderTargetSamples(renderTarget) { return Math.min(maxSamples, renderTarget.samples); } function useMultisampledRTT(renderTarget) { const renderTargetProperties = properties.get(renderTarget); return isWebGL2 && renderTarget.samples > 0 && extensions.has('WEBGL_multisampled_render_to_texture') === true && renderTargetProperties.__useRenderToTexture !== false; } function updateVideoTexture(texture) { const frame = info.render.frame; // Check the last frame we updated the VideoTexture if (_videoTextures.get(texture) !== frame) { _videoTextures.set(texture, frame); texture.update(); } } function verifyColorSpace(texture, image) { const colorSpace = texture.colorSpace; const format = texture.format; const type = texture.type; if (texture.isCompressedTexture === true || texture.format === _SRGBAFormat) return image; if (colorSpace !== LinearSRGBColorSpace && colorSpace !== NoColorSpace) { // sRGB if (colorSpace === SRGBColorSpace) { if (isWebGL2 === false) { // in WebGL 1, try to use EXT_sRGB extension and unsized formats if (extensions.has('EXT_sRGB') === true && format === RGBAFormat) { texture.format = _SRGBAFormat; // it's not possible to generate mips in WebGL 1 with this extension texture.minFilter = LinearFilter; texture.generateMipmaps = false; } else { // slow fallback (CPU decode) image = ImageUtils.sRGBToLinear(image); } } else { // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format if (format !== RGBAFormat || type !== UnsignedByteType) { console.warn('THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType.'); } } } else { console.error('THREE.WebGLTextures: Unsupported texture color space:', colorSpace); } } return image; } // this.allocateTextureUnit = allocateTextureUnit; this.resetTextureUnits = resetTextureUnits; this.setTexture2D = setTexture2D; this.setTexture2DArray = setTexture2DArray; this.setTexture3D = setTexture3D; this.setTextureCube = setTextureCube; this.rebindTextures = rebindTextures; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; this.setupDepthRenderbuffer = setupDepthRenderbuffer; this.setupFrameBufferTexture = setupFrameBufferTexture; this.useMultisampledRTT = useMultisampledRTT; } function WebGLUtils(gl, extensions, capabilities) { const isWebGL2 = capabilities.isWebGL2; function convert(p, colorSpace = NoColorSpace) { let extension; if (p === UnsignedByteType) return gl.UNSIGNED_BYTE; if (p === UnsignedShort4444Type) return gl.UNSIGNED_SHORT_4_4_4_4; if (p === UnsignedShort5551Type) return gl.UNSIGNED_SHORT_5_5_5_1; if (p === ByteType) return gl.BYTE; if (p === ShortType) return gl.SHORT; if (p === UnsignedShortType) return gl.UNSIGNED_SHORT; if (p === IntType) return gl.INT; if (p === UnsignedIntType) return gl.UNSIGNED_INT; if (p === FloatType) return gl.FLOAT; if (p === HalfFloatType) { if (isWebGL2) return gl.HALF_FLOAT; extension = extensions.get('OES_texture_half_float'); if (extension !== null) { return extension.HALF_FLOAT_OES; } else { return null; } } if (p === AlphaFormat) return gl.ALPHA; if (p === RGBAFormat) return gl.RGBA; if (p === LuminanceFormat) return gl.LUMINANCE; if (p === LuminanceAlphaFormat) return gl.LUMINANCE_ALPHA; if (p === DepthFormat) return gl.DEPTH_COMPONENT; if (p === DepthStencilFormat) return gl.DEPTH_STENCIL; // WebGL 1 sRGB fallback if (p === _SRGBAFormat) { extension = extensions.get('EXT_sRGB'); if (extension !== null) { return extension.SRGB_ALPHA_EXT; } else { return null; } } // WebGL2 formats. if (p === RedFormat) return gl.RED; if (p === RedIntegerFormat) return gl.RED_INTEGER; if (p === RGFormat) return gl.RG; if (p === RGIntegerFormat) return gl.RG_INTEGER; if (p === RGBAIntegerFormat) return gl.RGBA_INTEGER; // S3TC if (p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format) { if (colorSpace === SRGBColorSpace) { extension = extensions.get('WEBGL_compressed_texture_s3tc_srgb'); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; } else { return null; } } else { extension = extensions.get('WEBGL_compressed_texture_s3tc'); if (extension !== null) { if (p === RGB_S3TC_DXT1_Format) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT1_Format) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if (p === RGBA_S3TC_DXT3_Format) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if (p === RGBA_S3TC_DXT5_Format) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } else { return null; } } } // PVRTC if (p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format) { extension = extensions.get('WEBGL_compressed_texture_pvrtc'); if (extension !== null) { if (p === RGB_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if (p === RGB_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if (p === RGBA_PVRTC_4BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if (p === RGBA_PVRTC_2BPPV1_Format) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } else { return null; } } // ETC1 if (p === RGB_ETC1_Format) { extension = extensions.get('WEBGL_compressed_texture_etc1'); if (extension !== null) { return extension.COMPRESSED_RGB_ETC1_WEBGL; } else { return null; } } // ETC2 if (p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format) { extension = extensions.get('WEBGL_compressed_texture_etc'); if (extension !== null) { if (p === RGB_ETC2_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; if (p === RGBA_ETC2_EAC_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; } else { return null; } } // ASTC if (p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format) { extension = extensions.get('WEBGL_compressed_texture_astc'); if (extension !== null) { if (p === RGBA_ASTC_4x4_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; if (p === RGBA_ASTC_5x4_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; if (p === RGBA_ASTC_5x5_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; if (p === RGBA_ASTC_6x5_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; if (p === RGBA_ASTC_6x6_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; if (p === RGBA_ASTC_8x5_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; if (p === RGBA_ASTC_8x6_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; if (p === RGBA_ASTC_8x8_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; if (p === RGBA_ASTC_10x5_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; if (p === RGBA_ASTC_10x6_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; if (p === RGBA_ASTC_10x8_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; if (p === RGBA_ASTC_10x10_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; if (p === RGBA_ASTC_12x10_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; if (p === RGBA_ASTC_12x12_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; } else { return null; } } // BPTC if (p === RGBA_BPTC_Format) { extension = extensions.get('EXT_texture_compression_bptc'); if (extension !== null) { if (p === RGBA_BPTC_Format) return colorSpace === SRGBColorSpace ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; } else { return null; } } // RGTC if (p === RED_RGTC1_Format || p === SIGNED_RED_RGTC1_Format || p === RED_GREEN_RGTC2_Format || p === SIGNED_RED_GREEN_RGTC2_Format) { extension = extensions.get('EXT_texture_compression_rgtc'); if (extension !== null) { if (p === RGBA_BPTC_Format) return extension.COMPRESSED_RED_RGTC1_EXT; if (p === SIGNED_RED_RGTC1_Format) return extension.COMPRESSED_SIGNED_RED_RGTC1_EXT; if (p === RED_GREEN_RGTC2_Format) return extension.COMPRESSED_RED_GREEN_RGTC2_EXT; if (p === SIGNED_RED_GREEN_RGTC2_Format) return extension.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT; } else { return null; } } // if (p === UnsignedInt248Type) { if (isWebGL2) return gl.UNSIGNED_INT_24_8; extension = extensions.get('WEBGL_depth_texture'); if (extension !== null) { return extension.UNSIGNED_INT_24_8_WEBGL; } else { return null; } } // if "p" can't be resolved, assume the user defines a WebGL constant as a string (fallback/workaround for packed RGB formats) return gl[p] !== undefined ? gl[p] : null; } return { convert: convert }; } class ArrayCamera extends PerspectiveCamera { constructor(array = []) { super(); this.isArrayCamera = true; this.cameras = array; } } class Group extends Object3D { constructor() { super(); this.isGroup = true; this.type = 'Group'; } } const _moveEvent = { type: 'move' }; class WebXRController { constructor() { this._targetRay = null; this._grip = null; this._hand = null; } getHandSpace() { if (this._hand === null) { this._hand = new Group(); this._hand.matrixAutoUpdate = false; this._hand.visible = false; this._hand.joints = {}; this._hand.inputState = { pinching: false }; } return this._hand; } getTargetRaySpace() { if (this._targetRay === null) { this._targetRay = new Group(); this._targetRay.matrixAutoUpdate = false; this._targetRay.visible = false; this._targetRay.hasLinearVelocity = false; this._targetRay.linearVelocity = new Vector3(); this._targetRay.hasAngularVelocity = false; this._targetRay.angularVelocity = new Vector3(); } return this._targetRay; } getGripSpace() { if (this._grip === null) { this._grip = new Group(); this._grip.matrixAutoUpdate = false; this._grip.visible = false; this._grip.hasLinearVelocity = false; this._grip.linearVelocity = new Vector3(); this._grip.hasAngularVelocity = false; this._grip.angularVelocity = new Vector3(); } return this._grip; } dispatchEvent(event) { if (this._targetRay !== null) { this._targetRay.dispatchEvent(event); } if (this._grip !== null) { this._grip.dispatchEvent(event); } if (this._hand !== null) { this._hand.dispatchEvent(event); } return this; } connect(inputSource) { if (inputSource && inputSource.hand) { const hand = this._hand; if (hand) { for (const inputjoint of inputSource.hand.values()) { // Initialize hand with joints when connected this._getHandJoint(hand, inputjoint); } } } this.dispatchEvent({ type: 'connected', data: inputSource }); return this; } disconnect(inputSource) { this.dispatchEvent({ type: 'disconnected', data: inputSource }); if (this._targetRay !== null) { this._targetRay.visible = false; } if (this._grip !== null) { this._grip.visible = false; } if (this._hand !== null) { this._hand.visible = false; } return this; } update(inputSource, frame, referenceSpace) { let inputPose = null; let gripPose = null; let handPose = null; const targetRay = this._targetRay; const grip = this._grip; const hand = this._hand; if (inputSource && frame.session.visibilityState !== 'visible-blurred') { if (hand && inputSource.hand) { handPose = true; for (const inputjoint of inputSource.hand.values()) { // Update the joints groups with the XRJoint poses const jointPose = frame.getJointPose(inputjoint, referenceSpace); // The transform of this joint will be updated with the joint pose on each frame const joint = this._getHandJoint(hand, inputjoint); if (jointPose !== null) { joint.matrix.fromArray(jointPose.transform.matrix); joint.matrix.decompose(joint.position, joint.rotation, joint.scale); joint.matrixWorldNeedsUpdate = true; joint.jointRadius = jointPose.radius; } joint.visible = jointPose !== null; } // Custom events // Check pinchz const indexTip = hand.joints['index-finger-tip']; const thumbTip = hand.joints['thumb-tip']; const distance = indexTip.position.distanceTo(thumbTip.position); const distanceToPinch = 0.02; const threshold = 0.005; if (hand.inputState.pinching && distance > distanceToPinch + threshold) { hand.inputState.pinching = false; this.dispatchEvent({ type: 'pinchend', handedness: inputSource.handedness, target: this }); } else if (!hand.inputState.pinching && distance <= distanceToPinch - threshold) { hand.inputState.pinching = true; this.dispatchEvent({ type: 'pinchstart', handedness: inputSource.handedness, target: this }); } } else { if (grip !== null && inputSource.gripSpace) { gripPose = frame.getPose(inputSource.gripSpace, referenceSpace); if (gripPose !== null) { grip.matrix.fromArray(gripPose.transform.matrix); grip.matrix.decompose(grip.position, grip.rotation, grip.scale); grip.matrixWorldNeedsUpdate = true; if (gripPose.linearVelocity) { grip.hasLinearVelocity = true; grip.linearVelocity.copy(gripPose.linearVelocity); } else { grip.hasLinearVelocity = false; } if (gripPose.angularVelocity) { grip.hasAngularVelocity = true; grip.angularVelocity.copy(gripPose.angularVelocity); } else { grip.hasAngularVelocity = false; } } } } if (targetRay !== null) { inputPose = frame.getPose(inputSource.targetRaySpace, referenceSpace); // Some runtimes (namely Vive Cosmos with Vive OpenXR Runtime) have only grip space and ray space is equal to it if (inputPose === null && gripPose !== null) { inputPose = gripPose; } if (inputPose !== null) { targetRay.matrix.fromArray(inputPose.transform.matrix); targetRay.matrix.decompose(targetRay.position, targetRay.rotation, targetRay.scale); targetRay.matrixWorldNeedsUpdate = true; if (inputPose.linearVelocity) { targetRay.hasLinearVelocity = true; targetRay.linearVelocity.copy(inputPose.linearVelocity); } else { targetRay.hasLinearVelocity = false; } if (inputPose.angularVelocity) { targetRay.hasAngularVelocity = true; targetRay.angularVelocity.copy(inputPose.angularVelocity); } else { targetRay.hasAngularVelocity = false; } this.dispatchEvent(_moveEvent); } } } if (targetRay !== null) { targetRay.visible = inputPose !== null; } if (grip !== null) { grip.visible = gripPose !== null; } if (hand !== null) { hand.visible = handPose !== null; } return this; } // private method _getHandJoint(hand, inputjoint) { if (hand.joints[inputjoint.jointName] === undefined) { const joint = new Group(); joint.matrixAutoUpdate = false; joint.visible = false; hand.joints[inputjoint.jointName] = joint; hand.add(joint); } return hand.joints[inputjoint.jointName]; } } class DepthTexture extends Texture { constructor(width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format) { format = format !== undefined ? format : DepthFormat; if (format !== DepthFormat && format !== DepthStencilFormat) { throw new Error('DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat'); } if (type === undefined && format === DepthFormat) type = UnsignedIntType; if (type === undefined && format === DepthStencilFormat) type = UnsignedInt248Type; super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.isDepthTexture = true; this.image = { width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; this.compareFunction = null; } copy(source) { super.copy(source); this.compareFunction = source.compareFunction; return this; } toJSON(meta) { const data = super.toJSON(meta); if (this.compareFunction !== null) data.compareFunction = this.compareFunction; return data; } } class WebXRManager extends EventDispatcher { constructor(renderer, gl) { super(); const scope = this; let session = null; let framebufferScaleFactor = 1.0; let referenceSpace = null; let referenceSpaceType = 'local-floor'; // Set default foveation to maximum. let foveation = 1.0; let customReferenceSpace = null; let pose = null; let glBinding = null; let glProjLayer = null; let glBaseLayer = null; let xrFrame = null; const attributes = gl.getContextAttributes(); let initialRenderTarget = null; let newRenderTarget = null; const controllers = []; const controllerInputSources = []; // const cameraL = new PerspectiveCamera(); cameraL.layers.enable(1); cameraL.viewport = new Vector4(); const cameraR = new PerspectiveCamera(); cameraR.layers.enable(2); cameraR.viewport = new Vector4(); const cameras = [cameraL, cameraR]; const cameraXR = new ArrayCamera(); cameraXR.layers.enable(1); cameraXR.layers.enable(2); let _currentDepthNear = null; let _currentDepthFar = null; // this.cameraAutoUpdate = true; this.enabled = false; this.isPresenting = false; this.getController = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getTargetRaySpace(); }; this.getControllerGrip = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getGripSpace(); }; this.getHand = function (index) { let controller = controllers[index]; if (controller === undefined) { controller = new WebXRController(); controllers[index] = controller; } return controller.getHandSpace(); }; // function onSessionEvent(event) { const controllerIndex = controllerInputSources.indexOf(event.inputSource); if (controllerIndex === -1) { return; } const controller = controllers[controllerIndex]; if (controller !== undefined) { controller.update(event.inputSource, event.frame, customReferenceSpace || referenceSpace); controller.dispatchEvent({ type: event.type, data: event.inputSource }); } } function onSessionEnd() { session.removeEventListener('select', onSessionEvent); session.removeEventListener('selectstart', onSessionEvent); session.removeEventListener('selectend', onSessionEvent); session.removeEventListener('squeeze', onSessionEvent); session.removeEventListener('squeezestart', onSessionEvent); session.removeEventListener('squeezeend', onSessionEvent); session.removeEventListener('end', onSessionEnd); session.removeEventListener('inputsourceschange', onInputSourcesChange); for (let i = 0; i < controllers.length; i++) { const inputSource = controllerInputSources[i]; if (inputSource === null) continue; controllerInputSources[i] = null; controllers[i].disconnect(inputSource); } _currentDepthNear = null; _currentDepthFar = null; // restore framebuffer/rendering state renderer.setRenderTarget(initialRenderTarget); glBaseLayer = null; glProjLayer = null; glBinding = null; session = null; newRenderTarget = null; // animation.stop(); scope.isPresenting = false; scope.dispatchEvent({ type: 'sessionend' }); } this.setFramebufferScaleFactor = function (value) { framebufferScaleFactor = value; if (scope.isPresenting === true) { console.warn('THREE.WebXRManager: Cannot change framebuffer scale while presenting.'); } }; this.setReferenceSpaceType = function (value) { referenceSpaceType = value; if (scope.isPresenting === true) { console.warn('THREE.WebXRManager: Cannot change reference space type while presenting.'); } }; this.getReferenceSpace = function () { return customReferenceSpace || referenceSpace; }; this.setReferenceSpace = function (space) { customReferenceSpace = space; }; this.getBaseLayer = function () { return glProjLayer !== null ? glProjLayer : glBaseLayer; }; this.getBinding = function () { return glBinding; }; this.getFrame = function () { return xrFrame; }; this.getSession = function () { return session; }; this.setSession = /*#__PURE__*/function () { var _ref = (0,_home_sebastian_mindmap3d_node_modules_babel_runtime_helpers_esm_asyncToGenerator_js__WEBPACK_IMPORTED_MODULE_0__["default"])(function* (value) { session = value; if (session !== null) { initialRenderTarget = renderer.getRenderTarget(); session.addEventListener('select', onSessionEvent); session.addEventListener('selectstart', onSessionEvent); session.addEventListener('selectend', onSessionEvent); session.addEventListener('squeeze', onSessionEvent); session.addEventListener('squeezestart', onSessionEvent); session.addEventListener('squeezeend', onSessionEvent); session.addEventListener('end', onSessionEnd); session.addEventListener('inputsourceschange', onInputSourcesChange); if (attributes.xrCompatible !== true) { yield gl.makeXRCompatible(); } if (session.renderState.layers === undefined || renderer.capabilities.isWebGL2 === false) { const layerInit = { antialias: session.renderState.layers === undefined ? attributes.antialias : true, alpha: true, depth: attributes.depth, stencil: attributes.stencil, framebufferScaleFactor: framebufferScaleFactor }; glBaseLayer = new XRWebGLLayer(session, gl, layerInit); session.updateRenderState({ baseLayer: glBaseLayer }); newRenderTarget = new WebGLRenderTarget(glBaseLayer.framebufferWidth, glBaseLayer.framebufferHeight, { format: RGBAFormat, type: UnsignedByteType, colorSpace: renderer.outputColorSpace, stencilBuffer: attributes.stencil }); } else { let depthFormat = null; let depthType = null; let glDepthFormat = null; if (attributes.depth) { glDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24; depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; depthType = attributes.stencil ? UnsignedInt248Type : UnsignedIntType; } const projectionlayerInit = { colorFormat: gl.RGBA8, depthFormat: glDepthFormat, scaleFactor: framebufferScaleFactor }; glBinding = new XRWebGLBinding(session, gl); glProjLayer = glBinding.createProjectionLayer(projectionlayerInit); session.updateRenderState({ layers: [glProjLayer] }); newRenderTarget = new WebGLRenderTarget(glProjLayer.textureWidth, glProjLayer.textureHeight, { format: RGBAFormat, type: UnsignedByteType, depthTexture: new DepthTexture(glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat), stencilBuffer: attributes.stencil, colorSpace: renderer.outputColorSpace, samples: attributes.antialias ? 4 : 0 }); const renderTargetProperties = renderer.properties.get(newRenderTarget); renderTargetProperties.__ignoreDepthValues = glProjLayer.ignoreDepthValues; } newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 this.setFoveation(foveation); customReferenceSpace = null; referenceSpace = yield session.requestReferenceSpace(referenceSpaceType); animation.setContext(session); animation.start(); scope.isPresenting = true; scope.dispatchEvent({ type: 'sessionstart' }); } }); return function (_x2) { return _ref.apply(this, arguments); }; }(); this.getEnvironmentBlendMode = function () { if (session !== null) { return session.environmentBlendMode; } }; function onInputSourcesChange(event) { // Notify disconnected for (let i = 0; i < event.removed.length; i++) { const inputSource = event.removed[i]; const index = controllerInputSources.indexOf(inputSource); if (index >= 0) { controllerInputSources[index] = null; controllers[index].disconnect(inputSource); } } // Notify connected for (let i = 0; i < event.added.length; i++) { const inputSource = event.added[i]; let controllerIndex = controllerInputSources.indexOf(inputSource); if (controllerIndex === -1) { // Assign input source a controller that currently has no input source for (let i = 0; i < controllers.length; i++) { if (i >= controllerInputSources.length) { controllerInputSources.push(inputSource); controllerIndex = i; break; } else if (controllerInputSources[i] === null) { controllerInputSources[i] = inputSource; controllerIndex = i; break; } } // If all controllers do currently receive input we ignore new ones if (controllerIndex === -1) break; } const controller = controllers[controllerIndex]; if (controller) { controller.connect(inputSource); } } } // const cameraLPos = new Vector3(); const cameraRPos = new Vector3(); /** * Assumes 2 cameras that are parallel and share an X-axis, and that * the cameras' projection and world matrices have already been set. * And that near and far planes are identical for both cameras. * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 */ function setProjectionFromUnion(camera, cameraL, cameraR) { cameraLPos.setFromMatrixPosition(cameraL.matrixWorld); cameraRPos.setFromMatrixPosition(cameraR.matrixWorld); const ipd = cameraLPos.distanceTo(cameraRPos); const projL = cameraL.projectionMatrix.elements; const projR = cameraR.projectionMatrix.elements; // VR systems will have identical far and near planes, and // most likely identical top and bottom frustum extents. // Use the left camera for these values. const near = projL[14] / (projL[10] - 1); const far = projL[14] / (projL[10] + 1); const topFov = (projL[9] + 1) / projL[5]; const bottomFov = (projL[9] - 1) / projL[5]; const leftFov = (projL[8] - 1) / projL[0]; const rightFov = (projR[8] + 1) / projR[0]; const left = near * leftFov; const right = near * rightFov; // Calculate the new camera's position offset from the // left camera. xOffset should be roughly half `ipd`. const zOffset = ipd / (-leftFov + rightFov); const xOffset = zOffset * -leftFov; // TODO: Better way to apply this offset? cameraL.matrixWorld.decompose(camera.position, camera.quaternion, camera.scale); camera.translateX(xOffset); camera.translateZ(zOffset); camera.matrixWorld.compose(camera.position, camera.quaternion, camera.scale); camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); // Find the union of the frustum values of the cameras and scale // the values so that the near plane's position does not change in world space, // although must now be relative to the new union camera. const near2 = near + zOffset; const far2 = far + zOffset; const left2 = left - xOffset; const right2 = right + (ipd - xOffset); const top2 = topFov * far / far2 * near2; const bottom2 = bottomFov * far / far2 * near2; camera.projectionMatrix.makePerspective(left2, right2, top2, bottom2, near2, far2); camera.projectionMatrixInverse.copy(camera.projectionMatrix).invert(); } function updateCamera(camera, parent) { if (parent === null) { camera.matrixWorld.copy(camera.matrix); } else { camera.matrixWorld.multiplyMatrices(parent.matrixWorld, camera.matrix); } camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); } this.updateCamera = function (camera) { if (session === null) return; cameraXR.near = cameraR.near = cameraL.near = camera.near; cameraXR.far = cameraR.far = cameraL.far = camera.far; if (_currentDepthNear !== cameraXR.near || _currentDepthFar !== cameraXR.far) { // Note that the new renderState won't apply until the next frame. See #18320 session.updateRenderState({ depthNear: cameraXR.near, depthFar: cameraXR.far }); _currentDepthNear = cameraXR.near; _currentDepthFar = cameraXR.far; } const parent = camera.parent; const cameras = cameraXR.cameras; updateCamera(cameraXR, parent); for (let i = 0; i < cameras.length; i++) { updateCamera(cameras[i], parent); } // update projection matrix for proper view frustum culling if (cameras.length === 2) { setProjectionFromUnion(cameraXR, cameraL, cameraR); } else { // assume single camera setup (AR) cameraXR.projectionMatrix.copy(cameraL.projectionMatrix); } // update user camera and its children updateUserCamera(camera, cameraXR, parent); }; function updateUserCamera(camera, cameraXR, parent) { if (parent === null) { camera.matrix.copy(cameraXR.matrixWorld); } else { camera.matrix.copy(parent.matrixWorld); camera.matrix.invert(); camera.matrix.multiply(cameraXR.matrixWorld); } camera.matrix.decompose(camera.position, camera.quaternion, camera.scale); camera.updateMatrixWorld(true); const children = camera.children; for (let i = 0, l = children.length; i < l; i++) { children[i].updateMatrixWorld(true); } camera.projectionMatrix.copy(cameraXR.projectionMatrix); camera.projectionMatrixInverse.copy(cameraXR.projectionMatrixInverse); if (camera.isPerspectiveCamera) { camera.fov = RAD2DEG * 2 * Math.atan(1 / camera.projectionMatrix.elements[5]); camera.zoom = 1; } } this.getCamera = function () { return cameraXR; }; this.getFoveation = function () { if (glProjLayer === null && glBaseLayer === null) { return undefined; } return foveation; }; this.setFoveation = function (value) { // 0 = no foveation = full resolution // 1 = maximum foveation = the edges render at lower resolution foveation = value; if (glProjLayer !== null) { glProjLayer.fixedFoveation = value; } if (glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined) { glBaseLayer.fixedFoveation = value; } }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time, frame) { pose = frame.getViewerPose(customReferenceSpace || referenceSpace); xrFrame = frame; if (pose !== null) { const views = pose.views; if (glBaseLayer !== null) { renderer.setRenderTargetFramebuffer(newRenderTarget, glBaseLayer.framebuffer); renderer.setRenderTarget(newRenderTarget); } let cameraXRNeedsUpdate = false; // check if it's necessary to rebuild cameraXR's camera list if (views.length !== cameraXR.cameras.length) { cameraXR.cameras.length = 0; cameraXRNeedsUpdate = true; } for (let i = 0; i < views.length; i++) { const view = views[i]; let viewport = null; if (glBaseLayer !== null) { viewport = glBaseLayer.getViewport(view); } else { const glSubImage = glBinding.getViewSubImage(glProjLayer, view); viewport = glSubImage.viewport; // For side-by-side projection, we only produce a single texture for both eyes. if (i === 0) { renderer.setRenderTargetTextures(newRenderTarget, glSubImage.colorTexture, glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture); renderer.setRenderTarget(newRenderTarget); } } let camera = cameras[i]; if (camera === undefined) { camera = new PerspectiveCamera(); camera.layers.enable(i); camera.viewport = new Vector4(); cameras[i] = camera; } camera.matrix.fromArray(view.transform.matrix); camera.matrix.decompose(camera.position, camera.quaternion, camera.scale); camera.projectionMatrix.fromArray(view.projectionMatrix); camera.projectionMatrixInverse.copy(camera.projectionMatrix).invert(); camera.viewport.set(viewport.x, viewport.y, viewport.width, viewport.height); if (i === 0) { cameraXR.matrix.copy(camera.matrix); cameraXR.matrix.decompose(cameraXR.position, cameraXR.quaternion, cameraXR.scale); } if (cameraXRNeedsUpdate === true) { cameraXR.cameras.push(camera); } } } // for (let i = 0; i < controllers.length; i++) { const inputSource = controllerInputSources[i]; const controller = controllers[i]; if (inputSource !== null && controller !== undefined) { controller.update(inputSource, frame, customReferenceSpace || referenceSpace); } } if (onAnimationFrameCallback) onAnimationFrameCallback(time, frame); if (frame.detectedPlanes) { scope.dispatchEvent({ type: 'planesdetected', data: frame }); } xrFrame = null; } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; }; this.dispose = function () {}; } } function WebGLMaterials(renderer, properties) { function refreshTransformUniform(map, uniform) { if (map.matrixAutoUpdate === true) { map.updateMatrix(); } uniform.value.copy(map.matrix); } function refreshFogUniforms(uniforms, fog) { fog.color.getRGB(uniforms.fogColor.value, getUnlitUniformColorSpace(renderer)); if (fog.isFog) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if (fog.isFogExp2) { uniforms.fogDensity.value = fog.density; } } function refreshMaterialUniforms(uniforms, material, pixelRatio, height, transmissionRenderTarget) { if (material.isMeshBasicMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshLambertMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshToonMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsToon(uniforms, material); } else if (material.isMeshPhongMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsPhong(uniforms, material); } else if (material.isMeshStandardMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsStandard(uniforms, material); if (material.isMeshPhysicalMaterial) { refreshUniformsPhysical(uniforms, material, transmissionRenderTarget); } } else if (material.isMeshMatcapMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsMatcap(uniforms, material); } else if (material.isMeshDepthMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isMeshDistanceMaterial) { refreshUniformsCommon(uniforms, material); refreshUniformsDistance(uniforms, material); } else if (material.isMeshNormalMaterial) { refreshUniformsCommon(uniforms, material); } else if (material.isLineBasicMaterial) { refreshUniformsLine(uniforms, material); if (material.isLineDashedMaterial) { refreshUniformsDash(uniforms, material); } } else if (material.isPointsMaterial) { refreshUniformsPoints(uniforms, material, pixelRatio, height); } else if (material.isSpriteMaterial) { refreshUniformsSprites(uniforms, material); } else if (material.isShadowMaterial) { uniforms.color.value.copy(material.color); uniforms.opacity.value = material.opacity; } else if (material.isShaderMaterial) { material.uniformsNeedUpdate = false; // #15581 } } function refreshUniformsCommon(uniforms, material) { uniforms.opacity.value = material.opacity; if (material.color) { uniforms.diffuse.value.copy(material.color); } if (material.emissive) { uniforms.emissive.value.copy(material.emissive).multiplyScalar(material.emissiveIntensity); } if (material.map) { uniforms.map.value = material.map; refreshTransformUniform(material.map, uniforms.mapTransform); } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform(material.alphaMap, uniforms.alphaMapTransform); } if (material.bumpMap) { uniforms.bumpMap.value = material.bumpMap; refreshTransformUniform(material.bumpMap, uniforms.bumpMapTransform); uniforms.bumpScale.value = material.bumpScale; if (material.side === BackSide) { uniforms.bumpScale.value *= -1; } } if (material.normalMap) { uniforms.normalMap.value = material.normalMap; refreshTransformUniform(material.normalMap, uniforms.normalMapTransform); uniforms.normalScale.value.copy(material.normalScale); if (material.side === BackSide) { uniforms.normalScale.value.negate(); } } if (material.displacementMap) { uniforms.displacementMap.value = material.displacementMap; refreshTransformUniform(material.displacementMap, uniforms.displacementMapTransform); uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } if (material.emissiveMap) { uniforms.emissiveMap.value = material.emissiveMap; refreshTransformUniform(material.emissiveMap, uniforms.emissiveMapTransform); } if (material.specularMap) { uniforms.specularMap.value = material.specularMap; refreshTransformUniform(material.specularMap, uniforms.specularMapTransform); } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } const envMap = properties.get(material).envMap; if (envMap) { uniforms.envMap.value = envMap; uniforms.flipEnvMap.value = envMap.isCubeTexture && envMap.isRenderTargetTexture === false ? -1 : 1; uniforms.reflectivity.value = material.reflectivity; uniforms.ior.value = material.ior; uniforms.refractionRatio.value = material.refractionRatio; } if (material.lightMap) { uniforms.lightMap.value = material.lightMap; // artist-friendly light intensity scaling factor const scaleFactor = renderer.useLegacyLights === true ? Math.PI : 1; uniforms.lightMapIntensity.value = material.lightMapIntensity * scaleFactor; refreshTransformUniform(material.lightMap, uniforms.lightMapTransform); } if (material.aoMap) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; refreshTransformUniform(material.aoMap, uniforms.aoMapTransform); } } function refreshUniformsLine(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; if (material.map) { uniforms.map.value = material.map; refreshTransformUniform(material.map, uniforms.mapTransform); } } function refreshUniformsDash(uniforms, material) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints(uniforms, material, pixelRatio, height) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * pixelRatio; uniforms.scale.value = height * 0.5; if (material.map) { uniforms.map.value = material.map; refreshTransformUniform(material.map, uniforms.uvTransform); } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform(material.alphaMap, uniforms.alphaMapTransform); } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } } function refreshUniformsSprites(uniforms, material) { uniforms.diffuse.value.copy(material.color); uniforms.opacity.value = material.opacity; uniforms.rotation.value = material.rotation; if (material.map) { uniforms.map.value = material.map; refreshTransformUniform(material.map, uniforms.mapTransform); } if (material.alphaMap) { uniforms.alphaMap.value = material.alphaMap; refreshTransformUniform(material.alphaMap, uniforms.alphaMapTransform); } if (material.alphaTest > 0) { uniforms.alphaTest.value = material.alphaTest; } } function refreshUniformsPhong(uniforms, material) { uniforms.specular.value.copy(material.specular); uniforms.shininess.value = Math.max(material.shininess, 1e-4); // to prevent pow( 0.0, 0.0 ) } function refreshUniformsToon(uniforms, material) { if (material.gradientMap) { uniforms.gradientMap.value = material.gradientMap; } } function refreshUniformsStandard(uniforms, material) { uniforms.metalness.value = material.metalness; if (material.metalnessMap) { uniforms.metalnessMap.value = material.metalnessMap; refreshTransformUniform(material.metalnessMap, uniforms.metalnessMapTransform); } uniforms.roughness.value = material.roughness; if (material.roughnessMap) { uniforms.roughnessMap.value = material.roughnessMap; refreshTransformUniform(material.roughnessMap, uniforms.roughnessMapTransform); } const envMap = properties.get(material).envMap; if (envMap) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical(uniforms, material, transmissionRenderTarget) { uniforms.ior.value = material.ior; // also part of uniforms common if (material.sheen > 0) { uniforms.sheenColor.value.copy(material.sheenColor).multiplyScalar(material.sheen); uniforms.sheenRoughness.value = material.sheenRoughness; if (material.sheenColorMap) { uniforms.sheenColorMap.value = material.sheenColorMap; refreshTransformUniform(material.sheenColorMap, uniforms.sheenColorMapTransform); } if (material.sheenRoughnessMap) { uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; refreshTransformUniform(material.sheenRoughnessMap, uniforms.sheenRoughnessMapTransform); } } if (material.clearcoat > 0) { uniforms.clearcoat.value = material.clearcoat; uniforms.clearcoatRoughness.value = material.clearcoatRoughness; if (material.clearcoatMap) { uniforms.clearcoatMap.value = material.clearcoatMap; refreshTransformUniform(material.clearcoatMap, uniforms.clearcoatMapTransform); } if (material.clearcoatRoughnessMap) { uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; refreshTransformUniform(material.clearcoatRoughnessMap, uniforms.clearcoatRoughnessMapTransform); } if (material.clearcoatNormalMap) { uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; refreshTransformUniform(material.clearcoatNormalMap, uniforms.clearcoatNormalMapTransform); uniforms.clearcoatNormalScale.value.copy(material.clearcoatNormalScale); if (material.side === BackSide) { uniforms.clearcoatNormalScale.value.negate(); } } } if (material.iridescence > 0) { uniforms.iridescence.value = material.iridescence; uniforms.iridescenceIOR.value = material.iridescenceIOR; uniforms.iridescenceThicknessMinimum.value = material.iridescenceThicknessRange[0]; uniforms.iridescenceThicknessMaximum.value = material.iridescenceThicknessRange[1]; if (material.iridescenceMap) { uniforms.iridescenceMap.value = material.iridescenceMap; refreshTransformUniform(material.iridescenceMap, uniforms.iridescenceMapTransform); } if (material.iridescenceThicknessMap) { uniforms.iridescenceThicknessMap.value = material.iridescenceThicknessMap; refreshTransformUniform(material.iridescenceThicknessMap, uniforms.iridescenceThicknessMapTransform); } } if (material.transmission > 0) { uniforms.transmission.value = material.transmission; uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; uniforms.transmissionSamplerSize.value.set(transmissionRenderTarget.width, transmissionRenderTarget.height); if (material.transmissionMap) { uniforms.transmissionMap.value = material.transmissionMap; refreshTransformUniform(material.transmissionMap, uniforms.transmissionMapTransform); } uniforms.thickness.value = material.thickness; if (material.thicknessMap) { uniforms.thicknessMap.value = material.thicknessMap; refreshTransformUniform(material.thicknessMap, uniforms.thicknessMapTransform); } uniforms.attenuationDistance.value = material.attenuationDistance; uniforms.attenuationColor.value.copy(material.attenuationColor); } if (material.anisotropy > 0) { uniforms.anisotropyVector.value.set(material.anisotropy * Math.cos(material.anisotropyRotation), material.anisotropy * Math.sin(material.anisotropyRotation)); if (material.anisotropyMap) { uniforms.anisotropyMap.value = material.anisotropyMap; refreshTransformUniform(material.anisotropyMap, uniforms.anisotropyMapTransform); } } uniforms.specularIntensity.value = material.specularIntensity; uniforms.specularColor.value.copy(material.specularColor); if (material.specularColorMap) { uniforms.specularColorMap.value = material.specularColorMap; refreshTransformUniform(material.specularColorMap, uniforms.specularColorMapTransform); } if (material.specularIntensityMap) { uniforms.specularIntensityMap.value = material.specularIntensityMap; refreshTransformUniform(material.specularIntensityMap, uniforms.specularIntensityMapTransform); } } function refreshUniformsMatcap(uniforms, material) { if (material.matcap) { uniforms.matcap.value = material.matcap; } } function refreshUniformsDistance(uniforms, material) { const light = properties.get(material).light; uniforms.referencePosition.value.setFromMatrixPosition(light.matrixWorld); uniforms.nearDistance.value = light.shadow.camera.near; uniforms.farDistance.value = light.shadow.camera.far; } return { refreshFogUniforms: refreshFogUniforms, refreshMaterialUniforms: refreshMaterialUniforms }; } function WebGLUniformsGroups(gl, info, capabilities, state) { let buffers = {}; let updateList = {}; let allocatedBindingPoints = []; const maxBindingPoints = capabilities.isWebGL2 ? gl.getParameter(gl.MAX_UNIFORM_BUFFER_BINDINGS) : 0; // binding points are global whereas block indices are per shader program function bind(uniformsGroup, program) { const webglProgram = program.program; state.uniformBlockBinding(uniformsGroup, webglProgram); } function update(uniformsGroup, program) { let buffer = buffers[uniformsGroup.id]; if (buffer === undefined) { prepareUniformsGroup(uniformsGroup); buffer = createBuffer(uniformsGroup); buffers[uniformsGroup.id] = buffer; uniformsGroup.addEventListener('dispose', onUniformsGroupsDispose); } // ensure to update the binding points/block indices mapping for this program const webglProgram = program.program; state.updateUBOMapping(uniformsGroup, webglProgram); // update UBO once per frame const frame = info.render.frame; if (updateList[uniformsGroup.id] !== frame) { updateBufferData(uniformsGroup); updateList[uniformsGroup.id] = frame; } } function createBuffer(uniformsGroup) { // the setup of an UBO is independent of a particular shader program but global const bindingPointIndex = allocateBindingPointIndex(); uniformsGroup.__bindingPointIndex = bindingPointIndex; const buffer = gl.createBuffer(); const size = uniformsGroup.__size; const usage = uniformsGroup.usage; gl.bindBuffer(gl.UNIFORM_BUFFER, buffer); gl.bufferData(gl.UNIFORM_BUFFER, size, usage); gl.bindBuffer(gl.UNIFORM_BUFFER, null); gl.bindBufferBase(gl.UNIFORM_BUFFER, bindingPointIndex, buffer); return buffer; } function allocateBindingPointIndex() { for (let i = 0; i < maxBindingPoints; i++) { if (allocatedBindingPoints.indexOf(i) === -1) { allocatedBindingPoints.push(i); return i; } } console.error('THREE.WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached.'); return 0; } function updateBufferData(uniformsGroup) { const buffer = buffers[uniformsGroup.id]; const uniforms = uniformsGroup.uniforms; const cache = uniformsGroup.__cache; gl.bindBuffer(gl.UNIFORM_BUFFER, buffer); for (let i = 0, il = uniforms.length; i < il; i++) { const uniform = uniforms[i]; // partly update the buffer if necessary if (hasUniformChanged(uniform, i, cache) === true) { const offset = uniform.__offset; const values = Array.isArray(uniform.value) ? uniform.value : [uniform.value]; let arrayOffset = 0; for (let i = 0; i < values.length; i++) { const value = values[i]; const info = getUniformSize(value); if (typeof value === 'number') { uniform.__data[0] = value; gl.bufferSubData(gl.UNIFORM_BUFFER, offset + arrayOffset, uniform.__data); } else if (value.isMatrix3) { // manually converting 3x3 to 3x4 uniform.__data[0] = value.elements[0]; uniform.__data[1] = value.elements[1]; uniform.__data[2] = value.elements[2]; uniform.__data[3] = value.elements[0]; uniform.__data[4] = value.elements[3]; uniform.__data[5] = value.elements[4]; uniform.__data[6] = value.elements[5]; uniform.__data[7] = value.elements[0]; uniform.__data[8] = value.elements[6]; uniform.__data[9] = value.elements[7]; uniform.__data[10] = value.elements[8]; uniform.__data[11] = value.elements[0]; } else { value.toArray(uniform.__data, arrayOffset); arrayOffset += info.storage / Float32Array.BYTES_PER_ELEMENT; } } gl.bufferSubData(gl.UNIFORM_BUFFER, offset, uniform.__data); } } gl.bindBuffer(gl.UNIFORM_BUFFER, null); } function hasUniformChanged(uniform, index, cache) { const value = uniform.value; if (cache[index] === undefined) { // cache entry does not exist so far if (typeof value === 'number') { cache[index] = value; } else { const values = Array.isArray(value) ? value : [value]; const tempValues = []; for (let i = 0; i < values.length; i++) { tempValues.push(values[i].clone()); } cache[index] = tempValues; } return true; } else { // compare current value with cached entry if (typeof value === 'number') { if (cache[index] !== value) { cache[index] = value; return true; } } else { const cachedObjects = Array.isArray(cache[index]) ? cache[index] : [cache[index]]; const values = Array.isArray(value) ? value : [value]; for (let i = 0; i < cachedObjects.length; i++) { const cachedObject = cachedObjects[i]; if (cachedObject.equals(values[i]) === false) { cachedObject.copy(values[i]); return true; } } } } return false; } function prepareUniformsGroup(uniformsGroup) { // determine total buffer size according to the STD140 layout // Hint: STD140 is the only supported layout in WebGL 2 const uniforms = uniformsGroup.uniforms; let offset = 0; // global buffer offset in bytes const chunkSize = 16; // size of a chunk in bytes let chunkOffset = 0; // offset within a single chunk in bytes for (let i = 0, l = uniforms.length; i < l; i++) { const uniform = uniforms[i]; const infos = { boundary: 0, // bytes storage: 0 // bytes }; const values = Array.isArray(uniform.value) ? uniform.value : [uniform.value]; for (let j = 0, jl = values.length; j < jl; j++) { const value = values[j]; const info = getUniformSize(value); infos.boundary += info.boundary; infos.storage += info.storage; } // the following two properties will be used for partial buffer updates uniform.__data = new Float32Array(infos.storage / Float32Array.BYTES_PER_ELEMENT); uniform.__offset = offset; // if (i > 0) { chunkOffset = offset % chunkSize; const remainingSizeInChunk = chunkSize - chunkOffset; // check for chunk overflow if (chunkOffset !== 0 && remainingSizeInChunk - infos.boundary < 0) { // add padding and adjust offset offset += chunkSize - chunkOffset; uniform.__offset = offset; } } offset += infos.storage; } // ensure correct final padding chunkOffset = offset % chunkSize; if (chunkOffset > 0) offset += chunkSize - chunkOffset; // uniformsGroup.__size = offset; uniformsGroup.__cache = {}; return this; } function getUniformSize(value) { const info = { boundary: 0, // bytes storage: 0 // bytes }; // determine sizes according to STD140 if (typeof value === 'number') { // float/int info.boundary = 4; info.storage = 4; } else if (value.isVector2) { // vec2 info.boundary = 8; info.storage = 8; } else if (value.isVector3 || value.isColor) { // vec3 info.boundary = 16; info.storage = 12; // evil: vec3 must start on a 16-byte boundary but it only consumes 12 bytes } else if (value.isVector4) { // vec4 info.boundary = 16; info.storage = 16; } else if (value.isMatrix3) { // mat3 (in STD140 a 3x3 matrix is represented as 3x4) info.boundary = 48; info.storage = 48; } else if (value.isMatrix4) { // mat4 info.boundary = 64; info.storage = 64; } else if (value.isTexture) { console.warn('THREE.WebGLRenderer: Texture samplers can not be part of an uniforms group.'); } else { console.warn('THREE.WebGLRenderer: Unsupported uniform value type.', value); } return info; } function onUniformsGroupsDispose(event) { const uniformsGroup = event.target; uniformsGroup.removeEventListener('dispose', onUniformsGroupsDispose); const index = allocatedBindingPoints.indexOf(uniformsGroup.__bindingPointIndex); allocatedBindingPoints.splice(index, 1); gl.deleteBuffer(buffers[uniformsGroup.id]); delete buffers[uniformsGroup.id]; delete updateList[uniformsGroup.id]; } function dispose() { for (const id in buffers) { gl.deleteBuffer(buffers[id]); } allocatedBindingPoints = []; buffers = {}; updateList = {}; } return { bind: bind, update: update, dispose: dispose }; } function createCanvasElement() { const canvas = createElementNS('canvas'); canvas.style.display = 'block'; return canvas; } class WebGLRenderer { constructor(parameters = {}) { const { canvas = createCanvasElement(), context = null, depth = true, stencil = true, alpha = false, antialias = false, premultipliedAlpha = true, preserveDrawingBuffer = false, powerPreference = 'default', failIfMajorPerformanceCaveat = false } = parameters; this.isWebGLRenderer = true; let _alpha; if (context !== null) { _alpha = context.getContextAttributes().alpha; } else { _alpha = alpha; } const uintClearColor = new Uint32Array(4); const intClearColor = new Int32Array(4); let currentRenderList = null; let currentRenderState = null; // render() can be called from within a callback triggered by another render. // We track this so that the nested render call gets its list and state isolated from the parent render call. const renderListStack = []; const renderStateStack = []; // public properties this.domElement = canvas; // Debug configuration container this.debug = { /** * Enables error checking and reporting when shader programs are being compiled * @type {boolean} */ checkShaderErrors: true, /** * Callback for custom error reporting. * @type {?Function} */ onShaderError: null }; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.outputColorSpace = SRGBColorSpace; // physical lights this.useLegacyLights = true; // tone mapping this.toneMapping = NoToneMapping; this.toneMappingExposure = 1.0; // internal properties const _this = this; let _isContextLost = false; // internal state cache let _currentActiveCubeFace = 0; let _currentActiveMipmapLevel = 0; let _currentRenderTarget = null; let _currentMaterialId = -1; let _currentCamera = null; const _currentViewport = new Vector4(); const _currentScissor = new Vector4(); let _currentScissorTest = null; const _currentClearColor = new Color(0x000000); let _currentClearAlpha = 0; // let _width = canvas.width; let _height = canvas.height; let _pixelRatio = 1; let _opaqueSort = null; let _transparentSort = null; const _viewport = new Vector4(0, 0, _width, _height); const _scissor = new Vector4(0, 0, _width, _height); let _scissorTest = false; // frustum const _frustum = new Frustum(); // clipping let _clippingEnabled = false; let _localClippingEnabled = false; // transmission let _transmissionRenderTarget = null; // camera matrices cache const _projScreenMatrix = new Matrix4(); const _vector2 = new Vector2(); const _vector3 = new Vector3(); const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } // initialize let _gl = context; function getContext(contextNames, contextAttributes) { for (let i = 0; i < contextNames.length; i++) { const contextName = contextNames[i]; const context = canvas.getContext(contextName, contextAttributes); if (context !== null) return context; } return null; } try { const contextAttributes = { alpha: true, depth, stencil, antialias, premultipliedAlpha, preserveDrawingBuffer, powerPreference, failIfMajorPerformanceCaveat }; // OffscreenCanvas does not have setAttribute, see #22811 if ('setAttribute' in canvas) canvas.setAttribute('data-engine', `three.js r${REVISION}`); // event listeners must be registered before WebGL context is created, see #12753 canvas.addEventListener('webglcontextlost', onContextLost, false); canvas.addEventListener('webglcontextrestored', onContextRestore, false); canvas.addEventListener('webglcontextcreationerror', onContextCreationError, false); if (_gl === null) { const contextNames = ['webgl2', 'webgl', 'experimental-webgl']; if (_this.isWebGL1Renderer === true) { contextNames.shift(); } _gl = getContext(contextNames, contextAttributes); if (_gl === null) { if (getContext(contextNames)) { throw new Error('Error creating WebGL context with your selected attributes.'); } else { throw new Error('Error creating WebGL context.'); } } } if (typeof WebGLRenderingContext !== 'undefined' && _gl instanceof WebGLRenderingContext) { // @deprecated, r153 console.warn('THREE.WebGLRenderer: WebGL 1 support was deprecated in r153 and will be removed in r163.'); } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if (_gl.getShaderPrecisionFormat === undefined) { _gl.getShaderPrecisionFormat = function () { return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; }; } } catch (error) { console.error('THREE.WebGLRenderer: ' + error.message); throw error; } let extensions, capabilities, state, info; let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; let background, morphtargets, bufferRenderer, indexedBufferRenderer; let utils, bindingStates, uniformsGroups; function initGLContext() { extensions = new WebGLExtensions(_gl); capabilities = new WebGLCapabilities(_gl, extensions, parameters); extensions.init(capabilities); utils = new WebGLUtils(_gl, extensions, capabilities); state = new WebGLState(_gl, extensions, capabilities); info = new WebGLInfo(_gl); properties = new WebGLProperties(); textures = new WebGLTextures(_gl, extensions, state, properties, capabilities, utils, info); cubemaps = new WebGLCubeMaps(_this); cubeuvmaps = new WebGLCubeUVMaps(_this); attributes = new WebGLAttributes(_gl, capabilities); bindingStates = new WebGLBindingStates(_gl, extensions, attributes, capabilities); geometries = new WebGLGeometries(_gl, attributes, info, bindingStates); objects = new WebGLObjects(_gl, geometries, attributes, info); morphtargets = new WebGLMorphtargets(_gl, capabilities, textures); clipping = new WebGLClipping(properties); programCache = new WebGLPrograms(_this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping); materials = new WebGLMaterials(_this, properties); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates(extensions, capabilities); background = new WebGLBackground(_this, cubemaps, cubeuvmaps, state, objects, _alpha, premultipliedAlpha); shadowMap = new WebGLShadowMap(_this, objects, capabilities); uniformsGroups = new WebGLUniformsGroups(_gl, info, capabilities, state); bufferRenderer = new WebGLBufferRenderer(_gl, extensions, info, capabilities); indexedBufferRenderer = new WebGLIndexedBufferRenderer(_gl, extensions, info, capabilities); info.programs = programCache.programs; _this.capabilities = capabilities; _this.extensions = extensions; _this.properties = properties; _this.renderLists = renderLists; _this.shadowMap = shadowMap; _this.state = state; _this.info = info; } initGLContext(); // xr const xr = new WebXRManager(_this, _gl); this.xr = xr; // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { const extension = extensions.get('WEBGL_lose_context'); if (extension) extension.loseContext(); }; this.forceContextRestore = function () { const extension = extensions.get('WEBGL_lose_context'); if (extension) extension.restoreContext(); }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function (value) { if (value === undefined) return; _pixelRatio = value; this.setSize(_width, _height, false); }; this.getSize = function (target) { return target.set(_width, _height); }; this.setSize = function (width, height, updateStyle = true) { if (xr.isPresenting) { console.warn('THREE.WebGLRenderer: Can\'t change size while VR device is presenting.'); return; } _width = width; _height = height; canvas.width = Math.floor(width * _pixelRatio); canvas.height = Math.floor(height * _pixelRatio); if (updateStyle === true) { canvas.style.width = width + 'px'; canvas.style.height = height + 'px'; } this.setViewport(0, 0, width, height); }; this.getDrawingBufferSize = function (target) { return target.set(_width * _pixelRatio, _height * _pixelRatio).floor(); }; this.setDrawingBufferSize = function (width, height, pixelRatio) { _width = width; _height = height; _pixelRatio = pixelRatio; canvas.width = Math.floor(width * pixelRatio); canvas.height = Math.floor(height * pixelRatio); this.setViewport(0, 0, width, height); }; this.getCurrentViewport = function (target) { return target.copy(_currentViewport); }; this.getViewport = function (target) { return target.copy(_viewport); }; this.setViewport = function (x, y, width, height) { if (x.isVector4) { _viewport.set(x.x, x.y, x.z, x.w); } else { _viewport.set(x, y, width, height); } state.viewport(_currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor()); }; this.getScissor = function (target) { return target.copy(_scissor); }; this.setScissor = function (x, y, width, height) { if (x.isVector4) { _scissor.set(x.x, x.y, x.z, x.w); } else { _scissor.set(x, y, width, height); } state.scissor(_currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor()); }; this.getScissorTest = function () { return _scissorTest; }; this.setScissorTest = function (boolean) { state.setScissorTest(_scissorTest = boolean); }; this.setOpaqueSort = function (method) { _opaqueSort = method; }; this.setTransparentSort = function (method) { _transparentSort = method; }; // Clearing this.getClearColor = function (target) { return target.copy(background.getClearColor()); }; this.setClearColor = function () { background.setClearColor.apply(background, arguments); }; this.getClearAlpha = function () { return background.getClearAlpha(); }; this.setClearAlpha = function () { background.setClearAlpha.apply(background, arguments); }; this.clear = function (color = true, depth = true, stencil = true) { let bits = 0; if (color) { // check if we're trying to clear an integer target let isIntegerFormat = false; if (_currentRenderTarget !== null) { const targetFormat = _currentRenderTarget.texture.format; isIntegerFormat = targetFormat === RGBAIntegerFormat || targetFormat === RGIntegerFormat || targetFormat === RedIntegerFormat; } // use the appropriate clear functions to clear the target if it's a signed // or unsigned integer target if (isIntegerFormat) { const targetType = _currentRenderTarget.texture.type; const isUnsignedType = targetType === UnsignedByteType || targetType === UnsignedIntType || targetType === UnsignedShortType || targetType === UnsignedInt248Type || targetType === UnsignedShort4444Type || targetType === UnsignedShort5551Type; const clearColor = background.getClearColor(); const a = background.getClearAlpha(); const r = clearColor.r; const g = clearColor.g; const b = clearColor.b; if (isUnsignedType) { uintClearColor[0] = r; uintClearColor[1] = g; uintClearColor[2] = b; uintClearColor[3] = a; _gl.clearBufferuiv(_gl.COLOR, 0, uintClearColor); } else { intClearColor[0] = r; intClearColor[1] = g; intClearColor[2] = b; intClearColor[3] = a; _gl.clearBufferiv(_gl.COLOR, 0, intClearColor); } } else { bits |= _gl.COLOR_BUFFER_BIT; } } if (depth) bits |= _gl.DEPTH_BUFFER_BIT; if (stencil) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear(bits); }; this.clearColor = function () { this.clear(true, false, false); }; this.clearDepth = function () { this.clear(false, true, false); }; this.clearStencil = function () { this.clear(false, false, true); }; // this.dispose = function () { canvas.removeEventListener('webglcontextlost', onContextLost, false); canvas.removeEventListener('webglcontextrestored', onContextRestore, false); canvas.removeEventListener('webglcontextcreationerror', onContextCreationError, false); renderLists.dispose(); renderStates.dispose(); properties.dispose(); cubemaps.dispose(); cubeuvmaps.dispose(); objects.dispose(); bindingStates.dispose(); uniformsGroups.dispose(); programCache.dispose(); xr.dispose(); xr.removeEventListener('sessionstart', onXRSessionStart); xr.removeEventListener('sessionend', onXRSessionEnd); if (_transmissionRenderTarget) { _transmissionRenderTarget.dispose(); _transmissionRenderTarget = null; } animation.stop(); }; // Events function onContextLost(event) { event.preventDefault(); console.log('THREE.WebGLRenderer: Context Lost.'); _isContextLost = true; } function onContextRestore( /* event */ ) { console.log('THREE.WebGLRenderer: Context Restored.'); _isContextLost = false; const infoAutoReset = info.autoReset; const shadowMapEnabled = shadowMap.enabled; const shadowMapAutoUpdate = shadowMap.autoUpdate; const shadowMapNeedsUpdate = shadowMap.needsUpdate; const shadowMapType = shadowMap.type; initGLContext(); info.autoReset = infoAutoReset; shadowMap.enabled = shadowMapEnabled; shadowMap.autoUpdate = shadowMapAutoUpdate; shadowMap.needsUpdate = shadowMapNeedsUpdate; shadowMap.type = shadowMapType; } function onContextCreationError(event) { console.error('THREE.WebGLRenderer: A WebGL context could not be created. Reason: ', event.statusMessage); } function onMaterialDispose(event) { const material = event.target; material.removeEventListener('dispose', onMaterialDispose); deallocateMaterial(material); } // Buffer deallocation function deallocateMaterial(material) { releaseMaterialProgramReferences(material); properties.remove(material); } function releaseMaterialProgramReferences(material) { const programs = properties.get(material).programs; if (programs !== undefined) { programs.forEach(function (program) { programCache.releaseProgram(program); }); if (material.isShaderMaterial) { programCache.releaseShaderCache(material); } } } // Buffer rendering this.renderBufferDirect = function (camera, scene, geometry, material, object, group) { if (scene === null) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) const frontFaceCW = object.isMesh && object.matrixWorld.determinant() < 0; const program = setProgram(camera, scene, geometry, material, object); state.setMaterial(material, frontFaceCW); // let index = geometry.index; let rangeFactor = 1; if (material.wireframe === true) { index = geometries.getWireframeAttribute(geometry); rangeFactor = 2; } // const drawRange = geometry.drawRange; const position = geometry.attributes.position; let drawStart = drawRange.start * rangeFactor; let drawEnd = (drawRange.start + drawRange.count) * rangeFactor; if (group !== null) { drawStart = Math.max(drawStart, group.start * rangeFactor); drawEnd = Math.min(drawEnd, (group.start + group.count) * rangeFactor); } if (index !== null) { drawStart = Math.max(drawStart, 0); drawEnd = Math.min(drawEnd, index.count); } else if (position !== undefined && position !== null) { drawStart = Math.max(drawStart, 0); drawEnd = Math.min(drawEnd, position.count); } const drawCount = drawEnd - drawStart; if (drawCount < 0 || drawCount === Infinity) return; // bindingStates.setup(object, material, program, geometry, index); let attribute; let renderer = bufferRenderer; if (index !== null) { attribute = attributes.get(index); renderer = indexedBufferRenderer; renderer.setIndex(attribute); } // if (object.isMesh) { if (material.wireframe === true) { state.setLineWidth(material.wireframeLinewidth * getTargetPixelRatio()); renderer.setMode(_gl.LINES); } else { renderer.setMode(_gl.TRIANGLES); } } else if (object.isLine) { let lineWidth = material.linewidth; if (lineWidth === undefined) lineWidth = 1; // Not using Line*Material state.setLineWidth(lineWidth * getTargetPixelRatio()); if (object.isLineSegments) { renderer.setMode(_gl.LINES); } else if (object.isLineLoop) { renderer.setMode(_gl.LINE_LOOP); } else { renderer.setMode(_gl.LINE_STRIP); } } else if (object.isPoints) { renderer.setMode(_gl.POINTS); } else if (object.isSprite) { renderer.setMode(_gl.TRIANGLES); } if (object.isInstancedMesh) { renderer.renderInstances(drawStart, drawCount, object.count); } else if (geometry.isInstancedBufferGeometry) { const maxInstanceCount = geometry._maxInstanceCount !== undefined ? geometry._maxInstanceCount : Infinity; const instanceCount = Math.min(geometry.instanceCount, maxInstanceCount); renderer.renderInstances(drawStart, drawCount, instanceCount); } else { renderer.render(drawStart, drawCount); } }; // Compile this.compile = function (scene, camera) { function prepare(material, scene, object) { if (material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false) { material.side = BackSide; material.needsUpdate = true; getProgram(material, scene, object); material.side = FrontSide; material.needsUpdate = true; getProgram(material, scene, object); material.side = DoubleSide; } else { getProgram(material, scene, object); } } currentRenderState = renderStates.get(scene); currentRenderState.init(); renderStateStack.push(currentRenderState); scene.traverseVisible(function (object) { if (object.isLight && object.layers.test(camera.layers)) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } }); currentRenderState.setupLights(_this.useLegacyLights); scene.traverse(function (object) { const material = object.material; if (material) { if (Array.isArray(material)) { for (let i = 0; i < material.length; i++) { const material2 = material[i]; prepare(material2, scene, object); } } else { prepare(material, scene, object); } } }); renderStateStack.pop(); currentRenderState = null; }; // Animation Loop let onAnimationFrameCallback = null; function onAnimationFrame(time) { if (onAnimationFrameCallback) onAnimationFrameCallback(time); } function onXRSessionStart() { animation.stop(); } function onXRSessionEnd() { animation.start(); } const animation = new WebGLAnimation(); animation.setAnimationLoop(onAnimationFrame); if (typeof self !== 'undefined') animation.setContext(self); this.setAnimationLoop = function (callback) { onAnimationFrameCallback = callback; xr.setAnimationLoop(callback); callback === null ? animation.stop() : animation.start(); }; xr.addEventListener('sessionstart', onXRSessionStart); xr.addEventListener('sessionend', onXRSessionEnd); // Rendering this.render = function (scene, camera) { if (camera !== undefined && camera.isCamera !== true) { console.error('THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.'); return; } if (_isContextLost === true) return; // update scene graph if (scene.matrixWorldAutoUpdate === true) scene.updateMatrixWorld(); // update camera matrices and frustum if (camera.parent === null && camera.matrixWorldAutoUpdate === true) camera.updateMatrixWorld(); if (xr.enabled === true && xr.isPresenting === true) { if (xr.cameraAutoUpdate === true) xr.updateCamera(camera); camera = xr.getCamera(); // use XR camera for rendering } // if (scene.isScene === true) scene.onBeforeRender(_this, scene, camera, _currentRenderTarget); currentRenderState = renderStates.get(scene, renderStateStack.length); currentRenderState.init(); renderStateStack.push(currentRenderState); _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); _frustum.setFromProjectionMatrix(_projScreenMatrix); _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = clipping.init(this.clippingPlanes, _localClippingEnabled); currentRenderList = renderLists.get(scene, renderListStack.length); currentRenderList.init(); renderListStack.push(currentRenderList); projectObject(scene, camera, 0, _this.sortObjects); currentRenderList.finish(); if (_this.sortObjects === true) { currentRenderList.sort(_opaqueSort, _transparentSort); } // this.info.render.frame++; if (_clippingEnabled === true) clipping.beginShadows(); const shadowsArray = currentRenderState.state.shadowsArray; shadowMap.render(shadowsArray, scene, camera); if (_clippingEnabled === true) clipping.endShadows(); // if (this.info.autoReset === true) this.info.reset(); // background.render(currentRenderList, scene); // render scene currentRenderState.setupLights(_this.useLegacyLights); if (camera.isArrayCamera) { const cameras = camera.cameras; for (let i = 0, l = cameras.length; i < l; i++) { const camera2 = cameras[i]; renderScene(currentRenderList, scene, camera2, camera2.viewport); } } else { renderScene(currentRenderList, scene, camera); } // if (_currentRenderTarget !== null) { // resolve multisample renderbuffers to a single-sample texture if necessary textures.updateMultisampleRenderTarget(_currentRenderTarget); // Generate mipmap if we're using any kind of mipmap filtering textures.updateRenderTargetMipmap(_currentRenderTarget); } // if (scene.isScene === true) scene.onAfterRender(_this, scene, camera); // _gl.finish(); bindingStates.resetDefaultState(); _currentMaterialId = -1; _currentCamera = null; renderStateStack.pop(); if (renderStateStack.length > 0) { currentRenderState = renderStateStack[renderStateStack.length - 1]; } else { currentRenderState = null; } renderListStack.pop(); if (renderListStack.length > 0) { currentRenderList = renderListStack[renderListStack.length - 1]; } else { currentRenderList = null; } }; function projectObject(object, camera, groupOrder, sortObjects) { if (object.visible === false) return; const visible = object.layers.test(camera.layers); if (visible) { if (object.isGroup) { groupOrder = object.renderOrder; } else if (object.isLOD) { if (object.autoUpdate === true) object.update(camera); } else if (object.isLight) { currentRenderState.pushLight(object); if (object.castShadow) { currentRenderState.pushShadow(object); } } else if (object.isSprite) { if (!object.frustumCulled || _frustum.intersectsSprite(object)) { if (sortObjects) { _vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix); } const geometry = objects.update(object); const material = object.material; if (material.visible) { currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); } } } else if (object.isMesh || object.isLine || object.isPoints) { if (!object.frustumCulled || _frustum.intersectsObject(object)) { const geometry = objects.update(object); const material = object.material; if (sortObjects) { if (object.boundingSphere !== undefined) { if (object.boundingSphere === null) object.computeBoundingSphere(); _vector3.copy(object.boundingSphere.center); } else { if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _vector3.copy(geometry.boundingSphere.center); } _vector3.applyMatrix4(object.matrixWorld).applyMatrix4(_projScreenMatrix); } if (Array.isArray(material)) { const groups = geometry.groups; for (let i = 0, l = groups.length; i < l; i++) { const group = groups[i]; const groupMaterial = material[group.materialIndex]; if (groupMaterial && groupMaterial.visible) { currentRenderList.push(object, geometry, groupMaterial, groupOrder, _vector3.z, group); } } } else if (material.visible) { currentRenderList.push(object, geometry, material, groupOrder, _vector3.z, null); } } } } const children = object.children; for (let i = 0, l = children.length; i < l; i++) { projectObject(children[i], camera, groupOrder, sortObjects); } } function renderScene(currentRenderList, scene, camera, viewport) { const opaqueObjects = currentRenderList.opaque; const transmissiveObjects = currentRenderList.transmissive; const transparentObjects = currentRenderList.transparent; currentRenderState.setupLightsView(camera); if (_clippingEnabled === true) clipping.setGlobalState(_this.clippingPlanes, camera); if (transmissiveObjects.length > 0) renderTransmissionPass(opaqueObjects, transmissiveObjects, scene, camera); if (viewport) state.viewport(_currentViewport.copy(viewport)); if (opaqueObjects.length > 0) renderObjects(opaqueObjects, scene, camera); if (transmissiveObjects.length > 0) renderObjects(transmissiveObjects, scene, camera); if (transparentObjects.length > 0) renderObjects(transparentObjects, scene, camera); // Ensure depth buffer writing is enabled so it can be cleared on next render state.buffers.depth.setTest(true); state.buffers.depth.setMask(true); state.buffers.color.setMask(true); state.setPolygonOffset(false); } function renderTransmissionPass(opaqueObjects, transmissiveObjects, scene, camera) { const isWebGL2 = capabilities.isWebGL2; if (_transmissionRenderTarget === null) { _transmissionRenderTarget = new WebGLRenderTarget(1, 1, { generateMipmaps: true, type: extensions.has('EXT_color_buffer_half_float') ? HalfFloatType : UnsignedByteType, minFilter: LinearMipmapLinearFilter, samples: isWebGL2 ? 4 : 0 }); // debug /* const geometry = new PlaneGeometry(); const material = new MeshBasicMaterial( { map: _transmissionRenderTarget.texture } ); const mesh = new Mesh( geometry, material ); scene.add( mesh ); */ } _this.getDrawingBufferSize(_vector2); if (isWebGL2) { _transmissionRenderTarget.setSize(_vector2.x, _vector2.y); } else { _transmissionRenderTarget.setSize(floorPowerOfTwo(_vector2.x), floorPowerOfTwo(_vector2.y)); } // const currentRenderTarget = _this.getRenderTarget(); _this.setRenderTarget(_transmissionRenderTarget); _this.getClearColor(_currentClearColor); _currentClearAlpha = _this.getClearAlpha(); if (_currentClearAlpha < 1) _this.setClearColor(0xffffff, 0.5); _this.clear(); // Turn off the features which can affect the frag color for opaque objects pass. // Otherwise they are applied twice in opaque objects pass and transmission objects pass. const currentToneMapping = _this.toneMapping; _this.toneMapping = NoToneMapping; renderObjects(opaqueObjects, scene, camera); textures.updateMultisampleRenderTarget(_transmissionRenderTarget); textures.updateRenderTargetMipmap(_transmissionRenderTarget); let renderTargetNeedsUpdate = false; for (let i = 0, l = transmissiveObjects.length; i < l; i++) { const renderItem = transmissiveObjects[i]; const object = renderItem.object; const geometry = renderItem.geometry; const material = renderItem.material; const group = renderItem.group; if (material.side === DoubleSide && object.layers.test(camera.layers)) { const currentSide = material.side; material.side = BackSide; material.needsUpdate = true; renderObject(object, scene, camera, geometry, material, group); material.side = currentSide; material.needsUpdate = true; renderTargetNeedsUpdate = true; } } if (renderTargetNeedsUpdate === true) { textures.updateMultisampleRenderTarget(_transmissionRenderTarget); textures.updateRenderTargetMipmap(_transmissionRenderTarget); } _this.setRenderTarget(currentRenderTarget); _this.setClearColor(_currentClearColor, _currentClearAlpha); _this.toneMapping = currentToneMapping; } function renderObjects(renderList, scene, camera) { const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; for (let i = 0, l = renderList.length; i < l; i++) { const renderItem = renderList[i]; const object = renderItem.object; const geometry = renderItem.geometry; const material = overrideMaterial === null ? renderItem.material : overrideMaterial; const group = renderItem.group; if (object.layers.test(camera.layers)) { renderObject(object, scene, camera, geometry, material, group); } } } function renderObject(object, scene, camera, geometry, material, group) { object.onBeforeRender(_this, scene, camera, geometry, material, group); object.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse, object.matrixWorld); object.normalMatrix.getNormalMatrix(object.modelViewMatrix); material.onBeforeRender(_this, scene, camera, geometry, object, group); if (material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false) { material.side = BackSide; material.needsUpdate = true; _this.renderBufferDirect(camera, scene, geometry, material, object, group); material.side = FrontSide; material.needsUpdate = true; _this.renderBufferDirect(camera, scene, geometry, material, object, group); material.side = DoubleSide; } else { _this.renderBufferDirect(camera, scene, geometry, material, object, group); } object.onAfterRender(_this, scene, camera, geometry, material, group); } function getProgram(material, scene, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; const shadowsArray = currentRenderState.state.shadowsArray; const lightsStateVersion = lights.state.version; const parameters = programCache.getParameters(material, lights.state, shadowsArray, scene, object); const programCacheKey = programCache.getProgramCacheKey(parameters); let programs = materialProperties.programs; // always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; materialProperties.fog = scene.fog; materialProperties.envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || materialProperties.environment); if (programs === undefined) { // new material material.addEventListener('dispose', onMaterialDispose); programs = new Map(); materialProperties.programs = programs; } let program = programs.get(programCacheKey); if (program !== undefined) { // early out if program and light state is identical if (materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion) { updateCommonMaterialProperties(material, parameters); return program; } } else { parameters.uniforms = programCache.getUniforms(material); material.onBuild(object, parameters, _this); material.onBeforeCompile(parameters, _this); program = programCache.acquireProgram(parameters, programCacheKey); programs.set(programCacheKey, program); materialProperties.uniforms = parameters.uniforms; } const uniforms = materialProperties.uniforms; if (!material.isShaderMaterial && !material.isRawShaderMaterial || material.clipping === true) { uniforms.clippingPlanes = clipping.uniform; } updateCommonMaterialProperties(material, parameters); // store the light setup it was created for materialProperties.needsLights = materialNeedsLights(material); materialProperties.lightsStateVersion = lightsStateVersion; if (materialProperties.needsLights) { // wire up the material to this renderer's lighting state uniforms.ambientLightColor.value = lights.state.ambient; uniforms.lightProbe.value = lights.state.probe; uniforms.directionalLights.value = lights.state.directional; uniforms.directionalLightShadows.value = lights.state.directionalShadow; uniforms.spotLights.value = lights.state.spot; uniforms.spotLightShadows.value = lights.state.spotShadow; uniforms.rectAreaLights.value = lights.state.rectArea; uniforms.ltc_1.value = lights.state.rectAreaLTC1; uniforms.ltc_2.value = lights.state.rectAreaLTC2; uniforms.pointLights.value = lights.state.point; uniforms.pointLightShadows.value = lights.state.pointShadow; uniforms.hemisphereLights.value = lights.state.hemi; uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotLightMatrix.value = lights.state.spotLightMatrix; uniforms.spotLightMap.value = lights.state.spotLightMap; uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } const progUniforms = program.getUniforms(); const uniformsList = WebGLUniforms.seqWithValue(progUniforms.seq, uniforms); materialProperties.currentProgram = program; materialProperties.uniformsList = uniformsList; return program; } function updateCommonMaterialProperties(material, parameters) { const materialProperties = properties.get(material); materialProperties.outputColorSpace = parameters.outputColorSpace; materialProperties.instancing = parameters.instancing; materialProperties.skinning = parameters.skinning; materialProperties.morphTargets = parameters.morphTargets; materialProperties.morphNormals = parameters.morphNormals; materialProperties.morphColors = parameters.morphColors; materialProperties.morphTargetsCount = parameters.morphTargetsCount; materialProperties.numClippingPlanes = parameters.numClippingPlanes; materialProperties.numIntersection = parameters.numClipIntersection; materialProperties.vertexAlphas = parameters.vertexAlphas; materialProperties.vertexTangents = parameters.vertexTangents; materialProperties.toneMapping = parameters.toneMapping; } function setProgram(camera, scene, geometry, material, object) { if (scene.isScene !== true) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... textures.resetTextureUnits(); const fog = scene.fog; const environment = material.isMeshStandardMaterial ? scene.environment : null; const colorSpace = _currentRenderTarget === null ? _this.outputColorSpace : _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace; const envMap = (material.isMeshStandardMaterial ? cubeuvmaps : cubemaps).get(material.envMap || environment); const vertexAlphas = material.vertexColors === true && !!geometry.attributes.color && geometry.attributes.color.itemSize === 4; const vertexTangents = !!geometry.attributes.tangent && (!!material.normalMap || material.anisotropy > 0); const morphTargets = !!geometry.morphAttributes.position; const morphNormals = !!geometry.morphAttributes.normal; const morphColors = !!geometry.morphAttributes.color; const toneMapping = material.toneMapped ? _this.toneMapping : NoToneMapping; const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; const morphTargetsCount = morphAttribute !== undefined ? morphAttribute.length : 0; const materialProperties = properties.get(material); const lights = currentRenderState.state.lights; if (_clippingEnabled === true) { if (_localClippingEnabled === true || camera !== _currentCamera) { const useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) clipping.setState(material, camera, useCache); } } // let needsProgramChange = false; if (material.version === materialProperties.__version) { if (materialProperties.needsLights && materialProperties.lightsStateVersion !== lights.state.version) { needsProgramChange = true; } else if (materialProperties.outputColorSpace !== colorSpace) { needsProgramChange = true; } else if (object.isInstancedMesh && materialProperties.instancing === false) { needsProgramChange = true; } else if (!object.isInstancedMesh && materialProperties.instancing === true) { needsProgramChange = true; } else if (object.isSkinnedMesh && materialProperties.skinning === false) { needsProgramChange = true; } else if (!object.isSkinnedMesh && materialProperties.skinning === true) { needsProgramChange = true; } else if (materialProperties.envMap !== envMap) { needsProgramChange = true; } else if (material.fog === true && materialProperties.fog !== fog) { needsProgramChange = true; } else if (materialProperties.numClippingPlanes !== undefined && (materialProperties.numClippingPlanes !== clipping.numPlanes || materialProperties.numIntersection !== clipping.numIntersection)) { needsProgramChange = true; } else if (materialProperties.vertexAlphas !== vertexAlphas) { needsProgramChange = true; } else if (materialProperties.vertexTangents !== vertexTangents) { needsProgramChange = true; } else if (materialProperties.morphTargets !== morphTargets) { needsProgramChange = true; } else if (materialProperties.morphNormals !== morphNormals) { needsProgramChange = true; } else if (materialProperties.morphColors !== morphColors) { needsProgramChange = true; } else if (materialProperties.toneMapping !== toneMapping) { needsProgramChange = true; } else if (capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount) { needsProgramChange = true; } } else { needsProgramChange = true; materialProperties.__version = material.version; } // let program = materialProperties.currentProgram; if (needsProgramChange === true) { program = getProgram(material, scene, object); } let refreshProgram = false; let refreshMaterial = false; let refreshLights = false; const p_uniforms = program.getUniforms(), m_uniforms = materialProperties.uniforms; if (state.useProgram(program.program)) { refreshProgram = true; refreshMaterial = true; refreshLights = true; } if (material.id !== _currentMaterialId) { _currentMaterialId = material.id; refreshMaterial = true; } if (refreshProgram || _currentCamera !== camera) { p_uniforms.setValue(_gl, 'projectionMatrix', camera.projectionMatrix); if (capabilities.logarithmicDepthBuffer) { p_uniforms.setValue(_gl, 'logDepthBufFC', 2.0 / (Math.log(camera.far + 1.0) / Math.LN2)); } if (_currentCamera !== camera) { _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if (material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshStandardMaterial || material.envMap) { const uCamPos = p_uniforms.map.cameraPosition; if (uCamPos !== undefined) { uCamPos.setValue(_gl, _vector3.setFromMatrixPosition(camera.matrixWorld)); } } if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial) { p_uniforms.setValue(_gl, 'isOrthographic', camera.isOrthographicCamera === true); } if (material.isMeshPhongMaterial || material.isMeshToonMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.isShadowMaterial || object.isSkinnedMesh) { p_uniforms.setValue(_gl, 'viewMatrix', camera.matrixWorldInverse); } } // skinning and morph target uniforms must be set even if material didn't change // auto-setting of texture unit for bone and morph texture must go before other textures // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures if (object.isSkinnedMesh) { p_uniforms.setOptional(_gl, object, 'bindMatrix'); p_uniforms.setOptional(_gl, object, 'bindMatrixInverse'); const skeleton = object.skeleton; if (skeleton) { if (capabilities.floatVertexTextures) { if (skeleton.boneTexture === null) skeleton.computeBoneTexture(); p_uniforms.setValue(_gl, 'boneTexture', skeleton.boneTexture, textures); p_uniforms.setValue(_gl, 'boneTextureSize', skeleton.boneTextureSize); } else { console.warn('THREE.WebGLRenderer: SkinnedMesh can only be used with WebGL 2. With WebGL 1 OES_texture_float and vertex textures support is required.'); } } } const morphAttributes = geometry.morphAttributes; if (morphAttributes.position !== undefined || morphAttributes.normal !== undefined || morphAttributes.color !== undefined && capabilities.isWebGL2 === true) { morphtargets.update(object, geometry, program); } if (refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow) { materialProperties.receiveShadow = object.receiveShadow; p_uniforms.setValue(_gl, 'receiveShadow', object.receiveShadow); } // https://github.com/mrdoob/three.js/pull/24467#issuecomment-1209031512 if (material.isMeshGouraudMaterial && material.envMap !== null) { m_uniforms.envMap.value = envMap; m_uniforms.flipEnvMap.value = envMap.isCubeTexture && envMap.isRenderTargetTexture === false ? -1 : 1; } if (refreshMaterial) { p_uniforms.setValue(_gl, 'toneMappingExposure', _this.toneMappingExposure); if (materialProperties.needsLights) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer's state for their // values // // use the current material's .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate(m_uniforms, refreshLights); } // refresh uniforms common to several materials if (fog && material.fog === true) { materials.refreshFogUniforms(m_uniforms, fog); } materials.refreshMaterialUniforms(m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget); WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); } if (material.isShaderMaterial && material.uniformsNeedUpdate === true) { WebGLUniforms.upload(_gl, materialProperties.uniformsList, m_uniforms, textures); material.uniformsNeedUpdate = false; } if (material.isSpriteMaterial) { p_uniforms.setValue(_gl, 'center', object.center); } // common matrices p_uniforms.setValue(_gl, 'modelViewMatrix', object.modelViewMatrix); p_uniforms.setValue(_gl, 'normalMatrix', object.normalMatrix); p_uniforms.setValue(_gl, 'modelMatrix', object.matrixWorld); // UBOs if (material.isShaderMaterial || material.isRawShaderMaterial) { const groups = material.uniformsGroups; for (let i = 0, l = groups.length; i < l; i++) { if (capabilities.isWebGL2) { const group = groups[i]; uniformsGroups.update(group, program); uniformsGroups.bind(group, program); } else { console.warn('THREE.WebGLRenderer: Uniform Buffer Objects can only be used with WebGL 2.'); } } } return program; } // If uniforms are marked as clean, they don't need to be loaded to the GPU. function markUniformsLightsNeedsUpdate(uniforms, value) { uniforms.ambientLightColor.needsUpdate = value; uniforms.lightProbe.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.directionalLightShadows.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.pointLightShadows.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.spotLightShadows.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } function materialNeedsLights(material) { return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isShadowMaterial || material.isShaderMaterial && material.lights === true; } this.getActiveCubeFace = function () { return _currentActiveCubeFace; }; this.getActiveMipmapLevel = function () { return _currentActiveMipmapLevel; }; this.getRenderTarget = function () { return _currentRenderTarget; }; this.setRenderTargetTextures = function (renderTarget, colorTexture, depthTexture) { properties.get(renderTarget.texture).__webglTexture = colorTexture; properties.get(renderTarget.depthTexture).__webglTexture = depthTexture; const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__hasExternalTextures = true; if (renderTargetProperties.__hasExternalTextures) { renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; if (!renderTargetProperties.__autoAllocateDepthBuffer) { // The multisample_render_to_texture extension doesn't work properly if there // are midframe flushes and an external depth buffer. Disable use of the extension. if (extensions.has('WEBGL_multisampled_render_to_texture') === true) { console.warn('THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided'); renderTargetProperties.__useRenderToTexture = false; } } } }; this.setRenderTargetFramebuffer = function (renderTarget, defaultFramebuffer) { const renderTargetProperties = properties.get(renderTarget); renderTargetProperties.__webglFramebuffer = defaultFramebuffer; renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; }; this.setRenderTarget = function (renderTarget, activeCubeFace = 0, activeMipmapLevel = 0) { _currentRenderTarget = renderTarget; _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; let useDefaultFramebuffer = true; let framebuffer = null; let isCube = false; let isRenderTarget3D = false; if (renderTarget) { const renderTargetProperties = properties.get(renderTarget); if (renderTargetProperties.__useDefaultFramebuffer !== undefined) { // We need to make sure to rebind the framebuffer. state.bindFramebuffer(_gl.FRAMEBUFFER, null); useDefaultFramebuffer = false; } else if (renderTargetProperties.__webglFramebuffer === undefined) { textures.setupRenderTarget(renderTarget); } else if (renderTargetProperties.__hasExternalTextures) { // Color and depth texture must be rebound in order for the swapchain to update. textures.rebindTextures(renderTarget, properties.get(renderTarget.texture).__webglTexture, properties.get(renderTarget.depthTexture).__webglTexture); } const texture = renderTarget.texture; if (texture.isData3DTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture) { isRenderTarget3D = true; } const __webglFramebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget) { framebuffer = __webglFramebuffer[activeCubeFace]; isCube = true; } else if (capabilities.isWebGL2 && renderTarget.samples > 0 && textures.useMultisampledRTT(renderTarget) === false) { framebuffer = properties.get(renderTarget).__webglMultisampledFramebuffer; } else { framebuffer = __webglFramebuffer; } _currentViewport.copy(renderTarget.viewport); _currentScissor.copy(renderTarget.scissor); _currentScissorTest = renderTarget.scissorTest; } else { _currentViewport.copy(_viewport).multiplyScalar(_pixelRatio).floor(); _currentScissor.copy(_scissor).multiplyScalar(_pixelRatio).floor(); _currentScissorTest = _scissorTest; } const framebufferBound = state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); if (framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer) { state.drawBuffers(renderTarget, framebuffer); } state.viewport(_currentViewport); state.scissor(_currentScissor); state.setScissorTest(_currentScissorTest); if (isCube) { const textureProperties = properties.get(renderTarget.texture); _gl.framebufferTexture2D(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel); } else if (isRenderTarget3D) { const textureProperties = properties.get(renderTarget.texture); const layer = activeCubeFace || 0; _gl.framebufferTextureLayer(_gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer); } _currentMaterialId = -1; // reset current material to ensure correct uniform bindings }; this.readRenderTargetPixels = function (renderTarget, x, y, width, height, buffer, activeCubeFaceIndex) { if (!(renderTarget && renderTarget.isWebGLRenderTarget)) { console.error('THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.'); return; } let framebuffer = properties.get(renderTarget).__webglFramebuffer; if (renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined) { framebuffer = framebuffer[activeCubeFaceIndex]; } if (framebuffer) { state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); try { const texture = renderTarget.texture; const textureFormat = texture.format; const textureType = texture.type; if (textureFormat !== RGBAFormat && utils.convert(textureFormat) !== _gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_FORMAT)) { console.error('THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.'); return; } const halfFloatSupportedByExt = textureType === HalfFloatType && (extensions.has('EXT_color_buffer_half_float') || capabilities.isWebGL2 && extensions.has('EXT_color_buffer_float')); if (textureType !== UnsignedByteType && utils.convert(textureType) !== _gl.getParameter(_gl.IMPLEMENTATION_COLOR_READ_TYPE) && // Edge and Chrome Mac < 52 (#9513) !(textureType === FloatType && (capabilities.isWebGL2 || extensions.has('OES_texture_float') || extensions.has('WEBGL_color_buffer_float'))) && // Chrome Mac >= 52 and Firefox !halfFloatSupportedByExt) { console.error('THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.'); return; } // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if (x >= 0 && x <= renderTarget.width - width && y >= 0 && y <= renderTarget.height - height) { _gl.readPixels(x, y, width, height, utils.convert(textureFormat), utils.convert(textureType), buffer); } } finally { // restore framebuffer of current render target if necessary const framebuffer = _currentRenderTarget !== null ? properties.get(_currentRenderTarget).__webglFramebuffer : null; state.bindFramebuffer(_gl.FRAMEBUFFER, framebuffer); } } }; this.copyFramebufferToTexture = function (position, texture, level = 0) { const levelScale = Math.pow(2, -level); const width = Math.floor(texture.image.width * levelScale); const height = Math.floor(texture.image.height * levelScale); textures.setTexture2D(texture, 0); _gl.copyTexSubImage2D(_gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height); state.unbindTexture(); }; this.copyTextureToTexture = function (position, srcTexture, dstTexture, level = 0) { const width = srcTexture.image.width; const height = srcTexture.image.height; const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); textures.setTexture2D(dstTexture, 0); // As another texture upload may have changed pixelStorei // parameters, make sure they are correct for the dstTexture _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); if (srcTexture.isDataTexture) { _gl.texSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data); } else { if (srcTexture.isCompressedTexture) { _gl.compressedTexSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[0].width, srcTexture.mipmaps[0].height, glFormat, srcTexture.mipmaps[0].data); } else { _gl.texSubImage2D(_gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image); } } // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(_gl.TEXTURE_2D); state.unbindTexture(); }; this.copyTextureToTexture3D = function (sourceBox, position, srcTexture, dstTexture, level = 0) { if (_this.isWebGL1Renderer) { console.warn('THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.'); return; } const width = sourceBox.max.x - sourceBox.min.x + 1; const height = sourceBox.max.y - sourceBox.min.y + 1; const depth = sourceBox.max.z - sourceBox.min.z + 1; const glFormat = utils.convert(dstTexture.format); const glType = utils.convert(dstTexture.type); let glTarget; if (dstTexture.isData3DTexture) { textures.setTexture3D(dstTexture, 0); glTarget = _gl.TEXTURE_3D; } else if (dstTexture.isDataArrayTexture) { textures.setTexture2DArray(dstTexture, 0); glTarget = _gl.TEXTURE_2D_ARRAY; } else { console.warn('THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.'); return; } _gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY); _gl.pixelStorei(_gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha); _gl.pixelStorei(_gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment); const unpackRowLen = _gl.getParameter(_gl.UNPACK_ROW_LENGTH); const unpackImageHeight = _gl.getParameter(_gl.UNPACK_IMAGE_HEIGHT); const unpackSkipPixels = _gl.getParameter(_gl.UNPACK_SKIP_PIXELS); const unpackSkipRows = _gl.getParameter(_gl.UNPACK_SKIP_ROWS); const unpackSkipImages = _gl.getParameter(_gl.UNPACK_SKIP_IMAGES); const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[0] : srcTexture.image; _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, image.width); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, image.height); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, sourceBox.min.x); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, sourceBox.min.y); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, sourceBox.min.z); if (srcTexture.isDataTexture || srcTexture.isData3DTexture) { _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data); } else { if (srcTexture.isCompressedArrayTexture) { console.warn('THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture.'); _gl.compressedTexSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data); } else { _gl.texSubImage3D(glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image); } } _gl.pixelStorei(_gl.UNPACK_ROW_LENGTH, unpackRowLen); _gl.pixelStorei(_gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight); _gl.pixelStorei(_gl.UNPACK_SKIP_PIXELS, unpackSkipPixels); _gl.pixelStorei(_gl.UNPACK_SKIP_ROWS, unpackSkipRows); _gl.pixelStorei(_gl.UNPACK_SKIP_IMAGES, unpackSkipImages); // Generate mipmaps only when copying level 0 if (level === 0 && dstTexture.generateMipmaps) _gl.generateMipmap(glTarget); state.unbindTexture(); }; this.initTexture = function (texture) { if (texture.isCubeTexture) { textures.setTextureCube(texture, 0); } else if (texture.isData3DTexture) { textures.setTexture3D(texture, 0); } else if (texture.isDataArrayTexture || texture.isCompressedArrayTexture) { textures.setTexture2DArray(texture, 0); } else { textures.setTexture2D(texture, 0); } state.unbindTexture(); }; this.resetState = function () { _currentActiveCubeFace = 0; _currentActiveMipmapLevel = 0; _currentRenderTarget = null; state.reset(); bindingStates.reset(); }; if (typeof __THREE_DEVTOOLS__ !== 'undefined') { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent('observe', { detail: this })); } } get coordinateSystem() { return WebGLCoordinateSystem; } get physicallyCorrectLights() { // @deprecated, r150 console.warn('THREE.WebGLRenderer: the property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead.'); return !this.useLegacyLights; } set physicallyCorrectLights(value) { // @deprecated, r150 console.warn('THREE.WebGLRenderer: the property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead.'); this.useLegacyLights = !value; } get outputEncoding() { // @deprecated, r152 console.warn('THREE.WebGLRenderer: Property .outputEncoding has been removed. Use .outputColorSpace instead.'); return this.outputColorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding; } set outputEncoding(encoding) { // @deprecated, r152 console.warn('THREE.WebGLRenderer: Property .outputEncoding has been removed. Use .outputColorSpace instead.'); this.outputColorSpace = encoding === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace; } } class WebGL1Renderer extends WebGLRenderer {} WebGL1Renderer.prototype.isWebGL1Renderer = true; class FogExp2 { constructor(color, density = 0.00025) { this.isFogExp2 = true; this.name = ''; this.color = new Color(color); this.density = density; } clone() { return new FogExp2(this.color, this.density); } toJSON( /* meta */ ) { return { type: 'FogExp2', color: this.color.getHex(), density: this.density }; } } class Fog { constructor(color, near = 1, far = 1000) { this.isFog = true; this.name = ''; this.color = new Color(color); this.near = near; this.far = far; } clone() { return new Fog(this.color, this.near, this.far); } toJSON( /* meta */ ) { return { type: 'Fog', color: this.color.getHex(), near: this.near, far: this.far }; } } class Scene extends Object3D { constructor() { super(); this.isScene = true; this.type = 'Scene'; this.background = null; this.environment = null; this.fog = null; this.backgroundBlurriness = 0; this.backgroundIntensity = 1; this.overrideMaterial = null; if (typeof __THREE_DEVTOOLS__ !== 'undefined') { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent('observe', { detail: this })); } } copy(source, recursive) { super.copy(source, recursive); if (source.background !== null) this.background = source.background.clone(); if (source.environment !== null) this.environment = source.environment.clone(); if (source.fog !== null) this.fog = source.fog.clone(); this.backgroundBlurriness = source.backgroundBlurriness; this.backgroundIntensity = source.backgroundIntensity; if (source.overrideMaterial !== null) this.overrideMaterial = source.overrideMaterial.clone(); this.matrixAutoUpdate = source.matrixAutoUpdate; return this; } toJSON(meta) { const data = super.toJSON(meta); if (this.fog !== null) data.object.fog = this.fog.toJSON(); if (this.backgroundBlurriness > 0) data.object.backgroundBlurriness = this.backgroundBlurriness; if (this.backgroundIntensity !== 1) data.object.backgroundIntensity = this.backgroundIntensity; return data; } } class InterleavedBuffer { constructor(array, stride) { this.isInterleavedBuffer = true; this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.usage = StaticDrawUsage; this.updateRange = { offset: 0, count: -1 }; this.version = 0; this.uuid = generateUUID(); } onUploadCallback() {} set needsUpdate(value) { if (value === true) this.version++; } setUsage(value) { this.usage = value; return this; } copy(source) { this.array = new source.array.constructor(source.array); this.count = source.count; this.stride = source.stride; this.usage = source.usage; return this; } copyAt(index1, attribute, index2) { index1 *= this.stride; index2 *= attribute.stride; for (let i = 0, l = this.stride; i < l; i++) { this.array[index1 + i] = attribute.array[index2 + i]; } return this; } set(value, offset = 0) { this.array.set(value, offset); return this; } clone(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = this.array.slice(0).buffer; } const array = new this.array.constructor(data.arrayBuffers[this.array.buffer._uuid]); const ib = new this.constructor(array, this.stride); ib.setUsage(this.usage); return ib; } onUpload(callback) { this.onUploadCallback = callback; return this; } toJSON(data) { if (data.arrayBuffers === undefined) { data.arrayBuffers = {}; } // generate UUID for array buffer if necessary if (this.array.buffer._uuid === undefined) { this.array.buffer._uuid = generateUUID(); } if (data.arrayBuffers[this.array.buffer._uuid] === undefined) { data.arrayBuffers[this.array.buffer._uuid] = Array.from(new Uint32Array(this.array.buffer)); } // return { uuid: this.uuid, buffer: this.array.buffer._uuid, type: this.array.constructor.name, stride: this.stride }; } } const _vector$5 = /*@__PURE__*/new Vector3(); class InterleavedBufferAttribute { constructor(interleavedBuffer, itemSize, offset, normalized = false) { this.isInterleavedBufferAttribute = true; this.name = ''; this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized; } get count() { return this.data.count; } get array() { return this.data.array; } set needsUpdate(value) { this.data.needsUpdate = value; } applyMatrix4(m) { for (let i = 0, l = this.data.count; i < l; i++) { _vector$5.fromBufferAttribute(this, i); _vector$5.applyMatrix4(m); this.setXYZ(i, _vector$5.x, _vector$5.y, _vector$5.z); } return this; } applyNormalMatrix(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$5.fromBufferAttribute(this, i); _vector$5.applyNormalMatrix(m); this.setXYZ(i, _vector$5.x, _vector$5.y, _vector$5.z); } return this; } transformDirection(m) { for (let i = 0, l = this.count; i < l; i++) { _vector$5.fromBufferAttribute(this, i); _vector$5.transformDirection(m); this.setXYZ(i, _vector$5.x, _vector$5.y, _vector$5.z); } return this; } setX(index, x) { if (this.normalized) x = normalize(x, this.array); this.data.array[index * this.data.stride + this.offset] = x; return this; } setY(index, y) { if (this.normalized) y = normalize(y, this.array); this.data.array[index * this.data.stride + this.offset + 1] = y; return this; } setZ(index, z) { if (this.normalized) z = normalize(z, this.array); this.data.array[index * this.data.stride + this.offset + 2] = z; return this; } setW(index, w) { if (this.normalized) w = normalize(w, this.array); this.data.array[index * this.data.stride + this.offset + 3] = w; return this; } getX(index) { let x = this.data.array[index * this.data.stride + this.offset]; if (this.normalized) x = denormalize(x, this.array); return x; } getY(index) { let y = this.data.array[index * this.data.stride + this.offset + 1]; if (this.normalized) y = denormalize(y, this.array); return y; } getZ(index) { let z = this.data.array[index * this.data.stride + this.offset + 2]; if (this.normalized) z = denormalize(z, this.array); return z; } getW(index) { let w = this.data.array[index * this.data.stride + this.offset + 3]; if (this.normalized) w = denormalize(w, this.array); return w; } setXY(index, x, y) { index = index * this.data.stride + this.offset; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); } this.data.array[index + 0] = x; this.data.array[index + 1] = y; return this; } setXYZ(index, x, y, z) { index = index * this.data.stride + this.offset; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); } this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; return this; } setXYZW(index, x, y, z, w) { index = index * this.data.stride + this.offset; if (this.normalized) { x = normalize(x, this.array); y = normalize(y, this.array); z = normalize(z, this.array); w = normalize(w, this.array); } this.data.array[index + 0] = x; this.data.array[index + 1] = y; this.data.array[index + 2] = z; this.data.array[index + 3] = w; return this; } clone(data) { if (data === undefined) { console.log('THREE.InterleavedBufferAttribute.clone(): Cloning an interleaved buffer attribute will de-interleave buffer data.'); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } return new BufferAttribute(new this.array.constructor(array), this.itemSize, this.normalized); } else { if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.clone(data); } return new InterleavedBufferAttribute(data.interleavedBuffers[this.data.uuid], this.itemSize, this.offset, this.normalized); } } toJSON(data) { if (data === undefined) { console.log('THREE.InterleavedBufferAttribute.toJSON(): Serializing an interleaved buffer attribute will de-interleave buffer data.'); const array = []; for (let i = 0; i < this.count; i++) { const index = i * this.data.stride + this.offset; for (let j = 0; j < this.itemSize; j++) { array.push(this.data.array[index + j]); } } // de-interleave data and save it as an ordinary buffer attribute for now return { itemSize: this.itemSize, type: this.array.constructor.name, array: array, normalized: this.normalized }; } else { // save as true interleaved attribute if (data.interleavedBuffers === undefined) { data.interleavedBuffers = {}; } if (data.interleavedBuffers[this.data.uuid] === undefined) { data.interleavedBuffers[this.data.uuid] = this.data.toJSON(data); } return { isInterleavedBufferAttribute: true, itemSize: this.itemSize, data: this.data.uuid, offset: this.offset, normalized: this.normalized }; } } } class SpriteMaterial extends Material { constructor(parameters) { super(); this.isSpriteMaterial = true; this.type = 'SpriteMaterial'; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.rotation = 0; this.sizeAttenuation = true; this.transparent = true; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.rotation = source.rotation; this.sizeAttenuation = source.sizeAttenuation; this.fog = source.fog; return this; } } let _geometry; const _intersectPoint = /*@__PURE__*/new Vector3(); const _worldScale = /*@__PURE__*/new Vector3(); const _mvPosition = /*@__PURE__*/new Vector3(); const _alignedPosition = /*@__PURE__*/new Vector2(); const _rotatedPosition = /*@__PURE__*/new Vector2(); const _viewWorldMatrix = /*@__PURE__*/new Matrix4(); const _vA = /*@__PURE__*/new Vector3(); const _vB = /*@__PURE__*/new Vector3(); const _vC = /*@__PURE__*/new Vector3(); const _uvA = /*@__PURE__*/new Vector2(); const _uvB = /*@__PURE__*/new Vector2(); const _uvC = /*@__PURE__*/new Vector2(); class Sprite extends Object3D { constructor(material) { super(); this.isSprite = true; this.type = 'Sprite'; if (_geometry === undefined) { _geometry = new BufferGeometry(); const float32Array = new Float32Array([-0.5, -0.5, 0, 0, 0, 0.5, -0.5, 0, 1, 0, 0.5, 0.5, 0, 1, 1, -0.5, 0.5, 0, 0, 1]); const interleavedBuffer = new InterleavedBuffer(float32Array, 5); _geometry.setIndex([0, 1, 2, 0, 2, 3]); _geometry.setAttribute('position', new InterleavedBufferAttribute(interleavedBuffer, 3, 0, false)); _geometry.setAttribute('uv', new InterleavedBufferAttribute(interleavedBuffer, 2, 3, false)); } this.geometry = _geometry; this.material = material !== undefined ? material : new SpriteMaterial(); this.center = new Vector2(0.5, 0.5); } raycast(raycaster, intersects) { if (raycaster.camera === null) { console.error('THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.'); } _worldScale.setFromMatrixScale(this.matrixWorld); _viewWorldMatrix.copy(raycaster.camera.matrixWorld); this.modelViewMatrix.multiplyMatrices(raycaster.camera.matrixWorldInverse, this.matrixWorld); _mvPosition.setFromMatrixPosition(this.modelViewMatrix); if (raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false) { _worldScale.multiplyScalar(-_mvPosition.z); } const rotation = this.material.rotation; let sin, cos; if (rotation !== 0) { cos = Math.cos(rotation); sin = Math.sin(rotation); } const center = this.center; transformVertex(_vA.set(-0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); transformVertex(_vB.set(0.5, -0.5, 0), _mvPosition, center, _worldScale, sin, cos); transformVertex(_vC.set(0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); _uvA.set(0, 0); _uvB.set(1, 0); _uvC.set(1, 1); // check first triangle let intersect = raycaster.ray.intersectTriangle(_vA, _vB, _vC, false, _intersectPoint); if (intersect === null) { // check second triangle transformVertex(_vB.set(-0.5, 0.5, 0), _mvPosition, center, _worldScale, sin, cos); _uvB.set(0, 1); intersect = raycaster.ray.intersectTriangle(_vA, _vC, _vB, false, _intersectPoint); if (intersect === null) { return; } } const distance = raycaster.ray.origin.distanceTo(_intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance: distance, point: _intersectPoint.clone(), uv: Triangle.getInterpolation(_intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2()), face: null, object: this }); } copy(source, recursive) { super.copy(source, recursive); if (source.center !== undefined) this.center.copy(source.center); this.material = source.material; return this; } } function transformVertex(vertexPosition, mvPosition, center, scale, sin, cos) { // compute position in camera space _alignedPosition.subVectors(vertexPosition, center).addScalar(0.5).multiply(scale); // to check if rotation is not zero if (sin !== undefined) { _rotatedPosition.x = cos * _alignedPosition.x - sin * _alignedPosition.y; _rotatedPosition.y = sin * _alignedPosition.x + cos * _alignedPosition.y; } else { _rotatedPosition.copy(_alignedPosition); } vertexPosition.copy(mvPosition); vertexPosition.x += _rotatedPosition.x; vertexPosition.y += _rotatedPosition.y; // transform to world space vertexPosition.applyMatrix4(_viewWorldMatrix); } const _v1$2 = /*@__PURE__*/new Vector3(); const _v2$1 = /*@__PURE__*/new Vector3(); class LOD extends Object3D { constructor() { super(); this._currentLevel = 0; this.type = 'LOD'; Object.defineProperties(this, { levels: { enumerable: true, value: [] }, isLOD: { value: true } }); this.autoUpdate = true; } copy(source) { super.copy(source, false); const levels = source.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; this.addLevel(level.object.clone(), level.distance, level.hysteresis); } this.autoUpdate = source.autoUpdate; return this; } addLevel(object, distance = 0, hysteresis = 0) { distance = Math.abs(distance); const levels = this.levels; let l; for (l = 0; l < levels.length; l++) { if (distance < levels[l].distance) { break; } } levels.splice(l, 0, { distance: distance, hysteresis: hysteresis, object: object }); this.add(object); return this; } getCurrentLevel() { return this._currentLevel; } getObjectForDistance(distance) { const levels = this.levels; if (levels.length > 0) { let i, l; for (i = 1, l = levels.length; i < l; i++) { let levelDistance = levels[i].distance; if (levels[i].object.visible) { levelDistance -= levelDistance * levels[i].hysteresis; } if (distance < levelDistance) { break; } } return levels[i - 1].object; } return null; } raycast(raycaster, intersects) { const levels = this.levels; if (levels.length > 0) { _v1$2.setFromMatrixPosition(this.matrixWorld); const distance = raycaster.ray.origin.distanceTo(_v1$2); this.getObjectForDistance(distance).raycast(raycaster, intersects); } } update(camera) { const levels = this.levels; if (levels.length > 1) { _v1$2.setFromMatrixPosition(camera.matrixWorld); _v2$1.setFromMatrixPosition(this.matrixWorld); const distance = _v1$2.distanceTo(_v2$1) / camera.zoom; levels[0].object.visible = true; let i, l; for (i = 1, l = levels.length; i < l; i++) { let levelDistance = levels[i].distance; if (levels[i].object.visible) { levelDistance -= levelDistance * levels[i].hysteresis; } if (distance >= levelDistance) { levels[i - 1].object.visible = false; levels[i].object.visible = true; } else { break; } } this._currentLevel = i - 1; for (; i < l; i++) { levels[i].object.visible = false; } } } toJSON(meta) { const data = super.toJSON(meta); if (this.autoUpdate === false) data.object.autoUpdate = false; data.object.levels = []; const levels = this.levels; for (let i = 0, l = levels.length; i < l; i++) { const level = levels[i]; data.object.levels.push({ object: level.object.uuid, distance: level.distance, hysteresis: level.hysteresis }); } return data; } } const _basePosition = /*@__PURE__*/new Vector3(); const _skinIndex = /*@__PURE__*/new Vector4(); const _skinWeight = /*@__PURE__*/new Vector4(); const _vector3 = /*@__PURE__*/new Vector3(); const _matrix4 = /*@__PURE__*/new Matrix4(); const _vertex = /*@__PURE__*/new Vector3(); const _sphere$3 = /*@__PURE__*/new Sphere(); const _inverseMatrix$2 = /*@__PURE__*/new Matrix4(); const _ray$2 = /*@__PURE__*/new Ray(); class SkinnedMesh extends Mesh { constructor(geometry, material) { super(geometry, material); this.isSkinnedMesh = true; this.type = 'SkinnedMesh'; this.bindMode = 'attached'; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); this.boundingBox = null; this.boundingSphere = null; } computeBoundingBox() { const geometry = this.geometry; if (this.boundingBox === null) { this.boundingBox = new Box3(); } this.boundingBox.makeEmpty(); const positionAttribute = geometry.getAttribute('position'); for (let i = 0; i < positionAttribute.count; i++) { _vertex.fromBufferAttribute(positionAttribute, i); this.applyBoneTransform(i, _vertex); this.boundingBox.expandByPoint(_vertex); } } computeBoundingSphere() { const geometry = this.geometry; if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } this.boundingSphere.makeEmpty(); const positionAttribute = geometry.getAttribute('position'); for (let i = 0; i < positionAttribute.count; i++) { _vertex.fromBufferAttribute(positionAttribute, i); this.applyBoneTransform(i, _vertex); this.boundingSphere.expandByPoint(_vertex); } } copy(source, recursive) { super.copy(source, recursive); this.bindMode = source.bindMode; this.bindMatrix.copy(source.bindMatrix); this.bindMatrixInverse.copy(source.bindMatrixInverse); this.skeleton = source.skeleton; if (source.boundingBox !== null) this.boundingBox = source.boundingBox.clone(); if (source.boundingSphere !== null) this.boundingSphere = source.boundingSphere.clone(); return this; } raycast(raycaster, intersects) { const material = this.material; const matrixWorld = this.matrixWorld; if (material === undefined) return; // test with bounding sphere in world space if (this.boundingSphere === null) this.computeBoundingSphere(); _sphere$3.copy(this.boundingSphere); _sphere$3.applyMatrix4(matrixWorld); if (raycaster.ray.intersectsSphere(_sphere$3) === false) return; // convert ray to local space of skinned mesh _inverseMatrix$2.copy(matrixWorld).invert(); _ray$2.copy(raycaster.ray).applyMatrix4(_inverseMatrix$2); // test with bounding box in local space if (this.boundingBox !== null) { if (_ray$2.intersectsBox(this.boundingBox) === false) return; } // test for intersections with geometry this._computeIntersections(raycaster, intersects, _ray$2); } getVertexPosition(index, target) { super.getVertexPosition(index, target); this.applyBoneTransform(index, target); return target; } bind(skeleton, bindMatrix) { this.skeleton = skeleton; if (bindMatrix === undefined) { this.updateMatrixWorld(true); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy(bindMatrix); this.bindMatrixInverse.copy(bindMatrix).invert(); } pose() { this.skeleton.pose(); } normalizeSkinWeights() { const vector = new Vector4(); const skinWeight = this.geometry.attributes.skinWeight; for (let i = 0, l = skinWeight.count; i < l; i++) { vector.fromBufferAttribute(skinWeight, i); const scale = 1.0 / vector.manhattanLength(); if (scale !== Infinity) { vector.multiplyScalar(scale); } else { vector.set(1, 0, 0, 0); // do something reasonable } skinWeight.setXYZW(i, vector.x, vector.y, vector.z, vector.w); } } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.bindMode === 'attached') { this.bindMatrixInverse.copy(this.matrixWorld).invert(); } else if (this.bindMode === 'detached') { this.bindMatrixInverse.copy(this.bindMatrix).invert(); } else { console.warn('THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode); } } applyBoneTransform(index, vector) { const skeleton = this.skeleton; const geometry = this.geometry; _skinIndex.fromBufferAttribute(geometry.attributes.skinIndex, index); _skinWeight.fromBufferAttribute(geometry.attributes.skinWeight, index); _basePosition.copy(vector).applyMatrix4(this.bindMatrix); vector.set(0, 0, 0); for (let i = 0; i < 4; i++) { const weight = _skinWeight.getComponent(i); if (weight !== 0) { const boneIndex = _skinIndex.getComponent(i); _matrix4.multiplyMatrices(skeleton.bones[boneIndex].matrixWorld, skeleton.boneInverses[boneIndex]); vector.addScaledVector(_vector3.copy(_basePosition).applyMatrix4(_matrix4), weight); } } return vector.applyMatrix4(this.bindMatrixInverse); } boneTransform(index, vector) { // @deprecated, r151 console.warn('THREE.SkinnedMesh: .boneTransform() was renamed to .applyBoneTransform() in r151.'); return this.applyBoneTransform(index, vector); } } class Bone extends Object3D { constructor() { super(); this.isBone = true; this.type = 'Bone'; } } class DataTexture extends Texture { constructor(data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, colorSpace) { super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace); this.isDataTexture = true; this.image = { data: data, width: width, height: height }; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } } const _offsetMatrix = /*@__PURE__*/new Matrix4(); const _identityMatrix = /*@__PURE__*/new Matrix4(); class Skeleton { constructor(bones = [], boneInverses = []) { this.uuid = generateUUID(); this.bones = bones.slice(0); this.boneInverses = boneInverses; this.boneMatrices = null; this.boneTexture = null; this.boneTextureSize = 0; this.init(); } init() { const bones = this.bones; const boneInverses = this.boneInverses; this.boneMatrices = new Float32Array(bones.length * 16); // calculate inverse bone matrices if necessary if (boneInverses.length === 0) { this.calculateInverses(); } else { // handle special case if (bones.length !== boneInverses.length) { console.warn('THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.'); this.boneInverses = []; for (let i = 0, il = this.bones.length; i < il; i++) { this.boneInverses.push(new Matrix4()); } } } } calculateInverses() { this.boneInverses.length = 0; for (let i = 0, il = this.bones.length; i < il; i++) { const inverse = new Matrix4(); if (this.bones[i]) { inverse.copy(this.bones[i].matrixWorld).invert(); } this.boneInverses.push(inverse); } } pose() { // recover the bind-time world matrices for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { bone.matrixWorld.copy(this.boneInverses[i]).invert(); } } // compute the local matrices, positions, rotations and scales for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone) { if (bone.parent && bone.parent.isBone) { bone.matrix.copy(bone.parent.matrixWorld).invert(); bone.matrix.multiply(bone.matrixWorld); } else { bone.matrix.copy(bone.matrixWorld); } bone.matrix.decompose(bone.position, bone.quaternion, bone.scale); } } } update() { const bones = this.bones; const boneInverses = this.boneInverses; const boneMatrices = this.boneMatrices; const boneTexture = this.boneTexture; // flatten bone matrices to array for (let i = 0, il = bones.length; i < il; i++) { // compute the offset between the current and the original transform const matrix = bones[i] ? bones[i].matrixWorld : _identityMatrix; _offsetMatrix.multiplyMatrices(matrix, boneInverses[i]); _offsetMatrix.toArray(boneMatrices, i * 16); } if (boneTexture !== null) { boneTexture.needsUpdate = true; } } clone() { return new Skeleton(this.bones, this.boneInverses); } computeBoneTexture() { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) let size = Math.sqrt(this.bones.length * 4); // 4 pixels needed for 1 matrix size = ceilPowerOfTwo(size); size = Math.max(size, 4); const boneMatrices = new Float32Array(size * size * 4); // 4 floats per RGBA pixel boneMatrices.set(this.boneMatrices); // copy current values const boneTexture = new DataTexture(boneMatrices, size, size, RGBAFormat, FloatType); boneTexture.needsUpdate = true; this.boneMatrices = boneMatrices; this.boneTexture = boneTexture; this.boneTextureSize = size; return this; } getBoneByName(name) { for (let i = 0, il = this.bones.length; i < il; i++) { const bone = this.bones[i]; if (bone.name === name) { return bone; } } return undefined; } dispose() { if (this.boneTexture !== null) { this.boneTexture.dispose(); this.boneTexture = null; } } fromJSON(json, bones) { this.uuid = json.uuid; for (let i = 0, l = json.bones.length; i < l; i++) { const uuid = json.bones[i]; let bone = bones[uuid]; if (bone === undefined) { console.warn('THREE.Skeleton: No bone found with UUID:', uuid); bone = new Bone(); } this.bones.push(bone); this.boneInverses.push(new Matrix4().fromArray(json.boneInverses[i])); } this.init(); return this; } toJSON() { const data = { metadata: { version: 4.6, type: 'Skeleton', generator: 'Skeleton.toJSON' }, bones: [], boneInverses: [] }; data.uuid = this.uuid; const bones = this.bones; const boneInverses = this.boneInverses; for (let i = 0, l = bones.length; i < l; i++) { const bone = bones[i]; data.bones.push(bone.uuid); const boneInverse = boneInverses[i]; data.boneInverses.push(boneInverse.toArray()); } return data; } } class InstancedBufferAttribute extends BufferAttribute { constructor(array, itemSize, normalized, meshPerAttribute = 1) { super(array, itemSize, normalized); this.isInstancedBufferAttribute = true; this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } toJSON() { const data = super.toJSON(); data.meshPerAttribute = this.meshPerAttribute; data.isInstancedBufferAttribute = true; return data; } } const _instanceLocalMatrix = /*@__PURE__*/new Matrix4(); const _instanceWorldMatrix = /*@__PURE__*/new Matrix4(); const _instanceIntersects = []; const _box3 = /*@__PURE__*/new Box3(); const _identity = /*@__PURE__*/new Matrix4(); const _mesh = /*@__PURE__*/new Mesh(); const _sphere$2 = /*@__PURE__*/new Sphere(); class InstancedMesh extends Mesh { constructor(geometry, material, count) { super(geometry, material); this.isInstancedMesh = true; this.instanceMatrix = new InstancedBufferAttribute(new Float32Array(count * 16), 16); this.instanceColor = null; this.count = count; this.boundingBox = null; this.boundingSphere = null; for (let i = 0; i < count; i++) { this.setMatrixAt(i, _identity); } } computeBoundingBox() { const geometry = this.geometry; const count = this.count; if (this.boundingBox === null) { this.boundingBox = new Box3(); } if (geometry.boundingBox === null) { geometry.computeBoundingBox(); } this.boundingBox.makeEmpty(); for (let i = 0; i < count; i++) { this.getMatrixAt(i, _instanceLocalMatrix); _box3.copy(geometry.boundingBox).applyMatrix4(_instanceLocalMatrix); this.boundingBox.union(_box3); } } computeBoundingSphere() { const geometry = this.geometry; const count = this.count; if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } if (geometry.boundingSphere === null) { geometry.computeBoundingSphere(); } this.boundingSphere.makeEmpty(); for (let i = 0; i < count; i++) { this.getMatrixAt(i, _instanceLocalMatrix); _sphere$2.copy(geometry.boundingSphere).applyMatrix4(_instanceLocalMatrix); this.boundingSphere.union(_sphere$2); } } copy(source, recursive) { super.copy(source, recursive); this.instanceMatrix.copy(source.instanceMatrix); if (source.instanceColor !== null) this.instanceColor = source.instanceColor.clone(); this.count = source.count; if (source.boundingBox !== null) this.boundingBox = source.boundingBox.clone(); if (source.boundingSphere !== null) this.boundingSphere = source.boundingSphere.clone(); return this; } getColorAt(index, color) { color.fromArray(this.instanceColor.array, index * 3); } getMatrixAt(index, matrix) { matrix.fromArray(this.instanceMatrix.array, index * 16); } raycast(raycaster, intersects) { const matrixWorld = this.matrixWorld; const raycastTimes = this.count; _mesh.geometry = this.geometry; _mesh.material = this.material; if (_mesh.material === undefined) return; // test with bounding sphere first if (this.boundingSphere === null) this.computeBoundingSphere(); _sphere$2.copy(this.boundingSphere); _sphere$2.applyMatrix4(matrixWorld); if (raycaster.ray.intersectsSphere(_sphere$2) === false) return; // now test each instance for (let instanceId = 0; instanceId < raycastTimes; instanceId++) { // calculate the world matrix for each instance this.getMatrixAt(instanceId, _instanceLocalMatrix); _instanceWorldMatrix.multiplyMatrices(matrixWorld, _instanceLocalMatrix); // the mesh represents this single instance _mesh.matrixWorld = _instanceWorldMatrix; _mesh.raycast(raycaster, _instanceIntersects); // process the result of raycast for (let i = 0, l = _instanceIntersects.length; i < l; i++) { const intersect = _instanceIntersects[i]; intersect.instanceId = instanceId; intersect.object = this; intersects.push(intersect); } _instanceIntersects.length = 0; } } setColorAt(index, color) { if (this.instanceColor === null) { this.instanceColor = new InstancedBufferAttribute(new Float32Array(this.instanceMatrix.count * 3), 3); } color.toArray(this.instanceColor.array, index * 3); } setMatrixAt(index, matrix) { matrix.toArray(this.instanceMatrix.array, index * 16); } updateMorphTargets() {} dispose() { this.dispatchEvent({ type: 'dispose' }); } } class LineBasicMaterial extends Material { constructor(parameters) { super(); this.isLineBasicMaterial = true; this.type = 'LineBasicMaterial'; this.color = new Color(0xffffff); this.map = null; this.linewidth = 1; this.linecap = 'round'; this.linejoin = 'round'; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; this.fog = source.fog; return this; } } const _start$1 = /*@__PURE__*/new Vector3(); const _end$1 = /*@__PURE__*/new Vector3(); const _inverseMatrix$1 = /*@__PURE__*/new Matrix4(); const _ray$1 = /*@__PURE__*/new Ray(); const _sphere$1 = /*@__PURE__*/new Sphere(); class Line extends Object3D { constructor(geometry = new BufferGeometry(), material = new LineBasicMaterial()) { super(); this.isLine = true; this.type = 'Line'; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source, recursive) { super.copy(source, recursive); this.material = source.material; this.geometry = source.geometry; return this; } computeLineDistances() { const geometry = this.geometry; // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = [0]; for (let i = 1, l = positionAttribute.count; i < l; i++) { _start$1.fromBufferAttribute(positionAttribute, i - 1); _end$1.fromBufferAttribute(positionAttribute, i); lineDistances[i] = lineDistances[i - 1]; lineDistances[i] += _start$1.distanceTo(_end$1); } geometry.setAttribute('lineDistance', new Float32BufferAttribute(lineDistances, 1)); } else { console.warn('THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.'); } return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Line.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere$1.copy(geometry.boundingSphere); _sphere$1.applyMatrix4(matrixWorld); _sphere$1.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere$1) === false) return; // _inverseMatrix$1.copy(matrixWorld).invert(); _ray$1.copy(raycaster.ray).applyMatrix4(_inverseMatrix$1); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; const vStart = new Vector3(); const vEnd = new Vector3(); const interSegment = new Vector3(); const interRay = new Vector3(); const step = this.isLineSegments ? 2 : 1; const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { const a = index.getX(i); const b = index.getX(i + 1); vStart.fromBufferAttribute(positionAttribute, a); vEnd.fromBufferAttribute(positionAttribute, b); const distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); if (distSq > localThresholdSq) continue; interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(interRay); if (distance < raycaster.near || distance > raycaster.far) continue; intersects.push({ distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4(this.matrixWorld), index: i, face: null, faceIndex: null, object: this }); } } else { const start = Math.max(0, drawRange.start); const end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (let i = start, l = end - 1; i < l; i += step) { vStart.fromBufferAttribute(positionAttribute, i); vEnd.fromBufferAttribute(positionAttribute, i + 1); const distSq = _ray$1.distanceSqToSegment(vStart, vEnd, interRay, interSegment); if (distSq > localThresholdSq) continue; interRay.applyMatrix4(this.matrixWorld); //Move back to world space for distance calculation const distance = raycaster.ray.origin.distanceTo(interRay); if (distance < raycaster.near || distance > raycaster.far) continue; intersects.push({ distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4(this.matrixWorld), index: i, face: null, faceIndex: null, object: this }); } } } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } } const _start = /*@__PURE__*/new Vector3(); const _end = /*@__PURE__*/new Vector3(); class LineSegments extends Line { constructor(geometry, material) { super(geometry, material); this.isLineSegments = true; this.type = 'LineSegments'; } computeLineDistances() { const geometry = this.geometry; // we assume non-indexed geometry if (geometry.index === null) { const positionAttribute = geometry.attributes.position; const lineDistances = []; for (let i = 0, l = positionAttribute.count; i < l; i += 2) { _start.fromBufferAttribute(positionAttribute, i); _end.fromBufferAttribute(positionAttribute, i + 1); lineDistances[i] = i === 0 ? 0 : lineDistances[i - 1]; lineDistances[i + 1] = lineDistances[i] + _start.distanceTo(_end); } geometry.setAttribute('lineDistance', new Float32BufferAttribute(lineDistances, 1)); } else { console.warn('THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.'); } return this; } } class LineLoop extends Line { constructor(geometry, material) { super(geometry, material); this.isLineLoop = true; this.type = 'LineLoop'; } } class PointsMaterial extends Material { constructor(parameters) { super(); this.isPointsMaterial = true; this.type = 'PointsMaterial'; this.color = new Color(0xffffff); this.map = null; this.alphaMap = null; this.size = 1; this.sizeAttenuation = true; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.alphaMap = source.alphaMap; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; this.fog = source.fog; return this; } } const _inverseMatrix = /*@__PURE__*/new Matrix4(); const _ray = /*@__PURE__*/new Ray(); const _sphere = /*@__PURE__*/new Sphere(); const _position$2 = /*@__PURE__*/new Vector3(); class Points extends Object3D { constructor(geometry = new BufferGeometry(), material = new PointsMaterial()) { super(); this.isPoints = true; this.type = 'Points'; this.geometry = geometry; this.material = material; this.updateMorphTargets(); } copy(source, recursive) { super.copy(source, recursive); this.material = source.material; this.geometry = source.geometry; return this; } raycast(raycaster, intersects) { const geometry = this.geometry; const matrixWorld = this.matrixWorld; const threshold = raycaster.params.Points.threshold; const drawRange = geometry.drawRange; // Checking boundingSphere distance to ray if (geometry.boundingSphere === null) geometry.computeBoundingSphere(); _sphere.copy(geometry.boundingSphere); _sphere.applyMatrix4(matrixWorld); _sphere.radius += threshold; if (raycaster.ray.intersectsSphere(_sphere) === false) return; // _inverseMatrix.copy(matrixWorld).invert(); _ray.copy(raycaster.ray).applyMatrix4(_inverseMatrix); const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3); const localThresholdSq = localThreshold * localThreshold; const index = geometry.index; const attributes = geometry.attributes; const positionAttribute = attributes.position; if (index !== null) { const start = Math.max(0, drawRange.start); const end = Math.min(index.count, drawRange.start + drawRange.count); for (let i = start, il = end; i < il; i++) { const a = index.getX(i); _position$2.fromBufferAttribute(positionAttribute, a); testPoint(_position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this); } } else { const start = Math.max(0, drawRange.start); const end = Math.min(positionAttribute.count, drawRange.start + drawRange.count); for (let i = start, l = end; i < l; i++) { _position$2.fromBufferAttribute(positionAttribute, i); testPoint(_position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this); } } } updateMorphTargets() { const geometry = this.geometry; const morphAttributes = geometry.morphAttributes; const keys = Object.keys(morphAttributes); if (keys.length > 0) { const morphAttribute = morphAttributes[keys[0]]; if (morphAttribute !== undefined) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for (let m = 0, ml = morphAttribute.length; m < ml; m++) { const name = morphAttribute[m].name || String(m); this.morphTargetInfluences.push(0); this.morphTargetDictionary[name] = m; } } } } } function testPoint(point, index, localThresholdSq, matrixWorld, raycaster, intersects, object) { const rayPointDistanceSq = _ray.distanceSqToPoint(point); if (rayPointDistanceSq < localThresholdSq) { const intersectPoint = new Vector3(); _ray.closestPointToPoint(point, intersectPoint); intersectPoint.applyMatrix4(matrixWorld); const distance = raycaster.ray.origin.distanceTo(intersectPoint); if (distance < raycaster.near || distance > raycaster.far) return; intersects.push({ distance: distance, distanceToRay: Math.sqrt(rayPointDistanceSq), point: intersectPoint, index: index, face: null, object: object }); } } class VideoTexture extends Texture { constructor(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { super(video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.isVideoTexture = true; this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.generateMipmaps = false; const scope = this; function updateVideo() { scope.needsUpdate = true; video.requestVideoFrameCallback(updateVideo); } if ('requestVideoFrameCallback' in video) { video.requestVideoFrameCallback(updateVideo); } } clone() { return new this.constructor(this.image).copy(this); } update() { const video = this.image; const hasVideoFrameCallback = ('requestVideoFrameCallback' in video); if (hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA) { this.needsUpdate = true; } } } class FramebufferTexture extends Texture { constructor(width, height) { super({ width, height }); this.isFramebufferTexture = true; this.magFilter = NearestFilter; this.minFilter = NearestFilter; this.generateMipmaps = false; this.needsUpdate = true; } } class CompressedTexture extends Texture { constructor(mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, colorSpace) { super(null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace); this.isCompressedTexture = true; this.image = { width: width, height: height }; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn't work for compressed textures ) this.flipY = false; // can't generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } } class CompressedArrayTexture extends CompressedTexture { constructor(mipmaps, width, height, depth, format, type) { super(mipmaps, width, height, format, type); this.isCompressedArrayTexture = true; this.image.depth = depth; this.wrapR = ClampToEdgeWrapping; } } class CanvasTexture extends Texture { constructor(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy) { super(canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy); this.isCanvasTexture = true; this.needsUpdate = true; } } /** * Extensible curve object. * * Some common of curve methods: * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following curves inherit from THREE.Curve: * * -- 2D curves -- * THREE.ArcCurve * THREE.CubicBezierCurve * THREE.EllipseCurve * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.SplineCurve * * -- 3D curves -- * THREE.CatmullRomCurve3 * THREE.CubicBezierCurve3 * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * * A series of curves can be represented as a THREE.CurvePath. * **/ class Curve { constructor() { this.type = 'Curve'; this.arcLengthDivisions = 200; } // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint( /* t, optionalTarget */ ) { console.warn('THREE.Curve: .getPoint() not implemented.'); return null; } // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getPoint(t, optionalTarget); } // Get sequence of points using getPoint( t ) getPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPoint(d / divisions)); } return points; } // Get sequence of points using getPointAt( u ) getSpacedPoints(divisions = 5) { const points = []; for (let d = 0; d <= divisions; d++) { points.push(this.getPointAt(d / divisions)); } return points; } // Get total curve arc length getLength() { const lengths = this.getLengths(); return lengths[lengths.length - 1]; } // Get list of cumulative segment lengths getLengths(divisions = this.arcLengthDivisions) { if (this.cacheArcLengths && this.cacheArcLengths.length === divisions + 1 && !this.needsUpdate) { return this.cacheArcLengths; } this.needsUpdate = false; const cache = []; let current, last = this.getPoint(0); let sum = 0; cache.push(0); for (let p = 1; p <= divisions; p++) { current = this.getPoint(p / divisions); sum += current.distanceTo(last); cache.push(sum); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum: sum }; Sum is in the last element. } updateArcLengths() { this.needsUpdate = true; this.getLengths(); } // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping(u, distance) { const arcLengths = this.getLengths(); let i = 0; const il = arcLengths.length; let targetArcLength; // The targeted u distance value to get if (distance) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[il - 1]; } // binary search for the index with largest value smaller than target u distance let low = 0, high = il - 1, comparison; while (low <= high) { i = Math.floor(low + (high - low) / 2); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats comparison = arcLengths[i] - targetArcLength; if (comparison < 0) { low = i + 1; } else if (comparison > 0) { high = i - 1; } else { high = i; break; // DONE } } i = high; if (arcLengths[i] === targetArcLength) { return i / (il - 1); } // we could get finer grain at lengths, or use simple interpolation between two points const lengthBefore = arcLengths[i]; const lengthAfter = arcLengths[i + 1]; const segmentLength = lengthAfter - lengthBefore; // determine where we are between the 'before' and 'after' points const segmentFraction = (targetArcLength - lengthBefore) / segmentLength; // add that fractional amount to t const t = (i + segmentFraction) / (il - 1); return t; } // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent(t, optionalTarget) { const delta = 0.0001; let t1 = t - delta; let t2 = t + delta; // Capping in case of danger if (t1 < 0) t1 = 0; if (t2 > 1) t2 = 1; const pt1 = this.getPoint(t1); const pt2 = this.getPoint(t2); const tangent = optionalTarget || (pt1.isVector2 ? new Vector2() : new Vector3()); tangent.copy(pt2).sub(pt1).normalize(); return tangent; } getTangentAt(u, optionalTarget) { const t = this.getUtoTmapping(u); return this.getTangent(t, optionalTarget); } computeFrenetFrames(segments, closed) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf const normal = new Vector3(); const tangents = []; const normals = []; const binormals = []; const vec = new Vector3(); const mat = new Matrix4(); // compute the tangent vectors for each segment on the curve for (let i = 0; i <= segments; i++) { const u = i / segments; tangents[i] = this.getTangentAt(u, new Vector3()); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[0] = new Vector3(); binormals[0] = new Vector3(); let min = Number.MAX_VALUE; const tx = Math.abs(tangents[0].x); const ty = Math.abs(tangents[0].y); const tz = Math.abs(tangents[0].z); if (tx <= min) { min = tx; normal.set(1, 0, 0); } if (ty <= min) { min = ty; normal.set(0, 1, 0); } if (tz <= min) { normal.set(0, 0, 1); } vec.crossVectors(tangents[0], normal).normalize(); normals[0].crossVectors(tangents[0], vec); binormals[0].crossVectors(tangents[0], normals[0]); // compute the slowly-varying normal and binormal vectors for each segment on the curve for (let i = 1; i <= segments; i++) { normals[i] = normals[i - 1].clone(); binormals[i] = binormals[i - 1].clone(); vec.crossVectors(tangents[i - 1], tangents[i]); if (vec.length() > Number.EPSILON) { vec.normalize(); const theta = Math.acos(clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta)); } binormals[i].crossVectors(tangents[i], normals[i]); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if (closed === true) { let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1)); theta /= segments; if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) { theta = -theta; } for (let i = 1; i <= segments; i++) { // twist a little... normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i)); binormals[i].crossVectors(tangents[i], normals[i]); } } return { tangents: tangents, normals: normals, binormals: binormals }; } clone() { return new this.constructor().copy(this); } copy(source) { this.arcLengthDivisions = source.arcLengthDivisions; return this; } toJSON() { const data = { metadata: { version: 4.6, type: 'Curve', generator: 'Curve.toJSON' } }; data.arcLengthDivisions = this.arcLengthDivisions; data.type = this.type; return data; } fromJSON(json) { this.arcLengthDivisions = json.arcLengthDivisions; return this; } } class EllipseCurve extends Curve { constructor(aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0) { super(); this.isEllipseCurve = true; this.type = 'EllipseCurve'; this.aX = aX; this.aY = aY; this.xRadius = xRadius; this.yRadius = yRadius; this.aStartAngle = aStartAngle; this.aEndAngle = aEndAngle; this.aClockwise = aClockwise; this.aRotation = aRotation; } getPoint(t, optionalTarget) { const point = optionalTarget || new Vector2(); const twoPi = Math.PI * 2; let deltaAngle = this.aEndAngle - this.aStartAngle; const samePoints = Math.abs(deltaAngle) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while (deltaAngle < 0) deltaAngle += twoPi; while (deltaAngle > twoPi) deltaAngle -= twoPi; if (deltaAngle < Number.EPSILON) { if (samePoints) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if (this.aClockwise === true && !samePoints) { if (deltaAngle === twoPi) { deltaAngle = -twoPi; } else { deltaAngle = deltaAngle - twoPi; } } const angle = this.aStartAngle + t * deltaAngle; let x = this.aX + this.xRadius * Math.cos(angle); let y = this.aY + this.yRadius * Math.sin(angle); if (this.aRotation !== 0) { const cos = Math.cos(this.aRotation); const sin = Math.sin(this.aRotation); const tx = x - this.aX; const ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return point.set(x, y); } copy(source) { super.copy(source); this.aX = source.aX; this.aY = source.aY; this.xRadius = source.xRadius; this.yRadius = source.yRadius; this.aStartAngle = source.aStartAngle; this.aEndAngle = source.aEndAngle; this.aClockwise = source.aClockwise; this.aRotation = source.aRotation; return this; } toJSON() { const data = super.toJSON(); data.aX = this.aX; data.aY = this.aY; data.xRadius = this.xRadius; data.yRadius = this.yRadius; data.aStartAngle = this.aStartAngle; data.aEndAngle = this.aEndAngle; data.aClockwise = this.aClockwise; data.aRotation = this.aRotation; return data; } fromJSON(json) { super.fromJSON(json); this.aX = json.aX; this.aY = json.aY; this.xRadius = json.xRadius; this.yRadius = json.yRadius; this.aStartAngle = json.aStartAngle; this.aEndAngle = json.aEndAngle; this.aClockwise = json.aClockwise; this.aRotation = json.aRotation; return this; } } class ArcCurve extends EllipseCurve { constructor(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { super(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); this.isArcCurve = true; this.type = 'ArcCurve'; } } /** * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() { let c0 = 0, c1 = 0, c2 = 0, c3 = 0; /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p'(0) = t0, p'(1) = t1. */ function init(x0, x1, t0, t1) { c0 = x0; c1 = t0; c2 = -3 * x0 + 3 * x1 - 2 * t0 - t1; c3 = 2 * x0 - 2 * x1 + t0 + t1; } return { initCatmullRom: function (x0, x1, x2, x3, tension) { init(x1, x2, tension * (x2 - x0), tension * (x3 - x1)); }, initNonuniformCatmullRom: function (x0, x1, x2, x3, dt0, dt1, dt2) { // compute tangents when parameterized in [t1,t2] let t1 = (x1 - x0) / dt0 - (x2 - x0) / (dt0 + dt1) + (x2 - x1) / dt1; let t2 = (x2 - x1) / dt1 - (x3 - x1) / (dt1 + dt2) + (x3 - x2) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; init(x1, x2, t1, t2); }, calc: function (t) { const t2 = t * t; const t3 = t2 * t; return c0 + c1 * t + c2 * t2 + c3 * t3; } }; } // const tmp = /*@__PURE__*/new Vector3(); const px = /*@__PURE__*/new CubicPoly(); const py = /*@__PURE__*/new CubicPoly(); const pz = /*@__PURE__*/new CubicPoly(); class CatmullRomCurve3 extends Curve { constructor(points = [], closed = false, curveType = 'centripetal', tension = 0.5) { super(); this.isCatmullRomCurve3 = true; this.type = 'CatmullRomCurve3'; this.points = points; this.closed = closed; this.curveType = curveType; this.tension = tension; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const points = this.points; const l = points.length; const p = (l - (this.closed ? 0 : 1)) * t; let intPoint = Math.floor(p); let weight = p - intPoint; if (this.closed) { intPoint += intPoint > 0 ? 0 : (Math.floor(Math.abs(intPoint) / l) + 1) * l; } else if (weight === 0 && intPoint === l - 1) { intPoint = l - 2; weight = 1; } let p0, p3; // 4 points (p1 & p2 defined below) if (this.closed || intPoint > 0) { p0 = points[(intPoint - 1) % l]; } else { // extrapolate first point tmp.subVectors(points[0], points[1]).add(points[0]); p0 = tmp; } const p1 = points[intPoint % l]; const p2 = points[(intPoint + 1) % l]; if (this.closed || intPoint + 2 < l) { p3 = points[(intPoint + 2) % l]; } else { // extrapolate last point tmp.subVectors(points[l - 1], points[l - 2]).add(points[l - 1]); p3 = tmp; } if (this.curveType === 'centripetal' || this.curveType === 'chordal') { // init Centripetal / Chordal Catmull-Rom const pow = this.curveType === 'chordal' ? 0.5 : 0.25; let dt0 = Math.pow(p0.distanceToSquared(p1), pow); let dt1 = Math.pow(p1.distanceToSquared(p2), pow); let dt2 = Math.pow(p2.distanceToSquared(p3), pow); // safety check for repeated points if (dt1 < 1e-4) dt1 = 1.0; if (dt0 < 1e-4) dt0 = dt1; if (dt2 < 1e-4) dt2 = dt1; px.initNonuniformCatmullRom(p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2); py.initNonuniformCatmullRom(p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2); pz.initNonuniformCatmullRom(p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2); } else if (this.curveType === 'catmullrom') { px.initCatmullRom(p0.x, p1.x, p2.x, p3.x, this.tension); py.initCatmullRom(p0.y, p1.y, p2.y, p3.y, this.tension); pz.initCatmullRom(p0.z, p1.z, p2.z, p3.z, this.tension); } point.set(px.calc(weight), py.calc(weight), pz.calc(weight)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } this.closed = source.closed; this.curveType = source.curveType; this.tension = source.tension; return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } data.closed = this.closed; data.curveType = this.curveType; data.tension = this.tension; return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector3().fromArray(point)); } this.closed = json.closed; this.curveType = json.curveType; this.tension = json.tension; return this; } } /** * Bezier Curves formulas obtained from * https://en.wikipedia.org/wiki/B%C3%A9zier_curve */ function CatmullRom(t, p0, p1, p2, p3) { const v0 = (p2 - p0) * 0.5; const v1 = (p3 - p1) * 0.5; const t2 = t * t; const t3 = t * t2; return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1; } // function QuadraticBezierP0(t, p) { const k = 1 - t; return k * k * p; } function QuadraticBezierP1(t, p) { return 2 * (1 - t) * t * p; } function QuadraticBezierP2(t, p) { return t * t * p; } function QuadraticBezier(t, p0, p1, p2) { return QuadraticBezierP0(t, p0) + QuadraticBezierP1(t, p1) + QuadraticBezierP2(t, p2); } // function CubicBezierP0(t, p) { const k = 1 - t; return k * k * k * p; } function CubicBezierP1(t, p) { const k = 1 - t; return 3 * k * k * t * p; } function CubicBezierP2(t, p) { return 3 * (1 - t) * t * t * p; } function CubicBezierP3(t, p) { return t * t * t * p; } function CubicBezier(t, p0, p1, p2, p3) { return CubicBezierP0(t, p0) + CubicBezierP1(t, p1) + CubicBezierP2(t, p2) + CubicBezierP3(t, p3); } class CubicBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2()) { super(); this.isCubicBezierCurve = true; this.type = 'CubicBezierCurve'; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } class CubicBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3()) { super(); this.isCubicBezierCurve3 = true; this.type = 'CubicBezierCurve3'; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; point.set(CubicBezier(t, v0.x, v1.x, v2.x, v3.x), CubicBezier(t, v0.y, v1.y, v2.y, v3.y), CubicBezier(t, v0.z, v1.z, v2.z, v3.z)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); this.v3.copy(source.v3); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); data.v3 = this.v3.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); this.v3.fromArray(json.v3); return this; } } class LineCurve extends Curve { constructor(v1 = new Vector2(), v2 = new Vector2()) { super(); this.isLineCurve = true; this.type = 'LineCurve'; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } getTangent(t, optionalTarget = new Vector2()) { return optionalTarget.subVectors(this.v2, this.v1).normalize(); } getTangentAt(u, optionalTarget) { return this.getTangent(u, optionalTarget); } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class LineCurve3 extends Curve { constructor(v1 = new Vector3(), v2 = new Vector3()) { super(); this.isLineCurve3 = true; this.type = 'LineCurve3'; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; if (t === 1) { point.copy(this.v2); } else { point.copy(this.v2).sub(this.v1); point.multiplyScalar(t).add(this.v1); } return point; } // Line curve is linear, so we can overwrite default getPointAt getPointAt(u, optionalTarget) { return this.getPoint(u, optionalTarget); } getTangent(t, optionalTarget = new Vector3()) { return optionalTarget.subVectors(this.v2, this.v1).normalize(); } getTangentAt(u, optionalTarget) { return this.getTangent(u, optionalTarget); } copy(source) { super.copy(source); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class QuadraticBezierCurve extends Curve { constructor(v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2()) { super(); this.isQuadraticBezierCurve = true; this.type = 'QuadraticBezierCurve'; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class QuadraticBezierCurve3 extends Curve { constructor(v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3()) { super(); this.isQuadraticBezierCurve3 = true; this.type = 'QuadraticBezierCurve3'; this.v0 = v0; this.v1 = v1; this.v2 = v2; } getPoint(t, optionalTarget = new Vector3()) { const point = optionalTarget; const v0 = this.v0, v1 = this.v1, v2 = this.v2; point.set(QuadraticBezier(t, v0.x, v1.x, v2.x), QuadraticBezier(t, v0.y, v1.y, v2.y), QuadraticBezier(t, v0.z, v1.z, v2.z)); return point; } copy(source) { super.copy(source); this.v0.copy(source.v0); this.v1.copy(source.v1); this.v2.copy(source.v2); return this; } toJSON() { const data = super.toJSON(); data.v0 = this.v0.toArray(); data.v1 = this.v1.toArray(); data.v2 = this.v2.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.v0.fromArray(json.v0); this.v1.fromArray(json.v1); this.v2.fromArray(json.v2); return this; } } class SplineCurve extends Curve { constructor(points = []) { super(); this.isSplineCurve = true; this.type = 'SplineCurve'; this.points = points; } getPoint(t, optionalTarget = new Vector2()) { const point = optionalTarget; const points = this.points; const p = (points.length - 1) * t; const intPoint = Math.floor(p); const weight = p - intPoint; const p0 = points[intPoint === 0 ? intPoint : intPoint - 1]; const p1 = points[intPoint]; const p2 = points[intPoint > points.length - 2 ? points.length - 1 : intPoint + 1]; const p3 = points[intPoint > points.length - 3 ? points.length - 1 : intPoint + 2]; point.set(CatmullRom(weight, p0.x, p1.x, p2.x, p3.x), CatmullRom(weight, p0.y, p1.y, p2.y, p3.y)); return point; } copy(source) { super.copy(source); this.points = []; for (let i = 0, l = source.points.length; i < l; i++) { const point = source.points[i]; this.points.push(point.clone()); } return this; } toJSON() { const data = super.toJSON(); data.points = []; for (let i = 0, l = this.points.length; i < l; i++) { const point = this.points[i]; data.points.push(point.toArray()); } return data; } fromJSON(json) { super.fromJSON(json); this.points = []; for (let i = 0, l = json.points.length; i < l; i++) { const point = json.points[i]; this.points.push(new Vector2().fromArray(point)); } return this; } } var Curves = /*#__PURE__*/Object.freeze({ __proto__: null, ArcCurve: ArcCurve, CatmullRomCurve3: CatmullRomCurve3, CubicBezierCurve: CubicBezierCurve, CubicBezierCurve3: CubicBezierCurve3, EllipseCurve: EllipseCurve, LineCurve: LineCurve, LineCurve3: LineCurve3, QuadraticBezierCurve: QuadraticBezierCurve, QuadraticBezierCurve3: QuadraticBezierCurve3, SplineCurve: SplineCurve }); /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ class CurvePath extends Curve { constructor() { super(); this.type = 'CurvePath'; this.curves = []; this.autoClose = false; // Automatically closes the path } add(curve) { this.curves.push(curve); } closePath() { // Add a line curve if start and end of lines are not connected const startPoint = this.curves[0].getPoint(0); const endPoint = this.curves[this.curves.length - 1].getPoint(1); if (!startPoint.equals(endPoint)) { this.curves.push(new LineCurve(endPoint, startPoint)); } } // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t') getPoint(t, optionalTarget) { const d = t * this.getLength(); const curveLengths = this.getCurveLengths(); let i = 0; // To think about boundaries points. while (i < curveLengths.length) { if (curveLengths[i] >= d) { const diff = curveLengths[i] - d; const curve = this.curves[i]; const segmentLength = curve.getLength(); const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt(u, optionalTarget); } i++; } return null; // loop where sum != 0, sum > d , sum+1 <d } // We cannot use the default THREE.Curve getPoint() with getLength() because in // THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath // getPoint() depends on getLength getLength() { const lens = this.getCurveLengths(); return lens[lens.length - 1]; } // cacheLengths must be recalculated. updateArcLengths() { this.needsUpdate = true; this.cacheLengths = null; this.getCurveLengths(); } // Compute lengths and cache them // We cannot overwrite getLengths() because UtoT mapping uses it. getCurveLengths() { // We use cache values if curves and cache array are same length if (this.cacheLengths && this.cacheLengths.length === this.curves.length) { return this.cacheLengths; } // Get length of sub-curve // Push sums into cached array const lengths = []; let sums = 0; for (let i = 0, l = this.curves.length; i < l; i++) { sums += this.curves[i].getLength(); lengths.push(sums); } this.cacheLengths = lengths; return lengths; } getSpacedPoints(divisions = 40) { const points = []; for (let i = 0; i <= divisions; i++) { points.push(this.getPoint(i / divisions)); } if (this.autoClose) { points.push(points[0]); } return points; } getPoints(divisions = 12) { const points = []; let last; for (let i = 0, curves = this.curves; i < curves.length; i++) { const curve = curves[i]; const resolution = curve.isEllipseCurve ? divisions * 2 : curve.isLineCurve || curve.isLineCurve3 ? 1 : curve.isSplineCurve ? divisions * curve.points.length : divisions; const pts = curve.getPoints(resolution); for (let j = 0; j < pts.length; j++) { const point = pts[j]; if (last && last.equals(point)) continue; // ensures no consecutive points are duplicates points.push(point); last = point; } } if (this.autoClose && points.length > 1 && !points[points.length - 1].equals(points[0])) { points.push(points[0]); } return points; } copy(source) { super.copy(source); this.curves = []; for (let i = 0, l = source.curves.length; i < l; i++) { const curve = source.curves[i]; this.curves.push(curve.clone()); } this.autoClose = source.autoClose; return this; } toJSON() { const data = super.toJSON(); data.autoClose = this.autoClose; data.curves = []; for (let i = 0, l = this.curves.length; i < l; i++) { const curve = this.curves[i]; data.curves.push(curve.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.autoClose = json.autoClose; this.curves = []; for (let i = 0, l = json.curves.length; i < l; i++) { const curve = json.curves[i]; this.curves.push(new Curves[curve.type]().fromJSON(curve)); } return this; } } class Path extends CurvePath { constructor(points) { super(); this.type = 'Path'; this.currentPoint = new Vector2(); if (points) { this.setFromPoints(points); } } setFromPoints(points) { this.moveTo(points[0].x, points[0].y); for (let i = 1, l = points.length; i < l; i++) { this.lineTo(points[i].x, points[i].y); } return this; } moveTo(x, y) { this.currentPoint.set(x, y); // TODO consider referencing vectors instead of copying? return this; } lineTo(x, y) { const curve = new LineCurve(this.currentPoint.clone(), new Vector2(x, y)); this.curves.push(curve); this.currentPoint.set(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { const curve = new QuadraticBezierCurve(this.currentPoint.clone(), new Vector2(aCPx, aCPy), new Vector2(aX, aY)); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { const curve = new CubicBezierCurve(this.currentPoint.clone(), new Vector2(aCP1x, aCP1y), new Vector2(aCP2x, aCP2y), new Vector2(aX, aY)); this.curves.push(curve); this.currentPoint.set(aX, aY); return this; } splineThru(pts /*Array of Vector*/) { const npts = [this.currentPoint.clone()].concat(pts); const curve = new SplineCurve(npts); this.curves.push(curve); this.currentPoint.copy(pts[pts.length - 1]); return this; } arc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absarc(aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } absarc(aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise) { this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); return this; } ellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { const x0 = this.currentPoint.x; const y0 = this.currentPoint.y; this.absellipse(aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); return this; } absellipse(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation) { const curve = new EllipseCurve(aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation); if (this.curves.length > 0) { // if a previous curve is present, attempt to join const firstPoint = curve.getPoint(0); if (!firstPoint.equals(this.currentPoint)) { this.lineTo(firstPoint.x, firstPoint.y); } } this.curves.push(curve); const lastPoint = curve.getPoint(1); this.currentPoint.copy(lastPoint); return this; } copy(source) { super.copy(source); this.currentPoint.copy(source.currentPoint); return this; } toJSON() { const data = super.toJSON(); data.currentPoint = this.currentPoint.toArray(); return data; } fromJSON(json) { super.fromJSON(json); this.currentPoint.fromArray(json.currentPoint); return this; } } class LatheGeometry extends BufferGeometry { constructor(points = [new Vector2(0, -0.5), new Vector2(0.5, 0), new Vector2(0, 0.5)], segments = 12, phiStart = 0, phiLength = Math.PI * 2) { super(); this.type = 'LatheGeometry'; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; segments = Math.floor(segments); // clamp phiLength so it's in range of [ 0, 2PI ] phiLength = clamp(phiLength, 0, Math.PI * 2); // buffers const indices = []; const vertices = []; const uvs = []; const initNormals = []; const normals = []; // helper variables const inverseSegments = 1.0 / segments; const vertex = new Vector3(); const uv = new Vector2(); const normal = new Vector3(); const curNormal = new Vector3(); const prevNormal = new Vector3(); let dx = 0; let dy = 0; // pre-compute normals for initial "meridian" for (let j = 0; j <= points.length - 1; j++) { switch (j) { case 0: // special handling for 1st vertex on path dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; prevNormal.copy(normal); normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); break; case points.length - 1: // special handling for last Vertex on path initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z); break; default: // default handling for all vertices in between dx = points[j + 1].x - points[j].x; dy = points[j + 1].y - points[j].y; normal.x = dy * 1.0; normal.y = -dx; normal.z = dy * 0.0; curNormal.copy(normal); normal.x += prevNormal.x; normal.y += prevNormal.y; normal.z += prevNormal.z; normal.normalize(); initNormals.push(normal.x, normal.y, normal.z); prevNormal.copy(curNormal); } } // generate vertices, uvs and normals for (let i = 0; i <= segments; i++) { const phi = phiStart + i * inverseSegments * phiLength; const sin = Math.sin(phi); const cos = Math.cos(phi); for (let j = 0; j <= points.length - 1; j++) { // vertex vertex.x = points[j].x * sin; vertex.y = points[j].y; vertex.z = points[j].x * cos; vertices.push(vertex.x, vertex.y, vertex.z); // uv uv.x = i / segments; uv.y = j / (points.length - 1); uvs.push(uv.x, uv.y); // normal const x = initNormals[3 * j + 0] * sin; const y = initNormals[3 * j + 1]; const z = initNormals[3 * j + 0] * cos; normals.push(x, y, z); } } // indices for (let i = 0; i < segments; i++) { for (let j = 0; j < points.length - 1; j++) { const base = j + i * points.length; const a = base; const b = base + points.length; const c = base + points.length + 1; const d = base + 1; // faces indices.push(a, b, d); indices.push(c, d, b); } } // build geometry this.setIndex(indices); this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new LatheGeometry(data.points, data.segments, data.phiStart, data.phiLength); } } class CapsuleGeometry extends LatheGeometry { constructor(radius = 1, length = 1, capSegments = 4, radialSegments = 8) { const path = new Path(); path.absarc(0, -length / 2, radius, Math.PI * 1.5, 0); path.absarc(0, length / 2, radius, 0, Math.PI * 0.5); super(path.getPoints(capSegments), radialSegments); this.type = 'CapsuleGeometry'; this.parameters = { radius: radius, height: length, capSegments: capSegments, radialSegments: radialSegments }; } static fromJSON(data) { return new CapsuleGeometry(data.radius, data.length, data.capSegments, data.radialSegments); } } class CircleGeometry extends BufferGeometry { constructor(radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = 'CircleGeometry'; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; segments = Math.max(3, segments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const uv = new Vector2(); // center point vertices.push(0, 0, 0); normals.push(0, 0, 1); uvs.push(0.5, 0.5); for (let s = 0, i = 3; s <= segments; s++, i += 3) { const segment = thetaStart + s / segments * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uvs uv.x = (vertices[i] / radius + 1) / 2; uv.y = (vertices[i + 1] / radius + 1) / 2; uvs.push(uv.x, uv.y); } // indices for (let i = 1; i <= segments; i++) { indices.push(i, i + 1, 0); } // build geometry this.setIndex(indices); this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new CircleGeometry(data.radius, data.segments, data.thetaStart, data.thetaLength); } } class CylinderGeometry extends BufferGeometry { constructor(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = 'CylinderGeometry'; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; const scope = this; radialSegments = Math.floor(radialSegments); heightSegments = Math.floor(heightSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let index = 0; const indexArray = []; const halfHeight = height / 2; let groupStart = 0; // generate geometry generateTorso(); if (openEnded === false) { if (radiusTop > 0) generateCap(true); if (radiusBottom > 0) generateCap(false); } // build geometry this.setIndex(indices); this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); function generateTorso() { const normal = new Vector3(); const vertex = new Vector3(); let groupCount = 0; // this will be used to calculate the normal const slope = (radiusBottom - radiusTop) / height; // generate vertices, normals and uvs for (let y = 0; y <= heightSegments; y++) { const indexRow = []; const v = y / heightSegments; // calculate the radius of the current row const radius = v * (radiusBottom - radiusTop) + radiusTop; for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const sinTheta = Math.sin(theta); const cosTheta = Math.cos(theta); // vertex vertex.x = radius * sinTheta; vertex.y = -v * height + halfHeight; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.set(sinTheta, slope, cosTheta).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u, 1 - v); // save index of vertex in respective row indexRow.push(index++); } // now save vertices of the row in our index array indexArray.push(indexRow); } // generate indices for (let x = 0; x < radialSegments; x++) { for (let y = 0; y < heightSegments; y++) { // we use the index array to access the correct indices const a = indexArray[y][x]; const b = indexArray[y + 1][x]; const c = indexArray[y + 1][x + 1]; const d = indexArray[y][x + 1]; // faces indices.push(a, b, d); indices.push(b, c, d); // update group counter groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, 0); // calculate new start value for groups groupStart += groupCount; } function generateCap(top) { // save the index of the first center vertex const centerIndexStart = index; const uv = new Vector2(); const vertex = new Vector3(); let groupCount = 0; const radius = top === true ? radiusTop : radiusBottom; const sign = top === true ? 1 : -1; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for (let x = 1; x <= radialSegments; x++) { // vertex vertices.push(0, halfHeight * sign, 0); // normal normals.push(0, sign, 0); // uv uvs.push(0.5, 0.5); // increase index index++; } // save the index of the last center vertex const centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for (let x = 0; x <= radialSegments; x++) { const u = x / radialSegments; const theta = u * thetaLength + thetaStart; const cosTheta = Math.cos(theta); const sinTheta = Math.sin(theta); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, sign, 0); // uv uv.x = cosTheta * 0.5 + 0.5; uv.y = sinTheta * 0.5 * sign + 0.5; uvs.push(uv.x, uv.y); // increase index index++; } // generate indices for (let x = 0; x < radialSegments; x++) { const c = centerIndexStart + x; const i = centerIndexEnd + x; if (top === true) { // face top indices.push(i, i + 1, c); } else { // face bottom indices.push(i + 1, i, c); } groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup(groupStart, groupCount, top === true ? 1 : 2); // calculate new start value for groups groupStart += groupCount; } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new CylinderGeometry(data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); } } class ConeGeometry extends CylinderGeometry { constructor(radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) { super(0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength); this.type = 'ConeGeometry'; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } static fromJSON(data) { return new ConeGeometry(data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength); } } class PolyhedronGeometry extends BufferGeometry { constructor(vertices = [], indices = [], radius = 1, detail = 0) { super(); this.type = 'PolyhedronGeometry'; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; // default buffer data const vertexBuffer = []; const uvBuffer = []; // the subdivision creates the vertex buffer data subdivide(detail); // all vertices should lie on a conceptual sphere with a given radius applyRadius(radius); // finally, create the uv data generateUVs(); // build non-indexed geometry this.setAttribute('position', new Float32BufferAttribute(vertexBuffer, 3)); this.setAttribute('normal', new Float32BufferAttribute(vertexBuffer.slice(), 3)); this.setAttribute('uv', new Float32BufferAttribute(uvBuffer, 2)); if (detail === 0) { this.computeVertexNormals(); // flat normals } else { this.normalizeNormals(); // smooth normals } // helper functions function subdivide(detail) { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); // iterate over all faces and apply a subdivision with the given detail value for (let i = 0; i < indices.length; i += 3) { // get the vertices of the face getVertexByIndex(indices[i + 0], a); getVertexByIndex(indices[i + 1], b); getVertexByIndex(indices[i + 2], c); // perform subdivision subdivideFace(a, b, c, detail); } } function subdivideFace(a, b, c, detail) { const cols = detail + 1; // we use this multidimensional array as a data structure for creating the subdivision const v = []; // construct all of the vertices for this subdivision for (let i = 0; i <= cols; i++) { v[i] = []; const aj = a.clone().lerp(c, i / cols); const bj = b.clone().lerp(c, i / cols); const rows = cols - i; for (let j = 0; j <= rows; j++) { if (j === 0 && i === cols) { v[i][j] = aj; } else { v[i][j] = aj.clone().lerp(bj, j / rows); } } } // construct all of the faces for (let i = 0; i < cols; i++) { for (let j = 0; j < 2 * (cols - i) - 1; j++) { const k = Math.floor(j / 2); if (j % 2 === 0) { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k]); pushVertex(v[i][k]); } else { pushVertex(v[i][k + 1]); pushVertex(v[i + 1][k + 1]); pushVertex(v[i + 1][k]); } } } } function applyRadius(radius) { const vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; vertex.normalize().multiplyScalar(radius); vertexBuffer[i + 0] = vertex.x; vertexBuffer[i + 1] = vertex.y; vertexBuffer[i + 2] = vertex.z; } } function generateUVs() { const vertex = new Vector3(); for (let i = 0; i < vertexBuffer.length; i += 3) { vertex.x = vertexBuffer[i + 0]; vertex.y = vertexBuffer[i + 1]; vertex.z = vertexBuffer[i + 2]; const u = azimuth(vertex) / 2 / Math.PI + 0.5; const v = inclination(vertex) / Math.PI + 0.5; uvBuffer.push(u, 1 - v); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for (let i = 0; i < uvBuffer.length; i += 6) { // uv data of a single face const x0 = uvBuffer[i + 0]; const x1 = uvBuffer[i + 2]; const x2 = uvBuffer[i + 4]; const max = Math.max(x0, x1, x2); const min = Math.min(x0, x1, x2); // 0.9 is somewhat arbitrary if (max > 0.9 && min < 0.1) { if (x0 < 0.2) uvBuffer[i + 0] += 1; if (x1 < 0.2) uvBuffer[i + 2] += 1; if (x2 < 0.2) uvBuffer[i + 4] += 1; } } } function pushVertex(vertex) { vertexBuffer.push(vertex.x, vertex.y, vertex.z); } function getVertexByIndex(index, vertex) { const stride = index * 3; vertex.x = vertices[stride + 0]; vertex.y = vertices[stride + 1]; vertex.z = vertices[stride + 2]; } function correctUVs() { const a = new Vector3(); const b = new Vector3(); const c = new Vector3(); const centroid = new Vector3(); const uvA = new Vector2(); const uvB = new Vector2(); const uvC = new Vector2(); for (let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6) { a.set(vertexBuffer[i + 0], vertexBuffer[i + 1], vertexBuffer[i + 2]); b.set(vertexBuffer[i + 3], vertexBuffer[i + 4], vertexBuffer[i + 5]); c.set(vertexBuffer[i + 6], vertexBuffer[i + 7], vertexBuffer[i + 8]); uvA.set(uvBuffer[j + 0], uvBuffer[j + 1]); uvB.set(uvBuffer[j + 2], uvBuffer[j + 3]); uvC.set(uvBuffer[j + 4], uvBuffer[j + 5]); centroid.copy(a).add(b).add(c).divideScalar(3); const azi = azimuth(centroid); correctUV(uvA, j + 0, a, azi); correctUV(uvB, j + 2, b, azi); correctUV(uvC, j + 4, c, azi); } } function correctUV(uv, stride, vector, azimuth) { if (azimuth < 0 && uv.x === 1) { uvBuffer[stride] = uv.x - 1; } if (vector.x === 0 && vector.z === 0) { uvBuffer[stride] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth(vector) { return Math.atan2(vector.z, -vector.x); } // Angle above the XZ plane. function inclination(vector) { return Math.atan2(-vector.y, Math.sqrt(vector.x * vector.x + vector.z * vector.z)); } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new PolyhedronGeometry(data.vertices, data.indices, data.radius, data.details); } } class DodecahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const r = 1 / t; const vertices = [ // (±1, ±1, ±1) -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, -r, -t, 0, -r, t, 0, r, -t, 0, r, t, // (±1/φ, ±φ, 0) -r, -t, 0, -r, t, 0, r, -t, 0, r, t, 0, // (±φ, 0, ±1/φ) -t, 0, -r, t, 0, -r, -t, 0, r, t, 0, r]; const indices = [3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9]; super(vertices, indices, radius, detail); this.type = 'DodecahedronGeometry'; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new DodecahedronGeometry(data.radius, data.detail); } } const _v0 = /*@__PURE__*/new Vector3(); const _v1$1 = /*@__PURE__*/new Vector3(); const _normal = /*@__PURE__*/new Vector3(); const _triangle = /*@__PURE__*/new Triangle(); class EdgesGeometry extends BufferGeometry { constructor(geometry = null, thresholdAngle = 1) { super(); this.type = 'EdgesGeometry'; this.parameters = { geometry: geometry, thresholdAngle: thresholdAngle }; if (geometry !== null) { const precisionPoints = 4; const precision = Math.pow(10, precisionPoints); const thresholdDot = Math.cos(DEG2RAD * thresholdAngle); const indexAttr = geometry.getIndex(); const positionAttr = geometry.getAttribute('position'); const indexCount = indexAttr ? indexAttr.count : positionAttr.count; const indexArr = [0, 0, 0]; const vertKeys = ['a', 'b', 'c']; const hashes = new Array(3); const edgeData = {}; const vertices = []; for (let i = 0; i < indexCount; i += 3) { if (indexAttr) { indexArr[0] = indexAttr.getX(i); indexArr[1] = indexAttr.getX(i + 1); indexArr[2] = indexAttr.getX(i + 2); } else { indexArr[0] = i; indexArr[1] = i + 1; indexArr[2] = i + 2; } const { a, b, c } = _triangle; a.fromBufferAttribute(positionAttr, indexArr[0]); b.fromBufferAttribute(positionAttr, indexArr[1]); c.fromBufferAttribute(positionAttr, indexArr[2]); _triangle.getNormal(_normal); // create hashes for the edge from the vertices hashes[0] = `${Math.round(a.x * precision)},${Math.round(a.y * precision)},${Math.round(a.z * precision)}`; hashes[1] = `${Math.round(b.x * precision)},${Math.round(b.y * precision)},${Math.round(b.z * precision)}`; hashes[2] = `${Math.round(c.x * precision)},${Math.round(c.y * precision)},${Math.round(c.z * precision)}`; // skip degenerate triangles if (hashes[0] === hashes[1] || hashes[1] === hashes[2] || hashes[2] === hashes[0]) { continue; } // iterate over every edge for (let j = 0; j < 3; j++) { // get the first and next vertex making up the edge const jNext = (j + 1) % 3; const vecHash0 = hashes[j]; const vecHash1 = hashes[jNext]; const v0 = _triangle[vertKeys[j]]; const v1 = _triangle[vertKeys[jNext]]; const hash = `${vecHash0}_${vecHash1}`; const reverseHash = `${vecHash1}_${vecHash0}`; if (reverseHash in edgeData && edgeData[reverseHash]) { // if we found a sibling edge add it into the vertex array if // it meets the angle threshold and delete the edge from the map. if (_normal.dot(edgeData[reverseHash].normal) <= thresholdDot) { vertices.push(v0.x, v0.y, v0.z); vertices.push(v1.x, v1.y, v1.z); } edgeData[reverseHash] = null; } else if (!(hash in edgeData)) { // if we've already got an edge here then skip adding a new one edgeData[hash] = { index0: indexArr[j], index1: indexArr[jNext], normal: _normal.clone() }; } } } // iterate over all remaining, unmatched edges and add them to the vertex array for (const key in edgeData) { if (edgeData[key]) { const { index0, index1 } = edgeData[key]; _v0.fromBufferAttribute(positionAttr, index0); _v1$1.fromBufferAttribute(positionAttr, index1); vertices.push(_v0.x, _v0.y, _v0.z); vertices.push(_v1$1.x, _v1$1.y, _v1$1.z); } } this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } } class Shape extends Path { constructor(points) { super(points); this.uuid = generateUUID(); this.type = 'Shape'; this.holes = []; } getPointsHoles(divisions) { const holesPts = []; for (let i = 0, l = this.holes.length; i < l; i++) { holesPts[i] = this.holes[i].getPoints(divisions); } return holesPts; } // get points of shape and holes (keypoints based on segments parameter) extractPoints(divisions) { return { shape: this.getPoints(divisions), holes: this.getPointsHoles(divisions) }; } copy(source) { super.copy(source); this.holes = []; for (let i = 0, l = source.holes.length; i < l; i++) { const hole = source.holes[i]; this.holes.push(hole.clone()); } return this; } toJSON() { const data = super.toJSON(); data.uuid = this.uuid; data.holes = []; for (let i = 0, l = this.holes.length; i < l; i++) { const hole = this.holes[i]; data.holes.push(hole.toJSON()); } return data; } fromJSON(json) { super.fromJSON(json); this.uuid = json.uuid; this.holes = []; for (let i = 0, l = json.holes.length; i < l; i++) { const hole = json.holes[i]; this.holes.push(new Path().fromJSON(hole)); } return this; } } /** * Port from https://github.com/mapbox/earcut (v2.2.4) */ const Earcut = { triangulate: function (data, holeIndices, dim = 2) { const hasHoles = holeIndices && holeIndices.length; const outerLen = hasHoles ? holeIndices[0] * dim : data.length; let outerNode = linkedList(data, 0, outerLen, dim, true); const triangles = []; if (!outerNode || outerNode.next === outerNode.prev) return triangles; let minX, minY, maxX, maxY, x, y, invSize; if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox if (data.length > 80 * dim) { minX = maxX = data[0]; minY = maxY = data[1]; for (let i = dim; i < outerLen; i += dim) { x = data[i]; y = data[i + 1]; if (x < minX) minX = x; if (y < minY) minY = y; if (x > maxX) maxX = x; if (y > maxY) maxY = y; } // minX, minY and invSize are later used to transform coords into integers for z-order calculation invSize = Math.max(maxX - minX, maxY - minY); invSize = invSize !== 0 ? 32767 / invSize : 0; } earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0); return triangles; } }; // create a circular doubly linked list from polygon points in the specified winding order function linkedList(data, start, end, dim, clockwise) { let i, last; if (clockwise === signedArea(data, start, end, dim) > 0) { for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last); } else { for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last); } if (last && equals(last, last.next)) { removeNode(last); last = last.next; } return last; } // eliminate colinear or duplicate points function filterPoints(start, end) { if (!start) return start; if (!end) end = start; let p = start, again; do { again = false; if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) { removeNode(p); p = end = p.prev; if (p === p.next) break; again = true; } else { p = p.next; } } while (again || p !== end); return end; } // main ear slicing loop which triangulates a polygon (given as a linked list) function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) { if (!ear) return; // interlink polygon nodes in z-order if (!pass && invSize) indexCurve(ear, minX, minY, invSize); let stop = ear, prev, next; // iterate through ears, slicing them one by one while (ear.prev !== ear.next) { prev = ear.prev; next = ear.next; if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) { // cut off the triangle triangles.push(prev.i / dim | 0); triangles.push(ear.i / dim | 0); triangles.push(next.i / dim | 0); removeNode(ear); // skipping the next vertex leads to less sliver triangles ear = next.next; stop = next.next; continue; } ear = next; // if we looped through the whole remaining polygon and can't find any more ears if (ear === stop) { // try filtering points and slicing again if (!pass) { earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); // if this didn't work, try curing all small self-intersections locally } else if (pass === 1) { ear = cureLocalIntersections(filterPoints(ear), triangles, dim); earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); // as a last resort, try splitting the remaining polygon into two } else if (pass === 2) { splitEarcut(ear, triangles, dim, minX, minY, invSize); } break; } } } // check whether a polygon node forms a valid ear with adjacent nodes function isEar(ear) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can't be an ear // now make sure we don't have other points inside the potential ear const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; // triangle bbox; min & max are calculated like this for speed const x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy; let p = c.next; while (p !== a) { if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.next; } return true; } function isEarHashed(ear, minX, minY, invSize) { const a = ear.prev, b = ear, c = ear.next; if (area(a, b, c) >= 0) return false; // reflex, can't be an ear const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; // triangle bbox; min & max are calculated like this for speed const x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy; // z-order range for the current triangle bbox; const minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize); let p = ear.prevZ, n = ear.nextZ; // look for points inside the triangle in both directions while (p && p.z >= minZ && n && n.z <= maxZ) { if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } // look for remaining points in decreasing z-order while (p && p.z >= minZ) { if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false; p = p.prevZ; } // look for remaining points in increasing z-order while (n && n.z <= maxZ) { if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false; n = n.nextZ; } return true; } // go through all polygon nodes and cure small local self-intersections function cureLocalIntersections(start, triangles, dim) { let p = start; do { const a = p.prev, b = p.next.next; if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) { triangles.push(a.i / dim | 0); triangles.push(p.i / dim | 0); triangles.push(b.i / dim | 0); // remove two nodes involved removeNode(p); removeNode(p.next); p = start = b; } p = p.next; } while (p !== start); return filterPoints(p); } // try splitting polygon into two and triangulate them independently function splitEarcut(start, triangles, dim, minX, minY, invSize) { // look for a valid diagonal that divides the polygon into two let a = start; do { let b = a.next.next; while (b !== a.prev) { if (a.i !== b.i && isValidDiagonal(a, b)) { // split the polygon in two by the diagonal let c = splitPolygon(a, b); // filter colinear points around the cuts a = filterPoints(a, a.next); c = filterPoints(c, c.next); // run earcut on each half earcutLinked(a, triangles, dim, minX, minY, invSize, 0); earcutLinked(c, triangles, dim, minX, minY, invSize, 0); return; } b = b.next; } a = a.next; } while (a !== start); } // link every hole into the outer loop, producing a single-ring polygon without holes function eliminateHoles(data, holeIndices, outerNode, dim) { const queue = []; let i, len, start, end, list; for (i = 0, len = holeIndices.length; i < len; i++) { start = holeIndices[i] * dim; end = i < len - 1 ? holeIndices[i + 1] * dim : data.length; list = linkedList(data, start, end, dim, false); if (list === list.next) list.steiner = true; queue.push(getLeftmost(list)); } queue.sort(compareX); // process holes from left to right for (i = 0; i < queue.length; i++) { outerNode = eliminateHole(queue[i], outerNode); } return outerNode; } function compareX(a, b) { return a.x - b.x; } // find a bridge between vertices that connects hole with an outer ring and link it function eliminateHole(hole, outerNode) { const bridge = findHoleBridge(hole, outerNode); if (!bridge) { return outerNode; } const bridgeReverse = splitPolygon(bridge, hole); // filter collinear points around the cuts filterPoints(bridgeReverse, bridgeReverse.next); return filterPoints(bridge, bridge.next); } // David Eberly's algorithm for finding a bridge between hole and outer polygon function findHoleBridge(hole, outerNode) { let p = outerNode, qx = -Infinity, m; const hx = hole.x, hy = hole.y; // find a segment intersected by a ray from the hole's leftmost point to the left; // segment's endpoint with lesser x will be potential connection point do { if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) { const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y); if (x <= hx && x > qx) { qx = x; m = p.x < p.next.x ? p : p.next; if (x === hx) return m; // hole touches outer segment; pick leftmost endpoint } } p = p.next; } while (p !== outerNode); if (!m) return null; // look for points inside the triangle of hole point, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the point of the minimum angle with the ray as connection point const stop = m, mx = m.x, my = m.y; let tanMin = Infinity, tan; p = m; do { if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) { tan = Math.abs(hy - p.y) / (hx - p.x); // tangential if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) { m = p; tanMin = tan; } } p = p.next; } while (p !== stop); return m; } // whether sector in vertex m contains sector in vertex p in the same coordinates function sectorContainsSector(m, p) { return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0; } // interlink polygon nodes in z-order function indexCurve(start, minX, minY, invSize) { let p = start; do { if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize); p.prevZ = p.prev; p.nextZ = p.next; p = p.next; } while (p !== start); p.prevZ.nextZ = null; p.prevZ = null; sortLinked(p); } // Simon Tatham's linked list merge sort algorithm // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html function sortLinked(list) { let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1; do { p = list; list = null; tail = null; numMerges = 0; while (p) { numMerges++; q = p; pSize = 0; for (i = 0; i < inSize; i++) { pSize++; q = q.nextZ; if (!q) break; } qSize = inSize; while (pSize > 0 || qSize > 0 && q) { if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) { e = p; p = p.nextZ; pSize--; } else { e = q; q = q.nextZ; qSize--; } if (tail) tail.nextZ = e;else list = e; e.prevZ = tail; tail = e; } p = q; } tail.nextZ = null; inSize *= 2; } while (numMerges > 1); return list; } // z-order of a point given coords and inverse of the longer side of data bbox function zOrder(x, y, minX, minY, invSize) { // coords are transformed into non-negative 15-bit integer range x = (x - minX) * invSize | 0; y = (y - minY) * invSize | 0; x = (x | x << 8) & 0x00FF00FF; x = (x | x << 4) & 0x0F0F0F0F; x = (x | x << 2) & 0x33333333; x = (x | x << 1) & 0x55555555; y = (y | y << 8) & 0x00FF00FF; y = (y | y << 4) & 0x0F0F0F0F; y = (y | y << 2) & 0x33333333; y = (y | y << 1) & 0x55555555; return x | y << 1; } // find the leftmost node of a polygon ring function getLeftmost(start) { let p = start, leftmost = start; do { if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p; p = p.next; } while (p !== start); return leftmost; } // check if a point lies within a convex triangle function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) { return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) && (bx - px) * (cy - py) >= (cx - px) * (by - py); } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) function isValidDiagonal(a, b) { return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && ( // dones't intersect other edges locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && ( // locally visible area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case } // signed area of a triangle function area(p, q, r) { return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); } // check if two points are equal function equals(p1, p2) { return p1.x === p2.x && p1.y === p2.y; } // check if two segments intersect function intersects(p1, q1, p2, q2) { const o1 = sign(area(p1, q1, p2)); const o2 = sign(area(p1, q1, q2)); const o3 = sign(area(p2, q2, p1)); const o4 = sign(area(p2, q2, q1)); if (o1 !== o2 && o3 !== o4) return true; // general case if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 return false; } // for collinear points p, q, r, check if point q lies on segment pr function onSegment(p, q, r) { return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y); } function sign(num) { return num > 0 ? 1 : num < 0 ? -1 : 0; } // check if a polygon diagonal intersects any polygon segments function intersectsPolygon(a, b) { let p = a; do { if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true; p = p.next; } while (p !== a); return false; } // check if a polygon diagonal is locally inside the polygon function locallyInside(a, b) { return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0; } // check if the middle point of a polygon diagonal is inside the polygon function middleInside(a, b) { let p = a, inside = false; const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2; do { if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x) inside = !inside; p = p.next; } while (p !== a); return inside; } // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; // if one belongs to the outer ring and another to a hole, it merges it into a single ring function splitPolygon(a, b) { const a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev; a.next = b; b.prev = a; a2.next = an; an.prev = a2; b2.next = a2; a2.prev = b2; bp.next = b2; b2.prev = bp; return b2; } // create a node and optionally link it with previous one (in a circular doubly linked list) function insertNode(i, x, y, last) { const p = new Node(i, x, y); if (!last) { p.prev = p; p.next = p; } else { p.next = last.next; p.prev = last; last.next.prev = p; last.next = p; } return p; } function removeNode(p) { p.next.prev = p.prev; p.prev.next = p.next; if (p.prevZ) p.prevZ.nextZ = p.nextZ; if (p.nextZ) p.nextZ.prevZ = p.prevZ; } function Node(i, x, y) { // vertex index in coordinates array this.i = i; // vertex coordinates this.x = x; this.y = y; // previous and next vertex nodes in a polygon ring this.prev = null; this.next = null; // z-order curve value this.z = 0; // previous and next nodes in z-order this.prevZ = null; this.nextZ = null; // indicates whether this is a steiner point this.steiner = false; } function signedArea(data, start, end, dim) { let sum = 0; for (let i = start, j = end - dim; i < end; i += dim) { sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]); j = i; } return sum; } class ShapeUtils { // calculate area of the contour polygon static area(contour) { const n = contour.length; let a = 0.0; for (let p = n - 1, q = 0; q < n; p = q++) { a += contour[p].x * contour[q].y - contour[q].x * contour[p].y; } return a * 0.5; } static isClockWise(pts) { return ShapeUtils.area(pts) < 0; } static triangulateShape(contour, holes) { const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] const holeIndices = []; // array of hole indices const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] removeDupEndPts(contour); addContour(vertices, contour); // let holeIndex = contour.length; holes.forEach(removeDupEndPts); for (let i = 0; i < holes.length; i++) { holeIndices.push(holeIndex); holeIndex += holes[i].length; addContour(vertices, holes[i]); } // const triangles = Earcut.triangulate(vertices, holeIndices); // for (let i = 0; i < triangles.length; i += 3) { faces.push(triangles.slice(i, i + 3)); } return faces; } } function removeDupEndPts(points) { const l = points.length; if (l > 2 && points[l - 1].equals(points[0])) { points.pop(); } } function addContour(vertices, contour) { for (let i = 0; i < contour.length; i++) { vertices.push(contour[i].x); vertices.push(contour[i].y); } } /** * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: <int>, // number of points on the curves * steps: <int>, // number of points for z-side extrusions / used for subdividing segments of extrude spline too * depth: <float>, // Depth to extrude the shape * * bevelEnabled: <bool>, // turn on bevel * bevelThickness: <float>, // how deep into the original shape bevel goes * bevelSize: <float>, // how far from shape outline (including bevelOffset) is bevel * bevelOffset: <float>, // how far from shape outline does bevel start * bevelSegments: <int>, // number of bevel layers * * extrudePath: <THREE.Curve> // curve to extrude shape along * * UVGenerator: <Object> // object that provides UV generator functions * * } */ class ExtrudeGeometry extends BufferGeometry { constructor(shapes = new Shape([new Vector2(0.5, 0.5), new Vector2(-0.5, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5)]), options = {}) { super(); this.type = 'ExtrudeGeometry'; this.parameters = { shapes: shapes, options: options }; shapes = Array.isArray(shapes) ? shapes : [shapes]; const scope = this; const verticesArray = []; const uvArray = []; for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; addShape(shape); } // build geometry this.setAttribute('position', new Float32BufferAttribute(verticesArray, 3)); this.setAttribute('uv', new Float32BufferAttribute(uvArray, 2)); this.computeVertexNormals(); // functions function addShape(shape) { const placeholder = []; // options const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; const steps = options.steps !== undefined ? options.steps : 1; const depth = options.depth !== undefined ? options.depth : 1; let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; const extrudePath = options.extrudePath; const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; // let extrudePts, extrudeByPath = false; let splineTube, binormal, normal, position2; if (extrudePath) { extrudePts = extrudePath.getSpacedPoints(steps); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = extrudePath.computeFrenetFrames(steps, false); // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if (!bevelEnabled) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; bevelOffset = 0; } // Variables initialization const shapePoints = shape.extractPoints(curveSegments); let vertices = shapePoints.shape; const holes = shapePoints.holes; const reverse = !ShapeUtils.isClockWise(vertices); if (reverse) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; if (ShapeUtils.isClockWise(ahole)) { holes[h] = ahole.reverse(); } } } const faces = ShapeUtils.triangulateShape(vertices, holes); /* Vertices */ const contour = vertices; // vertices has all points but contour has only points of circumference for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; vertices = vertices.concat(ahole); } function scalePt2(pt, vec, size) { if (!vec) console.error('THREE.ExtrudeGeometry: vec does not exist'); return pt.clone().addScaledVector(vec, size); } const vlen = vertices.length, flen = faces.length; // Find directions for point movement function getBevelVec(inPt, inPrev, inNext) { // computes for inPt the corresponding point inPt' on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt' is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html const v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; const v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; const v_prev_lensq = v_prev_x * v_prev_x + v_prev_y * v_prev_y; // check for collinear edges const collinear0 = v_prev_x * v_next_y - v_prev_y * v_next_x; if (Math.abs(collinear0) > Number.EPSILON) { // not collinear // length of vectors for normalizing const v_prev_len = Math.sqrt(v_prev_lensq); const v_next_len = Math.sqrt(v_next_x * v_next_x + v_next_y * v_next_y); // shift adjacent points by unit vectors to the left const ptPrevShift_x = inPrev.x - v_prev_y / v_prev_len; const ptPrevShift_y = inPrev.y + v_prev_x / v_prev_len; const ptNextShift_x = inNext.x - v_next_y / v_next_len; const ptNextShift_y = inNext.y + v_next_x / v_next_len; // scaling factor for v_prev to intersection point const sf = ((ptNextShift_x - ptPrevShift_x) * v_next_y - (ptNextShift_y - ptPrevShift_y) * v_next_x) / (v_prev_x * v_next_y - v_prev_y * v_next_x); // vector from inPt to intersection point v_trans_x = ptPrevShift_x + v_prev_x * sf - inPt.x; v_trans_y = ptPrevShift_y + v_prev_y * sf - inPt.y; // Don't normalize!, otherwise sharp corners become ugly // but prevent crazy spikes const v_trans_lensq = v_trans_x * v_trans_x + v_trans_y * v_trans_y; if (v_trans_lensq <= 2) { return new Vector2(v_trans_x, v_trans_y); } else { shrink_by = Math.sqrt(v_trans_lensq / 2); } } else { // handle special case of collinear edges let direction_eq = false; // assumes: opposite if (v_prev_x > Number.EPSILON) { if (v_next_x > Number.EPSILON) { direction_eq = true; } } else { if (v_prev_x < -Number.EPSILON) { if (v_next_x < -Number.EPSILON) { direction_eq = true; } } else { if (Math.sign(v_prev_y) === Math.sign(v_next_y)) { direction_eq = true; } } } if (direction_eq) { // console.log("Warning: lines are a straight sequence"); v_trans_x = -v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt(v_prev_lensq); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt(v_prev_lensq / 2); } } return new Vector2(v_trans_x / shrink_by, v_trans_y / shrink_by); } const contourMovements = []; for (let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) // console.log('i,j,k', i, j , k) contourMovements[i] = getBevelVec(contour[i], contour[j], contour[k]); } const holesMovements = []; let oneHoleMovements, verticesMovements = contourMovements.concat(); for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = []; for (let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) { if (j === il) j = 0; if (k === il) k = 0; // (j)---(i)---(k) oneHoleMovements[i] = getBevelVec(ahole[i], ahole[j], ahole[k]); } holesMovements.push(oneHoleMovements); verticesMovements = verticesMovements.concat(oneHoleMovements); } // Loop bevelSegments, 1 for the front, 1 for the back for (let b = 0; b < bevelSegments; b++) { //for ( b = bevelSegments; b > 0; b -- ) { const t = b / bevelSegments; const z = bevelThickness * Math.cos(t * Math.PI / 2); const bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, -z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); v(vert.x, vert.y, -z); } } } const bs = bevelSize + bevelOffset; // Back facing vertices for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, 0); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy(splineTube.normals[0]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[0]).multiplyScalar(vert.y); position2.copy(extrudePts[0]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } // Add stepped vertices... // Including front facing vertices for (let s = 1; s <= steps; s++) { for (let i = 0; i < vlen; i++) { const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i]; if (!extrudeByPath) { v(vert.x, vert.y, depth / steps * s); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy(splineTube.normals[s]).multiplyScalar(vert.x); binormal.copy(splineTube.binormals[s]).multiplyScalar(vert.y); position2.copy(extrudePts[s]).add(normal).add(binormal); v(position2.x, position2.y, position2.z); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for (let b = bevelSegments - 1; b >= 0; b--) { const t = b / bevelSegments; const z = bevelThickness * Math.cos(t * Math.PI / 2); const bs = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset; // contract shape for (let i = 0, il = contour.length; i < il; i++) { const vert = scalePt2(contour[i], contourMovements[i], bs); v(vert.x, vert.y, depth + z); } // expand holes for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; oneHoleMovements = holesMovements[h]; for (let i = 0, il = ahole.length; i < il; i++) { const vert = scalePt2(ahole[i], oneHoleMovements[i], bs); if (!extrudeByPath) { v(vert.x, vert.y, depth + z); } else { v(vert.x, vert.y + extrudePts[steps - 1].y, extrudePts[steps - 1].x + z); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { const start = verticesArray.length / 3; if (bevelEnabled) { let layer = 0; // steps + 1 let offset = vlen * layer; // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2] + offset, face[1] + offset, face[0] + offset); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + offset, face[1] + offset, face[2] + offset); } } else { // Bottom faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[2], face[1], face[0]); } // Top faces for (let i = 0; i < flen; i++) { const face = faces[i]; f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps); } } scope.addGroup(start, verticesArray.length / 3 - start, 0); } // Create faces for the z-sides of the shape function buildSideFaces() { const start = verticesArray.length / 3; let layeroffset = 0; sidewalls(contour, layeroffset); layeroffset += contour.length; for (let h = 0, hl = holes.length; h < hl; h++) { const ahole = holes[h]; sidewalls(ahole, layeroffset); //, true layeroffset += ahole.length; } scope.addGroup(start, verticesArray.length / 3 - start, 1); } function sidewalls(contour, layeroffset) { let i = contour.length; while (--i >= 0) { const j = i; let k = i - 1; if (k < 0) k = contour.length - 1; //console.log('b', i,j, i-1, k,vertices.length); for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) { const slen1 = vlen * s; const slen2 = vlen * (s + 1); const a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4(a, b, c, d); } } } function v(x, y, z) { placeholder.push(x); placeholder.push(y); placeholder.push(z); } function f3(a, b, c) { addVertex(a); addVertex(b); addVertex(c); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateTopUV(scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[2]); } function f4(a, b, c, d) { addVertex(a); addVertex(b); addVertex(d); addVertex(b); addVertex(c); addVertex(d); const nextIndex = verticesArray.length / 3; const uvs = uvgen.generateSideWallUV(scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1); addUV(uvs[0]); addUV(uvs[1]); addUV(uvs[3]); addUV(uvs[1]); addUV(uvs[2]); addUV(uvs[3]); } function addVertex(index) { verticesArray.push(placeholder[index * 3 + 0]); verticesArray.push(placeholder[index * 3 + 1]); verticesArray.push(placeholder[index * 3 + 2]); } function addUV(vector2) { uvArray.push(vector2.x); uvArray.push(vector2.y); } } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; const options = this.parameters.options; return toJSON$1(shapes, options, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } const extrudePath = data.options.extrudePath; if (extrudePath !== undefined) { data.options.extrudePath = new Curves[extrudePath.type]().fromJSON(extrudePath); } return new ExtrudeGeometry(geometryShapes, data.options); } } const WorldUVGenerator = { generateTopUV: function (geometry, vertices, indexA, indexB, indexC) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; return [new Vector2(a_x, a_y), new Vector2(b_x, b_y), new Vector2(c_x, c_y)]; }, generateSideWallUV: function (geometry, vertices, indexA, indexB, indexC, indexD) { const a_x = vertices[indexA * 3]; const a_y = vertices[indexA * 3 + 1]; const a_z = vertices[indexA * 3 + 2]; const b_x = vertices[indexB * 3]; const b_y = vertices[indexB * 3 + 1]; const b_z = vertices[indexB * 3 + 2]; const c_x = vertices[indexC * 3]; const c_y = vertices[indexC * 3 + 1]; const c_z = vertices[indexC * 3 + 2]; const d_x = vertices[indexD * 3]; const d_y = vertices[indexD * 3 + 1]; const d_z = vertices[indexD * 3 + 2]; if (Math.abs(a_y - b_y) < Math.abs(a_x - b_x)) { return [new Vector2(a_x, 1 - a_z), new Vector2(b_x, 1 - b_z), new Vector2(c_x, 1 - c_z), new Vector2(d_x, 1 - d_z)]; } else { return [new Vector2(a_y, 1 - a_z), new Vector2(b_y, 1 - b_z), new Vector2(c_y, 1 - c_z), new Vector2(d_y, 1 - d_z)]; } } }; function toJSON$1(shapes, options, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } data.options = Object.assign({}, options); if (options.extrudePath !== undefined) data.options.extrudePath = options.extrudePath.toJSON(); return data; } class IcosahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const t = (1 + Math.sqrt(5)) / 2; const vertices = [-1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0, 0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1]; const indices = [0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1]; super(vertices, indices, radius, detail); this.type = 'IcosahedronGeometry'; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new IcosahedronGeometry(data.radius, data.detail); } } class OctahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1]; const indices = [0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2]; super(vertices, indices, radius, detail); this.type = 'OctahedronGeometry'; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new OctahedronGeometry(data.radius, data.detail); } } class RingGeometry extends BufferGeometry { constructor(innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2) { super(); this.type = 'RingGeometry'; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; thetaSegments = Math.max(3, thetaSegments); phiSegments = Math.max(1, phiSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // some helper variables let radius = innerRadius; const radiusStep = (outerRadius - innerRadius) / phiSegments; const vertex = new Vector3(); const uv = new Vector2(); // generate vertices, normals and uvs for (let j = 0; j <= phiSegments; j++) { for (let i = 0; i <= thetaSegments; i++) { // values are generate from the inside of the ring to the outside const segment = thetaStart + i / thetaSegments * thetaLength; // vertex vertex.x = radius * Math.cos(segment); vertex.y = radius * Math.sin(segment); vertices.push(vertex.x, vertex.y, vertex.z); // normal normals.push(0, 0, 1); // uv uv.x = (vertex.x / outerRadius + 1) / 2; uv.y = (vertex.y / outerRadius + 1) / 2; uvs.push(uv.x, uv.y); } // increase the radius for next row of vertices radius += radiusStep; } // indices for (let j = 0; j < phiSegments; j++) { const thetaSegmentLevel = j * (thetaSegments + 1); for (let i = 0; i < thetaSegments; i++) { const segment = i + thetaSegmentLevel; const a = segment; const b = segment + thetaSegments + 1; const c = segment + thetaSegments + 2; const d = segment + 1; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new RingGeometry(data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength); } } class ShapeGeometry extends BufferGeometry { constructor(shapes = new Shape([new Vector2(0, 0.5), new Vector2(-0.5, -0.5), new Vector2(0.5, -0.5)]), curveSegments = 12) { super(); this.type = 'ShapeGeometry'; this.parameters = { shapes: shapes, curveSegments: curveSegments }; // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables let groupStart = 0; let groupCount = 0; // allow single and array values for "shapes" parameter if (Array.isArray(shapes) === false) { addShape(shapes); } else { for (let i = 0; i < shapes.length; i++) { addShape(shapes[i]); this.addGroup(groupStart, groupCount, i); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex(indices); this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); // helper functions function addShape(shape) { const indexOffset = vertices.length / 3; const points = shape.extractPoints(curveSegments); let shapeVertices = points.shape; const shapeHoles = points.holes; // check direction of vertices if (ShapeUtils.isClockWise(shapeVertices) === false) { shapeVertices = shapeVertices.reverse(); } for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; if (ShapeUtils.isClockWise(shapeHole) === true) { shapeHoles[i] = shapeHole.reverse(); } } const faces = ShapeUtils.triangulateShape(shapeVertices, shapeHoles); // join vertices of inner and outer paths to a single array for (let i = 0, l = shapeHoles.length; i < l; i++) { const shapeHole = shapeHoles[i]; shapeVertices = shapeVertices.concat(shapeHole); } // vertices, normals, uvs for (let i = 0, l = shapeVertices.length; i < l; i++) { const vertex = shapeVertices[i]; vertices.push(vertex.x, vertex.y, 0); normals.push(0, 0, 1); uvs.push(vertex.x, vertex.y); // world uvs } // indices for (let i = 0, l = faces.length; i < l; i++) { const face = faces[i]; const a = face[0] + indexOffset; const b = face[1] + indexOffset; const c = face[2] + indexOffset; indices.push(a, b, c); groupCount += 3; } } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } toJSON() { const data = super.toJSON(); const shapes = this.parameters.shapes; return toJSON(shapes, data); } static fromJSON(data, shapes) { const geometryShapes = []; for (let j = 0, jl = data.shapes.length; j < jl; j++) { const shape = shapes[data.shapes[j]]; geometryShapes.push(shape); } return new ShapeGeometry(geometryShapes, data.curveSegments); } } function toJSON(shapes, data) { data.shapes = []; if (Array.isArray(shapes)) { for (let i = 0, l = shapes.length; i < l; i++) { const shape = shapes[i]; data.shapes.push(shape.uuid); } } else { data.shapes.push(shapes.uuid); } return data; } class SphereGeometry extends BufferGeometry { constructor(radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI) { super(); this.type = 'SphereGeometry'; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; widthSegments = Math.max(3, Math.floor(widthSegments)); heightSegments = Math.max(2, Math.floor(heightSegments)); const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI); let index = 0; const grid = []; const vertex = new Vector3(); const normal = new Vector3(); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // generate vertices, normals and uvs for (let iy = 0; iy <= heightSegments; iy++) { const verticesRow = []; const v = iy / heightSegments; // special case for the poles let uOffset = 0; if (iy === 0 && thetaStart === 0) { uOffset = 0.5 / widthSegments; } else if (iy === heightSegments && thetaEnd === Math.PI) { uOffset = -0.5 / widthSegments; } for (let ix = 0; ix <= widthSegments; ix++) { const u = ix / widthSegments; // vertex vertex.x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertex.y = radius * Math.cos(thetaStart + v * thetaLength); vertex.z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); vertices.push(vertex.x, vertex.y, vertex.z); // normal normal.copy(vertex).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(u + uOffset, 1 - v); verticesRow.push(index++); } grid.push(verticesRow); } // indices for (let iy = 0; iy < heightSegments; iy++) { for (let ix = 0; ix < widthSegments; ix++) { const a = grid[iy][ix + 1]; const b = grid[iy][ix]; const c = grid[iy + 1][ix]; const d = grid[iy + 1][ix + 1]; if (iy !== 0 || thetaStart > 0) indices.push(a, b, d); if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new SphereGeometry(data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength); } } class TetrahedronGeometry extends PolyhedronGeometry { constructor(radius = 1, detail = 0) { const vertices = [1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1]; const indices = [2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1]; super(vertices, indices, radius, detail); this.type = 'TetrahedronGeometry'; this.parameters = { radius: radius, detail: detail }; } static fromJSON(data) { return new TetrahedronGeometry(data.radius, data.detail); } } class TorusGeometry extends BufferGeometry { constructor(radius = 1, tube = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2) { super(); this.type = 'TorusGeometry'; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; radialSegments = Math.floor(radialSegments); tubularSegments = Math.floor(tubularSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const center = new Vector3(); const vertex = new Vector3(); const normal = new Vector3(); // generate vertices, normals and uvs for (let j = 0; j <= radialSegments; j++) { for (let i = 0; i <= tubularSegments; i++) { const u = i / tubularSegments * arc; const v = j / radialSegments * Math.PI * 2; // vertex vertex.x = (radius + tube * Math.cos(v)) * Math.cos(u); vertex.y = (radius + tube * Math.cos(v)) * Math.sin(u); vertex.z = tube * Math.sin(v); vertices.push(vertex.x, vertex.y, vertex.z); // normal center.x = radius * Math.cos(u); center.y = radius * Math.sin(u); normal.subVectors(vertex, center).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= radialSegments; j++) { for (let i = 1; i <= tubularSegments; i++) { // indices const a = (tubularSegments + 1) * j + i - 1; const b = (tubularSegments + 1) * (j - 1) + i - 1; const c = (tubularSegments + 1) * (j - 1) + i; const d = (tubularSegments + 1) * j + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new TorusGeometry(data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc); } } class TorusKnotGeometry extends BufferGeometry { constructor(radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3) { super(); this.type = 'TorusKnotGeometry'; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; tubularSegments = Math.floor(tubularSegments); radialSegments = Math.floor(radialSegments); // buffers const indices = []; const vertices = []; const normals = []; const uvs = []; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const P1 = new Vector3(); const P2 = new Vector3(); const B = new Vector3(); const T = new Vector3(); const N = new Vector3(); // generate vertices, normals and uvs for (let i = 0; i <= tubularSegments; ++i) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segment const u = i / tubularSegments * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve(u, p, q, radius, P1); calculatePositionOnCurve(u + 0.01, p, q, radius, P2); // calculate orthonormal basis T.subVectors(P2, P1); N.addVectors(P2, P1); B.crossVectors(T, N); N.crossVectors(B, T); // normalize B, N. T can be ignored, we don't use it B.normalize(); N.normalize(); for (let j = 0; j <= radialSegments; ++j) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. const v = j / radialSegments * Math.PI * 2; const cx = -tube * Math.cos(v); const cy = tube * Math.sin(v); // now calculate the final vertex position. // first we orient the extrusion with our basis vectors, then we add it to the current position on the curve vertex.x = P1.x + (cx * N.x + cy * B.x); vertex.y = P1.y + (cx * N.y + cy * B.y); vertex.z = P1.z + (cx * N.z + cy * B.z); vertices.push(vertex.x, vertex.y, vertex.z); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors(vertex, P1).normalize(); normals.push(normal.x, normal.y, normal.z); // uv uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } // generate indices for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { // indices const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } // build geometry this.setIndex(indices); this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); // this function calculates the current position on the torus curve function calculatePositionOnCurve(u, p, q, radius, position) { const cu = Math.cos(u); const su = Math.sin(u); const quOverP = q / p * u; const cs = Math.cos(quOverP); position.x = radius * (2 + cs) * 0.5 * cu; position.y = radius * (2 + cs) * su * 0.5; position.z = radius * Math.sin(quOverP) * 0.5; } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } static fromJSON(data) { return new TorusKnotGeometry(data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q); } } class TubeGeometry extends BufferGeometry { constructor(path = new QuadraticBezierCurve3(new Vector3(-1, -1, 0), new Vector3(-1, 1, 0), new Vector3(1, 1, 0)), tubularSegments = 64, radius = 1, radialSegments = 8, closed = false) { super(); this.type = 'TubeGeometry'; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; const frames = path.computeFrenetFrames(tubularSegments, closed); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables const vertex = new Vector3(); const normal = new Vector3(); const uv = new Vector2(); let P = new Vector3(); // buffer const vertices = []; const normals = []; const uvs = []; const indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex(indices); this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); // functions function generateBufferData() { for (let i = 0; i < tubularSegments; i++) { generateSegment(i); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment(closed === false ? tubularSegments : 0); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment(i) { // we use getPointAt to sample evenly distributed points from the given path P = path.getPointAt(i / tubularSegments, P); // retrieve corresponding normal and binormal const N = frames.normals[i]; const B = frames.binormals[i]; // generate normals and vertices for the current segment for (let j = 0; j <= radialSegments; j++) { const v = j / radialSegments * Math.PI * 2; const sin = Math.sin(v); const cos = -Math.cos(v); // normal normal.x = cos * N.x + sin * B.x; normal.y = cos * N.y + sin * B.y; normal.z = cos * N.z + sin * B.z; normal.normalize(); normals.push(normal.x, normal.y, normal.z); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push(vertex.x, vertex.y, vertex.z); } } function generateIndices() { for (let j = 1; j <= tubularSegments; j++) { for (let i = 1; i <= radialSegments; i++) { const a = (radialSegments + 1) * (j - 1) + (i - 1); const b = (radialSegments + 1) * j + (i - 1); const c = (radialSegments + 1) * j + i; const d = (radialSegments + 1) * (j - 1) + i; // faces indices.push(a, b, d); indices.push(b, c, d); } } } function generateUVs() { for (let i = 0; i <= tubularSegments; i++) { for (let j = 0; j <= radialSegments; j++) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push(uv.x, uv.y); } } } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } toJSON() { const data = super.toJSON(); data.path = this.parameters.path.toJSON(); return data; } static fromJSON(data) { // This only works for built-in curves (e.g. CatmullRomCurve3). // User defined curves or instances of CurvePath will not be deserialized. return new TubeGeometry(new Curves[data.path.type]().fromJSON(data.path), data.tubularSegments, data.radius, data.radialSegments, data.closed); } } class WireframeGeometry extends BufferGeometry { constructor(geometry = null) { super(); this.type = 'WireframeGeometry'; this.parameters = { geometry: geometry }; if (geometry !== null) { // buffer const vertices = []; const edges = new Set(); // helper variables const start = new Vector3(); const end = new Vector3(); if (geometry.index !== null) { // indexed BufferGeometry const position = geometry.attributes.position; const indices = geometry.index; let groups = geometry.groups; if (groups.length === 0) { groups = [{ start: 0, count: indices.count, materialIndex: 0 }]; } // create a data structure that contains all edges without duplicates for (let o = 0, ol = groups.length; o < ol; ++o) { const group = groups[o]; const groupStart = group.start; const groupCount = group.count; for (let i = groupStart, l = groupStart + groupCount; i < l; i += 3) { for (let j = 0; j < 3; j++) { const index1 = indices.getX(i + j); const index2 = indices.getX(i + (j + 1) % 3); start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } } else { // non-indexed BufferGeometry const position = geometry.attributes.position; for (let i = 0, l = position.count / 3; i < l; i++) { for (let j = 0; j < 3; j++) { // three edges per triangle, an edge is represented as (index1, index2) // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) const index1 = 3 * i + j; const index2 = 3 * i + (j + 1) % 3; start.fromBufferAttribute(position, index1); end.fromBufferAttribute(position, index2); if (isUniqueEdge(start, end, edges) === true) { vertices.push(start.x, start.y, start.z); vertices.push(end.x, end.y, end.z); } } } } // build geometry this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); } } copy(source) { super.copy(source); this.parameters = Object.assign({}, source.parameters); return this; } } function isUniqueEdge(start, end, edges) { const hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`; const hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge if (edges.has(hash1) === true || edges.has(hash2) === true) { return false; } else { edges.add(hash1); edges.add(hash2); return true; } } var Geometries = /*#__PURE__*/Object.freeze({ __proto__: null, BoxGeometry: BoxGeometry, CapsuleGeometry: CapsuleGeometry, CircleGeometry: CircleGeometry, ConeGeometry: ConeGeometry, CylinderGeometry: CylinderGeometry, DodecahedronGeometry: DodecahedronGeometry, EdgesGeometry: EdgesGeometry, ExtrudeGeometry: ExtrudeGeometry, IcosahedronGeometry: IcosahedronGeometry, LatheGeometry: LatheGeometry, OctahedronGeometry: OctahedronGeometry, PlaneGeometry: PlaneGeometry, PolyhedronGeometry: PolyhedronGeometry, RingGeometry: RingGeometry, ShapeGeometry: ShapeGeometry, SphereGeometry: SphereGeometry, TetrahedronGeometry: TetrahedronGeometry, TorusGeometry: TorusGeometry, TorusKnotGeometry: TorusKnotGeometry, TubeGeometry: TubeGeometry, WireframeGeometry: WireframeGeometry }); class ShadowMaterial extends Material { constructor(parameters) { super(); this.isShadowMaterial = true; this.type = 'ShadowMaterial'; this.color = new Color(0x000000); this.transparent = true; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.fog = source.fog; return this; } } class RawShaderMaterial extends ShaderMaterial { constructor(parameters) { super(parameters); this.isRawShaderMaterial = true; this.type = 'RawShaderMaterial'; } } class MeshStandardMaterial extends Material { constructor(parameters) { super(); this.isMeshStandardMaterial = true; this.defines = { 'STANDARD': '' }; this.type = 'MeshStandardMaterial'; this.color = new Color(0xffffff); // diffuse this.roughness = 1.0; this.metalness = 0.0; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapIntensity = 1.0; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = 'round'; this.wireframeLinejoin = 'round'; this.flatShading = false; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = { 'STANDARD': '' }; this.color.copy(source.color); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapIntensity = source.envMapIntensity; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshPhysicalMaterial extends MeshStandardMaterial { constructor(parameters) { super(); this.isMeshPhysicalMaterial = true; this.defines = { 'STANDARD': '', 'PHYSICAL': '' }; this.type = 'MeshPhysicalMaterial'; this.anisotropyRotation = 0; this.anisotropyMap = null; this.clearcoatMap = null; this.clearcoatRoughness = 0.0; this.clearcoatRoughnessMap = null; this.clearcoatNormalScale = new Vector2(1, 1); this.clearcoatNormalMap = null; this.ior = 1.5; Object.defineProperty(this, 'reflectivity', { get: function () { return clamp(2.5 * (this.ior - 1) / (this.ior + 1), 0, 1); }, set: function (reflectivity) { this.ior = (1 + 0.4 * reflectivity) / (1 - 0.4 * reflectivity); } }); this.iridescenceMap = null; this.iridescenceIOR = 1.3; this.iridescenceThicknessRange = [100, 400]; this.iridescenceThicknessMap = null; this.sheenColor = new Color(0x000000); this.sheenColorMap = null; this.sheenRoughness = 1.0; this.sheenRoughnessMap = null; this.transmissionMap = null; this.thickness = 0; this.thicknessMap = null; this.attenuationDistance = Infinity; this.attenuationColor = new Color(1, 1, 1); this.specularIntensity = 1.0; this.specularIntensityMap = null; this.specularColor = new Color(1, 1, 1); this.specularColorMap = null; this._anisotropy = 0; this._clearcoat = 0; this._iridescence = 0; this._sheen = 0.0; this._transmission = 0; this.setValues(parameters); } get anisotropy() { return this._anisotropy; } set anisotropy(value) { if (this._anisotropy > 0 !== value > 0) { this.version++; } this._anisotropy = value; } get clearcoat() { return this._clearcoat; } set clearcoat(value) { if (this._clearcoat > 0 !== value > 0) { this.version++; } this._clearcoat = value; } get iridescence() { return this._iridescence; } set iridescence(value) { if (this._iridescence > 0 !== value > 0) { this.version++; } this._iridescence = value; } get sheen() { return this._sheen; } set sheen(value) { if (this._sheen > 0 !== value > 0) { this.version++; } this._sheen = value; } get transmission() { return this._transmission; } set transmission(value) { if (this._transmission > 0 !== value > 0) { this.version++; } this._transmission = value; } copy(source) { super.copy(source); this.defines = { 'STANDARD': '', 'PHYSICAL': '' }; this.anisotropy = source.anisotropy; this.anisotropyRotation = source.anisotropyRotation; this.anisotropyMap = source.anisotropyMap; this.clearcoat = source.clearcoat; this.clearcoatMap = source.clearcoatMap; this.clearcoatRoughness = source.clearcoatRoughness; this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; this.clearcoatNormalMap = source.clearcoatNormalMap; this.clearcoatNormalScale.copy(source.clearcoatNormalScale); this.ior = source.ior; this.iridescence = source.iridescence; this.iridescenceMap = source.iridescenceMap; this.iridescenceIOR = source.iridescenceIOR; this.iridescenceThicknessRange = [...source.iridescenceThicknessRange]; this.iridescenceThicknessMap = source.iridescenceThicknessMap; this.sheen = source.sheen; this.sheenColor.copy(source.sheenColor); this.sheenColorMap = source.sheenColorMap; this.sheenRoughness = source.sheenRoughness; this.sheenRoughnessMap = source.sheenRoughnessMap; this.transmission = source.transmission; this.transmissionMap = source.transmissionMap; this.thickness = source.thickness; this.thicknessMap = source.thicknessMap; this.attenuationDistance = source.attenuationDistance; this.attenuationColor.copy(source.attenuationColor); this.specularIntensity = source.specularIntensity; this.specularIntensityMap = source.specularIntensityMap; this.specularColor.copy(source.specularColor); this.specularColorMap = source.specularColorMap; return this; } } class MeshPhongMaterial extends Material { constructor(parameters) { super(); this.isMeshPhongMaterial = true; this.type = 'MeshPhongMaterial'; this.color = new Color(0xffffff); // diffuse this.specular = new Color(0x111111); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = 'round'; this.wireframeLinejoin = 'round'; this.flatShading = false; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.specular.copy(source.specular); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshToonMaterial extends Material { constructor(parameters) { super(); this.isMeshToonMaterial = true; this.defines = { 'TOON': '' }; this.type = 'MeshToonMaterial'; this.color = new Color(0xffffff); this.map = null; this.gradientMap = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = 'round'; this.wireframeLinejoin = 'round'; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.gradientMap = source.gradientMap; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.fog = source.fog; return this; } } class MeshNormalMaterial extends Material { constructor(parameters) { super(); this.isMeshNormalMaterial = true; this.type = 'MeshNormalMaterial'; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.flatShading = false; this.setValues(parameters); } copy(source) { super.copy(source); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.flatShading = source.flatShading; return this; } } class MeshLambertMaterial extends Material { constructor(parameters) { super(); this.isMeshLambertMaterial = true; this.type = 'MeshLambertMaterial'; this.color = new Color(0xffffff); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color(0x000000); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = 'round'; this.wireframeLinejoin = 'round'; this.flatShading = false; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.color.copy(source.color); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy(source.emissive); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class MeshMatcapMaterial extends Material { constructor(parameters) { super(); this.isMeshMatcapMaterial = true; this.defines = { 'MATCAP': '' }; this.type = 'MeshMatcapMaterial'; this.color = new Color(0xffffff); // diffuse this.matcap = null; this.map = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalMapType = TangentSpaceNormalMap; this.normalScale = new Vector2(1, 1); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.alphaMap = null; this.flatShading = false; this.fog = true; this.setValues(parameters); } copy(source) { super.copy(source); this.defines = { 'MATCAP': '' }; this.color.copy(source.color); this.matcap = source.matcap; this.map = source.map; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalMapType = source.normalMapType; this.normalScale.copy(source.normalScale); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.alphaMap = source.alphaMap; this.flatShading = source.flatShading; this.fog = source.fog; return this; } } class LineDashedMaterial extends LineBasicMaterial { constructor(parameters) { super(); this.isLineDashedMaterial = true; this.type = 'LineDashedMaterial'; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.setValues(parameters); } copy(source) { super.copy(source); this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; } } // same as Array.prototype.slice, but also works on typed arrays function arraySlice(array, from, to) { if (isTypedArray(array)) { // in ios9 array.subarray(from, undefined) will return empty array // but array.subarray(from) or array.subarray(from, len) is correct return new array.constructor(array.subarray(from, to !== undefined ? to : array.length)); } return array.slice(from, to); } // converts an array to a specific type function convertArray(array, type, forceClone) { if (!array || // let 'undefined' and 'null' pass !forceClone && array.constructor === type) return array; if (typeof type.BYTES_PER_ELEMENT === 'number') { return new type(array); // create typed array } return Array.prototype.slice.call(array); // create Array } function isTypedArray(object) { return ArrayBuffer.isView(object) && !(object instanceof DataView); } // returns an array by which times and values can be sorted function getKeyframeOrder(times) { function compareTime(i, j) { return times[i] - times[j]; } const n = times.length; const result = new Array(n); for (let i = 0; i !== n; ++i) result[i] = i; result.sort(compareTime); return result; } // uses the array previously returned by 'getKeyframeOrder' to sort data function sortedArray(values, stride, order) { const nValues = values.length; const result = new values.constructor(nValues); for (let i = 0, dstOffset = 0; dstOffset !== nValues; ++i) { const srcOffset = order[i] * stride; for (let j = 0; j !== stride; ++j) { result[dstOffset++] = values[srcOffset + j]; } } return result; } // function for parsing AOS keyframe formats function flattenJSON(jsonKeys, times, values, valuePropertyName) { let i = 1, key = jsonKeys[0]; while (key !== undefined && key[valuePropertyName] === undefined) { key = jsonKeys[i++]; } if (key === undefined) return; // no data let value = key[valuePropertyName]; if (value === undefined) return; // no data if (Array.isArray(value)) { do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push.apply(values, value); // push all elements } key = jsonKeys[i++]; } while (key !== undefined); } else if (value.toArray !== undefined) { // ...assume THREE.Math-ish do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); value.toArray(values, values.length); } key = jsonKeys[i++]; } while (key !== undefined); } else { // otherwise push as-is do { value = key[valuePropertyName]; if (value !== undefined) { times.push(key.time); values.push(value); } key = jsonKeys[i++]; } while (key !== undefined); } } function subclip(sourceClip, name, startFrame, endFrame, fps = 30) { const clip = sourceClip.clone(); clip.name = name; const tracks = []; for (let i = 0; i < clip.tracks.length; ++i) { const track = clip.tracks[i]; const valueSize = track.getValueSize(); const times = []; const values = []; for (let j = 0; j < track.times.length; ++j) { const frame = track.times[j] * fps; if (frame < startFrame || frame >= endFrame) continue; times.push(track.times[j]); for (let k = 0; k < valueSize; ++k) { values.push(track.values[j * valueSize + k]); } } if (times.length === 0) continue; track.times = convertArray(times, track.times.constructor); track.values = convertArray(values, track.values.constructor); tracks.push(track); } clip.tracks = tracks; // find minimum .times value across all tracks in the trimmed clip let minStartTime = Infinity; for (let i = 0; i < clip.tracks.length; ++i) { if (minStartTime > clip.tracks[i].times[0]) { minStartTime = clip.tracks[i].times[0]; } } // shift all tracks such that clip begins at t=0 for (let i = 0; i < clip.tracks.length; ++i) { clip.tracks[i].shift(-1 * minStartTime); } clip.resetDuration(); return clip; } function makeClipAdditive(targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30) { if (fps <= 0) fps = 30; const numTracks = referenceClip.tracks.length; const referenceTime = referenceFrame / fps; // Make each track's values relative to the values at the reference frame for (let i = 0; i < numTracks; ++i) { const referenceTrack = referenceClip.tracks[i]; const referenceTrackType = referenceTrack.ValueTypeName; // Skip this track if it's non-numeric if (referenceTrackType === 'bool' || referenceTrackType === 'string') continue; // Find the track in the target clip whose name and type matches the reference track const targetTrack = targetClip.tracks.find(function (track) { return track.name === referenceTrack.name && track.ValueTypeName === referenceTrackType; }); if (targetTrack === undefined) continue; let referenceOffset = 0; const referenceValueSize = referenceTrack.getValueSize(); if (referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { referenceOffset = referenceValueSize / 3; } let targetOffset = 0; const targetValueSize = targetTrack.getValueSize(); if (targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { targetOffset = targetValueSize / 3; } const lastIndex = referenceTrack.times.length - 1; let referenceValue; // Find the value to subtract out of the track if (referenceTime <= referenceTrack.times[0]) { // Reference frame is earlier than the first keyframe, so just use the first keyframe const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; referenceValue = arraySlice(referenceTrack.values, startIndex, endIndex); } else if (referenceTime >= referenceTrack.times[lastIndex]) { // Reference frame is after the last keyframe, so just use the last keyframe const startIndex = lastIndex * referenceValueSize + referenceOffset; const endIndex = startIndex + referenceValueSize - referenceOffset; referenceValue = arraySlice(referenceTrack.values, startIndex, endIndex); } else { // Interpolate to the reference value const interpolant = referenceTrack.createInterpolant(); const startIndex = referenceOffset; const endIndex = referenceValueSize - referenceOffset; interpolant.evaluate(referenceTime); referenceValue = arraySlice(interpolant.resultBuffer, startIndex, endIndex); } // Conjugate the quaternion if (referenceTrackType === 'quaternion') { const referenceQuat = new Quaternion().fromArray(referenceValue).normalize().conjugate(); referenceQuat.toArray(referenceValue); } // Subtract the reference value from all of the track values const numTimes = targetTrack.times.length; for (let j = 0; j < numTimes; ++j) { const valueStart = j * targetValueSize + targetOffset; if (referenceTrackType === 'quaternion') { // Multiply the conjugate for quaternion track types Quaternion.multiplyQuaternionsFlat(targetTrack.values, valueStart, referenceValue, 0, targetTrack.values, valueStart); } else { const valueEnd = targetValueSize - targetOffset * 2; // Subtract each value for all other numeric track types for (let k = 0; k < valueEnd; ++k) { targetTrack.values[valueStart + k] -= referenceValue[k]; } } } } targetClip.blendMode = AdditiveAnimationBlendMode; return targetClip; } const AnimationUtils = { arraySlice: arraySlice, convertArray: convertArray, isTypedArray: isTypedArray, getKeyframeOrder: getKeyframeOrder, sortedArray: sortedArray, flattenJSON: flattenJSON, subclip: subclip, makeClipAdditive: makeClipAdditive }; /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * */ class Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor(sampleSize); this.sampleValues = sampleValues; this.valueSize = sampleSize; this.settings = null; this.DefaultSettings_ = {}; } evaluate(t) { const pp = this.parameterPositions; let i1 = this._cachedIndex, t1 = pp[i1], t0 = pp[i1 - 1]; validate_interval: { seek: { let right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if (!(t < t1)) { for (let giveUpAt = i1 + 2;;) { if (t1 === undefined) { if (t < t0) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.copySampleValue_(i1 - 1); } if (i1 === giveUpAt) break; // this loop t0 = t1; t1 = pp[++i1]; if (t < t1) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if (!(t >= t0)) { // looping? const t1global = pp[1]; if (t < t1global) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for (let giveUpAt = i1 - 2;;) { if (t0 === undefined) { // before start this._cachedIndex = 0; return this.copySampleValue_(0); } if (i1 === giveUpAt) break; // this loop t1 = t0; t0 = pp[--i1 - 1]; if (t >= t0) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while (i1 < right) { const mid = i1 + right >>> 1; if (t < pp[mid]) { right = mid; } else { i1 = mid + 1; } } t1 = pp[i1]; t0 = pp[i1 - 1]; // check boundary cases, again if (t0 === undefined) { this._cachedIndex = 0; return this.copySampleValue_(0); } if (t1 === undefined) { i1 = pp.length; this._cachedIndex = i1; return this.copySampleValue_(i1 - 1); } } // seek this._cachedIndex = i1; this.intervalChanged_(i1, t0, t1); } // validate_interval return this.interpolate_(i1, t0, t, t1); } getSettings_() { return this.settings || this.DefaultSettings_; } copySampleValue_(index) { // copies a sample value to the result buffer const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for (let i = 0; i !== stride; ++i) { result[i] = values[offset + i]; } return result; } // Template methods for derived classes: interpolate_( /* i1, t0, t, t1 */ ) { throw new Error('call to abstract method'); // implementations shall return this.resultBuffer } intervalChanged_( /* i1, t0, t1 */ ) { // empty } } /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. */ class CubicInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); this._weightPrev = -0; this._offsetPrev = -0; this._weightNext = -0; this._offsetNext = -0; this.DefaultSettings_ = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; } intervalChanged_(i1, t0, t1) { const pp = this.parameterPositions; let iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[iPrev], tNext = pp[iNext]; if (tPrev === undefined) { switch (this.getSettings_().endingStart) { case ZeroSlopeEnding: // f'(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[iPrev] - pp[iPrev + 1]; break; default: // ZeroCurvatureEnding // f''(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if (tNext === undefined) { switch (this.getSettings_().endingEnd) { case ZeroSlopeEnding: // f'(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[1] - pp[0]; break; default: // ZeroCurvatureEnding // f''(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } const halfDt = (t1 - t0) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / (t0 - tPrev); this._weightNext = halfDt / (tNext - t1); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = (t - t0) / (t1 - t0), pp = p * p, ppp = pp * p; // evaluate polynomials const sP = -wP * ppp + 2 * wP * pp - wP * p; const s0 = (1 + wP) * ppp + (-1.5 - 2 * wP) * pp + (-0.5 + wP) * p + 1; const s1 = (-1 - wN) * ppp + (1.5 + wN) * pp + 0.5 * p; const sN = wN * ppp - wN * pp; // combine data linearly for (let i = 0; i !== stride; ++i) { result[i] = sP * values[oP + i] + s0 * values[o0 + i] + s1 * values[o1 + i] + sN * values[oN + i]; } return result; } } class LinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = (t - t0) / (t1 - t0), weight0 = 1 - weight1; for (let i = 0; i !== stride; ++i) { result[i] = values[offset0 + i] * weight0 + values[offset1 + i] * weight1; } return result; } } /** * * Interpolant that evaluates to the sample value at the position preceding * the parameter. */ class DiscreteInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1 /*, t0, t, t1 */) { return this.copySampleValue_(i1 - 1); } } class KeyframeTrack { constructor(name, times, values, interpolation) { if (name === undefined) throw new Error('THREE.KeyframeTrack: track name is undefined'); if (times === undefined || times.length === 0) throw new Error('THREE.KeyframeTrack: no keyframes in track named ' + name); this.name = name; this.times = convertArray(times, this.TimeBufferType); this.values = convertArray(values, this.ValueBufferType); this.setInterpolation(interpolation || this.DefaultInterpolation); } // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): static toJSON(track) { const trackType = track.constructor; let json; // derived classes can define a static toJSON method if (trackType.toJSON !== this.toJSON) { json = trackType.toJSON(track); } else { // by default, we assume the data can be serialized as-is json = { 'name': track.name, 'times': convertArray(track.times, Array), 'values': convertArray(track.values, Array) }; const interpolation = track.getInterpolation(); if (interpolation !== track.DefaultInterpolation) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; } InterpolantFactoryMethodDiscrete(result) { return new DiscreteInterpolant(this.times, this.values, this.getValueSize(), result); } InterpolantFactoryMethodLinear(result) { return new LinearInterpolant(this.times, this.values, this.getValueSize(), result); } InterpolantFactoryMethodSmooth(result) { return new CubicInterpolant(this.times, this.values, this.getValueSize(), result); } setInterpolation(interpolation) { let factoryMethod; switch (interpolation) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if (factoryMethod === undefined) { const message = 'unsupported interpolation for ' + this.ValueTypeName + ' keyframe track named ' + this.name; if (this.createInterpolant === undefined) { // fall back to default, unless the default itself is messed up if (interpolation !== this.DefaultInterpolation) { this.setInterpolation(this.DefaultInterpolation); } else { throw new Error(message); // fatal, in this case } } console.warn('THREE.KeyframeTrack:', message); return this; } this.createInterpolant = factoryMethod; return this; } getInterpolation() { switch (this.createInterpolant) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } } getValueSize() { return this.values.length / this.times.length; } // move all keyframes either forwards or backwards in time shift(timeOffset) { if (timeOffset !== 0.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] += timeOffset; } } return this; } // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale(timeScale) { if (timeScale !== 1.0) { const times = this.times; for (let i = 0, n = times.length; i !== n; ++i) { times[i] *= timeScale; } } return this; } // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim(startTime, endTime) { const times = this.times, nKeys = times.length; let from = 0, to = nKeys - 1; while (from !== nKeys && times[from] < startTime) { ++from; } while (to !== -1 && times[to] > endTime) { --to; } ++to; // inclusive -> exclusive bound if (from !== 0 || to !== nKeys) { // empty tracks are forbidden, so keep at least one keyframe if (from >= to) { to = Math.max(to, 1); from = to - 1; } const stride = this.getValueSize(); this.times = arraySlice(times, from, to); this.values = arraySlice(this.values, from * stride, to * stride); } return this; } // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate() { let valid = true; const valueSize = this.getValueSize(); if (valueSize - Math.floor(valueSize) !== 0) { console.error('THREE.KeyframeTrack: Invalid value size in track.', this); valid = false; } const times = this.times, values = this.values, nKeys = times.length; if (nKeys === 0) { console.error('THREE.KeyframeTrack: Track is empty.', this); valid = false; } let prevTime = null; for (let i = 0; i !== nKeys; i++) { const currTime = times[i]; if (typeof currTime === 'number' && isNaN(currTime)) { console.error('THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime); valid = false; break; } if (prevTime !== null && prevTime > currTime) { console.error('THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime); valid = false; break; } prevTime = currTime; } if (values !== undefined) { if (isTypedArray(values)) { for (let i = 0, n = values.length; i !== n; ++i) { const value = values[i]; if (isNaN(value)) { console.error('THREE.KeyframeTrack: Value is not a valid number.', this, i, value); valid = false; break; } } } } return valid; } // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize() { // times or values may be shared with other tracks, so overwriting is unsafe const times = arraySlice(this.times), values = arraySlice(this.values), stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, lastIndex = times.length - 1; let writeIndex = 1; for (let i = 1; i < lastIndex; ++i) { let keep = false; const time = times[i]; const timeNext = times[i + 1]; // remove adjacent keyframes scheduled at the same time if (time !== timeNext && (i !== 1 || time !== times[0])) { if (!smoothInterpolation) { // remove unnecessary keyframes same as their neighbors const offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for (let j = 0; j !== stride; ++j) { const value = values[offset + j]; if (value !== values[offsetP + j] || value !== values[offsetN + j]) { keep = true; break; } } } else { keep = true; } } // in-place compaction if (keep) { if (i !== writeIndex) { times[writeIndex] = times[i]; const readOffset = i * stride, writeOffset = writeIndex * stride; for (let j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } } ++writeIndex; } } // flush last keyframe (compaction looks ahead) if (lastIndex > 0) { times[writeIndex] = times[lastIndex]; for (let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++j) { values[writeOffset + j] = values[readOffset + j]; } ++writeIndex; } if (writeIndex !== times.length) { this.times = arraySlice(times, 0, writeIndex); this.values = arraySlice(values, 0, writeIndex * stride); } else { this.times = times; this.values = values; } return this; } clone() { const times = arraySlice(this.times, 0); const values = arraySlice(this.values, 0); const TypedKeyframeTrack = this.constructor; const track = new TypedKeyframeTrack(this.name, times, values); // Interpolant argument to constructor is not saved, so copy the factory method directly. track.createInterpolant = this.createInterpolant; return track; } } KeyframeTrack.prototype.TimeBufferType = Float32Array; KeyframeTrack.prototype.ValueBufferType = Float32Array; KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; /** * A Track of Boolean keyframe values. */ class BooleanKeyframeTrack extends KeyframeTrack {} BooleanKeyframeTrack.prototype.ValueTypeName = 'bool'; BooleanKeyframeTrack.prototype.ValueBufferType = Array; BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of keyframe values that represent color. */ class ColorKeyframeTrack extends KeyframeTrack {} ColorKeyframeTrack.prototype.ValueTypeName = 'color'; /** * A Track of numeric keyframe values. */ class NumberKeyframeTrack extends KeyframeTrack {} NumberKeyframeTrack.prototype.ValueTypeName = 'number'; /** * Spherical linear unit quaternion interpolant. */ class QuaternionLinearInterpolant extends Interpolant { constructor(parameterPositions, sampleValues, sampleSize, resultBuffer) { super(parameterPositions, sampleValues, sampleSize, resultBuffer); } interpolate_(i1, t0, t, t1) { const result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, alpha = (t - t0) / (t1 - t0); let offset = i1 * stride; for (let end = offset + stride; offset !== end; offset += 4) { Quaternion.slerpFlat(result, 0, values, offset - stride, values, offset, alpha); } return result; } } /** * A Track of quaternion keyframe values. */ class QuaternionKeyframeTrack extends KeyframeTrack { InterpolantFactoryMethodLinear(result) { return new QuaternionLinearInterpolant(this.times, this.values, this.getValueSize(), result); } } QuaternionKeyframeTrack.prototype.ValueTypeName = 'quaternion'; // ValueBufferType is inherited QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track that interpolates Strings */ class StringKeyframeTrack extends KeyframeTrack {} StringKeyframeTrack.prototype.ValueTypeName = 'string'; StringKeyframeTrack.prototype.ValueBufferType = Array; StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; /** * A Track of vectored keyframe values. */ class VectorKeyframeTrack extends KeyframeTrack {} VectorKeyframeTrack.prototype.ValueTypeName = 'vector'; class AnimationClip { constructor(name, duration = -1, tracks, blendMode = NormalAnimationBlendMode) { this.name = name; this.tracks = tracks; this.duration = duration; this.blendMode = blendMode; this.uuid = generateUUID(); // this means it should figure out its duration by scanning the tracks if (this.duration < 0) { this.resetDuration(); } } static parse(json) { const tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / (json.fps || 1.0); for (let i = 0, n = jsonTracks.length; i !== n; ++i) { tracks.push(parseKeyframeTrack(jsonTracks[i]).scale(frameTime)); } const clip = new this(json.name, json.duration, tracks, json.blendMode); clip.uuid = json.uuid; return clip; } static toJSON(clip) { const tracks = [], clipTracks = clip.tracks; const json = { 'name': clip.name, 'duration': clip.duration, 'tracks': tracks, 'uuid': clip.uuid, 'blendMode': clip.blendMode }; for (let i = 0, n = clipTracks.length; i !== n; ++i) { tracks.push(KeyframeTrack.toJSON(clipTracks[i])); } return json; } static CreateFromMorphTargetSequence(name, morphTargetSequence, fps, noLoop) { const numMorphTargets = morphTargetSequence.length; const tracks = []; for (let i = 0; i < numMorphTargets; i++) { let times = []; let values = []; times.push((i + numMorphTargets - 1) % numMorphTargets, i, (i + 1) % numMorphTargets); values.push(0, 1, 0); const order = getKeyframeOrder(times); times = sortedArray(times, 1, order); values = sortedArray(values, 1, order); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if (!noLoop && times[0] === 0) { times.push(numMorphTargets); values.push(values[0]); } tracks.push(new NumberKeyframeTrack('.morphTargetInfluences[' + morphTargetSequence[i].name + ']', times, values).scale(1.0 / fps)); } return new this(name, -1, tracks); } static findByName(objectOrClipArray, name) { let clipArray = objectOrClipArray; if (!Array.isArray(objectOrClipArray)) { const o = objectOrClipArray; clipArray = o.geometry && o.geometry.animations || o.animations; } for (let i = 0; i < clipArray.length; i++) { if (clipArray[i].name === name) { return clipArray[i]; } } return null; } static CreateClipsFromMorphTargetSequences(morphTargets, fps, noLoop) { const animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 const pattern = /^([\w-]*?)([\d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for (let i = 0, il = morphTargets.length; i < il; i++) { const morphTarget = morphTargets[i]; const parts = morphTarget.name.match(pattern); if (parts && parts.length > 1) { const name = parts[1]; let animationMorphTargets = animationToMorphTargets[name]; if (!animationMorphTargets) { animationToMorphTargets[name] = animationMorphTargets = []; } animationMorphTargets.push(morphTarget); } } const clips = []; for (const name in animationToMorphTargets) { clips.push(this.CreateFromMorphTargetSequence(name, animationToMorphTargets[name], fps, noLoop)); } return clips; } // parse the animation.hierarchy format static parseAnimation(animation, bones) { if (!animation) { console.error('THREE.AnimationClip: No animation in JSONLoader data.'); return null; } const addNonemptyTrack = function (trackType, trackName, animationKeys, propertyName, destTracks) { // only return track if there are actually keys. if (animationKeys.length !== 0) { const times = []; const values = []; flattenJSON(animationKeys, times, values, propertyName); // empty keys are filtered out, so check again if (times.length !== 0) { destTracks.push(new trackType(trackName, times, values)); } } }; const tracks = []; const clipName = animation.name || 'default'; const fps = animation.fps || 30; const blendMode = animation.blendMode; // automatic length determination in AnimationClip. let duration = animation.length || -1; const hierarchyTracks = animation.hierarchy || []; for (let h = 0; h < hierarchyTracks.length; h++) { const animationKeys = hierarchyTracks[h].keys; // skip empty tracks if (!animationKeys || animationKeys.length === 0) continue; // process morph targets if (animationKeys[0].morphTargets) { // figure out all morph targets used in this track const morphTargetNames = {}; let k; for (k = 0; k < animationKeys.length; k++) { if (animationKeys[k].morphTargets) { for (let m = 0; m < animationKeys[k].morphTargets.length; m++) { morphTargetNames[animationKeys[k].morphTargets[m]] = -1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for (const morphTargetName in morphTargetNames) { const times = []; const values = []; for (let m = 0; m !== animationKeys[k].morphTargets.length; ++m) { const animationKey = animationKeys[k]; times.push(animationKey.time); values.push(animationKey.morphTarget === morphTargetName ? 1 : 0); } tracks.push(new NumberKeyframeTrack('.morphTargetInfluence[' + morphTargetName + ']', times, values)); } duration = morphTargetNames.length * fps; } else { // ...assume skeletal animation const boneName = '.bones[' + bones[h].name + ']'; addNonemptyTrack(VectorKeyframeTrack, boneName + '.position', animationKeys, 'pos', tracks); addNonemptyTrack(QuaternionKeyframeTrack, boneName + '.quaternion', animationKeys, 'rot', tracks); addNonemptyTrack(VectorKeyframeTrack, boneName + '.scale', animationKeys, 'scl', tracks); } } if (tracks.length === 0) { return null; } const clip = new this(clipName, duration, tracks, blendMode); return clip; } resetDuration() { const tracks = this.tracks; let duration = 0; for (let i = 0, n = tracks.length; i !== n; ++i) { const track = this.tracks[i]; duration = Math.max(duration, track.times[track.times.length - 1]); } this.duration = duration; return this; } trim() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].trim(0, this.duration); } return this; } validate() { let valid = true; for (let i = 0; i < this.tracks.length; i++) { valid = valid && this.tracks[i].validate(); } return valid; } optimize() { for (let i = 0; i < this.tracks.length; i++) { this.tracks[i].optimize(); } return this; } clone() { const tracks = []; for (let i = 0; i < this.tracks.length; i++) { tracks.push(this.tracks[i].clone()); } return new this.constructor(this.name, this.duration, tracks, this.blendMode); } toJSON() { return this.constructor.toJSON(this); } } function getTrackTypeForValueTypeName(typeName) { switch (typeName.toLowerCase()) { case 'scalar': case 'double': case 'float': case 'number': case 'integer': return NumberKeyframeTrack; case 'vector': case 'vector2': case 'vector3': case 'vector4': return VectorKeyframeTrack; case 'color': return ColorKeyframeTrack; case 'quaternion': return QuaternionKeyframeTrack; case 'bool': case 'boolean': return BooleanKeyframeTrack; case 'string': return StringKeyframeTrack; } throw new Error('THREE.KeyframeTrack: Unsupported typeName: ' + typeName); } function parseKeyframeTrack(json) { if (json.type === undefined) { throw new Error('THREE.KeyframeTrack: track type undefined, can not parse'); } const trackType = getTrackTypeForValueTypeName(json.type); if (json.times === undefined) { const times = [], values = []; flattenJSON(json.keys, times, values, 'value'); json.times = times; json.values = values; } // derived classes can define a static parse method if (trackType.parse !== undefined) { return trackType.parse(json); } else { // by default, we assume a constructor compatible with the base return new trackType(json.name, json.times, json.values, json.interpolation); } } const Cache = { enabled: false, files: {}, add: function (key, file) { if (this.enabled === false) return; // console.log( 'THREE.Cache', 'Adding key:', key ); this.files[key] = file; }, get: function (key) { if (this.enabled === false) return; // console.log( 'THREE.Cache', 'Checking key:', key ); return this.files[key]; }, remove: function (key) { delete this.files[key]; }, clear: function () { this.files = {}; } }; class LoadingManager { constructor(onLoad, onProgress, onError) { const scope = this; let isLoading = false; let itemsLoaded = 0; let itemsTotal = 0; let urlModifier = undefined; const handlers = []; // Refer to #5689 for the reason why we don't set .onStart // in the constructor this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function (url) { itemsTotal++; if (isLoading === false) { if (scope.onStart !== undefined) { scope.onStart(url, itemsLoaded, itemsTotal); } } isLoading = true; }; this.itemEnd = function (url) { itemsLoaded++; if (scope.onProgress !== undefined) { scope.onProgress(url, itemsLoaded, itemsTotal); } if (itemsLoaded === itemsTotal) { isLoading = false; if (scope.onLoad !== undefined) { scope.onLoad(); } } }; this.itemError = function (url) { if (scope.onError !== undefined) { scope.onError(url); } }; this.resolveURL = function (url) { if (urlModifier) { return urlModifier(url); } return url; }; this.setURLModifier = function (transform) { urlModifier = transform; return this; }; this.addHandler = function (regex, loader) { handlers.push(regex, loader); return this; }; this.removeHandler = function (regex) { const index = handlers.indexOf(regex); if (index !== -1) { handlers.splice(index, 2); } return this; }; this.getHandler = function (file) { for (let i = 0, l = handlers.length; i < l; i += 2) { const regex = handlers[i]; const loader = handlers[i + 1]; if (regex.global) regex.lastIndex = 0; // see #17920 if (regex.test(file)) { return loader; } } return null; }; } } const DefaultLoadingManager = /*@__PURE__*/new LoadingManager(); class Loader { constructor(manager) { this.manager = manager !== undefined ? manager : DefaultLoadingManager; this.crossOrigin = 'anonymous'; this.withCredentials = false; this.path = ''; this.resourcePath = ''; this.requestHeader = {}; } load( /* url, onLoad, onProgress, onError */) {} loadAsync(url, onProgress) { const scope = this; return new Promise(function (resolve, reject) { scope.load(url, resolve, onProgress, reject); }); } parse( /* data */) {} setCrossOrigin(crossOrigin) { this.crossOrigin = crossOrigin; return this; } setWithCredentials(value) { this.withCredentials = value; return this; } setPath(path) { this.path = path; return this; } setResourcePath(resourcePath) { this.resourcePath = resourcePath; return this; } setRequestHeader(requestHeader) { this.requestHeader = requestHeader; return this; } } Loader.DEFAULT_MATERIAL_NAME = '__DEFAULT'; const loading = {}; class HttpError extends Error { constructor(message, response) { super(message); this.response = response; } } class FileLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ''; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const cached = Cache.get(url); if (cached !== undefined) { this.manager.itemStart(url); setTimeout(() => { if (onLoad) onLoad(cached); this.manager.itemEnd(url); }, 0); return cached; } // Check if request is duplicate if (loading[url] !== undefined) { loading[url].push({ onLoad: onLoad, onProgress: onProgress, onError: onError }); return; } // Initialise array for duplicate requests loading[url] = []; loading[url].push({ onLoad: onLoad, onProgress: onProgress, onError: onError }); // create request const req = new Request(url, { headers: new Headers(this.requestHeader), credentials: this.withCredentials ? 'include' : 'same-origin' // An abort controller could be added within a future PR }); // record states ( avoid data race ) const mimeType = this.mimeType; const responseType = this.responseType; // start the fetch fetch(req).then(response => { if (response.status === 200 || response.status === 0) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. 'file://' or 'data://'. Handle as success. if (response.status === 0) { console.warn('THREE.FileLoader: HTTP Status 0 received.'); } // Workaround: Checking if response.body === undefined for Alipay browser #23548 if (typeof ReadableStream === 'undefined' || response.body === undefined || response.body.getReader === undefined) { return response; } const callbacks = loading[url]; const reader = response.body.getReader(); // Nginx needs X-File-Size check // https://serverfault.com/questions/482875/why-does-nginx-remove-content-length-header-for-chunked-content const contentLength = response.headers.get('Content-Length') || response.headers.get('X-File-Size'); const total = contentLength ? parseInt(contentLength) : 0; const lengthComputable = total !== 0; let loaded = 0; // periodically read data into the new stream tracking while download progress const stream = new ReadableStream({ start(controller) { readData(); function readData() { reader.read().then(({ done, value }) => { if (done) { controller.close(); } else { loaded += value.byteLength; const event = new ProgressEvent('progress', { lengthComputable, loaded, total }); for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onProgress) callback.onProgress(event); } controller.enqueue(value); readData(); } }); } } }); return new Response(stream); } else { throw new HttpError(`fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`, response); } }).then(response => { switch (responseType) { case 'arraybuffer': return response.arrayBuffer(); case 'blob': return response.blob(); case 'document': return response.text().then(text => { const parser = new DOMParser(); return parser.parseFromString(text, mimeType); }); case 'json': return response.json(); default: if (mimeType === undefined) { return response.text(); } else { // sniff encoding const re = /charset="?([^;"\s]*)"?/i; const exec = re.exec(mimeType); const label = exec && exec[1] ? exec[1].toLowerCase() : undefined; const decoder = new TextDecoder(label); return response.arrayBuffer().then(ab => decoder.decode(ab)); } } }).then(data => { // Add to cache only on HTTP success, so that we do not cache // error response bodies as proper responses to requests. Cache.add(url, data); const callbacks = loading[url]; delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onLoad) callback.onLoad(data); } }).catch(err => { // Abort errors and other errors are handled the same const callbacks = loading[url]; if (callbacks === undefined) { // When onLoad was called and url was deleted in `loading` this.manager.itemError(url); throw err; } delete loading[url]; for (let i = 0, il = callbacks.length; i < il; i++) { const callback = callbacks[i]; if (callback.onError) callback.onError(err); } this.manager.itemError(url); }).finally(() => { this.manager.itemEnd(url); }); this.manager.itemStart(url); } setResponseType(value) { this.responseType = value; return this; } setMimeType(value) { this.mimeType = value; return this; } } class AnimationLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const animations = []; for (let i = 0; i < json.length; i++) { const clip = AnimationClip.parse(json[i]); animations.push(clip); } return animations; } } /** * Abstract Base class to block based textures loader (dds, pvr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class CompressedTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const images = []; const texture = new CompressedTexture(); const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setResponseType('arraybuffer'); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(scope.withCredentials); let loaded = 0; function loadTexture(i) { loader.load(url[i], function (buffer) { const texDatas = scope.parse(buffer, true); images[i] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps }; loaded += 1; if (loaded === 6) { if (texDatas.mipmapCount === 1) texture.minFilter = LinearFilter; texture.image = images; texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, onProgress, onError); } if (Array.isArray(url)) { for (let i = 0, il = url.length; i < il; ++i) { loadTexture(i); } } else { // compressed cubemap texture stored in a single DDS file loader.load(url, function (buffer) { const texDatas = scope.parse(buffer, true); if (texDatas.isCubemap) { const faces = texDatas.mipmaps.length / texDatas.mipmapCount; for (let f = 0; f < faces; f++) { images[f] = { mipmaps: [] }; for (let i = 0; i < texDatas.mipmapCount; i++) { images[f].mipmaps.push(texDatas.mipmaps[f * texDatas.mipmapCount + i]); images[f].format = texDatas.format; images[f].width = texDatas.width; images[f].height = texDatas.height; } } texture.image = images; } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if (texDatas.mipmapCount === 1) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if (onLoad) onLoad(texture); }, onProgress, onError); } return texture; } } class ImageLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const image = createElementNS('img'); function onImageLoad() { removeEventListeners(); Cache.add(url, this); if (onLoad) onLoad(this); scope.manager.itemEnd(url); } function onImageError(event) { removeEventListeners(); if (onError) onError(event); scope.manager.itemError(url); scope.manager.itemEnd(url); } function removeEventListeners() { image.removeEventListener('load', onImageLoad, false); image.removeEventListener('error', onImageError, false); } image.addEventListener('load', onImageLoad, false); image.addEventListener('error', onImageError, false); if (url.slice(0, 5) !== 'data:') { if (this.crossOrigin !== undefined) image.crossOrigin = this.crossOrigin; } scope.manager.itemStart(url); image.src = url; return image; } } class CubeTextureLoader extends Loader { constructor(manager) { super(manager); } load(urls, onLoad, onProgress, onError) { const texture = new CubeTexture(); texture.colorSpace = SRGBColorSpace; const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); let loaded = 0; function loadTexture(i) { loader.load(urls[i], function (image) { texture.images[i] = image; loaded++; if (loaded === 6) { texture.needsUpdate = true; if (onLoad) onLoad(texture); } }, undefined, onError); } for (let i = 0; i < urls.length; ++i) { loadTexture(i); } return texture; } } /** * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) * * Sub classes have to implement the parse() method which will be used in load(). */ class DataTextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const texture = new DataTexture(); const loader = new FileLoader(this.manager); loader.setResponseType('arraybuffer'); loader.setRequestHeader(this.requestHeader); loader.setPath(this.path); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (buffer) { const texData = scope.parse(buffer); if (!texData) return; if (texData.image !== undefined) { texture.image = texData.image; } else if (texData.data !== undefined) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; if (texData.colorSpace !== undefined) { texture.colorSpace = texData.colorSpace; } else if (texData.encoding !== undefined) { // @deprecated, r152 texture.encoding = texData.encoding; } if (texData.flipY !== undefined) { texture.flipY = texData.flipY; } if (texData.format !== undefined) { texture.format = texData.format; } if (texData.type !== undefined) { texture.type = texData.type; } if (texData.mipmaps !== undefined) { texture.mipmaps = texData.mipmaps; texture.minFilter = LinearMipmapLinearFilter; // presumably... } if (texData.mipmapCount === 1) { texture.minFilter = LinearFilter; } if (texData.generateMipmaps !== undefined) { texture.generateMipmaps = texData.generateMipmaps; } texture.needsUpdate = true; if (onLoad) onLoad(texture, texData); }, onProgress, onError); return texture; } } class TextureLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const texture = new Texture(); const loader = new ImageLoader(this.manager); loader.setCrossOrigin(this.crossOrigin); loader.setPath(this.path); loader.load(url, function (image) { texture.image = image; texture.needsUpdate = true; if (onLoad !== undefined) { onLoad(texture); } }, onProgress, onError); return texture; } } class Light extends Object3D { constructor(color, intensity = 1) { super(); this.isLight = true; this.type = 'Light'; this.color = new Color(color); this.intensity = intensity; } dispose() { // Empty here in base class; some subclasses override. } copy(source, recursive) { super.copy(source, recursive); this.color.copy(source.color); this.intensity = source.intensity; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if (this.groundColor !== undefined) data.object.groundColor = this.groundColor.getHex(); if (this.distance !== undefined) data.object.distance = this.distance; if (this.angle !== undefined) data.object.angle = this.angle; if (this.decay !== undefined) data.object.decay = this.decay; if (this.penumbra !== undefined) data.object.penumbra = this.penumbra; if (this.shadow !== undefined) data.object.shadow = this.shadow.toJSON(); return data; } } class HemisphereLight extends Light { constructor(skyColor, groundColor, intensity) { super(skyColor, intensity); this.isHemisphereLight = true; this.type = 'HemisphereLight'; this.position.copy(Object3D.DEFAULT_UP); this.updateMatrix(); this.groundColor = new Color(groundColor); } copy(source, recursive) { super.copy(source, recursive); this.groundColor.copy(source.groundColor); return this; } } const _projScreenMatrix$1 = /*@__PURE__*/new Matrix4(); const _lightPositionWorld$1 = /*@__PURE__*/new Vector3(); const _lookTarget$1 = /*@__PURE__*/new Vector3(); class LightShadow { constructor(camera) { this.camera = camera; this.bias = 0; this.normalBias = 0; this.radius = 1; this.blurSamples = 8; this.mapSize = new Vector2(512, 512); this.map = null; this.mapPass = null; this.matrix = new Matrix4(); this.autoUpdate = true; this.needsUpdate = false; this._frustum = new Frustum(); this._frameExtents = new Vector2(1, 1); this._viewportCount = 1; this._viewports = [new Vector4(0, 0, 1, 1)]; } getViewportCount() { return this._viewportCount; } getFrustum() { return this._frustum; } updateMatrices(light) { const shadowCamera = this.camera; const shadowMatrix = this.matrix; _lightPositionWorld$1.setFromMatrixPosition(light.matrixWorld); shadowCamera.position.copy(_lightPositionWorld$1); _lookTarget$1.setFromMatrixPosition(light.target.matrixWorld); shadowCamera.lookAt(_lookTarget$1); shadowCamera.updateMatrixWorld(); _projScreenMatrix$1.multiplyMatrices(shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse); this._frustum.setFromProjectionMatrix(_projScreenMatrix$1); shadowMatrix.set(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0); shadowMatrix.multiply(_projScreenMatrix$1); } getViewport(viewportIndex) { return this._viewports[viewportIndex]; } getFrameExtents() { return this._frameExtents; } dispose() { if (this.map) { this.map.dispose(); } if (this.mapPass) { this.mapPass.dispose(); } } copy(source) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy(source.mapSize); return this; } clone() { return new this.constructor().copy(this); } toJSON() { const object = {}; if (this.bias !== 0) object.bias = this.bias; if (this.normalBias !== 0) object.normalBias = this.normalBias; if (this.radius !== 1) object.radius = this.radius; if (this.mapSize.x !== 512 || this.mapSize.y !== 512) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON(false).object; delete object.camera.matrix; return object; } } class SpotLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(50, 1, 0.5, 500)); this.isSpotLightShadow = true; this.focus = 1; } updateMatrices(light) { const camera = this.camera; const fov = RAD2DEG * 2 * light.angle * this.focus; const aspect = this.mapSize.width / this.mapSize.height; const far = light.distance || camera.far; if (fov !== camera.fov || aspect !== camera.aspect || far !== camera.far) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } super.updateMatrices(light); } copy(source) { super.copy(source); this.focus = source.focus; return this; } } class SpotLight extends Light { constructor(color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 2) { super(color, intensity); this.isSpotLight = true; this.type = 'SpotLight'; this.position.copy(Object3D.DEFAULT_UP); this.updateMatrix(); this.target = new Object3D(); this.distance = distance; this.angle = angle; this.penumbra = penumbra; this.decay = decay; this.map = null; this.shadow = new SpotLightShadow(); } get power() { // compute the light's luminous power (in lumens) from its intensity (in candela) // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) return this.intensity * Math.PI; } set power(power) { // set the light's intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / Math.PI; } dispose() { this.shadow.dispose(); } copy(source, recursive) { super.copy(source, recursive); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } const _projScreenMatrix = /*@__PURE__*/new Matrix4(); const _lightPositionWorld = /*@__PURE__*/new Vector3(); const _lookTarget = /*@__PURE__*/new Vector3(); class PointLightShadow extends LightShadow { constructor() { super(new PerspectiveCamera(90, 1, 0.5, 500)); this.isPointLightShadow = true; this._frameExtents = new Vector2(4, 2); this._viewportCount = 6; this._viewports = [ // These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X new Vector4(2, 1, 1, 1), // negative X new Vector4(0, 1, 1, 1), // positive Z new Vector4(3, 1, 1, 1), // negative Z new Vector4(1, 1, 1, 1), // positive Y new Vector4(3, 0, 1, 1), // negative Y new Vector4(1, 0, 1, 1)]; this._cubeDirections = [new Vector3(1, 0, 0), new Vector3(-1, 0, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(0, 1, 0), new Vector3(0, -1, 0)]; this._cubeUps = [new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1), new Vector3(0, 0, -1)]; } updateMatrices(light, viewportIndex = 0) { const camera = this.camera; const shadowMatrix = this.matrix; const far = light.distance || camera.far; if (far !== camera.far) { camera.far = far; camera.updateProjectionMatrix(); } _lightPositionWorld.setFromMatrixPosition(light.matrixWorld); camera.position.copy(_lightPositionWorld); _lookTarget.copy(camera.position); _lookTarget.add(this._cubeDirections[viewportIndex]); camera.up.copy(this._cubeUps[viewportIndex]); camera.lookAt(_lookTarget); camera.updateMatrixWorld(); shadowMatrix.makeTranslation(-_lightPositionWorld.x, -_lightPositionWorld.y, -_lightPositionWorld.z); _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); this._frustum.setFromProjectionMatrix(_projScreenMatrix); } } class PointLight extends Light { constructor(color, intensity, distance = 0, decay = 2) { super(color, intensity); this.isPointLight = true; this.type = 'PointLight'; this.distance = distance; this.decay = decay; this.shadow = new PointLightShadow(); } get power() { // compute the light's luminous power (in lumens) from its intensity (in candela) // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) return this.intensity * 4 * Math.PI; } set power(power) { // set the light's intensity (in candela) from the desired luminous power (in lumens) this.intensity = power / (4 * Math.PI); } dispose() { this.shadow.dispose(); } copy(source, recursive) { super.copy(source, recursive); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } class DirectionalLightShadow extends LightShadow { constructor() { super(new OrthographicCamera(-5, 5, 5, -5, 0.5, 500)); this.isDirectionalLightShadow = true; } } class DirectionalLight extends Light { constructor(color, intensity) { super(color, intensity); this.isDirectionalLight = true; this.type = 'DirectionalLight'; this.position.copy(Object3D.DEFAULT_UP); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } dispose() { this.shadow.dispose(); } copy(source) { super.copy(source); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } class AmbientLight extends Light { constructor(color, intensity) { super(color, intensity); this.isAmbientLight = true; this.type = 'AmbientLight'; } } class RectAreaLight extends Light { constructor(color, intensity, width = 10, height = 10) { super(color, intensity); this.isRectAreaLight = true; this.type = 'RectAreaLight'; this.width = width; this.height = height; } get power() { // compute the light's luminous power (in lumens) from its intensity (in nits) return this.intensity * this.width * this.height * Math.PI; } set power(power) { // set the light's intensity (in nits) from the desired luminous power (in lumens) this.intensity = power / (this.width * this.height * Math.PI); } copy(source) { super.copy(source); this.width = source.width; this.height = source.height; return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.width = this.width; data.object.height = this.height; return data; } } /** * Primary reference: * https://graphics.stanford.edu/papers/envmap/envmap.pdf * * Secondary reference: * https://www.ppsloan.org/publications/StupidSH36.pdf */ // 3-band SH defined by 9 coefficients class SphericalHarmonics3 { constructor() { this.isSphericalHarmonics3 = true; this.coefficients = []; for (let i = 0; i < 9; i++) { this.coefficients.push(new Vector3()); } } set(coefficients) { for (let i = 0; i < 9; i++) { this.coefficients[i].copy(coefficients[i]); } return this; } zero() { for (let i = 0; i < 9; i++) { this.coefficients[i].set(0, 0, 0); } return this; } // get the radiance in the direction of the normal // target is a Vector3 getAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.282095); // band 1 target.addScaledVector(coeff[1], 0.488603 * y); target.addScaledVector(coeff[2], 0.488603 * z); target.addScaledVector(coeff[3], 0.488603 * x); // band 2 target.addScaledVector(coeff[4], 1.092548 * (x * y)); target.addScaledVector(coeff[5], 1.092548 * (y * z)); target.addScaledVector(coeff[6], 0.315392 * (3.0 * z * z - 1.0)); target.addScaledVector(coeff[7], 1.092548 * (x * z)); target.addScaledVector(coeff[8], 0.546274 * (x * x - y * y)); return target; } // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal // target is a Vector3 // https://graphics.stanford.edu/papers/envmap/envmap.pdf getIrradianceAt(normal, target) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; const coeff = this.coefficients; // band 0 target.copy(coeff[0]).multiplyScalar(0.886227); // π * 0.282095 // band 1 target.addScaledVector(coeff[1], 2.0 * 0.511664 * y); // ( 2 * π / 3 ) * 0.488603 target.addScaledVector(coeff[2], 2.0 * 0.511664 * z); target.addScaledVector(coeff[3], 2.0 * 0.511664 * x); // band 2 target.addScaledVector(coeff[4], 2.0 * 0.429043 * x * y); // ( π / 4 ) * 1.092548 target.addScaledVector(coeff[5], 2.0 * 0.429043 * y * z); target.addScaledVector(coeff[6], 0.743125 * z * z - 0.247708); // ( π / 4 ) * 0.315392 * 3 target.addScaledVector(coeff[7], 2.0 * 0.429043 * x * z); target.addScaledVector(coeff[8], 0.429043 * (x * x - y * y)); // ( π / 4 ) * 0.546274 return target; } add(sh) { for (let i = 0; i < 9; i++) { this.coefficients[i].add(sh.coefficients[i]); } return this; } addScaledSH(sh, s) { for (let i = 0; i < 9; i++) { this.coefficients[i].addScaledVector(sh.coefficients[i], s); } return this; } scale(s) { for (let i = 0; i < 9; i++) { this.coefficients[i].multiplyScalar(s); } return this; } lerp(sh, alpha) { for (let i = 0; i < 9; i++) { this.coefficients[i].lerp(sh.coefficients[i], alpha); } return this; } equals(sh) { for (let i = 0; i < 9; i++) { if (!this.coefficients[i].equals(sh.coefficients[i])) { return false; } } return true; } copy(sh) { return this.set(sh.coefficients); } clone() { return new this.constructor().copy(this); } fromArray(array, offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].fromArray(array, offset + i * 3); } return this; } toArray(array = [], offset = 0) { const coefficients = this.coefficients; for (let i = 0; i < 9; i++) { coefficients[i].toArray(array, offset + i * 3); } return array; } // evaluate the basis functions // shBasis is an Array[ 9 ] static getBasisAt(normal, shBasis) { // normal is assumed to be unit length const x = normal.x, y = normal.y, z = normal.z; // band 0 shBasis[0] = 0.282095; // band 1 shBasis[1] = 0.488603 * y; shBasis[2] = 0.488603 * z; shBasis[3] = 0.488603 * x; // band 2 shBasis[4] = 1.092548 * x * y; shBasis[5] = 1.092548 * y * z; shBasis[6] = 0.315392 * (3 * z * z - 1); shBasis[7] = 1.092548 * x * z; shBasis[8] = 0.546274 * (x * x - y * y); } } class LightProbe extends Light { constructor(sh = new SphericalHarmonics3(), intensity = 1) { super(undefined, intensity); this.isLightProbe = true; this.sh = sh; } copy(source) { super.copy(source); this.sh.copy(source.sh); return this; } fromJSON(json) { this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); this.sh.fromArray(json.sh); return this; } toJSON(meta) { const data = super.toJSON(meta); data.object.sh = this.sh.toArray(); return data; } } class MaterialLoader extends Loader { constructor(manager) { super(manager); this.textures = {}; } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const textures = this.textures; function getTexture(name) { if (textures[name] === undefined) { console.warn('THREE.MaterialLoader: Undefined texture', name); } return textures[name]; } const material = MaterialLoader.createMaterialFromType(json.type); if (json.uuid !== undefined) material.uuid = json.uuid; if (json.name !== undefined) material.name = json.name; if (json.color !== undefined && material.color !== undefined) material.color.setHex(json.color); if (json.roughness !== undefined) material.roughness = json.roughness; if (json.metalness !== undefined) material.metalness = json.metalness; if (json.sheen !== undefined) material.sheen = json.sheen; if (json.sheenColor !== undefined) material.sheenColor = new Color().setHex(json.sheenColor); if (json.sheenRoughness !== undefined) material.sheenRoughness = json.sheenRoughness; if (json.emissive !== undefined && material.emissive !== undefined) material.emissive.setHex(json.emissive); if (json.specular !== undefined && material.specular !== undefined) material.specular.setHex(json.specular); if (json.specularIntensity !== undefined) material.specularIntensity = json.specularIntensity; if (json.specularColor !== undefined && material.specularColor !== undefined) material.specularColor.setHex(json.specularColor); if (json.shininess !== undefined) material.shininess = json.shininess; if (json.clearcoat !== undefined) material.clearcoat = json.clearcoat; if (json.clearcoatRoughness !== undefined) material.clearcoatRoughness = json.clearcoatRoughness; if (json.iridescence !== undefined) material.iridescence = json.iridescence; if (json.iridescenceIOR !== undefined) material.iridescenceIOR = json.iridescenceIOR; if (json.iridescenceThicknessRange !== undefined) material.iridescenceThicknessRange = json.iridescenceThicknessRange; if (json.transmission !== undefined) material.transmission = json.transmission; if (json.thickness !== undefined) material.thickness = json.thickness; if (json.attenuationDistance !== undefined) material.attenuationDistance = json.attenuationDistance; if (json.attenuationColor !== undefined && material.attenuationColor !== undefined) material.attenuationColor.setHex(json.attenuationColor); if (json.anisotropy !== undefined) material.anisotropy = json.anisotropy; if (json.anisotropyRotation !== undefined) material.anisotropyRotation = json.anisotropyRotation; if (json.fog !== undefined) material.fog = json.fog; if (json.flatShading !== undefined) material.flatShading = json.flatShading; if (json.blending !== undefined) material.blending = json.blending; if (json.combine !== undefined) material.combine = json.combine; if (json.side !== undefined) material.side = json.side; if (json.shadowSide !== undefined) material.shadowSide = json.shadowSide; if (json.opacity !== undefined) material.opacity = json.opacity; if (json.transparent !== undefined) material.transparent = json.transparent; if (json.alphaTest !== undefined) material.alphaTest = json.alphaTest; if (json.alphaHash !== undefined) material.alphaHash = json.alphaHash; if (json.depthTest !== undefined) material.depthTest = json.depthTest; if (json.depthWrite !== undefined) material.depthWrite = json.depthWrite; if (json.colorWrite !== undefined) material.colorWrite = json.colorWrite; if (json.stencilWrite !== undefined) material.stencilWrite = json.stencilWrite; if (json.stencilWriteMask !== undefined) material.stencilWriteMask = json.stencilWriteMask; if (json.stencilFunc !== undefined) material.stencilFunc = json.stencilFunc; if (json.stencilRef !== undefined) material.stencilRef = json.stencilRef; if (json.stencilFuncMask !== undefined) material.stencilFuncMask = json.stencilFuncMask; if (json.stencilFail !== undefined) material.stencilFail = json.stencilFail; if (json.stencilZFail !== undefined) material.stencilZFail = json.stencilZFail; if (json.stencilZPass !== undefined) material.stencilZPass = json.stencilZPass; if (json.wireframe !== undefined) material.wireframe = json.wireframe; if (json.wireframeLinewidth !== undefined) material.wireframeLinewidth = json.wireframeLinewidth; if (json.wireframeLinecap !== undefined) material.wireframeLinecap = json.wireframeLinecap; if (json.wireframeLinejoin !== undefined) material.wireframeLinejoin = json.wireframeLinejoin; if (json.rotation !== undefined) material.rotation = json.rotation; if (json.linewidth !== 1) material.linewidth = json.linewidth; if (json.dashSize !== undefined) material.dashSize = json.dashSize; if (json.gapSize !== undefined) material.gapSize = json.gapSize; if (json.scale !== undefined) material.scale = json.scale; if (json.polygonOffset !== undefined) material.polygonOffset = json.polygonOffset; if (json.polygonOffsetFactor !== undefined) material.polygonOffsetFactor = json.polygonOffsetFactor; if (json.polygonOffsetUnits !== undefined) material.polygonOffsetUnits = json.polygonOffsetUnits; if (json.dithering !== undefined) material.dithering = json.dithering; if (json.alphaToCoverage !== undefined) material.alphaToCoverage = json.alphaToCoverage; if (json.premultipliedAlpha !== undefined) material.premultipliedAlpha = json.premultipliedAlpha; if (json.forceSinglePass !== undefined) material.forceSinglePass = json.forceSinglePass; if (json.visible !== undefined) material.visible = json.visible; if (json.toneMapped !== undefined) material.toneMapped = json.toneMapped; if (json.userData !== undefined) material.userData = json.userData; if (json.vertexColors !== undefined) { if (typeof json.vertexColors === 'number') { material.vertexColors = json.vertexColors > 0 ? true : false; } else { material.vertexColors = json.vertexColors; } } // Shader Material if (json.uniforms !== undefined) { for (const name in json.uniforms) { const uniform = json.uniforms[name]; material.uniforms[name] = {}; switch (uniform.type) { case 't': material.uniforms[name].value = getTexture(uniform.value); break; case 'c': material.uniforms[name].value = new Color().setHex(uniform.value); break; case 'v2': material.uniforms[name].value = new Vector2().fromArray(uniform.value); break; case 'v3': material.uniforms[name].value = new Vector3().fromArray(uniform.value); break; case 'v4': material.uniforms[name].value = new Vector4().fromArray(uniform.value); break; case 'm3': material.uniforms[name].value = new Matrix3().fromArray(uniform.value); break; case 'm4': material.uniforms[name].value = new Matrix4().fromArray(uniform.value); break; default: material.uniforms[name].value = uniform.value; } } } if (json.defines !== undefined) material.defines = json.defines; if (json.vertexShader !== undefined) material.vertexShader = json.vertexShader; if (json.fragmentShader !== undefined) material.fragmentShader = json.fragmentShader; if (json.glslVersion !== undefined) material.glslVersion = json.glslVersion; if (json.extensions !== undefined) { for (const key in json.extensions) { material.extensions[key] = json.extensions[key]; } } if (json.lights !== undefined) material.lights = json.lights; if (json.clipping !== undefined) material.clipping = json.clipping; // for PointsMaterial if (json.size !== undefined) material.size = json.size; if (json.sizeAttenuation !== undefined) material.sizeAttenuation = json.sizeAttenuation; // maps if (json.map !== undefined) material.map = getTexture(json.map); if (json.matcap !== undefined) material.matcap = getTexture(json.matcap); if (json.alphaMap !== undefined) material.alphaMap = getTexture(json.alphaMap); if (json.bumpMap !== undefined) material.bumpMap = getTexture(json.bumpMap); if (json.bumpScale !== undefined) material.bumpScale = json.bumpScale; if (json.normalMap !== undefined) material.normalMap = getTexture(json.normalMap); if (json.normalMapType !== undefined) material.normalMapType = json.normalMapType; if (json.normalScale !== undefined) { let normalScale = json.normalScale; if (Array.isArray(normalScale) === false) { // Blender exporter used to export a scalar. See #7459 normalScale = [normalScale, normalScale]; } material.normalScale = new Vector2().fromArray(normalScale); } if (json.displacementMap !== undefined) material.displacementMap = getTexture(json.displacementMap); if (json.displacementScale !== undefined) material.displacementScale = json.displacementScale; if (json.displacementBias !== undefined) material.displacementBias = json.displacementBias; if (json.roughnessMap !== undefined) material.roughnessMap = getTexture(json.roughnessMap); if (json.metalnessMap !== undefined) material.metalnessMap = getTexture(json.metalnessMap); if (json.emissiveMap !== undefined) material.emissiveMap = getTexture(json.emissiveMap); if (json.emissiveIntensity !== undefined) material.emissiveIntensity = json.emissiveIntensity; if (json.specularMap !== undefined) material.specularMap = getTexture(json.specularMap); if (json.specularIntensityMap !== undefined) material.specularIntensityMap = getTexture(json.specularIntensityMap); if (json.specularColorMap !== undefined) material.specularColorMap = getTexture(json.specularColorMap); if (json.envMap !== undefined) material.envMap = getTexture(json.envMap); if (json.envMapIntensity !== undefined) material.envMapIntensity = json.envMapIntensity; if (json.reflectivity !== undefined) material.reflectivity = json.reflectivity; if (json.refractionRatio !== undefined) material.refractionRatio = json.refractionRatio; if (json.lightMap !== undefined) material.lightMap = getTexture(json.lightMap); if (json.lightMapIntensity !== undefined) material.lightMapIntensity = json.lightMapIntensity; if (json.aoMap !== undefined) material.aoMap = getTexture(json.aoMap); if (json.aoMapIntensity !== undefined) material.aoMapIntensity = json.aoMapIntensity; if (json.gradientMap !== undefined) material.gradientMap = getTexture(json.gradientMap); if (json.clearcoatMap !== undefined) material.clearcoatMap = getTexture(json.clearcoatMap); if (json.clearcoatRoughnessMap !== undefined) material.clearcoatRoughnessMap = getTexture(json.clearcoatRoughnessMap); if (json.clearcoatNormalMap !== undefined) material.clearcoatNormalMap = getTexture(json.clearcoatNormalMap); if (json.clearcoatNormalScale !== undefined) material.clearcoatNormalScale = new Vector2().fromArray(json.clearcoatNormalScale); if (json.iridescenceMap !== undefined) material.iridescenceMap = getTexture(json.iridescenceMap); if (json.iridescenceThicknessMap !== undefined) material.iridescenceThicknessMap = getTexture(json.iridescenceThicknessMap); if (json.transmissionMap !== undefined) material.transmissionMap = getTexture(json.transmissionMap); if (json.thicknessMap !== undefined) material.thicknessMap = getTexture(json.thicknessMap); if (json.anisotropyMap !== undefined) material.anisotropyMap = getTexture(json.anisotropyMap); if (json.sheenColorMap !== undefined) material.sheenColorMap = getTexture(json.sheenColorMap); if (json.sheenRoughnessMap !== undefined) material.sheenRoughnessMap = getTexture(json.sheenRoughnessMap); return material; } setTextures(value) { this.textures = value; return this; } static createMaterialFromType(type) { const materialLib = { ShadowMaterial, SpriteMaterial, RawShaderMaterial, ShaderMaterial, PointsMaterial, MeshPhysicalMaterial, MeshStandardMaterial, MeshPhongMaterial, MeshToonMaterial, MeshNormalMaterial, MeshLambertMaterial, MeshDepthMaterial, MeshDistanceMaterial, MeshBasicMaterial, MeshMatcapMaterial, LineDashedMaterial, LineBasicMaterial, Material }; return new materialLib[type](); } } class LoaderUtils { static decodeText(array) { if (typeof TextDecoder !== 'undefined') { return new TextDecoder().decode(array); } // Avoid the String.fromCharCode.apply(null, array) shortcut, which // throws a "maximum call stack size exceeded" error for large arrays. let s = ''; for (let i = 0, il = array.length; i < il; i++) { // Implicitly assumes little-endian. s += String.fromCharCode(array[i]); } try { // merges multi-byte utf-8 characters. return decodeURIComponent(escape(s)); } catch (e) { // see #16358 return s; } } static extractUrlBase(url) { const index = url.lastIndexOf('/'); if (index === -1) return './'; return url.slice(0, index + 1); } static resolveURL(url, path) { // Invalid URL if (typeof url !== 'string' || url === '') return ''; // Host Relative URL if (/^https?:\/\//i.test(path) && /^\//.test(url)) { path = path.replace(/(^https?:\/\/[^\/]+).*/i, '$1'); } // Absolute URL http://,https://,// if (/^(https?:)?\/\//i.test(url)) return url; // Data URI if (/^data:.*,.*$/i.test(url)) return url; // Blob URL if (/^blob:.*$/i.test(url)) return url; // Relative URL return path + url; } } class InstancedBufferGeometry extends BufferGeometry { constructor() { super(); this.isInstancedBufferGeometry = true; this.type = 'InstancedBufferGeometry'; this.instanceCount = Infinity; } copy(source) { super.copy(source); this.instanceCount = source.instanceCount; return this; } toJSON() { const data = super.toJSON(); data.instanceCount = this.instanceCount; data.isInstancedBufferGeometry = true; return data; } } class BufferGeometryLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (text) { try { onLoad(scope.parse(JSON.parse(text))); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } parse(json) { const interleavedBufferMap = {}; const arrayBufferMap = {}; function getInterleavedBuffer(json, uuid) { if (interleavedBufferMap[uuid] !== undefined) return interleavedBufferMap[uuid]; const interleavedBuffers = json.interleavedBuffers; const interleavedBuffer = interleavedBuffers[uuid]; const buffer = getArrayBuffer(json, interleavedBuffer.buffer); const array = getTypedArray(interleavedBuffer.type, buffer); const ib = new InterleavedBuffer(array, interleavedBuffer.stride); ib.uuid = interleavedBuffer.uuid; interleavedBufferMap[uuid] = ib; return ib; } function getArrayBuffer(json, uuid) { if (arrayBufferMap[uuid] !== undefined) return arrayBufferMap[uuid]; const arrayBuffers = json.arrayBuffers; const arrayBuffer = arrayBuffers[uuid]; const ab = new Uint32Array(arrayBuffer).buffer; arrayBufferMap[uuid] = ab; return ab; } const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); const index = json.data.index; if (index !== undefined) { const typedArray = getTypedArray(index.type, index.array); geometry.setIndex(new BufferAttribute(typedArray, 1)); } const attributes = json.data.attributes; for (const key in attributes) { const attribute = attributes[key]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); } else { const typedArray = getTypedArray(attribute.type, attribute.array); const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; bufferAttribute = new bufferAttributeConstr(typedArray, attribute.itemSize, attribute.normalized); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; if (attribute.usage !== undefined) bufferAttribute.setUsage(attribute.usage); if (attribute.updateRange !== undefined) { bufferAttribute.updateRange.offset = attribute.updateRange.offset; bufferAttribute.updateRange.count = attribute.updateRange.count; } geometry.setAttribute(key, bufferAttribute); } const morphAttributes = json.data.morphAttributes; if (morphAttributes) { for (const key in morphAttributes) { const attributeArray = morphAttributes[key]; const array = []; for (let i = 0, il = attributeArray.length; i < il; i++) { const attribute = attributeArray[i]; let bufferAttribute; if (attribute.isInterleavedBufferAttribute) { const interleavedBuffer = getInterleavedBuffer(json.data, attribute.data); bufferAttribute = new InterleavedBufferAttribute(interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized); } else { const typedArray = getTypedArray(attribute.type, attribute.array); bufferAttribute = new BufferAttribute(typedArray, attribute.itemSize, attribute.normalized); } if (attribute.name !== undefined) bufferAttribute.name = attribute.name; array.push(bufferAttribute); } geometry.morphAttributes[key] = array; } } const morphTargetsRelative = json.data.morphTargetsRelative; if (morphTargetsRelative) { geometry.morphTargetsRelative = true; } const groups = json.data.groups || json.data.drawcalls || json.data.offsets; if (groups !== undefined) { for (let i = 0, n = groups.length; i !== n; ++i) { const group = groups[i]; geometry.addGroup(group.start, group.count, group.materialIndex); } } const boundingSphere = json.data.boundingSphere; if (boundingSphere !== undefined) { const center = new Vector3(); if (boundingSphere.center !== undefined) { center.fromArray(boundingSphere.center); } geometry.boundingSphere = new Sphere(center, boundingSphere.radius); } if (json.name) geometry.name = json.name; if (json.userData) geometry.userData = json.userData; return geometry; } } class ObjectLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const path = this.path === '' ? LoaderUtils.extractUrlBase(url) : this.path; this.resourcePath = this.resourcePath || path; const loader = new FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { let json = null; try { json = JSON.parse(text); } catch (error) { if (onError !== undefined) onError(error); console.error('THREE:ObjectLoader: Can\'t parse ' + url + '.', error.message); return; } const metadata = json.metadata; if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry') { if (onError !== undefined) onError(new Error('THREE.ObjectLoader: Can\'t load ' + url)); console.error('THREE.ObjectLoader: Can\'t load ' + url); return; } scope.parse(json, onLoad); }, onProgress, onError); } loadAsync(url, onProgress) { var _this2 = this; return (0,_home_sebastian_mindmap3d_node_modules_babel_runtime_helpers_esm_asyncToGenerator_js__WEBPACK_IMPORTED_MODULE_0__["default"])(function* () { const scope = _this2; const path = _this2.path === '' ? LoaderUtils.extractUrlBase(url) : _this2.path; _this2.resourcePath = _this2.resourcePath || path; const loader = new FileLoader(_this2.manager); loader.setPath(_this2.path); loader.setRequestHeader(_this2.requestHeader); loader.setWithCredentials(_this2.withCredentials); const text = yield loader.loadAsync(url, onProgress); const json = JSON.parse(text); const metadata = json.metadata; if (metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry') { throw new Error('THREE.ObjectLoader: Can\'t load ' + url); } return yield scope.parseAsync(json); })(); } parse(json, onLoad) { const animations = this.parseAnimations(json.animations); const shapes = this.parseShapes(json.shapes); const geometries = this.parseGeometries(json.geometries, shapes); const images = this.parseImages(json.images, function () { if (onLoad !== undefined) onLoad(object); }); const textures = this.parseTextures(json.textures, images); const materials = this.parseMaterials(json.materials, textures); const object = this.parseObject(json.object, geometries, materials, textures, animations); const skeletons = this.parseSkeletons(json.skeletons, object); this.bindSkeletons(object, skeletons); // if (onLoad !== undefined) { let hasImages = false; for (const uuid in images) { if (images[uuid].data instanceof HTMLImageElement) { hasImages = true; break; } } if (hasImages === false) onLoad(object); } return object; } parseAsync(json) { var _this3 = this; return (0,_home_sebastian_mindmap3d_node_modules_babel_runtime_helpers_esm_asyncToGenerator_js__WEBPACK_IMPORTED_MODULE_0__["default"])(function* () { const animations = _this3.parseAnimations(json.animations); const shapes = _this3.parseShapes(json.shapes); const geometries = _this3.parseGeometries(json.geometries, shapes); const images = yield _this3.parseImagesAsync(json.images); const textures = _this3.parseTextures(json.textures, images); const materials = _this3.parseMaterials(json.materials, textures); const object = _this3.parseObject(json.object, geometries, materials, textures, animations); const skeletons = _this3.parseSkeletons(json.skeletons, object); _this3.bindSkeletons(object, skeletons); return object; })(); } parseShapes(json) { const shapes = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const shape = new Shape().fromJSON(json[i]); shapes[shape.uuid] = shape; } } return shapes; } parseSkeletons(json, object) { const skeletons = {}; const bones = {}; // generate bone lookup table object.traverse(function (child) { if (child.isBone) bones[child.uuid] = child; }); // create skeletons if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const skeleton = new Skeleton().fromJSON(json[i], bones); skeletons[skeleton.uuid] = skeleton; } } return skeletons; } parseGeometries(json, shapes) { const geometries = {}; if (json !== undefined) { const bufferGeometryLoader = new BufferGeometryLoader(); for (let i = 0, l = json.length; i < l; i++) { let geometry; const data = json[i]; switch (data.type) { case 'BufferGeometry': case 'InstancedBufferGeometry': geometry = bufferGeometryLoader.parse(data); break; default: if (data.type in Geometries) { geometry = Geometries[data.type].fromJSON(data, shapes); } else { console.warn(`THREE.ObjectLoader: Unsupported geometry type "${data.type}"`); } } geometry.uuid = data.uuid; if (data.name !== undefined) geometry.name = data.name; if (data.userData !== undefined) geometry.userData = data.userData; geometries[data.uuid] = geometry; } } return geometries; } parseMaterials(json, textures) { const cache = {}; // MultiMaterial const materials = {}; if (json !== undefined) { const loader = new MaterialLoader(); loader.setTextures(textures); for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (cache[data.uuid] === undefined) { cache[data.uuid] = loader.parse(data); } materials[data.uuid] = cache[data.uuid]; } } return materials; } parseAnimations(json) { const animations = {}; if (json !== undefined) { for (let i = 0; i < json.length; i++) { const data = json[i]; const clip = AnimationClip.parse(data); animations[clip.uuid] = clip; } } return animations; } parseImages(json, onLoad) { const scope = this; const images = {}; let loader; function loadImage(url) { scope.manager.itemStart(url); return loader.load(url, function () { scope.manager.itemEnd(url); }, undefined, function () { scope.manager.itemError(url); scope.manager.itemEnd(url); }); } function deserializeImage(image) { if (typeof image === 'string') { const url = image; const path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test(url) ? url : scope.resourcePath + url; return loadImage(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height }; } else { return null; } } } if (json !== undefined && json.length > 0) { const manager = new LoadingManager(onLoad); loader = new ImageLoader(manager); loader.setCrossOrigin(this.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture const imageArray = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { imageArray.push(deserializedImage); } else { // special case: handle array of data textures for cube textures imageArray.push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); } } } images[image.uuid] = new Source(imageArray); } else { // load single image const deserializedImage = deserializeImage(image.url); images[image.uuid] = new Source(deserializedImage); } } } return images; } parseImagesAsync(json) { var _this4 = this; return (0,_home_sebastian_mindmap3d_node_modules_babel_runtime_helpers_esm_asyncToGenerator_js__WEBPACK_IMPORTED_MODULE_0__["default"])(function* () { const scope = _this4; const images = {}; let loader; function deserializeImage(_x3) { return _deserializeImage.apply(this, arguments); } function _deserializeImage() { _deserializeImage = (0,_home_sebastian_mindmap3d_node_modules_babel_runtime_helpers_esm_asyncToGenerator_js__WEBPACK_IMPORTED_MODULE_0__["default"])(function* (image) { if (typeof image === 'string') { const url = image; const path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test(url) ? url : scope.resourcePath + url; return yield loader.loadAsync(path); } else { if (image.data) { return { data: getTypedArray(image.type, image.data), width: image.width, height: image.height }; } else { return null; } } }); return _deserializeImage.apply(this, arguments); } if (json !== undefined && json.length > 0) { loader = new ImageLoader(_this4.manager); loader.setCrossOrigin(_this4.crossOrigin); for (let i = 0, il = json.length; i < il; i++) { const image = json[i]; const url = image.url; if (Array.isArray(url)) { // load array of images e.g CubeTexture const imageArray = []; for (let j = 0, jl = url.length; j < jl; j++) { const currentUrl = url[j]; const deserializedImage = yield deserializeImage(currentUrl); if (deserializedImage !== null) { if (deserializedImage instanceof HTMLImageElement) { imageArray.push(deserializedImage); } else { // special case: handle array of data textures for cube textures imageArray.push(new DataTexture(deserializedImage.data, deserializedImage.width, deserializedImage.height)); } } } images[image.uuid] = new Source(imageArray); } else { // load single image const deserializedImage = yield deserializeImage(image.url); images[image.uuid] = new Source(deserializedImage); } } } return images; })(); } parseTextures(json, images) { function parseConstant(value, type) { if (typeof value === 'number') return value; console.warn('THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value); return type[value]; } const textures = {}; if (json !== undefined) { for (let i = 0, l = json.length; i < l; i++) { const data = json[i]; if (data.image === undefined) { console.warn('THREE.ObjectLoader: No "image" specified for', data.uuid); } if (images[data.image] === undefined) { console.warn('THREE.ObjectLoader: Undefined image', data.image); } const source = images[data.image]; const image = source.data; let texture; if (Array.isArray(image)) { texture = new CubeTexture(); if (image.length === 6) texture.needsUpdate = true; } else { if (image && image.data) { texture = new DataTexture(); } else { texture = new Texture(); } if (image) texture.needsUpdate = true; // textures can have undefined image data } texture.source = source; texture.uuid = data.uuid; if (data.name !== undefined) texture.name = data.name; if (data.mapping !== undefined) texture.mapping = parseConstant(data.mapping, TEXTURE_MAPPING); if (data.channel !== undefined) texture.channel = data.channel; if (data.offset !== undefined) texture.offset.fromArray(data.offset); if (data.repeat !== undefined) texture.repeat.fromArray(data.repeat); if (data.center !== undefined) texture.center.fromArray(data.center); if (data.rotation !== undefined) texture.rotation = data.rotation; if (data.wrap !== undefined) { texture.wrapS = parseConstant(data.wrap[0], TEXTURE_WRAPPING); texture.wrapT = parseConstant(data.wrap[1], TEXTURE_WRAPPING); } if (data.format !== undefined) texture.format = data.format; if (data.internalFormat !== undefined) texture.internalFormat = data.internalFormat; if (data.type !== undefined) texture.type = data.type; if (data.colorSpace !== undefined) texture.colorSpace = data.colorSpace; if (data.encoding !== undefined) texture.encoding = data.encoding; // @deprecated, r152 if (data.minFilter !== undefined) texture.minFilter = parseConstant(data.minFilter, TEXTURE_FILTER); if (data.magFilter !== undefined) texture.magFilter = parseConstant(data.magFilter, TEXTURE_FILTER); if (data.anisotropy !== undefined) texture.anisotropy = data.anisotropy; if (data.flipY !== undefined) texture.flipY = data.flipY; if (data.generateMipmaps !== undefined) texture.generateMipmaps = data.generateMipmaps; if (data.premultiplyAlpha !== undefined) texture.premultiplyAlpha = data.premultiplyAlpha; if (data.unpackAlignment !== undefined) texture.unpackAlignment = data.unpackAlignment; if (data.compareFunction !== undefined) texture.compareFunction = data.compareFunction; if (data.userData !== undefined) texture.userData = data.userData; textures[data.uuid] = texture; } } return textures; } parseObject(data, geometries, materials, textures, animations) { let object; function getGeometry(name) { if (geometries[name] === undefined) { console.warn('THREE.ObjectLoader: Undefined geometry', name); } return geometries[name]; } function getMaterial(name) { if (name === undefined) return undefined; if (Array.isArray(name)) { const array = []; for (let i = 0, l = name.length; i < l; i++) { const uuid = name[i]; if (materials[uuid] === undefined) { console.warn('THREE.ObjectLoader: Undefined material', uuid); } array.push(materials[uuid]); } return array; } if (materials[name] === undefined) { console.warn('THREE.ObjectLoader: Undefined material', name); } return materials[name]; } function getTexture(uuid) { if (textures[uuid] === undefined) { console.warn('THREE.ObjectLoader: Undefined texture', uuid); } return textures[uuid]; } let geometry, material; switch (data.type) { case 'Scene': object = new Scene(); if (data.background !== undefined) { if (Number.isInteger(data.background)) { object.background = new Color(data.background); } else { object.background = getTexture(data.background); } } if (data.environment !== undefined) { object.environment = getTexture(data.environment); } if (data.fog !== undefined) { if (data.fog.type === 'Fog') { object.fog = new Fog(data.fog.color, data.fog.near, data.fog.far); } else if (data.fog.type === 'FogExp2') { object.fog = new FogExp2(data.fog.color, data.fog.density); } } if (data.backgroundBlurriness !== undefined) object.backgroundBlurriness = data.backgroundBlurriness; if (data.backgroundIntensity !== undefined) object.backgroundIntensity = data.backgroundIntensity; break; case 'PerspectiveCamera': object = new PerspectiveCamera(data.fov, data.aspect, data.near, data.far); if (data.focus !== undefined) object.focus = data.focus; if (data.zoom !== undefined) object.zoom = data.zoom; if (data.filmGauge !== undefined) object.filmGauge = data.filmGauge; if (data.filmOffset !== undefined) object.filmOffset = data.filmOffset; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case 'OrthographicCamera': object = new OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far); if (data.zoom !== undefined) object.zoom = data.zoom; if (data.view !== undefined) object.view = Object.assign({}, data.view); break; case 'AmbientLight': object = new AmbientLight(data.color, data.intensity); break; case 'DirectionalLight': object = new DirectionalLight(data.color, data.intensity); break; case 'PointLight': object = new PointLight(data.color, data.intensity, data.distance, data.decay); break; case 'RectAreaLight': object = new RectAreaLight(data.color, data.intensity, data.width, data.height); break; case 'SpotLight': object = new SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay); break; case 'HemisphereLight': object = new HemisphereLight(data.color, data.groundColor, data.intensity); break; case 'LightProbe': object = new LightProbe().fromJSON(data); break; case 'SkinnedMesh': geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new SkinnedMesh(geometry, material); if (data.bindMode !== undefined) object.bindMode = data.bindMode; if (data.bindMatrix !== undefined) object.bindMatrix.fromArray(data.bindMatrix); if (data.skeleton !== undefined) object.skeleton = data.skeleton; break; case 'Mesh': geometry = getGeometry(data.geometry); material = getMaterial(data.material); object = new Mesh(geometry, material); break; case 'InstancedMesh': geometry = getGeometry(data.geometry); material = getMaterial(data.material); const count = data.count; const instanceMatrix = data.instanceMatrix; const instanceColor = data.instanceColor; object = new InstancedMesh(geometry, material, count); object.instanceMatrix = new InstancedBufferAttribute(new Float32Array(instanceMatrix.array), 16); if (instanceColor !== undefined) object.instanceColor = new InstancedBufferAttribute(new Float32Array(instanceColor.array), instanceColor.itemSize); break; case 'LOD': object = new LOD(); break; case 'Line': object = new Line(getGeometry(data.geometry), getMaterial(data.material)); break; case 'LineLoop': object = new LineLoop(getGeometry(data.geometry), getMaterial(data.material)); break; case 'LineSegments': object = new LineSegments(getGeometry(data.geometry), getMaterial(data.material)); break; case 'PointCloud': case 'Points': object = new Points(getGeometry(data.geometry), getMaterial(data.material)); break; case 'Sprite': object = new Sprite(getMaterial(data.material)); break; case 'Group': object = new Group(); break; case 'Bone': object = new Bone(); break; default: object = new Object3D(); } object.uuid = data.uuid; if (data.name !== undefined) object.name = data.name; if (data.matrix !== undefined) { object.matrix.fromArray(data.matrix); if (data.matrixAutoUpdate !== undefined) object.matrixAutoUpdate = data.matrixAutoUpdate; if (object.matrixAutoUpdate) object.matrix.decompose(object.position, object.quaternion, object.scale); } else { if (data.position !== undefined) object.position.fromArray(data.position); if (data.rotation !== undefined) object.rotation.fromArray(data.rotation); if (data.quaternion !== undefined) object.quaternion.fromArray(data.quaternion); if (data.scale !== undefined) object.scale.fromArray(data.scale); } if (data.up !== undefined) object.up.fromArray(data.up); if (data.castShadow !== undefined) object.castShadow = data.castShadow; if (data.receiveShadow !== undefined) object.receiveShadow = data.receiveShadow; if (data.shadow) { if (data.shadow.bias !== undefined) object.shadow.bias = data.shadow.bias; if (data.shadow.normalBias !== undefined) object.shadow.normalBias = data.shadow.normalBias; if (data.shadow.radius !== undefined) object.shadow.radius = data.shadow.radius; if (data.shadow.mapSize !== undefined) object.shadow.mapSize.fromArray(data.shadow.mapSize); if (data.shadow.camera !== undefined) object.shadow.camera = this.parseObject(data.shadow.camera); } if (data.visible !== undefined) object.visible = data.visible; if (data.frustumCulled !== undefined) object.frustumCulled = data.frustumCulled; if (data.renderOrder !== undefined) object.renderOrder = data.renderOrder; if (data.userData !== undefined) object.userData = data.userData; if (data.layers !== undefined) object.layers.mask = data.layers; if (data.children !== undefined) { const children = data.children; for (let i = 0; i < children.length; i++) { object.add(this.parseObject(children[i], geometries, materials, textures, animations)); } } if (data.animations !== undefined) { const objectAnimations = data.animations; for (let i = 0; i < objectAnimations.length; i++) { const uuid = objectAnimations[i]; object.animations.push(animations[uuid]); } } if (data.type === 'LOD') { if (data.autoUpdate !== undefined) object.autoUpdate = data.autoUpdate; const levels = data.levels; for (let l = 0; l < levels.length; l++) { const level = levels[l]; const child = object.getObjectByProperty('uuid', level.object); if (child !== undefined) { object.addLevel(child, level.distance, level.hysteresis); } } } return object; } bindSkeletons(object, skeletons) { if (Object.keys(skeletons).length === 0) return; object.traverse(function (child) { if (child.isSkinnedMesh === true && child.skeleton !== undefined) { const skeleton = skeletons[child.skeleton]; if (skeleton === undefined) { console.warn('THREE.ObjectLoader: No skeleton found with UUID:', child.skeleton); } else { child.bind(skeleton, child.bindMatrix); } } }); } } const TEXTURE_MAPPING = { UVMapping: UVMapping, CubeReflectionMapping: CubeReflectionMapping, CubeRefractionMapping: CubeRefractionMapping, EquirectangularReflectionMapping: EquirectangularReflectionMapping, EquirectangularRefractionMapping: EquirectangularRefractionMapping, CubeUVReflectionMapping: CubeUVReflectionMapping }; const TEXTURE_WRAPPING = { RepeatWrapping: RepeatWrapping, ClampToEdgeWrapping: ClampToEdgeWrapping, MirroredRepeatWrapping: MirroredRepeatWrapping }; const TEXTURE_FILTER = { NearestFilter: NearestFilter, NearestMipmapNearestFilter: NearestMipmapNearestFilter, NearestMipmapLinearFilter: NearestMipmapLinearFilter, LinearFilter: LinearFilter, LinearMipmapNearestFilter: LinearMipmapNearestFilter, LinearMipmapLinearFilter: LinearMipmapLinearFilter }; class ImageBitmapLoader extends Loader { constructor(manager) { super(manager); this.isImageBitmapLoader = true; if (typeof createImageBitmap === 'undefined') { console.warn('THREE.ImageBitmapLoader: createImageBitmap() not supported.'); } if (typeof fetch === 'undefined') { console.warn('THREE.ImageBitmapLoader: fetch() not supported.'); } this.options = { premultiplyAlpha: 'none' }; } setOptions(options) { this.options = options; return this; } load(url, onLoad, onProgress, onError) { if (url === undefined) url = ''; if (this.path !== undefined) url = this.path + url; url = this.manager.resolveURL(url); const scope = this; const cached = Cache.get(url); if (cached !== undefined) { scope.manager.itemStart(url); setTimeout(function () { if (onLoad) onLoad(cached); scope.manager.itemEnd(url); }, 0); return cached; } const fetchOptions = {}; fetchOptions.credentials = this.crossOrigin === 'anonymous' ? 'same-origin' : 'include'; fetchOptions.headers = this.requestHeader; fetch(url, fetchOptions).then(function (res) { return res.blob(); }).then(function (blob) { return createImageBitmap(blob, Object.assign(scope.options, { colorSpaceConversion: 'none' })); }).then(function (imageBitmap) { Cache.add(url, imageBitmap); if (onLoad) onLoad(imageBitmap); scope.manager.itemEnd(url); }).catch(function (e) { if (onError) onError(e); scope.manager.itemError(url); scope.manager.itemEnd(url); }); scope.manager.itemStart(url); } } let _context; class AudioContext { static getContext() { if (_context === undefined) { _context = new (window.AudioContext || window.webkitAudioContext)(); } return _context; } static setContext(value) { _context = value; } } class AudioLoader extends Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new FileLoader(this.manager); loader.setResponseType('arraybuffer'); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (buffer) { try { // Create a copy of the buffer. The `decodeAudioData` method // detaches the buffer when complete, preventing reuse. const bufferCopy = buffer.slice(0); const context = AudioContext.getContext(); context.decodeAudioData(bufferCopy, function (audioBuffer) { onLoad(audioBuffer); }, handleError); } catch (e) { handleError(e); } }, onProgress, onError); function handleError(e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } } } class HemisphereLightProbe extends LightProbe { constructor(skyColor, groundColor, intensity = 1) { super(undefined, intensity); this.isHemisphereLightProbe = true; const color1 = new Color().set(skyColor); const color2 = new Color().set(groundColor); const sky = new Vector3(color1.r, color1.g, color1.b); const ground = new Vector3(color2.r, color2.g, color2.b); // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); const c0 = Math.sqrt(Math.PI); const c1 = c0 * Math.sqrt(0.75); this.sh.coefficients[0].copy(sky).add(ground).multiplyScalar(c0); this.sh.coefficients[1].copy(sky).sub(ground).multiplyScalar(c1); } } class AmbientLightProbe extends LightProbe { constructor(color, intensity = 1) { super(undefined, intensity); this.isAmbientLightProbe = true; const color1 = new Color().set(color); // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); this.sh.coefficients[0].set(color1.r, color1.g, color1.b).multiplyScalar(2 * Math.sqrt(Math.PI)); } } const _eyeRight = /*@__PURE__*/new Matrix4(); const _eyeLeft = /*@__PURE__*/new Matrix4(); const _projectionMatrix = /*@__PURE__*/new Matrix4(); class StereoCamera { constructor() { this.type = 'StereoCamera'; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable(1); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable(2); this.cameraR.matrixAutoUpdate = false; this._cache = { focus: null, fov: null, aspect: null, near: null, far: null, zoom: null, eyeSep: null }; } update(camera) { const cache = this._cache; const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; if (needsUpdate) { cache.focus = camera.focus; cache.fov = camera.fov; cache.aspect = camera.aspect * this.aspect; cache.near = camera.near; cache.far = camera.far; cache.zoom = camera.zoom; cache.eyeSep = this.eyeSep; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ _projectionMatrix.copy(camera.projectionMatrix); const eyeSepHalf = cache.eyeSep / 2; const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; const ymax = cache.near * Math.tan(DEG2RAD * cache.fov * 0.5) / cache.zoom; let xmin, xmax; // translate xOffset _eyeLeft.elements[12] = -eyeSepHalf; _eyeRight.elements[12] = eyeSepHalf; // for left eye xmin = -ymax * cache.aspect + eyeSepOnProjection; xmax = ymax * cache.aspect + eyeSepOnProjection; _projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraL.projectionMatrix.copy(_projectionMatrix); // for right eye xmin = -ymax * cache.aspect - eyeSepOnProjection; xmax = ymax * cache.aspect - eyeSepOnProjection; _projectionMatrix.elements[0] = 2 * cache.near / (xmax - xmin); _projectionMatrix.elements[8] = (xmax + xmin) / (xmax - xmin); this.cameraR.projectionMatrix.copy(_projectionMatrix); } this.cameraL.matrixWorld.copy(camera.matrixWorld).multiply(_eyeLeft); this.cameraR.matrixWorld.copy(camera.matrixWorld).multiply(_eyeRight); } } class Clock { constructor(autoStart = true) { this.autoStart = autoStart; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } start() { this.startTime = now(); this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; } stop() { this.getElapsedTime(); this.running = false; this.autoStart = false; } getElapsedTime() { this.getDelta(); return this.elapsedTime; } getDelta() { let diff = 0; if (this.autoStart && !this.running) { this.start(); return 0; } if (this.running) { const newTime = now(); diff = (newTime - this.oldTime) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } } function now() { return (typeof performance === 'undefined' ? Date : performance).now(); // see #10732 } const _position$1 = /*@__PURE__*/new Vector3(); const _quaternion$1 = /*@__PURE__*/new Quaternion(); const _scale$1 = /*@__PURE__*/new Vector3(); const _orientation$1 = /*@__PURE__*/new Vector3(); class AudioListener extends Object3D { constructor() { super(); this.type = 'AudioListener'; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect(this.context.destination); this.filter = null; this.timeDelta = 0; // private this._clock = new Clock(); } getInput() { return this.gain; } removeFilter() { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); this.gain.connect(this.context.destination); this.filter = null; } return this; } getFilter() { return this.filter; } setFilter(value) { if (this.filter !== null) { this.gain.disconnect(this.filter); this.filter.disconnect(this.context.destination); } else { this.gain.disconnect(this.context.destination); } this.filter = value; this.gain.connect(this.filter); this.filter.connect(this.context.destination); return this; } getMasterVolume() { return this.gain.gain.value; } setMasterVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); const listener = this.context.listener; const up = this.up; this.timeDelta = this._clock.getDelta(); this.matrixWorld.decompose(_position$1, _quaternion$1, _scale$1); _orientation$1.set(0, 0, -1).applyQuaternion(_quaternion$1); if (listener.positionX) { // code path for Chrome (see #14393) const endTime = this.context.currentTime + this.timeDelta; listener.positionX.linearRampToValueAtTime(_position$1.x, endTime); listener.positionY.linearRampToValueAtTime(_position$1.y, endTime); listener.positionZ.linearRampToValueAtTime(_position$1.z, endTime); listener.forwardX.linearRampToValueAtTime(_orientation$1.x, endTime); listener.forwardY.linearRampToValueAtTime(_orientation$1.y, endTime); listener.forwardZ.linearRampToValueAtTime(_orientation$1.z, endTime); listener.upX.linearRampToValueAtTime(up.x, endTime); listener.upY.linearRampToValueAtTime(up.y, endTime); listener.upZ.linearRampToValueAtTime(up.z, endTime); } else { listener.setPosition(_position$1.x, _position$1.y, _position$1.z); listener.setOrientation(_orientation$1.x, _orientation$1.y, _orientation$1.z, up.x, up.y, up.z); } } } class Audio extends Object3D { constructor(listener) { super(); this.type = 'Audio'; this.listener = listener; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect(listener.getInput()); this.autoplay = false; this.buffer = null; this.detune = 0; this.loop = false; this.loopStart = 0; this.loopEnd = 0; this.offset = 0; this.duration = undefined; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.source = null; this.sourceType = 'empty'; this._startedAt = 0; this._progress = 0; this._connected = false; this.filters = []; } getOutput() { return this.gain; } setNodeSource(audioNode) { this.hasPlaybackControl = false; this.sourceType = 'audioNode'; this.source = audioNode; this.connect(); return this; } setMediaElementSource(mediaElement) { this.hasPlaybackControl = false; this.sourceType = 'mediaNode'; this.source = this.context.createMediaElementSource(mediaElement); this.connect(); return this; } setMediaStreamSource(mediaStream) { this.hasPlaybackControl = false; this.sourceType = 'mediaStreamNode'; this.source = this.context.createMediaStreamSource(mediaStream); this.connect(); return this; } setBuffer(audioBuffer) { this.buffer = audioBuffer; this.sourceType = 'buffer'; if (this.autoplay) this.play(); return this; } play(delay = 0) { if (this.isPlaying === true) { console.warn('THREE.Audio: Audio is already playing.'); return; } if (this.hasPlaybackControl === false) { console.warn('THREE.Audio: this Audio has no playback control.'); return; } this._startedAt = this.context.currentTime + delay; const source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.loopStart = this.loopStart; source.loopEnd = this.loopEnd; source.onended = this.onEnded.bind(this); source.start(this._startedAt, this._progress + this.offset, this.duration); this.isPlaying = true; this.source = source; this.setDetune(this.detune); this.setPlaybackRate(this.playbackRate); return this.connect(); } pause() { if (this.hasPlaybackControl === false) { console.warn('THREE.Audio: this Audio has no playback control.'); return; } if (this.isPlaying === true) { // update current progress this._progress += Math.max(this.context.currentTime - this._startedAt, 0) * this.playbackRate; if (this.loop === true) { // ensure _progress does not exceed duration with looped audios this._progress = this._progress % (this.duration || this.buffer.duration); } this.source.stop(); this.source.onended = null; this.isPlaying = false; } return this; } stop() { if (this.hasPlaybackControl === false) { console.warn('THREE.Audio: this Audio has no playback control.'); return; } this._progress = 0; if (this.source !== null) { this.source.stop(); this.source.onended = null; } this.isPlaying = false; return this; } connect() { if (this.filters.length > 0) { this.source.connect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].connect(this.filters[i]); } this.filters[this.filters.length - 1].connect(this.getOutput()); } else { this.source.connect(this.getOutput()); } this._connected = true; return this; } disconnect() { if (this.filters.length > 0) { this.source.disconnect(this.filters[0]); for (let i = 1, l = this.filters.length; i < l; i++) { this.filters[i - 1].disconnect(this.filters[i]); } this.filters[this.filters.length - 1].disconnect(this.getOutput()); } else { this.source.disconnect(this.getOutput()); } this._connected = false; return this; } getFilters() { return this.filters; } setFilters(value) { if (!value) value = []; if (this._connected === true) { this.disconnect(); this.filters = value.slice(); this.connect(); } else { this.filters = value.slice(); } return this; } setDetune(value) { this.detune = value; if (this.source.detune === undefined) return; // only set detune when available if (this.isPlaying === true) { this.source.detune.setTargetAtTime(this.detune, this.context.currentTime, 0.01); } return this; } getDetune() { return this.detune; } getFilter() { return this.getFilters()[0]; } setFilter(filter) { return this.setFilters(filter ? [filter] : []); } setPlaybackRate(value) { if (this.hasPlaybackControl === false) { console.warn('THREE.Audio: this Audio has no playback control.'); return; } this.playbackRate = value; if (this.isPlaying === true) { this.source.playbackRate.setTargetAtTime(this.playbackRate, this.context.currentTime, 0.01); } return this; } getPlaybackRate() { return this.playbackRate; } onEnded() { this.isPlaying = false; } getLoop() { if (this.hasPlaybackControl === false) { console.warn('THREE.Audio: this Audio has no playback control.'); return false; } return this.loop; } setLoop(value) { if (this.hasPlaybackControl === false) { console.warn('THREE.Audio: this Audio has no playback control.'); return; } this.loop = value; if (this.isPlaying === true) { this.source.loop = this.loop; } return this; } setLoopStart(value) { this.loopStart = value; return this; } setLoopEnd(value) { this.loopEnd = value; return this; } getVolume() { return this.gain.gain.value; } setVolume(value) { this.gain.gain.setTargetAtTime(value, this.context.currentTime, 0.01); return this; } } const _position = /*@__PURE__*/new Vector3(); const _quaternion = /*@__PURE__*/new Quaternion(); const _scale = /*@__PURE__*/new Vector3(); const _orientation = /*@__PURE__*/new Vector3(); class PositionalAudio extends Audio { constructor(listener) { super(listener); this.panner = this.context.createPanner(); this.panner.panningModel = 'HRTF'; this.panner.connect(this.gain); } connect() { super.connect(); this.panner.connect(this.gain); } disconnect() { super.disconnect(); this.panner.disconnect(this.gain); } getOutput() { return this.panner; } getRefDistance() { return this.panner.refDistance; } setRefDistance(value) { this.panner.refDistance = value; return this; } getRolloffFactor() { return this.panner.rolloffFactor; } setRolloffFactor(value) { this.panner.rolloffFactor = value; return this; } getDistanceModel() { return this.panner.distanceModel; } setDistanceModel(value) { this.panner.distanceModel = value; return this; } getMaxDistance() { return this.panner.maxDistance; } setMaxDistance(value) { this.panner.maxDistance = value; return this; } setDirectionalCone(coneInnerAngle, coneOuterAngle, coneOuterGain) { this.panner.coneInnerAngle = coneInnerAngle; this.panner.coneOuterAngle = coneOuterAngle; this.panner.coneOuterGain = coneOuterGain; return this; } updateMatrixWorld(force) { super.updateMatrixWorld(force); if (this.hasPlaybackControl === true && this.isPlaying === false) return; this.matrixWorld.decompose(_position, _quaternion, _scale); _orientation.set(0, 0, 1).applyQuaternion(_quaternion); const panner = this.panner; if (panner.positionX) { // code path for Chrome and Firefox (see #14393) const endTime = this.context.currentTime + this.listener.timeDelta; panner.positionX.linearRampToValueAtTime(_position.x, endTime); panner.positionY.linearRampToValueAtTime(_position.y, endTime); panner.positionZ.linearRampToValueAtTime(_position.z, endTime); panner.orientationX.linearRampToValueAtTime(_orientation.x, endTime); panner.orientationY.linearRampToValueAtTime(_orientation.y, endTime); panner.orientationZ.linearRampToValueAtTime(_orientation.z, endTime); } else { panner.setPosition(_position.x, _position.y, _position.z); panner.setOrientation(_orientation.x, _orientation.y, _orientation.z); } } } class AudioAnalyser { constructor(audio, fftSize = 2048) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize; this.data = new Uint8Array(this.analyser.frequencyBinCount); audio.getOutput().connect(this.analyser); } getFrequencyData() { this.analyser.getByteFrequencyData(this.data); return this.data; } getAverageFrequency() { let value = 0; const data = this.getFrequencyData(); for (let i = 0; i < data.length; i++) { value += data[i]; } return value / data.length; } } class PropertyMixer { constructor(binding, typeName, valueSize) { this.binding = binding; this.valueSize = valueSize; let mixFunction, mixFunctionAdditive, setIdentity; // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] // // interpolators can use .buffer as their .result // the data then goes to 'incoming' // // 'accu0' and 'accu1' are used frame-interleaved for // the cumulative result and are compared to detect // changes // // 'orig' stores the original state of the property // // 'add' is used for additive cumulative results // // 'work' is optional and is only present for quaternion types. It is used // to store intermediate quaternion multiplication results switch (typeName) { case 'quaternion': mixFunction = this._slerp; mixFunctionAdditive = this._slerpAdditive; setIdentity = this._setAdditiveIdentityQuaternion; this.buffer = new Float64Array(valueSize * 6); this._workIndex = 5; break; case 'string': case 'bool': mixFunction = this._select; // Use the regular mix function and for additive on these types, // additive is not relevant for non-numeric types mixFunctionAdditive = this._select; setIdentity = this._setAdditiveIdentityOther; this.buffer = new Array(valueSize * 5); break; default: mixFunction = this._lerp; mixFunctionAdditive = this._lerpAdditive; setIdentity = this._setAdditiveIdentityNumeric; this.buffer = new Float64Array(valueSize * 5); } this._mixBufferRegion = mixFunction; this._mixBufferRegionAdditive = mixFunctionAdditive; this._setIdentity = setIdentity; this._origIndex = 3; this._addIndex = 4; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; this.useCount = 0; this.referenceCount = 0; } // accumulate data in the 'incoming' region into 'accu<i>' accumulate(accuIndex, weight) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn't have made the call in the first place const buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride; let currentWeight = this.cumulativeWeight; if (currentWeight === 0) { // accuN := incoming * weight for (let i = 0; i !== stride; ++i) { buffer[offset + i] = buffer[i]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; const mix = weight / currentWeight; this._mixBufferRegion(buffer, offset, 0, mix, stride); } this.cumulativeWeight = currentWeight; } // accumulate data in the 'incoming' region into 'add' accumulateAdditive(weight) { const buffer = this.buffer, stride = this.valueSize, offset = stride * this._addIndex; if (this.cumulativeWeightAdditive === 0) { // add = identity this._setIdentity(); } // add := add + incoming * weight this._mixBufferRegionAdditive(buffer, offset, 0, weight, stride); this.cumulativeWeightAdditive += weight; } // apply the state of 'accu<i>' to the binding when accus differ apply(accuIndex) { const stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, weightAdditive = this.cumulativeWeightAdditive, binding = this.binding; this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; if (weight < 1) { // accuN := accuN + original * ( 1 - cumulativeWeight ) const originalValueOffset = stride * this._origIndex; this._mixBufferRegion(buffer, offset, originalValueOffset, 1 - weight, stride); } if (weightAdditive > 0) { // accuN := accuN + additive accuN this._mixBufferRegionAdditive(buffer, offset, this._addIndex * stride, 1, stride); } for (let i = stride, e = stride + stride; i !== e; ++i) { if (buffer[i] !== buffer[i + stride]) { // value has changed -> update scene graph binding.setValue(buffer, offset); break; } } } // remember the state of the bound property and copy it to both accus saveOriginalState() { const binding = this.binding; const buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * this._origIndex; binding.getValue(buffer, originalValueOffset); // accu[0..1] := orig -- initially detect changes against the original for (let i = stride, e = originalValueOffset; i !== e; ++i) { buffer[i] = buffer[originalValueOffset + i % stride]; } // Add to identity for additive this._setIdentity(); this.cumulativeWeight = 0; this.cumulativeWeightAdditive = 0; } // apply the state previously taken via 'saveOriginalState' to the binding restoreOriginalState() { const originalValueOffset = this.valueSize * 3; this.binding.setValue(this.buffer, originalValueOffset); } _setAdditiveIdentityNumeric() { const startIndex = this._addIndex * this.valueSize; const endIndex = startIndex + this.valueSize; for (let i = startIndex; i < endIndex; i++) { this.buffer[i] = 0; } } _setAdditiveIdentityQuaternion() { this._setAdditiveIdentityNumeric(); this.buffer[this._addIndex * this.valueSize + 3] = 1; } _setAdditiveIdentityOther() { const startIndex = this._origIndex * this.valueSize; const targetIndex = this._addIndex * this.valueSize; for (let i = 0; i < this.valueSize; i++) { this.buffer[targetIndex + i] = this.buffer[startIndex + i]; } } // mix functions _select(buffer, dstOffset, srcOffset, t, stride) { if (t >= 0.5) { for (let i = 0; i !== stride; ++i) { buffer[dstOffset + i] = buffer[srcOffset + i]; } } } _slerp(buffer, dstOffset, srcOffset, t) { Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t); } _slerpAdditive(buffer, dstOffset, srcOffset, t, stride) { const workOffset = this._workIndex * stride; // Store result in intermediate buffer offset Quaternion.multiplyQuaternionsFlat(buffer, workOffset, buffer, dstOffset, buffer, srcOffset); // Slerp to the intermediate result Quaternion.slerpFlat(buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t); } _lerp(buffer, dstOffset, srcOffset, t, stride) { const s = 1 - t; for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] * s + buffer[srcOffset + i] * t; } } _lerpAdditive(buffer, dstOffset, srcOffset, t, stride) { for (let i = 0; i !== stride; ++i) { const j = dstOffset + i; buffer[j] = buffer[j] + buffer[srcOffset + i] * t; } } } // Characters [].:/ are reserved for track binding syntax. const _RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; const _reservedRe = new RegExp('[' + _RESERVED_CHARS_RE + ']', 'g'); // Attempts to allow node names from any language. ES5's `\w` regexp matches // only latin characters, and the unicode \p{L} is not yet supported. So // instead, we exclude reserved characters and match everything else. const _wordChar = '[^' + _RESERVED_CHARS_RE + ']'; const _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace('\\.', '') + ']'; // Parent directories, delimited by '/' or ':'. Currently unused, but must // be matched to parse the rest of the track name. const _directoryRe = /*@__PURE__*/ /((?:WC+[\/:])*)/.source.replace('WC', _wordChar); // Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'. const _nodeRe = /*@__PURE__*/ /(WCOD+)?/.source.replace('WCOD', _wordCharOrDot); // Object on target node, and accessor. May not contain reserved // characters. Accessor may contain any character except closing bracket. const _objectRe = /*@__PURE__*/ /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace('WC', _wordChar); // Property and accessor. May not contain reserved characters. Accessor may // contain any non-bracket characters. const _propertyRe = /*@__PURE__*/ /\.(WC+)(?:\[(.+)\])?/.source.replace('WC', _wordChar); const _trackRe = new RegExp('' + '^' + _directoryRe + _nodeRe + _objectRe + _propertyRe + '$'); const _supportedObjectNames = ['material', 'materials', 'bones', 'map']; class Composite { constructor(targetGroup, path, optionalParsedPath) { const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName(path); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_(path, parsedPath); } getValue(array, offset) { this.bind(); // bind all binding const firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[firstValidIndex]; // and only call .getValue on the first if (binding !== undefined) binding.getValue(array, offset); } setValue(array, offset) { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].setValue(array, offset); } } bind() { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].bind(); } } unbind() { const bindings = this._bindings; for (let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) { bindings[i].unbind(); } } } // Note: This class uses a State pattern on a per-method basis: // 'bind' sets 'this.getValue' / 'setValue' and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. class PropertyBinding { constructor(rootNode, path, parsedPath) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName(path); this.node = PropertyBinding.findNode(rootNode, this.parsedPath.nodeName); this.rootNode = rootNode; // initial state of these methods that calls 'bind' this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } static create(root, path, parsedPath) { if (!(root && root.isAnimationObjectGroup)) { return new PropertyBinding(root, path, parsedPath); } else { return new PropertyBinding.Composite(root, path, parsedPath); } } /** * Replaces spaces with underscores and removes unsupported characters from * node names, to ensure compatibility with parseTrackName(). * * @param {string} name Node name to be sanitized. * @return {string} */ static sanitizeNodeName(name) { return name.replace(/\s/g, '_').replace(_reservedRe, ''); } static parseTrackName(trackName) { const matches = _trackRe.exec(trackName); if (matches === null) { throw new Error('PropertyBinding: Cannot parse trackName: ' + trackName); } const results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[2], objectName: matches[3], objectIndex: matches[4], propertyName: matches[5], // required propertyIndex: matches[6] }; const lastDot = results.nodeName && results.nodeName.lastIndexOf('.'); if (lastDot !== undefined && lastDot !== -1) { const objectName = results.nodeName.substring(lastDot + 1); // Object names must be checked against an allowlist. Otherwise, there // is no way to parse 'foo.bar.baz': 'baz' must be a property, but // 'bar' could be the objectName, or part of a nodeName (which can // include '.' characters). if (_supportedObjectNames.indexOf(objectName) !== -1) { results.nodeName = results.nodeName.substring(0, lastDot); results.objectName = objectName; } } if (results.propertyName === null || results.propertyName.length === 0) { throw new Error('PropertyBinding: can not parse propertyName from trackName: ' + trackName); } return results; } static findNode(root, nodeName) { if (nodeName === undefined || nodeName === '' || nodeName === '.' || nodeName === -1 || nodeName === root.name || nodeName === root.uuid) { return root; } // search into skeleton bones. if (root.skeleton) { const bone = root.skeleton.getBoneByName(nodeName); if (bone !== undefined) { return bone; } } // search into node subtree. if (root.children) { const searchNodeSubtree = function (children) { for (let i = 0; i < children.length; i++) { const childNode = children[i]; if (childNode.name === nodeName || childNode.uuid === nodeName) { return childNode; } const result = searchNodeSubtree(childNode.children); if (result) return result; } return null; }; const subTreeNode = searchNodeSubtree(root.children); if (subTreeNode) { return subTreeNode; } } return null; } // these are used to "bind" a nonexistent property _getValue_unavailable() {} _setValue_unavailable() {} // Getters _getValue_direct(buffer, offset) { buffer[offset] = this.targetObject[this.propertyName]; } _getValue_array(buffer, offset) { const source = this.resolvedProperty; for (let i = 0, n = source.length; i !== n; ++i) { buffer[offset++] = source[i]; } } _getValue_arrayElement(buffer, offset) { buffer[offset] = this.resolvedProperty[this.propertyIndex]; } _getValue_toArray(buffer, offset) { this.resolvedProperty.toArray(buffer, offset); } // Direct _setValue_direct(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; } _setValue_direct_setNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_direct_setMatrixWorldNeedsUpdate(buffer, offset) { this.targetObject[this.propertyName] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // EntireArray _setValue_array(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } } _setValue_array_setNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.needsUpdate = true; } _setValue_array_setMatrixWorldNeedsUpdate(buffer, offset) { const dest = this.resolvedProperty; for (let i = 0, n = dest.length; i !== n; ++i) { dest[i] = buffer[offset++]; } this.targetObject.matrixWorldNeedsUpdate = true; } // ArrayElement _setValue_arrayElement(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; } _setValue_arrayElement_setNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.needsUpdate = true; } _setValue_arrayElement_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty[this.propertyIndex] = buffer[offset]; this.targetObject.matrixWorldNeedsUpdate = true; } // HasToFromArray _setValue_fromArray(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); } _setValue_fromArray_setNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.needsUpdate = true; } _setValue_fromArray_setMatrixWorldNeedsUpdate(buffer, offset) { this.resolvedProperty.fromArray(buffer, offset); this.targetObject.matrixWorldNeedsUpdate = true; } _getValue_unbound(targetArray, offset) { this.bind(); this.getValue(targetArray, offset); } _setValue_unbound(sourceArray, offset) { this.bind(); this.setValue(sourceArray, offset); } // create getter / setter pair for a property in the scene graph bind() { let targetObject = this.node; const parsedPath = this.parsedPath; const objectName = parsedPath.objectName; const propertyName = parsedPath.propertyName; let propertyIndex = parsedPath.propertyIndex; if (!targetObject) { targetObject = PropertyBinding.findNode(this.rootNode, parsedPath.nodeName); this.node = targetObject; } // set fail state so we can just 'return' on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if (!targetObject) { console.error('THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\'t found.'); return; } if (objectName) { let objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch (objectName) { case 'materials': if (!targetObject.material) { console.error('THREE.PropertyBinding: Can not bind to material as node does not have a material.', this); return; } if (!targetObject.material.materials) { console.error('THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this); return; } targetObject = targetObject.material.materials; break; case 'bones': if (!targetObject.skeleton) { console.error('THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for (let i = 0; i < targetObject.length; i++) { if (targetObject[i].name === objectIndex) { objectIndex = i; break; } } break; case 'map': if ('map' in targetObject) { targetObject = targetObject.map; break; } if (!targetObject.material) { console.error('THREE.PropertyBinding: Can not bind to material as node does not have a material.', this); return; } if (!targetObject.material.map) { console.error('THREE.PropertyBinding: Can not bind to material.map as node.material does not have a map.', this); return; } targetObject = targetObject.material.map; break; default: if (targetObject[objectName] === undefined) { console.error('THREE.PropertyBinding: Can not bind to objectName of node undefined.', this); return; } targetObject = targetObject[objectName]; } if (objectIndex !== undefined) { if (targetObject[objectIndex] === undefined) { console.error('THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject); return; } targetObject = targetObject[objectIndex]; } } // resolve property const nodeProperty = targetObject[propertyName]; if (nodeProperty === undefined) { const nodeName = parsedPath.nodeName; console.error('THREE.PropertyBinding: Trying to update property for track: ' + nodeName + '.' + propertyName + ' but it wasn\'t found.', targetObject); return; } // determine versioning scheme let versioning = this.Versioning.None; this.targetObject = targetObject; if (targetObject.needsUpdate !== undefined) { // material versioning = this.Versioning.NeedsUpdate; } else if (targetObject.matrixWorldNeedsUpdate !== undefined) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; } // determine how the property gets bound let bindingType = this.BindingType.Direct; if (propertyIndex !== undefined) { // access a sub element of the property array (only primitives are supported right now) if (propertyName === 'morphTargetInfluences') { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if (!targetObject.geometry) { console.error('THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this); return; } if (!targetObject.geometry.morphAttributes) { console.error('THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this); return; } if (targetObject.morphTargetDictionary[propertyIndex] !== undefined) { propertyIndex = targetObject.morphTargetDictionary[propertyIndex]; } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if (nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if (Array.isArray(nodeProperty)) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[bindingType]; this.setValue = this.SetterByBindingTypeAndVersioning[bindingType][versioning]; } unbind() { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of 'this' via 'delete' this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } } PropertyBinding.Composite = Composite; PropertyBinding.prototype.BindingType = { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3 }; PropertyBinding.prototype.Versioning = { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2 }; PropertyBinding.prototype.GetterByBindingType = [PropertyBinding.prototype._getValue_direct, PropertyBinding.prototype._getValue_array, PropertyBinding.prototype._getValue_arrayElement, PropertyBinding.prototype._getValue_toArray]; PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [[ // Direct PropertyBinding.prototype._setValue_direct, PropertyBinding.prototype._setValue_direct_setNeedsUpdate, PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate], [ // EntireArray PropertyBinding.prototype._setValue_array, PropertyBinding.prototype._setValue_array_setNeedsUpdate, PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate], [ // ArrayElement PropertyBinding.prototype._setValue_arrayElement, PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate], [ // HasToFromArray PropertyBinding.prototype._setValue_fromArray, PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate]]; /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as 'root' to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as 'root'. * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. */ class AnimationObjectGroup { constructor() { this.isAnimationObjectGroup = true; this.uuid = generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call(arguments); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite const indices = {}; this._indicesByUUID = indices; // for bookkeeping for (let i = 0, n = arguments.length; i !== n; ++i) { indices[arguments[i].uuid] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don't care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays const scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; } }, get bindingsPerObject() { return scope._bindings.length; } }; } add() { const objects = this._objects, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length; let knownObject = undefined, nObjects = objects.length, nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid; let index = indicesByUUID[uuid]; if (index === undefined) { // unknown object -> add it to the ACTIVE region index = nObjects++; indicesByUUID[uuid] = index; objects.push(object); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { bindings[j].push(new PropertyBinding(object, paths[j], parsedPaths[j])); } } else if (index < nCachedObjects) { knownObject = objects[index]; // move existing object to the ACTIVE region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex]; indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; indicesByUUID[uuid] = firstActiveIndex; objects[firstActiveIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex]; let binding = bindingsForPath[index]; bindingsForPath[index] = lastCached; if (binding === undefined) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding(object, paths[j], parsedPaths[j]); } bindingsForPath[firstActiveIndex] = binding; } } else if (objects[index] !== knownObject) { console.error('THREE.AnimationObjectGroup: Different objects with the same UUID ' + 'detected. Clean the caches or recreate your infrastructure when reloading scenes.'); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; } remove() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined && index >= nCachedObjects) { // move existing object into the CACHED region const lastCachedIndex = nCachedObjects++, firstActiveObject = objects[lastCachedIndex]; indicesByUUID[firstActiveObject.uuid] = index; objects[index] = firstActiveObject; indicesByUUID[uuid] = lastCachedIndex; objects[lastCachedIndex] = object; // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], firstActive = bindingsForPath[lastCachedIndex], binding = bindingsForPath[index]; bindingsForPath[index] = firstActive; bindingsForPath[lastCachedIndex] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; } // remove & forget uncache() { const objects = this._objects, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; let nCachedObjects = this.nCachedObjects_, nObjects = objects.length; for (let i = 0, n = arguments.length; i !== n; ++i) { const object = arguments[i], uuid = object.uuid, index = indicesByUUID[uuid]; if (index !== undefined) { delete indicesByUUID[uuid]; if (index < nCachedObjects) { // object is cached, shrink the CACHED region const firstActiveIndex = --nCachedObjects, lastCachedObject = objects[firstActiveIndex], lastIndex = --nObjects, lastObject = objects[lastIndex]; // last cached object takes this object's place indicesByUUID[lastCachedObject.uuid] = index; objects[index] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[lastObject.uuid] = firstActiveIndex; objects[firstActiveIndex] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j], lastCached = bindingsForPath[firstActiveIndex], last = bindingsForPath[lastIndex]; bindingsForPath[index] = lastCached; bindingsForPath[firstActiveIndex] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop const lastIndex = --nObjects, lastObject = objects[lastIndex]; if (lastIndex > 0) { indicesByUUID[lastObject.uuid] = index; } objects[index] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for (let j = 0, m = nBindings; j !== m; ++j) { const bindingsForPath = bindings[j]; bindingsForPath[index] = bindingsForPath[lastIndex]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; } // Internal interface used by befriended PropertyBinding.Composite: subscribe_(path, parsedPath) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group const indicesByPath = this._bindingsIndicesByPath; let index = indicesByPath[path]; const bindings = this._bindings; if (index !== undefined) return bindings[index]; const paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array(nObjects); index = bindings.length; indicesByPath[path] = index; paths.push(path); parsedPaths.push(parsedPath); bindings.push(bindingsForPath); for (let i = nCachedObjects, n = objects.length; i !== n; ++i) { const object = objects[i]; bindingsForPath[i] = new PropertyBinding(object, path, parsedPath); } return bindingsForPath; } unsubscribe_(path) { // tells the group to forget about a property path and no longer // update the array previously obtained with 'subscribe_' const indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[path]; if (index !== undefined) { const paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[lastBindingsIndex], lastBindingsPath = path[lastBindingsIndex]; indicesByPath[lastBindingsPath] = index; bindings[index] = lastBindings; bindings.pop(); parsedPaths[index] = parsedPaths[lastBindingsIndex]; parsedPaths.pop(); paths[index] = paths[lastBindingsIndex]; paths.pop(); } } } class AnimationAction { constructor(mixer, clip, localRoot = null, blendMode = clip.blendMode) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot; this.blendMode = blendMode; const tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array(nTracks); const interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; for (let i = 0; i !== nTracks; ++i) { const interpolant = tracks[i].createInterpolant(null); interpolants[i] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array(nTracks); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = -1; // global mixer time when the action is to be started // it's set back to 'null' upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // true -> zero effective time scale this.enabled = true; // false -> zero effective weight this.clampWhenFinished = false; // keep feeding the last frame? this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate this.zeroSlopeAtEnd = true; // clips for start, loop and end } // State & Scheduling play() { this._mixer._activateAction(this); return this; } stop() { this._mixer._deactivateAction(this); return this.reset(); } reset() { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = -1; // forget previous loops this._startTime = null; // forget scheduling return this.stopFading().stopWarping(); } isRunning() { return this.enabled && !this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction(this); } // return true when play has been called isScheduled() { return this._mixer._isActiveAction(this); } startAt(time) { this._startTime = time; return this; } setLoop(mode, repetitions) { this.loop = mode; this.repetitions = repetitions; return this; } // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight(weight) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); } // return the weight considering fading and .enabled getEffectiveWeight() { return this._effectiveWeight; } fadeIn(duration) { return this._scheduleFading(duration, 0, 1); } fadeOut(duration) { return this._scheduleFading(duration, 1, 0); } crossFadeFrom(fadeOutAction, duration, warp) { fadeOutAction.fadeOut(duration); this.fadeIn(duration); if (warp) { const fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp(1.0, startEndRatio, duration); this.warp(endStartRatio, 1.0, duration); } return this; } crossFadeTo(fadeInAction, duration, warp) { return fadeInAction.crossFadeFrom(this, duration, warp); } stopFading() { const weightInterpolant = this._weightInterpolant; if (weightInterpolant !== null) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant(weightInterpolant); } return this; } // Time Scale Control // set the time scale stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale(timeScale) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 : timeScale; return this.stopWarping(); } // return the time scale considering warping and .paused getEffectiveTimeScale() { return this._effectiveTimeScale; } setDuration(duration) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); } syncWith(action) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); } halt(duration) { return this.warp(this._effectiveTimeScale, 0, duration); } warp(startTimeScale, endTimeScale, duration) { const mixer = this._mixer, now = mixer.time, timeScale = this.timeScale; let interpolant = this._timeScaleInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; times[1] = now + duration; values[0] = startTimeScale / timeScale; values[1] = endTimeScale / timeScale; return this; } stopWarping() { const timeScaleInterpolant = this._timeScaleInterpolant; if (timeScaleInterpolant !== null) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant(timeScaleInterpolant); } return this; } // Object Accessors getMixer() { return this._mixer; } getClip() { return this._clip; } getRoot() { return this._localRoot || this._mixer._root; } // Interna _update(time, deltaTime, timeDirection, accuIndex) { // called by the mixer if (!this.enabled) { // call ._updateWeight() to update ._effectiveWeight this._updateWeight(time); return; } const startTime = this._startTime; if (startTime !== null) { // check for scheduled start of action const timeRunning = (time - startTime) * timeDirection; if (timeRunning < 0 || timeDirection === 0) { deltaTime = 0; } else { this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } } // apply time scale and advance time deltaTime *= this._updateTimeScale(time); const clipTime = this._updateTime(deltaTime); // note: _updateTime may disable the action resulting in // an effective weight of 0 const weight = this._updateWeight(time); if (weight > 0) { const interpolants = this._interpolants; const propertyMixers = this._propertyBindings; switch (this.blendMode) { case AdditiveAnimationBlendMode: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulateAdditive(weight); } break; case NormalAnimationBlendMode: default: for (let j = 0, m = interpolants.length; j !== m; ++j) { interpolants[j].evaluate(clipTime); propertyMixers[j].accumulate(accuIndex, weight); } } } } _updateWeight(time) { let weight = 0; if (this.enabled) { weight = this.weight; const interpolant = this._weightInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; weight *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopFading(); if (interpolantValue === 0) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; } _updateTimeScale(time) { let timeScale = 0; if (!this.paused) { timeScale = this.timeScale; const interpolant = this._timeScaleInterpolant; if (interpolant !== null) { const interpolantValue = interpolant.evaluate(time)[0]; timeScale *= interpolantValue; if (time > interpolant.parameterPositions[1]) { this.stopWarping(); if (timeScale === 0) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; } _updateTime(deltaTime) { const duration = this._clip.duration; const loop = this.loop; let time = this.time + deltaTime; let loopCount = this._loopCount; const pingPong = loop === LoopPingPong; if (deltaTime === 0) { if (loopCount === -1) return time; return pingPong && (loopCount & 1) === 1 ? duration - time : time; } if (loop === LoopOnce) { if (loopCount === -1) { // just started this._loopCount = 0; this._setEndings(true, true, false); } handle_stop: { if (time >= duration) { time = duration; } else if (time < 0) { time = 0; } else { this.time = time; break handle_stop; } if (this.clampWhenFinished) this.paused = true;else this.enabled = false; this.time = time; this._mixer.dispatchEvent({ type: 'finished', action: this, direction: deltaTime < 0 ? -1 : 1 }); } } else { // repetitive Repeat or PingPong if (loopCount === -1) { // just started if (deltaTime >= 0) { loopCount = 0; this._setEndings(true, this.repetitions === 0, pingPong); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings(this.repetitions === 0, true, pingPong); } } if (time >= duration || time < 0) { // wrap around const loopDelta = Math.floor(time / duration); // signed time -= duration * loopDelta; loopCount += Math.abs(loopDelta); const pending = this.repetitions - loopCount; if (pending <= 0) { // have to stop (switch state, clamp time, fire event) if (this.clampWhenFinished) this.paused = true;else this.enabled = false; time = deltaTime > 0 ? duration : 0; this.time = time; this._mixer.dispatchEvent({ type: 'finished', action: this, direction: deltaTime > 0 ? 1 : -1 }); } else { // keep running if (pending === 1) { // entering the last round const atStart = deltaTime < 0; this._setEndings(atStart, !atStart, pingPong); } else { this._setEndings(false, false, pingPong); } this._loopCount = loopCount; this.time = time; this._mixer.dispatchEvent({ type: 'loop', action: this, loopDelta: loopDelta }); } } else { this.time = time; } if (pingPong && (loopCount & 1) === 1) { // invert time for the "pong round" return duration - time; } } return time; } _setEndings(atStart, atEnd, pingPong) { const settings = this._interpolantSettings; if (pingPong) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if (atStart) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if (atEnd) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } } _scheduleFading(duration, weightNow, weightThen) { const mixer = this._mixer, now = mixer.time; let interpolant = this._weightInterpolant; if (interpolant === null) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } const times = interpolant.parameterPositions, values = interpolant.sampleValues; times[0] = now; values[0] = weightNow; times[1] = now + duration; values[1] = weightThen; return this; } } const _controlInterpolantsResultBuffer = new Float32Array(1); class AnimationMixer extends EventDispatcher { constructor(root) { super(); this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } _bindAction(action, prototypeAction) { const root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName; let bindingsByName = bindingsByRoot[rootUuid]; if (bindingsByName === undefined) { bindingsByName = {}; bindingsByRoot[rootUuid] = bindingsByName; } for (let i = 0; i !== nTracks; ++i) { const track = tracks[i], trackName = track.name; let binding = bindingsByName[trackName]; if (binding !== undefined) { ++binding.referenceCount; bindings[i] = binding; } else { binding = bindings[i]; if (binding !== undefined) { // existing binding, make sure the cache knows if (binding._cacheIndex === null) { ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); } continue; } const path = prototypeAction && prototypeAction._propertyBindings[i].binding.parsedPath; binding = new PropertyMixer(PropertyBinding.create(root, trackName, path), track.ValueTypeName, track.getValueSize()); ++binding.referenceCount; this._addInactiveBinding(binding, rootUuid, trackName); bindings[i] = binding; } interpolants[i].resultBuffer = binding.buffer; } } _activateAction(action) { if (!this._isActiveAction(action)) { if (action._cacheIndex === null) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind const rootUuid = (action._localRoot || this._root).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[clipUuid]; this._bindAction(action, actionsForClip && actionsForClip.knownActions[0]); this._addInactiveAction(action, clipUuid, rootUuid); } const bindings = action._propertyBindings; // increment reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (binding.useCount++ === 0) { this._lendBinding(binding); binding.saveOriginalState(); } } this._lendAction(action); } } _deactivateAction(action) { if (this._isActiveAction(action)) { const bindings = action._propertyBindings; // decrement reference counts / sort out state for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.useCount === 0) { binding.restoreOriginalState(); this._takeBackBinding(binding); } } this._takeBackAction(action); } } // Memory manager _initMemoryManager() { this._actions = []; // 'nActiveActions' followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // 'nActiveBindings' followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; const scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; } }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; } }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; } } }; } // Memory management for AnimationAction objects _isActiveAction(action) { const index = action._cacheIndex; return index !== null && index < this._nActiveActions; } _addInactiveAction(action, clipUuid, rootUuid) { const actions = this._actions, actionsByClip = this._actionsByClip; let actionsForClip = actionsByClip[clipUuid]; if (actionsForClip === undefined) { actionsForClip = { knownActions: [action], actionByRoot: {} }; action._byClipCacheIndex = 0; actionsByClip[clipUuid] = actionsForClip; } else { const knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push(action); } action._cacheIndex = actions.length; actions.push(action); actionsForClip.actionByRoot[rootUuid] = action; } _removeInactiveAction(action) { const actions = this._actions, lastInactiveAction = actions[actions.length - 1], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); action._cacheIndex = null; const clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[knownActionsForClip.length - 1], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[byClipCacheIndex] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; const actionByRoot = actionsForClip.actionByRoot, rootUuid = (action._localRoot || this._root).uuid; delete actionByRoot[rootUuid]; if (knownActionsForClip.length === 0) { delete actionsByClip[clipUuid]; } this._removeInactiveBindingsForAction(action); } _removeInactiveBindingsForAction(action) { const bindings = action._propertyBindings; for (let i = 0, n = bindings.length; i !== n; ++i) { const binding = bindings[i]; if (--binding.referenceCount === 0) { this._removeInactiveBinding(binding); } } } _lendAction(action) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s const actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions++, firstInactiveAction = actions[lastActiveIndex]; action._cacheIndex = lastActiveIndex; actions[lastActiveIndex] = action; firstInactiveAction._cacheIndex = prevIndex; actions[prevIndex] = firstInactiveAction; } _takeBackAction(action) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a const actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = --this._nActiveActions, lastActiveAction = actions[firstInactiveIndex]; action._cacheIndex = firstInactiveIndex; actions[firstInactiveIndex] = action; lastActiveAction._cacheIndex = prevIndex; actions[prevIndex] = lastActiveAction; } // Memory management for PropertyMixer objects _addInactiveBinding(binding, rootUuid, trackName) { const bindingsByRoot = this._bindingsByRootAndName, bindings = this._bindings; let bindingByName = bindingsByRoot[rootUuid]; if (bindingByName === undefined) { bindingByName = {}; bindingsByRoot[rootUuid] = bindingByName; } bindingByName[trackName] = binding; binding._cacheIndex = bindings.length; bindings.push(binding); } _removeInactiveBinding(binding) { const bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid], lastInactiveBinding = bindings[bindings.length - 1], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[cacheIndex] = lastInactiveBinding; bindings.pop(); delete bindingByName[trackName]; if (Object.keys(bindingByName).length === 0) { delete bindingsByRoot[rootUuid]; } } _lendBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings++, firstInactiveBinding = bindings[lastActiveIndex]; binding._cacheIndex = lastActiveIndex; bindings[lastActiveIndex] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = firstInactiveBinding; } _takeBackBinding(binding) { const bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = --this._nActiveBindings, lastActiveBinding = bindings[firstInactiveIndex]; binding._cacheIndex = firstInactiveIndex; bindings[firstInactiveIndex] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[prevIndex] = lastActiveBinding; } // Memory management of Interpolants for weight and time scale _lendControlInterpolant() { const interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants++; let interpolant = interpolants[lastActiveIndex]; if (interpolant === undefined) { interpolant = new LinearInterpolant(new Float32Array(2), new Float32Array(2), 1, _controlInterpolantsResultBuffer); interpolant.__cacheIndex = lastActiveIndex; interpolants[lastActiveIndex] = interpolant; } return interpolant; } _takeBackControlInterpolant(interpolant) { const interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = --this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[firstInactiveIndex]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[firstInactiveIndex] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[prevIndex] = lastActiveInterpolant; } // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction(clip, optionalRoot, blendMode) { const root = optionalRoot || this._root, rootUuid = root.uuid; let clipObject = typeof clip === 'string' ? AnimationClip.findByName(root, clip) : clip; const clipUuid = clipObject !== null ? clipObject.uuid : clip; const actionsForClip = this._actionsByClip[clipUuid]; let prototypeAction = null; if (blendMode === undefined) { if (clipObject !== null) { blendMode = clipObject.blendMode; } else { blendMode = NormalAnimationBlendMode; } } if (actionsForClip !== undefined) { const existingAction = actionsForClip.actionByRoot[rootUuid]; if (existingAction !== undefined && existingAction.blendMode === blendMode) { return existingAction; } // we know the clip, so we don't have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[0]; // also, take the clip from the prototype action if (clipObject === null) clipObject = prototypeAction._clip; } // clip must be known when specified via string if (clipObject === null) return null; // allocate all resources required to run it const newAction = new AnimationAction(this, clipObject, optionalRoot, blendMode); this._bindAction(newAction, prototypeAction); // and make the action known to the memory manager this._addInactiveAction(newAction, clipUuid, rootUuid); return newAction; } // get an existing action existingAction(clip, optionalRoot) { const root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === 'string' ? AnimationClip.findByName(root, clip) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[clipUuid]; if (actionsForClip !== undefined) { return actionsForClip.actionByRoot[rootUuid] || null; } return null; } // deactivates all previously scheduled actions stopAllAction() { const actions = this._actions, nActions = this._nActiveActions; for (let i = nActions - 1; i >= 0; --i) { actions[i].stop(); } return this; } // advance the time and update apply the animation update(deltaTime) { deltaTime *= this.timeScale; const actions = this._actions, nActions = this._nActiveActions, time = this.time += deltaTime, timeDirection = Math.sign(deltaTime), accuIndex = this._accuIndex ^= 1; // run active actions for (let i = 0; i !== nActions; ++i) { const action = actions[i]; action._update(time, deltaTime, timeDirection, accuIndex); } // update scene graph const bindings = this._bindings, nBindings = this._nActiveBindings; for (let i = 0; i !== nBindings; ++i) { bindings[i].apply(accuIndex); } return this; } // Allows you to seek to a specific time in an animation. setTime(timeInSeconds) { this.time = 0; // Zero out time attribute for AnimationMixer object; for (let i = 0; i < this._actions.length; i++) { this._actions[i].time = 0; // Zero out time attribute for all associated AnimationAction objects. } return this.update(timeInSeconds); // Update used to set exact time. Returns "this" AnimationMixer object. } // return this mixer's root target object getRoot() { return this._root; } // free all resources specific to a particular clip uncacheClip(clip) { const actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[clipUuid]; if (actionsForClip !== undefined) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away const actionsToRemove = actionsForClip.knownActions; for (let i = 0, n = actionsToRemove.length; i !== n; ++i) { const action = actionsToRemove[i]; this._deactivateAction(action); const cacheIndex = action._cacheIndex, lastInactiveAction = actions[actions.length - 1]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[cacheIndex] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction(action); } delete actionsByClip[clipUuid]; } } // free all resources specific to a particular root target object uncacheRoot(root) { const rootUuid = root.uuid, actionsByClip = this._actionsByClip; for (const clipUuid in actionsByClip) { const actionByRoot = actionsByClip[clipUuid].actionByRoot, action = actionByRoot[rootUuid]; if (action !== undefined) { this._deactivateAction(action); this._removeInactiveAction(action); } } const bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[rootUuid]; if (bindingByName !== undefined) { for (const trackName in bindingByName) { const binding = bindingByName[trackName]; binding.restoreOriginalState(); this._removeInactiveBinding(binding); } } } // remove a targeted clip from the cache uncacheAction(clip, optionalRoot) { const action = this.existingAction(clip, optionalRoot); if (action !== null) { this._deactivateAction(action); this._removeInactiveAction(action); } } } class Uniform { constructor(value) { this.value = value; } clone() { return new Uniform(this.value.clone === undefined ? this.value : this.value.clone()); } } let id = 0; class UniformsGroup extends EventDispatcher { constructor() { super(); this.isUniformsGroup = true; Object.defineProperty(this, 'id', { value: id++ }); this.name = ''; this.usage = StaticDrawUsage; this.uniforms = []; } add(uniform) { this.uniforms.push(uniform); return this; } remove(uniform) { const index = this.uniforms.indexOf(uniform); if (index !== -1) this.uniforms.splice(index, 1); return this; } setName(name) { this.name = name; return this; } setUsage(value) { this.usage = value; return this; } dispose() { this.dispatchEvent({ type: 'dispose' }); return this; } copy(source) { this.name = source.name; this.usage = source.usage; const uniformsSource = source.uniforms; this.uniforms.length = 0; for (let i = 0, l = uniformsSource.length; i < l; i++) { this.uniforms.push(uniformsSource[i].clone()); } return this; } clone() { return new this.constructor().copy(this); } } class InstancedInterleavedBuffer extends InterleavedBuffer { constructor(array, stride, meshPerAttribute = 1) { super(array, stride); this.isInstancedInterleavedBuffer = true; this.meshPerAttribute = meshPerAttribute; } copy(source) { super.copy(source); this.meshPerAttribute = source.meshPerAttribute; return this; } clone(data) { const ib = super.clone(data); ib.meshPerAttribute = this.meshPerAttribute; return ib; } toJSON(data) { const json = super.toJSON(data); json.isInstancedInterleavedBuffer = true; json.meshPerAttribute = this.meshPerAttribute; return json; } } class GLBufferAttribute { constructor(buffer, type, itemSize, elementSize, count) { this.isGLBufferAttribute = true; this.name = ''; this.buffer = buffer; this.type = type; this.itemSize = itemSize; this.elementSize = elementSize; this.count = count; this.version = 0; } set needsUpdate(value) { if (value === true) this.version++; } setBuffer(buffer) { this.buffer = buffer; return this; } setType(type, elementSize) { this.type = type; this.elementSize = elementSize; return this; } setItemSize(itemSize) { this.itemSize = itemSize; return this; } setCount(count) { this.count = count; return this; } } class Raycaster { constructor(origin, direction, near = 0, far = Infinity) { this.ray = new Ray(origin, direction); // direction is assumed to be normalized (for accurate distance calculations) this.near = near; this.far = far; this.camera = null; this.layers = new Layers(); this.params = { Mesh: {}, Line: { threshold: 1 }, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; } set(origin, direction) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set(origin, direction); } setFromCamera(coords, camera) { if (camera.isPerspectiveCamera) { this.ray.origin.setFromMatrixPosition(camera.matrixWorld); this.ray.direction.set(coords.x, coords.y, 0.5).unproject(camera).sub(this.ray.origin).normalize(); this.camera = camera; } else if (camera.isOrthographicCamera) { this.ray.origin.set(coords.x, coords.y, (camera.near + camera.far) / (camera.near - camera.far)).unproject(camera); // set origin in plane of camera this.ray.direction.set(0, 0, -1).transformDirection(camera.matrixWorld); this.camera = camera; } else { console.error('THREE.Raycaster: Unsupported camera type: ' + camera.type); } } intersectObject(object, recursive = true, intersects = []) { intersectObject(object, this, intersects, recursive); intersects.sort(ascSort); return intersects; } intersectObjects(objects, recursive = true, intersects = []) { for (let i = 0, l = objects.length; i < l; i++) { intersectObject(objects[i], this, intersects, recursive); } intersects.sort(ascSort); return intersects; } } function ascSort(a, b) { return a.distance - b.distance; } function intersectObject(object, raycaster, intersects, recursive) { if (object.layers.test(raycaster.layers)) { object.raycast(raycaster, intersects); } if (recursive === true) { const children = object.children; for (let i = 0, l = children.length; i < l; i++) { intersectObject(children[i], raycaster, intersects, true); } } } /** * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. * The azimuthal angle (theta) is measured from the positive z-axis. */ class Spherical { constructor(radius = 1, phi = 0, theta = 0) { this.radius = radius; this.phi = phi; // polar angle this.theta = theta; // azimuthal angle return this; } set(radius, phi, theta) { this.radius = radius; this.phi = phi; this.theta = theta; return this; } copy(other) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; } // restrict phi to be between EPS and PI-EPS makeSafe() { const EPS = 0.000001; this.phi = Math.max(EPS, Math.min(Math.PI - EPS, this.phi)); return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + y * y + z * z); if (this.radius === 0) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2(x, z); this.phi = Math.acos(clamp(y / this.radius, -1, 1)); } return this; } clone() { return new this.constructor().copy(this); } } /** * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system */ class Cylindrical { constructor(radius = 1, theta = 0, y = 0) { this.radius = radius; // distance from the origin to a point in the x-z plane this.theta = theta; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = y; // height above the x-z plane return this; } set(radius, theta, y) { this.radius = radius; this.theta = theta; this.y = y; return this; } copy(other) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; } setFromVector3(v) { return this.setFromCartesianCoords(v.x, v.y, v.z); } setFromCartesianCoords(x, y, z) { this.radius = Math.sqrt(x * x + z * z); this.theta = Math.atan2(x, z); this.y = y; return this; } clone() { return new this.constructor().copy(this); } } const _vector$4 = /*@__PURE__*/new Vector2(); class Box2 { constructor(min = new Vector2(+Infinity, +Infinity), max = new Vector2(-Infinity, -Infinity)) { this.isBox2 = true; this.min = min; this.max = max; } set(min, max) { this.min.copy(min); this.max.copy(max); return this; } setFromPoints(points) { this.makeEmpty(); for (let i = 0, il = points.length; i < il; i++) { this.expandByPoint(points[i]); } return this; } setFromCenterAndSize(center, size) { const halfSize = _vector$4.copy(size).multiplyScalar(0.5); this.min.copy(center).sub(halfSize); this.max.copy(center).add(halfSize); return this; } clone() { return new this.constructor().copy(this); } copy(box) { this.min.copy(box.min); this.max.copy(box.max); return this; } makeEmpty() { this.min.x = this.min.y = +Infinity; this.max.x = this.max.y = -Infinity; return this; } isEmpty() { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return this.max.x < this.min.x || this.max.y < this.min.y; } getCenter(target) { return this.isEmpty() ? target.set(0, 0) : target.addVectors(this.min, this.max).multiplyScalar(0.5); } getSize(target) { return this.isEmpty() ? target.set(0, 0) : target.subVectors(this.max, this.min); } expandByPoint(point) { this.min.min(point); this.max.max(point); return this; } expandByVector(vector) { this.min.sub(vector); this.max.add(vector); return this; } expandByScalar(scalar) { this.min.addScalar(-scalar); this.max.addScalar(scalar); return this; } containsPoint(point) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; } containsBox(box) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y; } getParameter(point, target) { // This can potentially have a divide by zero if the box // has a size dimension of 0. return target.set((point.x - this.min.x) / (this.max.x - this.min.x), (point.y - this.min.y) / (this.max.y - this.min.y)); } intersectsBox(box) { // using 4 splitting planes to rule out intersections return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; } clampPoint(point, target) { return target.copy(point).clamp(this.min, this.max); } distanceToPoint(point) { return this.clampPoint(point, _vector$4).distanceTo(point); } intersect(box) { this.min.max(box.min); this.max.min(box.max); if (this.isEmpty()) this.makeEmpty(); return this; } union(box) { this.min.min(box.min); this.max.max(box.max); return this; } translate(offset) { this.min.add(offset); this.max.add(offset); return this; } equals(box) { return box.min.equals(this.min) && box.max.equals(this.max); } } const _startP = /*@__PURE__*/new Vector3(); const _startEnd = /*@__PURE__*/new Vector3(); class Line3 { constructor(start = new Vector3(), end = new Vector3()) { this.start = start; this.end = end; } set(start, end) { this.start.copy(start); this.end.copy(end); return this; } copy(line) { this.start.copy(line.start); this.end.copy(line.end); return this; } getCenter(target) { return target.addVectors(this.start, this.end).multiplyScalar(0.5); } delta(target) { return target.subVectors(this.end, this.start); } distanceSq() { return this.start.distanceToSquared(this.end); } distance() { return this.start.distanceTo(this.end); } at(t, target) { return this.delta(target).multiplyScalar(t).add(this.start); } closestPointToPointParameter(point, clampToLine) { _startP.subVectors(point, this.start); _startEnd.subVectors(this.end, this.start); const startEnd2 = _startEnd.dot(_startEnd); const startEnd_startP = _startEnd.dot(_startP); let t = startEnd_startP / startEnd2; if (clampToLine) { t = clamp(t, 0, 1); } return t; } closestPointToPoint(point, clampToLine, target) { const t = this.closestPointToPointParameter(point, clampToLine); return this.delta(target).multiplyScalar(t).add(this.start); } applyMatrix4(matrix) { this.start.applyMatrix4(matrix); this.end.applyMatrix4(matrix); return this; } equals(line) { return line.start.equals(this.start) && line.end.equals(this.end); } clone() { return new this.constructor().copy(this); } } const _vector$3 = /*@__PURE__*/new Vector3(); class SpotLightHelper extends Object3D { constructor(light, color) { super(); this.light = light; this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; this.type = 'SpotLightHelper'; const geometry = new BufferGeometry(); const positions = [0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, -1, 1]; for (let i = 0, j = 1, l = 32; i < l; i++, j++) { const p1 = i / l * Math.PI * 2; const p2 = j / l * Math.PI * 2; positions.push(Math.cos(p1), Math.sin(p1), 1, Math.cos(p2), Math.sin(p2), 1); } geometry.setAttribute('position', new Float32BufferAttribute(positions, 3)); const material = new LineBasicMaterial({ fog: false, toneMapped: false }); this.cone = new LineSegments(geometry, material); this.add(this.cone); this.update(); } dispose() { this.cone.geometry.dispose(); this.cone.material.dispose(); } update() { this.light.updateWorldMatrix(true, false); this.light.target.updateWorldMatrix(true, false); const coneLength = this.light.distance ? this.light.distance : 1000; const coneWidth = coneLength * Math.tan(this.light.angle); this.cone.scale.set(coneWidth, coneWidth, coneLength); _vector$3.setFromMatrixPosition(this.light.target.matrixWorld); this.cone.lookAt(_vector$3); if (this.color !== undefined) { this.cone.material.color.set(this.color); } else { this.cone.material.color.copy(this.light.color); } } } const _vector$2 = /*@__PURE__*/new Vector3(); const _boneMatrix = /*@__PURE__*/new Matrix4(); const _matrixWorldInv = /*@__PURE__*/new Matrix4(); class SkeletonHelper extends LineSegments { constructor(object) { const bones = getBoneList(object); const geometry = new BufferGeometry(); const vertices = []; const colors = []; const color1 = new Color(0, 0, 1); const color2 = new Color(0, 1, 0); for (let i = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { vertices.push(0, 0, 0); vertices.push(0, 0, 0); colors.push(color1.r, color1.g, color1.b); colors.push(color2.r, color2.g, color2.b); } } geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3)); geometry.setAttribute('color', new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true }); super(geometry, material); this.isSkeletonHelper = true; this.type = 'SkeletonHelper'; this.root = object; this.bones = bones; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; } updateMatrixWorld(force) { const bones = this.bones; const geometry = this.geometry; const position = geometry.getAttribute('position'); _matrixWorldInv.copy(this.root.matrixWorld).invert(); for (let i = 0, j = 0; i < bones.length; i++) { const bone = bones[i]; if (bone.parent && bone.parent.isBone) { _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j, _vector$2.x, _vector$2.y, _vector$2.z); _boneMatrix.multiplyMatrices(_matrixWorldInv, bone.parent.matrixWorld); _vector$2.setFromMatrixPosition(_boneMatrix); position.setXYZ(j + 1, _vector$2.x, _vector$2.y, _vector$2.z); j += 2; } } geometry.getAttribute('position').needsUpdate = true; super.updateMatrixWorld(force); } dispose() { this.geometry.dispose(); this.material.dispose(); } } function getBoneList(object) { const boneList = []; if (object.isBone === true) { boneList.push(object); } for (let i = 0; i < object.children.length; i++) { boneList.push.apply(boneList, getBoneList(object.children[i])); } return boneList; } class PointLightHelper extends Mesh { constructor(light, sphereSize, color) { const geometry = new SphereGeometry(sphereSize, 4, 2); const material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false }); super(geometry, material); this.light = light; this.color = color; this.type = 'PointLightHelper'; this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; this.update(); /* // TODO: delete this comment? const distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); const d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } dispose() { this.geometry.dispose(); this.material.dispose(); } update() { this.light.updateWorldMatrix(true, false); if (this.color !== undefined) { this.material.color.set(this.color); } else { this.material.color.copy(this.light.color); } /* const d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ } } const _vector$1 = /*@__PURE__*/new Vector3(); const _color1 = /*@__PURE__*/new Color(); const _color2 = /*@__PURE__*/new Color(); class HemisphereLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; this.type = 'HemisphereLightHelper'; const geometry = new OctahedronGeometry(size); geometry.rotateY(Math.PI * 0.5); this.material = new MeshBasicMaterial({ wireframe: true, fog: false, toneMapped: false }); if (this.color === undefined) this.material.vertexColors = true; const position = geometry.getAttribute('position'); const colors = new Float32Array(position.count * 3); geometry.setAttribute('color', new BufferAttribute(colors, 3)); this.add(new Mesh(geometry, this.material)); this.update(); } dispose() { this.children[0].geometry.dispose(); this.children[0].material.dispose(); } update() { const mesh = this.children[0]; if (this.color !== undefined) { this.material.color.set(this.color); } else { const colors = mesh.geometry.getAttribute('color'); _color1.copy(this.light.color); _color2.copy(this.light.groundColor); for (let i = 0, l = colors.count; i < l; i++) { const color = i < l / 2 ? _color1 : _color2; colors.setXYZ(i, color.r, color.g, color.b); } colors.needsUpdate = true; } this.light.updateWorldMatrix(true, false); mesh.lookAt(_vector$1.setFromMatrixPosition(this.light.matrixWorld).negate()); } } class GridHelper extends LineSegments { constructor(size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const center = divisions / 2; const step = size / divisions; const halfSize = size / 2; const vertices = [], colors = []; for (let i = 0, j = 0, k = -halfSize; i <= divisions; i++, k += step) { vertices.push(-halfSize, 0, k, halfSize, 0, k); vertices.push(k, 0, -halfSize, k, 0, halfSize); const color = i === center ? color1 : color2; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; color.toArray(colors, j); j += 3; } const geometry = new BufferGeometry(); geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3)); geometry.setAttribute('color', new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = 'GridHelper'; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class PolarGridHelper extends LineSegments { constructor(radius = 10, sectors = 16, rings = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888) { color1 = new Color(color1); color2 = new Color(color2); const vertices = []; const colors = []; // create the sectors if (sectors > 1) { for (let i = 0; i < sectors; i++) { const v = i / sectors * (Math.PI * 2); const x = Math.sin(v) * radius; const z = Math.cos(v) * radius; vertices.push(0, 0, 0); vertices.push(x, 0, z); const color = i & 1 ? color1 : color2; colors.push(color.r, color.g, color.b); colors.push(color.r, color.g, color.b); } } // create the rings for (let i = 0; i < rings; i++) { const color = i & 1 ? color1 : color2; const r = radius - radius / rings * i; for (let j = 0; j < divisions; j++) { // first vertex let v = j / divisions * (Math.PI * 2); let x = Math.sin(v) * r; let z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); // second vertex v = (j + 1) / divisions * (Math.PI * 2); x = Math.sin(v) * r; z = Math.cos(v) * r; vertices.push(x, 0, z); colors.push(color.r, color.g, color.b); } } const geometry = new BufferGeometry(); geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3)); geometry.setAttribute('color', new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = 'PolarGridHelper'; } dispose() { this.geometry.dispose(); this.material.dispose(); } } const _v1 = /*@__PURE__*/new Vector3(); const _v2 = /*@__PURE__*/new Vector3(); const _v3 = /*@__PURE__*/new Vector3(); class DirectionalLightHelper extends Object3D { constructor(light, size, color) { super(); this.light = light; this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; this.color = color; this.type = 'DirectionalLightHelper'; if (size === undefined) size = 1; let geometry = new BufferGeometry(); geometry.setAttribute('position', new Float32BufferAttribute([-size, size, 0, size, size, 0, size, -size, 0, -size, -size, 0, -size, size, 0], 3)); const material = new LineBasicMaterial({ fog: false, toneMapped: false }); this.lightPlane = new Line(geometry, material); this.add(this.lightPlane); geometry = new BufferGeometry(); geometry.setAttribute('position', new Float32BufferAttribute([0, 0, 0, 0, 0, 1], 3)); this.targetLine = new Line(geometry, material); this.add(this.targetLine); this.update(); } dispose() { this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); this.targetLine.material.dispose(); } update() { this.light.updateWorldMatrix(true, false); this.light.target.updateWorldMatrix(true, false); _v1.setFromMatrixPosition(this.light.matrixWorld); _v2.setFromMatrixPosition(this.light.target.matrixWorld); _v3.subVectors(_v2, _v1); this.lightPlane.lookAt(_v2); if (this.color !== undefined) { this.lightPlane.material.color.set(this.color); this.targetLine.material.color.set(this.color); } else { this.lightPlane.material.color.copy(this.light.color); this.targetLine.material.color.copy(this.light.color); } this.targetLine.lookAt(_v2); this.targetLine.scale.z = _v3.length(); } } const _vector = /*@__PURE__*/new Vector3(); const _camera = /*@__PURE__*/new Camera(); /** * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * https://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html */ class CameraHelper extends LineSegments { constructor(camera) { const geometry = new BufferGeometry(); const material = new LineBasicMaterial({ color: 0xffffff, vertexColors: true, toneMapped: false }); const vertices = []; const colors = []; const pointMap = {}; // near addLine('n1', 'n2'); addLine('n2', 'n4'); addLine('n4', 'n3'); addLine('n3', 'n1'); // far addLine('f1', 'f2'); addLine('f2', 'f4'); addLine('f4', 'f3'); addLine('f3', 'f1'); // sides addLine('n1', 'f1'); addLine('n2', 'f2'); addLine('n3', 'f3'); addLine('n4', 'f4'); // cone addLine('p', 'n1'); addLine('p', 'n2'); addLine('p', 'n3'); addLine('p', 'n4'); // up addLine('u1', 'u2'); addLine('u2', 'u3'); addLine('u3', 'u1'); // target addLine('c', 't'); addLine('p', 'c'); // cross addLine('cn1', 'cn2'); addLine('cn3', 'cn4'); addLine('cf1', 'cf2'); addLine('cf3', 'cf4'); function addLine(a, b) { addPoint(a); addPoint(b); } function addPoint(id) { vertices.push(0, 0, 0); colors.push(0, 0, 0); if (pointMap[id] === undefined) { pointMap[id] = []; } pointMap[id].push(vertices.length / 3 - 1); } geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3)); geometry.setAttribute('color', new Float32BufferAttribute(colors, 3)); super(geometry, material); this.type = 'CameraHelper'; this.camera = camera; if (this.camera.updateProjectionMatrix) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); // colors const colorFrustum = new Color(0xffaa00); const colorCone = new Color(0xff0000); const colorUp = new Color(0x00aaff); const colorTarget = new Color(0xffffff); const colorCross = new Color(0x333333); this.setColors(colorFrustum, colorCone, colorUp, colorTarget, colorCross); } setColors(frustum, cone, up, target, cross) { const geometry = this.geometry; const colorAttribute = geometry.getAttribute('color'); // near colorAttribute.setXYZ(0, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(1, frustum.r, frustum.g, frustum.b); // n1, n2 colorAttribute.setXYZ(2, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(3, frustum.r, frustum.g, frustum.b); // n2, n4 colorAttribute.setXYZ(4, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(5, frustum.r, frustum.g, frustum.b); // n4, n3 colorAttribute.setXYZ(6, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(7, frustum.r, frustum.g, frustum.b); // n3, n1 // far colorAttribute.setXYZ(8, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(9, frustum.r, frustum.g, frustum.b); // f1, f2 colorAttribute.setXYZ(10, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(11, frustum.r, frustum.g, frustum.b); // f2, f4 colorAttribute.setXYZ(12, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(13, frustum.r, frustum.g, frustum.b); // f4, f3 colorAttribute.setXYZ(14, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(15, frustum.r, frustum.g, frustum.b); // f3, f1 // sides colorAttribute.setXYZ(16, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(17, frustum.r, frustum.g, frustum.b); // n1, f1 colorAttribute.setXYZ(18, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(19, frustum.r, frustum.g, frustum.b); // n2, f2 colorAttribute.setXYZ(20, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(21, frustum.r, frustum.g, frustum.b); // n3, f3 colorAttribute.setXYZ(22, frustum.r, frustum.g, frustum.b); colorAttribute.setXYZ(23, frustum.r, frustum.g, frustum.b); // n4, f4 // cone colorAttribute.setXYZ(24, cone.r, cone.g, cone.b); colorAttribute.setXYZ(25, cone.r, cone.g, cone.b); // p, n1 colorAttribute.setXYZ(26, cone.r, cone.g, cone.b); colorAttribute.setXYZ(27, cone.r, cone.g, cone.b); // p, n2 colorAttribute.setXYZ(28, cone.r, cone.g, cone.b); colorAttribute.setXYZ(29, cone.r, cone.g, cone.b); // p, n3 colorAttribute.setXYZ(30, cone.r, cone.g, cone.b); colorAttribute.setXYZ(31, cone.r, cone.g, cone.b); // p, n4 // up colorAttribute.setXYZ(32, up.r, up.g, up.b); colorAttribute.setXYZ(33, up.r, up.g, up.b); // u1, u2 colorAttribute.setXYZ(34, up.r, up.g, up.b); colorAttribute.setXYZ(35, up.r, up.g, up.b); // u2, u3 colorAttribute.setXYZ(36, up.r, up.g, up.b); colorAttribute.setXYZ(37, up.r, up.g, up.b); // u3, u1 // target colorAttribute.setXYZ(38, target.r, target.g, target.b); colorAttribute.setXYZ(39, target.r, target.g, target.b); // c, t colorAttribute.setXYZ(40, cross.r, cross.g, cross.b); colorAttribute.setXYZ(41, cross.r, cross.g, cross.b); // p, c // cross colorAttribute.setXYZ(42, cross.r, cross.g, cross.b); colorAttribute.setXYZ(43, cross.r, cross.g, cross.b); // cn1, cn2 colorAttribute.setXYZ(44, cross.r, cross.g, cross.b); colorAttribute.setXYZ(45, cross.r, cross.g, cross.b); // cn3, cn4 colorAttribute.setXYZ(46, cross.r, cross.g, cross.b); colorAttribute.setXYZ(47, cross.r, cross.g, cross.b); // cf1, cf2 colorAttribute.setXYZ(48, cross.r, cross.g, cross.b); colorAttribute.setXYZ(49, cross.r, cross.g, cross.b); // cf3, cf4 colorAttribute.needsUpdate = true; } update() { const geometry = this.geometry; const pointMap = this.pointMap; const w = 1, h = 1; // we need just camera projection matrix inverse // world matrix must be identity _camera.projectionMatrixInverse.copy(this.camera.projectionMatrixInverse); // center / target setPoint('c', pointMap, geometry, _camera, 0, 0, -1); setPoint('t', pointMap, geometry, _camera, 0, 0, 1); // near setPoint('n1', pointMap, geometry, _camera, -w, -h, -1); setPoint('n2', pointMap, geometry, _camera, w, -h, -1); setPoint('n3', pointMap, geometry, _camera, -w, h, -1); setPoint('n4', pointMap, geometry, _camera, w, h, -1); // far setPoint('f1', pointMap, geometry, _camera, -w, -h, 1); setPoint('f2', pointMap, geometry, _camera, w, -h, 1); setPoint('f3', pointMap, geometry, _camera, -w, h, 1); setPoint('f4', pointMap, geometry, _camera, w, h, 1); // up setPoint('u1', pointMap, geometry, _camera, w * 0.7, h * 1.1, -1); setPoint('u2', pointMap, geometry, _camera, -w * 0.7, h * 1.1, -1); setPoint('u3', pointMap, geometry, _camera, 0, h * 2, -1); // cross setPoint('cf1', pointMap, geometry, _camera, -w, 0, 1); setPoint('cf2', pointMap, geometry, _camera, w, 0, 1); setPoint('cf3', pointMap, geometry, _camera, 0, -h, 1); setPoint('cf4', pointMap, geometry, _camera, 0, h, 1); setPoint('cn1', pointMap, geometry, _camera, -w, 0, -1); setPoint('cn2', pointMap, geometry, _camera, w, 0, -1); setPoint('cn3', pointMap, geometry, _camera, 0, -h, -1); setPoint('cn4', pointMap, geometry, _camera, 0, h, -1); geometry.getAttribute('position').needsUpdate = true; } dispose() { this.geometry.dispose(); this.material.dispose(); } } function setPoint(point, pointMap, geometry, camera, x, y, z) { _vector.set(x, y, z).unproject(camera); const points = pointMap[point]; if (points !== undefined) { const position = geometry.getAttribute('position'); for (let i = 0, l = points.length; i < l; i++) { position.setXYZ(points[i], _vector.x, _vector.y, _vector.z); } } } const _box = /*@__PURE__*/new Box3(); class BoxHelper extends LineSegments { constructor(object, color = 0xffff00) { const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); const positions = new Float32Array(8 * 3); const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute('position', new BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.object = object; this.type = 'BoxHelper'; this.matrixAutoUpdate = false; this.update(); } update(object) { if (object !== undefined) { console.warn('THREE.BoxHelper: .update() has no longer arguments.'); } if (this.object !== undefined) { _box.setFromObject(this.object); } if (_box.isEmpty()) return; const min = _box.min; const max = _box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ const position = this.geometry.attributes.position; const array = position.array; array[0] = max.x; array[1] = max.y; array[2] = max.z; array[3] = min.x; array[4] = max.y; array[5] = max.z; array[6] = min.x; array[7] = min.y; array[8] = max.z; array[9] = max.x; array[10] = min.y; array[11] = max.z; array[12] = max.x; array[13] = max.y; array[14] = min.z; array[15] = min.x; array[16] = max.y; array[17] = min.z; array[18] = min.x; array[19] = min.y; array[20] = min.z; array[21] = max.x; array[22] = min.y; array[23] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); } setFromObject(object) { this.object = object; this.update(); return this; } copy(source, recursive) { super.copy(source, recursive); this.object = source.object; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class Box3Helper extends LineSegments { constructor(box, color = 0xffff00) { const indices = new Uint16Array([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7]); const positions = [1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1]; const geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute('position', new Float32BufferAttribute(positions, 3)); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.box = box; this.type = 'Box3Helper'; this.geometry.computeBoundingSphere(); } updateMatrixWorld(force) { const box = this.box; if (box.isEmpty()) return; box.getCenter(this.position); box.getSize(this.scale); this.scale.multiplyScalar(0.5); super.updateMatrixWorld(force); } dispose() { this.geometry.dispose(); this.material.dispose(); } } class PlaneHelper extends Line { constructor(plane, size = 1, hex = 0xffff00) { const color = hex; const positions = [1, -1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0, 1, 1, 0]; const geometry = new BufferGeometry(); geometry.setAttribute('position', new Float32BufferAttribute(positions, 3)); geometry.computeBoundingSphere(); super(geometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.type = 'PlaneHelper'; this.plane = plane; this.size = size; const positions2 = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]; const geometry2 = new BufferGeometry(); geometry2.setAttribute('position', new Float32BufferAttribute(positions2, 3)); geometry2.computeBoundingSphere(); this.add(new Mesh(geometry2, new MeshBasicMaterial({ color: color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false }))); } updateMatrixWorld(force) { this.position.set(0, 0, 0); this.scale.set(0.5 * this.size, 0.5 * this.size, 1); this.lookAt(this.plane.normal); this.translateZ(-this.plane.constant); super.updateMatrixWorld(force); } dispose() { this.geometry.dispose(); this.material.dispose(); this.children[0].geometry.dispose(); this.children[0].material.dispose(); } } const _axis = /*@__PURE__*/new Vector3(); let _lineGeometry, _coneGeometry; class ArrowHelper extends Object3D { // dir is assumed to be normalized constructor(dir = new Vector3(0, 0, 1), origin = new Vector3(0, 0, 0), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2) { super(); this.type = 'ArrowHelper'; if (_lineGeometry === undefined) { _lineGeometry = new BufferGeometry(); _lineGeometry.setAttribute('position', new Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3)); _coneGeometry = new CylinderGeometry(0, 0.5, 1, 5, 1); _coneGeometry.translate(0, -0.5, 0); } this.position.copy(origin); this.line = new Line(_lineGeometry, new LineBasicMaterial({ color: color, toneMapped: false })); this.line.matrixAutoUpdate = false; this.add(this.line); this.cone = new Mesh(_coneGeometry, new MeshBasicMaterial({ color: color, toneMapped: false })); this.cone.matrixAutoUpdate = false; this.add(this.cone); this.setDirection(dir); this.setLength(length, headLength, headWidth); } setDirection(dir) { // dir is assumed to be normalized if (dir.y > 0.99999) { this.quaternion.set(0, 0, 0, 1); } else if (dir.y < -0.99999) { this.quaternion.set(1, 0, 0, 0); } else { _axis.set(dir.z, 0, -dir.x).normalize(); const radians = Math.acos(dir.y); this.quaternion.setFromAxisAngle(_axis, radians); } } setLength(length, headLength = length * 0.2, headWidth = headLength * 0.2) { this.line.scale.set(1, Math.max(0.0001, length - headLength), 1); // see #17458 this.line.updateMatrix(); this.cone.scale.set(headWidth, headLength, headWidth); this.cone.position.y = length; this.cone.updateMatrix(); } setColor(color) { this.line.material.color.set(color); this.cone.material.color.set(color); } copy(source) { super.copy(source, false); this.line.copy(source.line); this.cone.copy(source.cone); return this; } dispose() { this.line.geometry.dispose(); this.line.material.dispose(); this.cone.geometry.dispose(); this.cone.material.dispose(); } } class AxesHelper extends LineSegments { constructor(size = 1) { const vertices = [0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size]; const colors = [1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1]; const geometry = new BufferGeometry(); geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3)); geometry.setAttribute('color', new Float32BufferAttribute(colors, 3)); const material = new LineBasicMaterial({ vertexColors: true, toneMapped: false }); super(geometry, material); this.type = 'AxesHelper'; } setColors(xAxisColor, yAxisColor, zAxisColor) { const color = new Color(); const array = this.geometry.attributes.color.array; color.set(xAxisColor); color.toArray(array, 0); color.toArray(array, 3); color.set(yAxisColor); color.toArray(array, 6); color.toArray(array, 9); color.set(zAxisColor); color.toArray(array, 12); color.toArray(array, 15); this.geometry.attributes.color.needsUpdate = true; return this; } dispose() { this.geometry.dispose(); this.material.dispose(); } } class ShapePath { constructor() { this.type = 'ShapePath'; this.color = new Color(); this.subPaths = []; this.currentPath = null; } moveTo(x, y) { this.currentPath = new Path(); this.subPaths.push(this.currentPath); this.currentPath.moveTo(x, y); return this; } lineTo(x, y) { this.currentPath.lineTo(x, y); return this; } quadraticCurveTo(aCPx, aCPy, aX, aY) { this.currentPath.quadraticCurveTo(aCPx, aCPy, aX, aY); return this; } bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) { this.currentPath.bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY); return this; } splineThru(pts) { this.currentPath.splineThru(pts); return this; } toShapes(isCCW) { function toShapesNoHoles(inSubpaths) { const shapes = []; for (let i = 0, l = inSubpaths.length; i < l; i++) { const tmpPath = inSubpaths[i]; const tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); } return shapes; } function isPointInsidePolygon(inPt, inPolygon) { const polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line let inside = false; for (let p = polyLen - 1, q = 0; q < polyLen; p = q++) { let edgeLowPt = inPolygon[p]; let edgeHighPt = inPolygon[q]; let edgeDx = edgeHighPt.x - edgeLowPt.x; let edgeDy = edgeHighPt.y - edgeLowPt.y; if (Math.abs(edgeDy) > Number.EPSILON) { // not parallel if (edgeDy < 0) { edgeLowPt = inPolygon[q]; edgeDx = -edgeDx; edgeHighPt = inPolygon[p]; edgeDy = -edgeDy; } if (inPt.y < edgeLowPt.y || inPt.y > edgeHighPt.y) continue; if (inPt.y === edgeLowPt.y) { if (inPt.x === edgeLowPt.x) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn't count !!! } else { const perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); if (perpEdge === 0) return true; // inPt is on contour ? if (perpEdge < 0) continue; inside = !inside; // true intersection left of inPt } } else { // parallel or collinear if (inPt.y !== edgeLowPt.y) continue; // parallel // edge lies on the same horizontal line as inPt if (edgeHighPt.x <= inPt.x && inPt.x <= edgeLowPt.x || edgeLowPt.x <= inPt.x && inPt.x <= edgeHighPt.x) return true; // inPt: Point on contour ! // continue; } } return inside; } const isClockWise = ShapeUtils.isClockWise; const subPaths = this.subPaths; if (subPaths.length === 0) return []; let solid, tmpPath, tmpShape; const shapes = []; if (subPaths.length === 1) { tmpPath = subPaths[0]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push(tmpShape); return shapes; } let holesFirst = !isClockWise(subPaths[0].getPoints()); holesFirst = isCCW ? !holesFirst : holesFirst; // console.log("Holes first", holesFirst); const betterShapeHoles = []; const newShapes = []; let newShapeHoles = []; let mainIdx = 0; let tmpPoints; newShapes[mainIdx] = undefined; newShapeHoles[mainIdx] = []; for (let i = 0, l = subPaths.length; i < l; i++) { tmpPath = subPaths[i]; tmpPoints = tmpPath.getPoints(); solid = isClockWise(tmpPoints); solid = isCCW ? !solid : solid; if (solid) { if (!holesFirst && newShapes[mainIdx]) mainIdx++; newShapes[mainIdx] = { s: new Shape(), p: tmpPoints }; newShapes[mainIdx].s.curves = tmpPath.curves; if (holesFirst) mainIdx++; newShapeHoles[mainIdx] = []; //console.log('cw', i); } else { newShapeHoles[mainIdx].push({ h: tmpPath, p: tmpPoints[0] }); //console.log('ccw', i); } } // only Holes? -> probably all Shapes with wrong orientation if (!newShapes[0]) return toShapesNoHoles(subPaths); if (newShapes.length > 1) { let ambiguous = false; let toChange = 0; for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { betterShapeHoles[sIdx] = []; } for (let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx++) { const sho = newShapeHoles[sIdx]; for (let hIdx = 0; hIdx < sho.length; hIdx++) { const ho = sho[hIdx]; let hole_unassigned = true; for (let s2Idx = 0; s2Idx < newShapes.length; s2Idx++) { if (isPointInsidePolygon(ho.p, newShapes[s2Idx].p)) { if (sIdx !== s2Idx) toChange++; if (hole_unassigned) { hole_unassigned = false; betterShapeHoles[s2Idx].push(ho); } else { ambiguous = true; } } } if (hole_unassigned) { betterShapeHoles[sIdx].push(ho); } } } if (toChange > 0 && ambiguous === false) { newShapeHoles = betterShapeHoles; } } let tmpHoles; for (let i = 0, il = newShapes.length; i < il; i++) { tmpShape = newShapes[i].s; shapes.push(tmpShape); tmpHoles = newShapeHoles[i]; for (let j = 0, jl = tmpHoles.length; j < jl; j++) { tmpShape.holes.push(tmpHoles[j].h); } } //console.log("shape", shapes); return shapes; } } if (typeof __THREE_DEVTOOLS__ !== 'undefined') { __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent('register', { detail: { revision: REVISION } })); } if (typeof window !== 'undefined') { if (window.__THREE__) { console.warn('WARNING: Multiple instances of Three.js being imported.'); } else { window.__THREE__ = REVISION; } } /***/ }), /***/ 1100: /*!*******************************************************************!*\ !*** ./node_modules/three/examples/jsm/controls/OrbitControls.js ***! \*******************************************************************/ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "OrbitControls": () => (/* binding */ OrbitControls) /* harmony export */ }); /* harmony import */ var three__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! three */ 2845); // OrbitControls performs orbiting, dollying (zooming), and panning. // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). // // Orbit - left mouse / touch: one-finger move // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish // Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move const _changeEvent = { type: 'change' }; const _startEvent = { type: 'start' }; const _endEvent = { type: 'end' }; class OrbitControls extends three__WEBPACK_IMPORTED_MODULE_0__.EventDispatcher { constructor(object, domElement) { super(); this.object = object; this.domElement = domElement; this.domElement.style.touchAction = 'none'; // disable touch scroll // Set to false to disable this control this.enabled = true; // "target" sets the location of focus, where the object orbits around this.target = new three__WEBPACK_IMPORTED_MODULE_0__.Vector3(); // How far you can dolly in and out ( PerspectiveCamera only ) this.minDistance = 0; this.maxDistance = Infinity; // How far you can zoom in and out ( OrthographicCamera only ) this.minZoom = 0; this.maxZoom = Infinity; // How far you can orbit vertically, upper and lower limits. // Range is 0 to Math.PI radians. this.minPolarAngle = 0; // radians this.maxPolarAngle = Math.PI; // radians // How far you can orbit horizontally, upper and lower limits. // If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI ) this.minAzimuthAngle = -Infinity; // radians this.maxAzimuthAngle = Infinity; // radians // Set to true to enable damping (inertia) // If damping is enabled, you must call controls.update() in your animation loop this.enableDamping = false; this.dampingFactor = 0.05; // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. // Set to false to disable zooming this.enableZoom = true; this.zoomSpeed = 1.0; // Set to false to disable rotating this.enableRotate = true; this.rotateSpeed = 1.0; // Set to false to disable panning this.enablePan = true; this.panSpeed = 1.0; this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up this.keyPanSpeed = 7.0; // pixels moved per arrow key push // Set to true to automatically rotate around the target // If auto-rotate is enabled, you must call controls.update() in your animation loop this.autoRotate = false; this.autoRotateSpeed = 2.0; // 30 seconds per orbit when fps is 60 // The four arrow keys this.keys = { LEFT: 'ArrowLeft', UP: 'ArrowUp', RIGHT: 'ArrowRight', BOTTOM: 'ArrowDown' }; // Mouse buttons this.mouseButtons = { LEFT: three__WEBPACK_IMPORTED_MODULE_0__.MOUSE.ROTATE, MIDDLE: three__WEBPACK_IMPORTED_MODULE_0__.MOUSE.DOLLY, RIGHT: three__WEBPACK_IMPORTED_MODULE_0__.MOUSE.PAN }; // Touch fingers this.touches = { ONE: three__WEBPACK_IMPORTED_MODULE_0__.TOUCH.ROTATE, TWO: three__WEBPACK_IMPORTED_MODULE_0__.TOUCH.DOLLY_PAN }; // for reset this.target0 = this.target.clone(); this.position0 = this.object.position.clone(); this.zoom0 = this.object.zoom; // the target DOM element for key events this._domElementKeyEvents = null; // // public methods // this.getPolarAngle = function () { return spherical.phi; }; this.getAzimuthalAngle = function () { return spherical.theta; }; this.getDistance = function () { return this.object.position.distanceTo(this.target); }; this.listenToKeyEvents = function (domElement) { domElement.addEventListener('keydown', onKeyDown); this._domElementKeyEvents = domElement; }; this.stopListenToKeyEvents = function () { this._domElementKeyEvents.removeEventListener('keydown', onKeyDown); this._domElementKeyEvents = null; }; this.saveState = function () { scope.target0.copy(scope.target); scope.position0.copy(scope.object.position); scope.zoom0 = scope.object.zoom; }; this.reset = function () { scope.target.copy(scope.target0); scope.object.position.copy(scope.position0); scope.object.zoom = scope.zoom0; scope.object.updateProjectionMatrix(); scope.dispatchEvent(_changeEvent); scope.update(); state = STATE.NONE; }; // this method is exposed, but perhaps it would be better if we can make it private... this.update = function () { const offset = new three__WEBPACK_IMPORTED_MODULE_0__.Vector3(); // so camera.up is the orbit axis const quat = new three__WEBPACK_IMPORTED_MODULE_0__.Quaternion().setFromUnitVectors(object.up, new three__WEBPACK_IMPORTED_MODULE_0__.Vector3(0, 1, 0)); const quatInverse = quat.clone().invert(); const lastPosition = new three__WEBPACK_IMPORTED_MODULE_0__.Vector3(); const lastQuaternion = new three__WEBPACK_IMPORTED_MODULE_0__.Quaternion(); const lastTargetPosition = new three__WEBPACK_IMPORTED_MODULE_0__.Vector3(); const twoPI = 2 * Math.PI; return function update() { const position = scope.object.position; offset.copy(position).sub(scope.target); // rotate offset to "y-axis-is-up" space offset.applyQuaternion(quat); // angle from z-axis around y-axis spherical.setFromVector3(offset); if (scope.autoRotate && state === STATE.NONE) { rotateLeft(getAutoRotationAngle()); } if (scope.enableDamping) { spherical.theta += sphericalDelta.theta * scope.dampingFactor; spherical.phi += sphericalDelta.phi * scope.dampingFactor; } else { spherical.theta += sphericalDelta.theta; spherical.phi += sphericalDelta.phi; } // restrict theta to be between desired limits let min = scope.minAzimuthAngle; let max = scope.maxAzimuthAngle; if (isFinite(min) && isFinite(max)) { if (min < -Math.PI) min += twoPI;else if (min > Math.PI) min -= twoPI; if (max < -Math.PI) max += twoPI;else if (max > Math.PI) max -= twoPI; if (min <= max) { spherical.theta = Math.max(min, Math.min(max, spherical.theta)); } else { spherical.theta = spherical.theta > (min + max) / 2 ? Math.max(min, spherical.theta) : Math.min(max, spherical.theta); } } // restrict phi to be between desired limits spherical.phi = Math.max(scope.minPolarAngle, Math.min(scope.maxPolarAngle, spherical.phi)); spherical.makeSafe(); spherical.radius *= scale; // restrict radius to be between desired limits spherical.radius = Math.max(scope.minDistance, Math.min(scope.maxDistance, spherical.radius)); // move target to panned location if (scope.enableDamping === true) { scope.target.addScaledVector(panOffset, scope.dampingFactor); } else { scope.target.add(panOffset); } offset.setFromSpherical(spherical); // rotate offset back to "camera-up-vector-is-up" space offset.applyQuaternion(quatInverse); position.copy(scope.target).add(offset); scope.object.lookAt(scope.target); if (scope.enableDamping === true) { sphericalDelta.theta *= 1 - scope.dampingFactor; sphericalDelta.phi *= 1 - scope.dampingFactor; panOffset.multiplyScalar(1 - scope.dampingFactor); } else { sphericalDelta.set(0, 0, 0); panOffset.set(0, 0, 0); } scale = 1; // update condition is: // min(camera displacement, camera rotation in radians)^2 > EPS // using small-angle approximation cos(x/2) = 1 - x^2 / 8 if (zoomChanged || lastPosition.distanceToSquared(scope.object.position) > EPS || 8 * (1 - lastQuaternion.dot(scope.object.quaternion)) > EPS || lastTargetPosition.distanceToSquared(scope.target) > 0) { scope.dispatchEvent(_changeEvent); lastPosition.copy(scope.object.position); lastQuaternion.copy(scope.object.quaternion); lastTargetPosition.copy(scope.target); zoomChanged = false; return true; } return false; }; }(); this.dispose = function () { scope.domElement.removeEventListener('contextmenu', onContextMenu); scope.domElement.removeEventListener('pointerdown', onPointerDown); scope.domElement.removeEventListener('pointercancel', onPointerUp); scope.domElement.removeEventListener('wheel', onMouseWheel); scope.domElement.removeEventListener('pointermove', onPointerMove); scope.domElement.removeEventListener('pointerup', onPointerUp); if (scope._domElementKeyEvents !== null) { scope._domElementKeyEvents.removeEventListener('keydown', onKeyDown); scope._domElementKeyEvents = null; } //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? }; // // internals // const scope = this; const STATE = { NONE: -1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_PAN: 4, TOUCH_DOLLY_PAN: 5, TOUCH_DOLLY_ROTATE: 6 }; let state = STATE.NONE; const EPS = 0.000001; // current position in spherical coordinates const spherical = new three__WEBPACK_IMPORTED_MODULE_0__.Spherical(); const sphericalDelta = new three__WEBPACK_IMPORTED_MODULE_0__.Spherical(); let scale = 1; const panOffset = new three__WEBPACK_IMPORTED_MODULE_0__.Vector3(); let zoomChanged = false; const rotateStart = new three__WEBPACK_IMPORTED_MODULE_0__.Vector2(); const rotateEnd = new three__WEBPACK_IMPORTED_MODULE_0__.Vector2(); const rotateDelta = new three__WEBPACK_IMPORTED_MODULE_0__.Vector2(); const panStart = new three__WEBPACK_IMPORTED_MODULE_0__.Vector2(); const panEnd = new three__WEBPACK_IMPORTED_MODULE_0__.Vector2(); const panDelta = new three__WEBPACK_IMPORTED_MODULE_0__.Vector2(); const dollyStart = new three__WEBPACK_IMPORTED_MODULE_0__.Vector2(); const dollyEnd = new three__WEBPACK_IMPORTED_MODULE_0__.Vector2(); const dollyDelta = new three__WEBPACK_IMPORTED_MODULE_0__.Vector2(); const pointers = []; const pointerPositions = {}; function getAutoRotationAngle() { return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; } function getZoomScale() { return Math.pow(0.95, scope.zoomSpeed); } function rotateLeft(angle) { sphericalDelta.theta -= angle; } function rotateUp(angle) { sphericalDelta.phi -= angle; } const panLeft = function () { const v = new three__WEBPACK_IMPORTED_MODULE_0__.Vector3(); return function panLeft(distance, objectMatrix) { v.setFromMatrixColumn(objectMatrix, 0); // get X column of objectMatrix v.multiplyScalar(-distance); panOffset.add(v); }; }(); const panUp = function () { const v = new three__WEBPACK_IMPORTED_MODULE_0__.Vector3(); return function panUp(distance, objectMatrix) { if (scope.screenSpacePanning === true) { v.setFromMatrixColumn(objectMatrix, 1); } else { v.setFromMatrixColumn(objectMatrix, 0); v.crossVectors(scope.object.up, v); } v.multiplyScalar(distance); panOffset.add(v); }; }(); // deltaX and deltaY are in pixels; right and down are positive const pan = function () { const offset = new three__WEBPACK_IMPORTED_MODULE_0__.Vector3(); return function pan(deltaX, deltaY) { const element = scope.domElement; if (scope.object.isPerspectiveCamera) { // perspective const position = scope.object.position; offset.copy(position).sub(scope.target); let targetDistance = offset.length(); // half of the fov is center to top of screen targetDistance *= Math.tan(scope.object.fov / 2 * Math.PI / 180.0); // we use only clientHeight here so aspect ratio does not distort speed panLeft(2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix); panUp(2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix); } else if (scope.object.isOrthographicCamera) { // orthographic panLeft(deltaX * (scope.object.right - scope.object.left) / scope.object.zoom / element.clientWidth, scope.object.matrix); panUp(deltaY * (scope.object.top - scope.object.bottom) / scope.object.zoom / element.clientHeight, scope.object.matrix); } else { // camera neither orthographic nor perspective console.warn('WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.'); scope.enablePan = false; } }; }(); function dollyOut(dollyScale) { if (scope.object.isPerspectiveCamera) { scale /= dollyScale; } else if (scope.object.isOrthographicCamera) { scope.object.zoom = Math.max(scope.minZoom, Math.min(scope.maxZoom, scope.object.zoom * dollyScale)); scope.object.updateProjectionMatrix(); zoomChanged = true; } else { console.warn('WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.'); scope.enableZoom = false; } } function dollyIn(dollyScale) { if (scope.object.isPerspectiveCamera) { scale *= dollyScale; } else if (scope.object.isOrthographicCamera) { scope.object.zoom = Math.max(scope.minZoom, Math.min(scope.maxZoom, scope.object.zoom / dollyScale)); scope.object.updateProjectionMatrix(); zoomChanged = true; } else { console.warn('WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.'); scope.enableZoom = false; } } // // event callbacks - update the object state // function handleMouseDownRotate(event) { rotateStart.set(event.clientX, event.clientY); } function handleMouseDownDolly(event) { dollyStart.set(event.clientX, event.clientY); } function handleMouseDownPan(event) { panStart.set(event.clientX, event.clientY); } function handleMouseMoveRotate(event) { rotateEnd.set(event.clientX, event.clientY); rotateDelta.subVectors(rotateEnd, rotateStart).multiplyScalar(scope.rotateSpeed); const element = scope.domElement; rotateLeft(2 * Math.PI * rotateDelta.x / element.clientHeight); // yes, height rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight); rotateStart.copy(rotateEnd); scope.update(); } function handleMouseMoveDolly(event) { dollyEnd.set(event.clientX, event.clientY); dollyDelta.subVectors(dollyEnd, dollyStart); if (dollyDelta.y > 0) { dollyOut(getZoomScale()); } else if (dollyDelta.y < 0) { dollyIn(getZoomScale()); } dollyStart.copy(dollyEnd); scope.update(); } function handleMouseMovePan(event) { panEnd.set(event.clientX, event.clientY); panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed); pan(panDelta.x, panDelta.y); panStart.copy(panEnd); scope.update(); } function handleMouseWheel(event) { if (event.deltaY < 0) { dollyIn(getZoomScale()); } else if (event.deltaY > 0) { dollyOut(getZoomScale()); } scope.update(); } function handleKeyDown(event) { let needsUpdate = false; switch (event.code) { case scope.keys.UP: if (event.ctrlKey || event.metaKey || event.shiftKey) { rotateUp(2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight); } else { pan(0, scope.keyPanSpeed); } needsUpdate = true; break; case scope.keys.BOTTOM: if (event.ctrlKey || event.metaKey || event.shiftKey) { rotateUp(-2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight); } else { pan(0, -scope.keyPanSpeed); } needsUpdate = true; break; case scope.keys.LEFT: if (event.ctrlKey || event.metaKey || event.shiftKey) { rotateLeft(2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight); } else { pan(scope.keyPanSpeed, 0); } needsUpdate = true; break; case scope.keys.RIGHT: if (event.ctrlKey || event.metaKey || event.shiftKey) { rotateLeft(-2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight); } else { pan(-scope.keyPanSpeed, 0); } needsUpdate = true; break; } if (needsUpdate) { // prevent the browser from scrolling on cursor keys event.preventDefault(); scope.update(); } } function handleTouchStartRotate() { if (pointers.length === 1) { rotateStart.set(pointers[0].pageX, pointers[0].pageY); } else { const x = 0.5 * (pointers[0].pageX + pointers[1].pageX); const y = 0.5 * (pointers[0].pageY + pointers[1].pageY); rotateStart.set(x, y); } } function handleTouchStartPan() { if (pointers.length === 1) { panStart.set(pointers[0].pageX, pointers[0].pageY); } else { const x = 0.5 * (pointers[0].pageX + pointers[1].pageX); const y = 0.5 * (pointers[0].pageY + pointers[1].pageY); panStart.set(x, y); } } function handleTouchStartDolly() { const dx = pointers[0].pageX - pointers[1].pageX; const dy = pointers[0].pageY - pointers[1].pageY; const distance = Math.sqrt(dx * dx + dy * dy); dollyStart.set(0, distance); } function handleTouchStartDollyPan() { if (scope.enableZoom) handleTouchStartDolly(); if (scope.enablePan) handleTouchStartPan(); } function handleTouchStartDollyRotate() { if (scope.enableZoom) handleTouchStartDolly(); if (scope.enableRotate) handleTouchStartRotate(); } function handleTouchMoveRotate(event) { if (pointers.length == 1) { rotateEnd.set(event.pageX, event.pageY); } else { const position = getSecondPointerPosition(event); const x = 0.5 * (event.pageX + position.x); const y = 0.5 * (event.pageY + position.y); rotateEnd.set(x, y); } rotateDelta.subVectors(rotateEnd, rotateStart).multiplyScalar(scope.rotateSpeed); const element = scope.domElement; rotateLeft(2 * Math.PI * rotateDelta.x / element.clientHeight); // yes, height rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight); rotateStart.copy(rotateEnd); } function handleTouchMovePan(event) { if (pointers.length === 1) { panEnd.set(event.pageX, event.pageY); } else { const position = getSecondPointerPosition(event); const x = 0.5 * (event.pageX + position.x); const y = 0.5 * (event.pageY + position.y); panEnd.set(x, y); } panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed); pan(panDelta.x, panDelta.y); panStart.copy(panEnd); } function handleTouchMoveDolly(event) { const position = getSecondPointerPosition(event); const dx = event.pageX - position.x; const dy = event.pageY - position.y; const distance = Math.sqrt(dx * dx + dy * dy); dollyEnd.set(0, distance); dollyDelta.set(0, Math.pow(dollyEnd.y / dollyStart.y, scope.zoomSpeed)); dollyOut(dollyDelta.y); dollyStart.copy(dollyEnd); } function handleTouchMoveDollyPan(event) { if (scope.enableZoom) handleTouchMoveDolly(event); if (scope.enablePan) handleTouchMovePan(event); } function handleTouchMoveDollyRotate(event) { if (scope.enableZoom) handleTouchMoveDolly(event); if (scope.enableRotate) handleTouchMoveRotate(event); } // // event handlers - FSM: listen for events and reset state // function onPointerDown(event) { if (scope.enabled === false) return; if (pointers.length === 0) { scope.domElement.setPointerCapture(event.pointerId); scope.domElement.addEventListener('pointermove', onPointerMove); scope.domElement.addEventListener('pointerup', onPointerUp); } // addPointer(event); if (event.pointerType === 'touch') { onTouchStart(event); } else { onMouseDown(event); } } function onPointerMove(event) { if (scope.enabled === false) return; if (event.pointerType === 'touch') { onTouchMove(event); } else { onMouseMove(event); } } function onPointerUp(event) { removePointer(event); if (pointers.length === 0) { scope.domElement.releasePointerCapture(event.pointerId); scope.domElement.removeEventListener('pointermove', onPointerMove); scope.domElement.removeEventListener('pointerup', onPointerUp); } scope.dispatchEvent(_endEvent); state = STATE.NONE; } function onMouseDown(event) { let mouseAction; switch (event.button) { case 0: mouseAction = scope.mouseButtons.LEFT; break; case 1: mouseAction = scope.mouseButtons.MIDDLE; break; case 2: mouseAction = scope.mouseButtons.RIGHT; break; default: mouseAction = -1; } switch (mouseAction) { case three__WEBPACK_IMPORTED_MODULE_0__.MOUSE.DOLLY: if (scope.enableZoom === false) return; handleMouseDownDolly(event); state = STATE.DOLLY; break; case three__WEBPACK_IMPORTED_MODULE_0__.MOUSE.ROTATE: if (event.ctrlKey || event.metaKey || event.shiftKey) { if (scope.enablePan === false) return; handleMouseDownPan(event); state = STATE.PAN; } else { if (scope.enableRotate === false) return; handleMouseDownRotate(event); state = STATE.ROTATE; } break; case three__WEBPACK_IMPORTED_MODULE_0__.MOUSE.PAN: if (event.ctrlKey || event.metaKey || event.shiftKey) { if (scope.enableRotate === false) return; handleMouseDownRotate(event); state = STATE.ROTATE; } else { if (scope.enablePan === false) return; handleMouseDownPan(event); state = STATE.PAN; } break; default: state = STATE.NONE; } if (state !== STATE.NONE) { scope.dispatchEvent(_startEvent); } } function onMouseMove(event) { switch (state) { case STATE.ROTATE: if (scope.enableRotate === false) return; handleMouseMoveRotate(event); break; case STATE.DOLLY: if (scope.enableZoom === false) return; handleMouseMoveDolly(event); break; case STATE.PAN: if (scope.enablePan === false) return; handleMouseMovePan(event); break; } } function onMouseWheel(event) { if (scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE) return; event.preventDefault(); scope.dispatchEvent(_startEvent); handleMouseWheel(event); scope.dispatchEvent(_endEvent); } function onKeyDown(event) { if (scope.enabled === false || scope.enablePan === false) return; handleKeyDown(event); } function onTouchStart(event) { trackPointer(event); switch (pointers.length) { case 1: switch (scope.touches.ONE) { case three__WEBPACK_IMPORTED_MODULE_0__.TOUCH.ROTATE: if (scope.enableRotate === false) return; handleTouchStartRotate(); state = STATE.TOUCH_ROTATE; break; case three__WEBPACK_IMPORTED_MODULE_0__.TOUCH.PAN: if (scope.enablePan === false) return; handleTouchStartPan(); state = STATE.TOUCH_PAN; break; default: state = STATE.NONE; } break; case 2: switch (scope.touches.TWO) { case three__WEBPACK_IMPORTED_MODULE_0__.TOUCH.DOLLY_PAN: if (scope.enableZoom === false && scope.enablePan === false) return; handleTouchStartDollyPan(); state = STATE.TOUCH_DOLLY_PAN; break; case three__WEBPACK_IMPORTED_MODULE_0__.TOUCH.DOLLY_ROTATE: if (scope.enableZoom === false && scope.enableRotate === false) return; handleTouchStartDollyRotate(); state = STATE.TOUCH_DOLLY_ROTATE; break; default: state = STATE.NONE; } break; default: state = STATE.NONE; } if (state !== STATE.NONE) { scope.dispatchEvent(_startEvent); } } function onTouchMove(event) { trackPointer(event); switch (state) { case STATE.TOUCH_ROTATE: if (scope.enableRotate === false) return; handleTouchMoveRotate(event); scope.update(); break; case STATE.TOUCH_PAN: if (scope.enablePan === false) return; handleTouchMovePan(event); scope.update(); break; case STATE.TOUCH_DOLLY_PAN: if (scope.enableZoom === false && scope.enablePan === false) return; handleTouchMoveDollyPan(event); scope.update(); break; case STATE.TOUCH_DOLLY_ROTATE: if (scope.enableZoom === false && scope.enableRotate === false) return; handleTouchMoveDollyRotate(event); scope.update(); break; default: state = STATE.NONE; } } function onContextMenu(event) { if (scope.enabled === false) return; event.preventDefault(); } function addPointer(event) { pointers.push(event); } function removePointer(event) { delete pointerPositions[event.pointerId]; for (let i = 0; i < pointers.length; i++) { if (pointers[i].pointerId == event.pointerId) { pointers.splice(i, 1); return; } } } function trackPointer(event) { let position = pointerPositions[event.pointerId]; if (position === undefined) { position = new three__WEBPACK_IMPORTED_MODULE_0__.Vector2(); pointerPositions[event.pointerId] = position; } position.set(event.pageX, event.pageY); } function getSecondPointerPosition(event) { const pointer = event.pointerId === pointers[0].pointerId ? pointers[1] : pointers[0]; return pointerPositions[pointer.pointerId]; } // scope.domElement.addEventListener('contextmenu', onContextMenu); scope.domElement.addEventListener('pointerdown', onPointerDown); scope.domElement.addEventListener('pointercancel', onPointerUp); scope.domElement.addEventListener('wheel', onMouseWheel, { passive: false }); // force an update at start this.update(); } } /***/ }), /***/ 8884: /*!********************************************************************!*\ !*** ./node_modules/three/examples/jsm/geometries/TextGeometry.js ***! \********************************************************************/ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "TextGeometry": () => (/* binding */ TextGeometry) /* harmony export */ }); /* harmony import */ var three__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! three */ 2845); /** * Text = 3D Text * * parameters = { * font: <THREE.Font>, // font * * size: <float>, // size of the text * height: <float>, // thickness to extrude text * curveSegments: <int>, // number of points on the curves * * bevelEnabled: <bool>, // turn on bevel * bevelThickness: <float>, // how deep into text bevel goes * bevelSize: <float>, // how far from text outline (including bevelOffset) is bevel * bevelOffset: <float> // how far from text outline does bevel start * } */ class TextGeometry extends three__WEBPACK_IMPORTED_MODULE_0__.ExtrudeGeometry { constructor(text, parameters = {}) { const font = parameters.font; if (font === undefined) { super(); // generate default extrude geometry } else { const shapes = font.generateShapes(text, parameters.size); // translate parameters to ExtrudeGeometry API parameters.depth = parameters.height !== undefined ? parameters.height : 50; // defaults if (parameters.bevelThickness === undefined) parameters.bevelThickness = 10; if (parameters.bevelSize === undefined) parameters.bevelSize = 8; if (parameters.bevelEnabled === undefined) parameters.bevelEnabled = false; super(shapes, parameters); } this.type = 'TextGeometry'; } } /***/ }), /***/ 2972: /*!***************************************************************!*\ !*** ./node_modules/three/examples/jsm/loaders/FontLoader.js ***! \***************************************************************/ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "Font": () => (/* binding */ Font), /* harmony export */ "FontLoader": () => (/* binding */ FontLoader) /* harmony export */ }); /* harmony import */ var three__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! three */ 2845); class FontLoader extends three__WEBPACK_IMPORTED_MODULE_0__.Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const loader = new three__WEBPACK_IMPORTED_MODULE_0__.FileLoader(this.manager); loader.setPath(this.path); loader.setRequestHeader(this.requestHeader); loader.setWithCredentials(this.withCredentials); loader.load(url, function (text) { const font = scope.parse(JSON.parse(text)); if (onLoad) onLoad(font); }, onProgress, onError); } parse(json) { return new Font(json); } } // class Font { constructor(data) { this.isFont = true; this.type = 'Font'; this.data = data; } generateShapes(text, size = 100) { const shapes = []; const paths = createPaths(text, size, this.data); for (let p = 0, pl = paths.length; p < pl; p++) { shapes.push(...paths[p].toShapes()); } return shapes; } } function createPaths(text, size, data) { const chars = Array.from(text); const scale = size / data.resolution; const line_height = (data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness) * scale; const paths = []; let offsetX = 0, offsetY = 0; for (let i = 0; i < chars.length; i++) { const char = chars[i]; if (char === '\n') { offsetX = 0; offsetY -= line_height; } else { const ret = createPath(char, scale, offsetX, offsetY, data); offsetX += ret.offsetX; paths.push(ret.path); } } return paths; } function createPath(char, scale, offsetX, offsetY, data) { const glyph = data.glyphs[char] || data.glyphs['?']; if (!glyph) { console.error('THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.'); return; } const path = new three__WEBPACK_IMPORTED_MODULE_0__.ShapePath(); let x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; if (glyph.o) { const outline = glyph._cachedOutline || (glyph._cachedOutline = glyph.o.split(' ')); for (let i = 0, l = outline.length; i < l;) { const action = outline[i++]; switch (action) { case 'm': // moveTo x = outline[i++] * scale + offsetX; y = outline[i++] * scale + offsetY; path.moveTo(x, y); break; case 'l': // lineTo x = outline[i++] * scale + offsetX; y = outline[i++] * scale + offsetY; path.lineTo(x, y); break; case 'q': // quadraticCurveTo cpx = outline[i++] * scale + offsetX; cpy = outline[i++] * scale + offsetY; cpx1 = outline[i++] * scale + offsetX; cpy1 = outline[i++] * scale + offsetY; path.quadraticCurveTo(cpx1, cpy1, cpx, cpy); break; case 'b': // bezierCurveTo cpx = outline[i++] * scale + offsetX; cpy = outline[i++] * scale + offsetY; cpx1 = outline[i++] * scale + offsetX; cpy1 = outline[i++] * scale + offsetY; cpx2 = outline[i++] * scale + offsetX; cpy2 = outline[i++] * scale + offsetY; path.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, cpx, cpy); break; } } } return { offsetX: glyph.ha * scale, path: path }; } /***/ }), /***/ 1670: /*!*********************************************************************!*\ !*** ./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js ***! \*********************************************************************/ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (/* binding */ _asyncToGenerator) /* harmony export */ }); function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } /***/ }), /***/ 2321: /*!******************************************!*\ !*** ./node_modules/tslib/tslib.es6.mjs ***! \******************************************/ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "__addDisposableResource": () => (/* binding */ __addDisposableResource), /* harmony export */ "__assign": () => (/* binding */ __assign), /* harmony export */ "__asyncDelegator": () => (/* binding */ __asyncDelegator), /* harmony export */ "__asyncGenerator": () => (/* binding */ __asyncGenerator), /* harmony export */ "__asyncValues": () => (/* binding */ __asyncValues), /* harmony export */ "__await": () => (/* binding */ __await), /* harmony export */ "__awaiter": () => (/* binding */ __awaiter), /* harmony export */ "__classPrivateFieldGet": () => (/* binding */ __classPrivateFieldGet), /* harmony export */ "__classPrivateFieldIn": () => (/* binding */ __classPrivateFieldIn), /* harmony export */ "__classPrivateFieldSet": () => (/* binding */ __classPrivateFieldSet), /* harmony export */ "__createBinding": () => (/* binding */ __createBinding), /* harmony export */ "__decorate": () => (/* binding */ __decorate), /* harmony export */ "__disposeResources": () => (/* binding */ __disposeResources), /* harmony export */ "__esDecorate": () => (/* binding */ __esDecorate), /* harmony export */ "__exportStar": () => (/* binding */ __exportStar), /* harmony export */ "__extends": () => (/* binding */ __extends), /* harmony export */ "__generator": () => (/* binding */ __generator), /* harmony export */ "__importDefault": () => (/* binding */ __importDefault), /* harmony export */ "__importStar": () => (/* binding */ __importStar), /* harmony export */ "__makeTemplateObject": () => (/* binding */ __makeTemplateObject), /* harmony export */ "__metadata": () => (/* binding */ __metadata), /* harmony export */ "__param": () => (/* binding */ __param), /* harmony export */ "__propKey": () => (/* binding */ __propKey), /* harmony export */ "__read": () => (/* binding */ __read), /* harmony export */ "__rest": () => (/* binding */ __rest), /* harmony export */ "__runInitializers": () => (/* binding */ __runInitializers), /* harmony export */ "__setFunctionName": () => (/* binding */ __setFunctionName), /* harmony export */ "__spread": () => (/* binding */ __spread), /* harmony export */ "__spreadArray": () => (/* binding */ __spreadArray), /* harmony export */ "__spreadArrays": () => (/* binding */ __spreadArrays), /* harmony export */ "__values": () => (/* binding */ __values), /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; } return __assign.apply(this, arguments); } function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } function __decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function __param(paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } } function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); var _, done = false; for (var i = decorators.length - 1; i >= 0; i--) { var context = {}; for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; for (var p in contextIn.access) context.access[p] = contextIn.access[p]; context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); if (kind === "accessor") { if (result === void 0) continue; if (result === null || typeof result !== "object") throw new TypeError("Object expected"); if (_ = accept(result.get)) descriptor.get = _; if (_ = accept(result.set)) descriptor.set = _; if (_ = accept(result.init)) initializers.unshift(_); } else if (_ = accept(result)) { if (kind === "field") initializers.unshift(_); else descriptor[key] = _; } } if (target) Object.defineProperty(target, contextIn.name, descriptor); done = true; }; function __runInitializers(thisArg, initializers, value) { var useValue = arguments.length > 2; for (var i = 0; i < initializers.length; i++) { value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); } return useValue ? value : void 0; }; function __propKey(x) { return typeof x === "symbol" ? x : "".concat(x); }; function __setFunctionName(f, name, prefix) { if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : ""; return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name }); }; function __metadata(metadataKey, metadataValue) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); } function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { 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) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } var __createBinding = Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; }); function __exportStar(m, o) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p); } function __values(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } /** @deprecated */ function __spread() { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; } /** @deprecated */ function __spreadArrays() { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; } function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } function __await(v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } function __asyncGenerator(thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } } function __asyncDelegator(o) { var i, p; return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; } } function __asyncValues(o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; 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); 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); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } } function __makeTemplateObject(cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; }; var __setModuleDefault = Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }; function __importStar(mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; } function __importDefault(mod) { return (mod && mod.__esModule) ? mod : { default: mod }; } function __classPrivateFieldGet(receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); } function __classPrivateFieldSet(receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; } function __classPrivateFieldIn(state, receiver) { if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) throw new TypeError("Cannot use 'in' operator on non-object"); return typeof state === "function" ? receiver === state : state.has(receiver); } function __addDisposableResource(env, value, async) { if (value !== null && value !== void 0) { if (typeof value !== "object") throw new TypeError("Object expected."); var dispose; if (async) { if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); dispose = value[Symbol.asyncDispose]; } if (dispose === void 0) { if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); dispose = value[Symbol.dispose]; } if (typeof dispose !== "function") throw new TypeError("Object not disposable."); env.stack.push({ value: value, dispose: dispose, async: async }); } else if (async) { env.stack.push({ async: true }); } return value; } var _SuppressedError = typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; function __disposeResources(env) { function fail(e) { env.error = env.hasError ? new _SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; env.hasError = true; } function next() { while (env.stack.length) { var rec = env.stack.pop(); try { var result = rec.dispose && rec.dispose.call(rec.value); if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); } catch (e) { fail(e); } } if (env.hasError) throw env.error; } return next(); } /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __createBinding, __exportStar, __values, __read, __spread, __spreadArrays, __spreadArray, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet, __classPrivateFieldIn, __addDisposableResource, __disposeResources, }); /***/ }) }]); //# sourceMappingURL=vendor.js.map
Attack http://localhost:4200/vendor.js/
Evidence parent directory
Solution Disable directory browsing. If this is required, make sure the listed files does not induce risks.
-
Missing Anti-clickjacking Header (1)
GET http://localhost:4200
Alert tags Alert description The response does not include either Content-Security-Policy with 'frame-ancestors' directive or X-Frame-Options to protect against 'ClickJacking' attacks.
Request Request line and header section (197 bytes)
GET http://localhost:4200 HTTP/1.1 Host: localhost:4200 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0 Pragma: no-cache Cache-Control: no-cache
Request body (0 bytes)
Response Status line and header section (284 bytes)
HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: text/html; charset=utf-8 Accept-Ranges: bytes Content-Length: 605 ETag: W/"25d-UpBapEnLJL784/b7WXkIvMJx+u0" Date: Wed, 12 Jul 2023 12:17:59 GMT Connection: keep-alive Keep-Alive: timeout=5
Response body (605 bytes)
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Mindmap3d</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="stylesheet" href="styles.css"></head> <body> <app-root></app-root> <script src="runtime.js" type="module"></script><script src="polyfills.js" type="module"></script><script src="styles.js" defer></script><script src="scripts.js" defer></script><script src="vendor.js" type="module"></script><script src="main.js" type="module"></script></body> </html>
Parameter X-Frame-Options
Solution Modern Web browsers support the Content-Security-Policy and X-Frame-Options HTTP headers. Ensure one of them is set on all web pages returned by your site/app.
If you expect the page to be framed only by pages on your server (e.g. it's part of a FRAMESET) then you'll want to use SAMEORIGIN, otherwise if you never expect the page to be framed, you should use DENY. Alternatively consider implementing Content Security Policy's "frame-ancestors" directive.
-
-
-
Risk=Gering, Confidence=Mittel (2)
-
http://localhost:4200 (2)
-
Server Leaks Information via "X-Powered-By" HTTP Response Header Field(s) (1)
GET http://localhost:4200/robots.txt
Alert tags Alert description The web/application server is leaking information via one or more "X-Powered-By" HTTP response headers. Access to such information may facilitate attackers identifying other frameworks/components your web application is reliant upon and the vulnerabilities such components may be subject to.
Request Request line and header section (208 bytes)
GET http://localhost:4200/robots.txt HTTP/1.1 Host: localhost:4200 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0 Pragma: no-cache Cache-Control: no-cache
Request body (0 bytes)
Response Status line and header section (304 bytes)
HTTP/1.1 404 Not Found X-Powered-By: Express Access-Control-Allow-Origin: * Content-Security-Policy: default-src 'none' X-Content-Type-Options: nosniff Content-Type: text/html; charset=utf-8 Content-Length: 149 Date: Wed, 12 Jul 2023 12:17:59 GMT Connection: keep-alive Keep-Alive: timeout=5
Response body (149 bytes)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>Cannot GET /robots.txt</pre> </body> </html>
Evidence X-Powered-By: Express
Solution Ensure that your web server, application server, load balancer, etc. is configured to suppress "X-Powered-By" headers.
-
X-Content-Type-Options Header Missing (1)
GET http://localhost:4200
Alert tags Alert description The Anti-MIME-Sniffing header X-Content-Type-Options was not set to 'nosniff'. This allows older versions of Internet Explorer and Chrome to perform MIME-sniffing on the response body, potentially causing the response body to be interpreted and displayed as a content type other than the declared content type. Current (early 2014) and legacy versions of Firefox will use the declared content type (if one is set), rather than performing MIME-sniffing.
Other info This issue still applies to error type pages (401, 403, 500, etc.) as those pages are often still affected by injection issues, in which case there is still concern for browsers sniffing pages away from their actual content type.
At "High" threshold this scan rule will not alert on client or server error responses.
Request Request line and header section (197 bytes)
GET http://localhost:4200 HTTP/1.1 Host: localhost:4200 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0 Pragma: no-cache Cache-Control: no-cache
Request body (0 bytes)
Response Status line and header section (284 bytes)
HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: text/html; charset=utf-8 Accept-Ranges: bytes Content-Length: 605 ETag: W/"25d-UpBapEnLJL784/b7WXkIvMJx+u0" Date: Wed, 12 Jul 2023 12:17:59 GMT Connection: keep-alive Keep-Alive: timeout=5
Response body (605 bytes)
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Mindmap3d</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="stylesheet" href="styles.css"></head> <body> <app-root></app-root> <script src="runtime.js" type="module"></script><script src="polyfills.js" type="module"></script><script src="styles.js" defer></script><script src="scripts.js" defer></script><script src="vendor.js" type="module"></script><script src="main.js" type="module"></script></body> </html>
Parameter X-Content-Type-Options
Solution Ensure that the application/web server sets the Content-Type header appropriately, and that it sets the X-Content-Type-Options header to 'nosniff' for all web pages.
If possible, ensure that the end user uses a standards-compliant and modern web browser that does not perform MIME-sniffing at all, or that can be directed by the web application/web server to not perform MIME-sniffing.
-
-
-
Risk=Informational, Confidence=Mittel (1)
-
http://localhost:4200 (1)
-
Modern Web Application (1)
GET http://localhost:4200
Alert tags Alert description The application appears to be a modern web application. If you need to explore it automatically then the Ajax Spider may well be more effective than the standard one.
Other info No links have been found while there are scripts, which is an indication that this is a modern web application.
Request Request line and header section (197 bytes)
GET http://localhost:4200 HTTP/1.1 Host: localhost:4200 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0 Pragma: no-cache Cache-Control: no-cache
Request body (0 bytes)
Response Status line and header section (284 bytes)
HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: text/html; charset=utf-8 Accept-Ranges: bytes Content-Length: 605 ETag: W/"25d-UpBapEnLJL784/b7WXkIvMJx+u0" Date: Wed, 12 Jul 2023 12:17:59 GMT Connection: keep-alive Keep-Alive: timeout=5
Response body (605 bytes)
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Mindmap3d</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="stylesheet" href="styles.css"></head> <body> <app-root></app-root> <script src="runtime.js" type="module"></script><script src="polyfills.js" type="module"></script><script src="styles.js" defer></script><script src="scripts.js" defer></script><script src="vendor.js" type="module"></script><script src="main.js" type="module"></script></body> </html>
Evidence <script src="runtime.js" type="module"></script>
Solution This is an informational alert and so no changes are required.
-
-
-
Risk=Informational, Confidence=Gering (1)
-
http://localhost:4200 (1)
-
Information Disclosure - Suspicious Comments (1)
GET http://localhost:4200/scripts.js
Alert tags Alert description The response appears to contain suspicious comments which may help an attacker. Note: Matches made within script blocks or files are against the entire content not only comments.
Other info The following pattern was used: \bLATER\b and was detected in the element starting with: " // can be removed later when multiple key/instances are fine to be used", see evidence field for the suspicious comment/snippet.
Request Request line and header section (240 bytes)
GET http://localhost:4200/scripts.js HTTP/1.1 Host: localhost:4200 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0 Pragma: no-cache Cache-Control: no-cache Referer: http://localhost:4200
Request body (0 bytes)
Response Status line and header section (302 bytes)
HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * Content-Type: application/javascript; charset=utf-8 Accept-Ranges: bytes Content-Length: 145045 ETag: W/"23695-HX99fDshw9/zNVWtZ997JGxBVv8" Date: Wed, 12 Jul 2023 12:17:59 GMT Connection: keep-alive Keep-Alive: timeout=5
Response body (145045 bytes)
/*! * Bootstrap v5.3.0 (https://getbootstrap.com/) * Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core')) : typeof define === 'function' && define.amd ? define(['@popperjs/core'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bootstrap = factory(global.Popper)); })(this, (function (Popper) { 'use strict'; function _interopNamespaceDefault(e) { const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } }); if (e) { for (const k in e) { if (k !== 'default') { const d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: () => e[k] }); } } } n.default = e; return Object.freeze(n); } const Popper__namespace = /*#__PURE__*/_interopNamespaceDefault(Popper); /** * -------------------------------------------------------------------------- * Bootstrap dom/data.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const elementMap = new Map(); const Data = { set(element, key, instance) { if (!elementMap.has(element)) { elementMap.set(element, new Map()); } const instanceMap = elementMap.get(element); // make it clear we only want one instance per element // can be removed later when multiple key/instances are fine to be used if (!instanceMap.has(key) && instanceMap.size !== 0) { // eslint-disable-next-line no-console console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`); return; } instanceMap.set(key, instance); }, get(element, key) { if (elementMap.has(element)) { return elementMap.get(element).get(key) || null; } return null; }, remove(element, key) { if (!elementMap.has(element)) { return; } const instanceMap = elementMap.get(element); instanceMap.delete(key); // free up element references if there are no instances left for an element if (instanceMap.size === 0) { elementMap.delete(element); } } }; /** * -------------------------------------------------------------------------- * Bootstrap util/index.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ const MAX_UID = 1000000; const MILLISECONDS_MULTIPLIER = 1000; const TRANSITION_END = 'transitionend'; /** * Properly escape IDs selectors to handle weird IDs * @param {string} selector * @returns {string} */ const parseSelector = selector => { if (selector && window.CSS && window.CSS.escape) { // document.querySelector needs escaping to handle IDs (html5+) containing for instance / selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`); } return selector; }; // Shout-out Angus Croll (https://goo.gl/pxwQGp) const toType = object => { if (object === null || object === undefined) { return `${object}`; } return Object.prototype.toString.call(object).match(/\s([a-z]+)/i)[1].toLowerCase(); }; /** * Public Util API */ const getUID = prefix => { do { prefix += Math.floor(Math.random() * MAX_UID); } while (document.getElementById(prefix)); return prefix; }; const getTransitionDurationFromElement = element => { if (!element) { return 0; } // Get transition-duration of the element let { transitionDuration, transitionDelay } = window.getComputedStyle(element); const floatTransitionDuration = Number.parseFloat(transitionDuration); const floatTransitionDelay = Number.parseFloat(transitionDelay); // Return 0 if element or transition duration is not found if (!floatTransitionDuration && !floatTransitionDelay) { return 0; } // If multiple durations are defined, take the first transitionDuration = transitionDuration.split(',')[0]; transitionDelay = transitionDelay.split(',')[0]; return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER; }; const triggerTransitionEnd = element => { element.dispatchEvent(new Event(TRANSITION_END)); }; const isElement = object => { if (!object || typeof object !== 'object') { return false; } if (typeof object.jquery !== 'undefined') { object = object[0]; } return typeof object.nodeType !== 'undefined'; }; const getElement = object => { // it's a jQuery object or a node element if (isElement(object)) { return object.jquery ? object[0] : object; } if (typeof object === 'string' && object.length > 0) { return document.querySelector(parseSelector(object)); } return null; }; const isVisible = element => { if (!isElement(element) || element.getClientRects().length === 0) { return false; } const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'; // Handle `details` element as its content may falsie appear visible when it is closed const closedDetails = element.closest('details:not([open])'); if (!closedDetails) { return elementIsVisible; } if (closedDetails !== element) { const summary = element.closest('summary'); if (summary && summary.parentNode !== closedDetails) { return false; } if (summary === null) { return false; } } return elementIsVisible; }; const isDisabled = element => { if (!element || element.nodeType !== Node.ELEMENT_NODE) { return true; } if (element.classList.contains('disabled')) { return true; } if (typeof element.disabled !== 'undefined') { return element.disabled; } return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'; }; const findShadowRoot = element => { if (!document.documentElement.attachShadow) { return null; } // Can find the shadow root otherwise it'll return the document if (typeof element.getRootNode === 'function') { const root = element.getRootNode(); return root instanceof ShadowRoot ? root : null; } if (element instanceof ShadowRoot) { return element; } // when we don't find a shadow root if (!element.parentNode) { return null; } return findShadowRoot(element.parentNode); }; const noop = () => {}; /** * Trick to restart an element's animation * * @param {HTMLElement} element * @return void * * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation */ const reflow = element => { element.offsetHeight; // eslint-disable-line no-unused-expressions }; const getjQuery = () => { if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) { return window.jQuery; } return null; }; const DOMContentLoadedCallbacks = []; const onDOMContentLoaded = callback => { if (document.readyState === 'loading') { // add listener on the first call when the document is in loading state if (!DOMContentLoadedCallbacks.length) { document.addEventListener('DOMContentLoaded', () => { for (const callback of DOMContentLoadedCallbacks) { callback(); } }); } DOMContentLoadedCallbacks.push(callback); } else { callback(); } }; const isRTL = () => document.documentElement.dir === 'rtl'; const defineJQueryPlugin = plugin => { onDOMContentLoaded(() => { const $ = getjQuery(); /* istanbul ignore if */ if ($) { const name = plugin.NAME; const JQUERY_NO_CONFLICT = $.fn[name]; $.fn[name] = plugin.jQueryInterface; $.fn[name].Constructor = plugin; $.fn[name].noConflict = () => { $.fn[name] = JQUERY_NO_CONFLICT; return plugin.jQueryInterface; }; } }); }; const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => { return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue; }; const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => { if (!waitForTransition) { execute(callback); return; } const durationPadding = 5; const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding; let called = false; const handler = ({ target }) => { if (target !== transitionElement) { return; } called = true; transitionElement.removeEventListener(TRANSITION_END, handler); execute(callback); }; transitionElement.addEventListener(TRANSITION_END, handler); setTimeout(() => { if (!called) { triggerTransitionEnd(transitionElement); } }, emulatedDuration); }; /** * Return the previous/next element of a list. * * @param {array} list The list of elements * @param activeElement The active element * @param shouldGetNext Choose to get next or previous element * @param isCycleAllowed * @return {Element|elem} The proper element */ const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => { const listLength = list.length; let index = list.indexOf(activeElement); // if the element does not exist in the list return an element // depending on the direction and if cycle is allowed if (index === -1) { return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]; } index += shouldGetNext ? 1 : -1; if (isCycleAllowed) { index = (index + listLength) % listLength; } return list[Math.max(0, Math.min(index, listLength - 1))]; }; /** * -------------------------------------------------------------------------- * Bootstrap dom/event-handler.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const namespaceRegex = /[^.]*(?=\..*)\.|.*/; const stripNameRegex = /\..*/; const stripUidRegex = /::\d+$/; const eventRegistry = {}; // Events storage let uidEvent = 1; const customEvents = { mouseenter: 'mouseover', mouseleave: 'mouseout' }; const nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']); /** * Private methods */ function makeEventUid(element, uid) { return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++; } function getElementEvents(element) { const uid = makeEventUid(element); element.uidEvent = uid; eventRegistry[uid] = eventRegistry[uid] || {}; return eventRegistry[uid]; } function bootstrapHandler(element, fn) { return function handler(event) { hydrateObj(event, { delegateTarget: element }); if (handler.oneOff) { EventHandler.off(element, event.type, fn); } return fn.apply(element, [event]); }; } function bootstrapDelegationHandler(element, selector, fn) { return function handler(event) { const domElements = element.querySelectorAll(selector); for (let { target } = event; target && target !== this; target = target.parentNode) { for (const domElement of domElements) { if (domElement !== target) { continue; } hydrateObj(event, { delegateTarget: target }); if (handler.oneOff) { EventHandler.off(element, event.type, selector, fn); } return fn.apply(target, [event]); } } }; } function findHandler(events, callable, delegationSelector = null) { return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector); } function normalizeParameters(originalTypeEvent, handler, delegationFunction) { const isDelegated = typeof handler === 'string'; // TODO: tooltip passes `false` instead of selector, so we need to check const callable = isDelegated ? delegationFunction : handler || delegationFunction; let typeEvent = getTypeEvent(originalTypeEvent); if (!nativeEvents.has(typeEvent)) { typeEvent = originalTypeEvent; } return [isDelegated, callable, typeEvent]; } function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) { if (typeof originalTypeEvent !== 'string' || !element) { return; } let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position // this prevents the handler from being dispatched the same way as mouseover or mouseout does if (originalTypeEvent in customEvents) { const wrapFunction = fn => { return function (event) { if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) { return fn.call(this, event); } }; }; callable = wrapFunction(callable); } const events = getElementEvents(element); const handlers = events[typeEvent] || (events[typeEvent] = {}); const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null); if (previousFunction) { previousFunction.oneOff = previousFunction.oneOff && oneOff; return; } const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, '')); const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable); fn.delegationSelector = isDelegated ? handler : null; fn.callable = callable; fn.oneOff = oneOff; fn.uidEvent = uid; handlers[uid] = fn; element.addEventListener(typeEvent, fn, isDelegated); } function removeHandler(element, events, typeEvent, handler, delegationSelector) { const fn = findHandler(events[typeEvent], handler, delegationSelector); if (!fn) { return; } element.removeEventListener(typeEvent, fn, Boolean(delegationSelector)); delete events[typeEvent][fn.uidEvent]; } function removeNamespacedHandlers(element, events, typeEvent, namespace) { const storeElementEvent = events[typeEvent] || {}; for (const [handlerKey, event] of Object.entries(storeElementEvent)) { if (handlerKey.includes(namespace)) { removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); } } } function getTypeEvent(event) { // allow to get the native events from namespaced events ('click.bs.button' --> 'click') event = event.replace(stripNameRegex, ''); return customEvents[event] || event; } const EventHandler = { on(element, event, handler, delegationFunction) { addHandler(element, event, handler, delegationFunction, false); }, one(element, event, handler, delegationFunction) { addHandler(element, event, handler, delegationFunction, true); }, off(element, originalTypeEvent, handler, delegationFunction) { if (typeof originalTypeEvent !== 'string' || !element) { return; } const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); const inNamespace = typeEvent !== originalTypeEvent; const events = getElementEvents(element); const storeElementEvent = events[typeEvent] || {}; const isNamespace = originalTypeEvent.startsWith('.'); if (typeof callable !== 'undefined') { // Simplest case: handler is passed, remove that listener ONLY. if (!Object.keys(storeElementEvent).length) { return; } removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null); return; } if (isNamespace) { for (const elementEvent of Object.keys(events)) { removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1)); } } for (const [keyHandlers, event] of Object.entries(storeElementEvent)) { const handlerKey = keyHandlers.replace(stripUidRegex, ''); if (!inNamespace || originalTypeEvent.includes(handlerKey)) { removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); } } }, trigger(element, event, args) { if (typeof event !== 'string' || !element) { return null; } const $ = getjQuery(); const typeEvent = getTypeEvent(event); const inNamespace = event !== typeEvent; let jQueryEvent = null; let bubbles = true; let nativeDispatch = true; let defaultPrevented = false; if (inNamespace && $) { jQueryEvent = $.Event(event, args); $(element).trigger(jQueryEvent); bubbles = !jQueryEvent.isPropagationStopped(); nativeDispatch = !jQueryEvent.isImmediatePropagationStopped(); defaultPrevented = jQueryEvent.isDefaultPrevented(); } const evt = hydrateObj(new Event(event, { bubbles, cancelable: true }), args); if (defaultPrevented) { evt.preventDefault(); } if (nativeDispatch) { element.dispatchEvent(evt); } if (evt.defaultPrevented && jQueryEvent) { jQueryEvent.preventDefault(); } return evt; } }; function hydrateObj(obj, meta = {}) { for (const [key, value] of Object.entries(meta)) { try { obj[key] = value; } catch (_unused) { Object.defineProperty(obj, key, { configurable: true, get() { return value; } }); } } return obj; } /** * -------------------------------------------------------------------------- * Bootstrap dom/manipulator.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ function normalizeData(value) { if (value === 'true') { return true; } if (value === 'false') { return false; } if (value === Number(value).toString()) { return Number(value); } if (value === '' || value === 'null') { return null; } if (typeof value !== 'string') { return value; } try { return JSON.parse(decodeURIComponent(value)); } catch (_unused) { return value; } } function normalizeDataKey(key) { return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`); } const Manipulator = { setDataAttribute(element, key, value) { element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value); }, removeDataAttribute(element, key) { element.removeAttribute(`data-bs-${normalizeDataKey(key)}`); }, getDataAttributes(element) { if (!element) { return {}; } const attributes = {}; const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig')); for (const key of bsKeys) { let pureKey = key.replace(/^bs/, ''); pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length); attributes[pureKey] = normalizeData(element.dataset[key]); } return attributes; }, getDataAttribute(element, key) { return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`)); } }; /** * -------------------------------------------------------------------------- * Bootstrap util/config.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Class definition */ class Config { // Getters static get Default() { return {}; } static get DefaultType() { return {}; } static get NAME() { throw new Error('You have to implement the static method "NAME", for each component!'); } _getConfig(config) { config = this._mergeConfigObj(config); config = this._configAfterMerge(config); this._typeCheckConfig(config); return config; } _configAfterMerge(config) { return config; } _mergeConfigObj(config, element) { const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse return { ...this.constructor.Default, ...(typeof jsonConfig === 'object' ? jsonConfig : {}), ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}), ...(typeof config === 'object' ? config : {}) }; } _typeCheckConfig(config, configTypes = this.constructor.DefaultType) { for (const [property, expectedTypes] of Object.entries(configTypes)) { const value = config[property]; const valueType = isElement(value) ? 'element' : toType(value); if (!new RegExp(expectedTypes).test(valueType)) { throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`); } } } } /** * -------------------------------------------------------------------------- * Bootstrap base-component.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const VERSION = '5.3.0'; /** * Class definition */ class BaseComponent extends Config { constructor(element, config) { super(); element = getElement(element); if (!element) { return; } this._element = element; this._config = this._getConfig(config); Data.set(this._element, this.constructor.DATA_KEY, this); } // Public dispose() { Data.remove(this._element, this.constructor.DATA_KEY); EventHandler.off(this._element, this.constructor.EVENT_KEY); for (const propertyName of Object.getOwnPropertyNames(this)) { this[propertyName] = null; } } _queueCallback(callback, element, isAnimated = true) { executeAfterTransition(callback, element, isAnimated); } _getConfig(config) { config = this._mergeConfigObj(config, this._element); config = this._configAfterMerge(config); this._typeCheckConfig(config); return config; } // Static static getInstance(element) { return Data.get(getElement(element), this.DATA_KEY); } static getOrCreateInstance(element, config = {}) { return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null); } static get VERSION() { return VERSION; } static get DATA_KEY() { return `bs.${this.NAME}`; } static get EVENT_KEY() { return `.${this.DATA_KEY}`; } static eventName(name) { return `${name}${this.EVENT_KEY}`; } } /** * -------------------------------------------------------------------------- * Bootstrap dom/selector-engine.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ const getSelector = element => { let selector = element.getAttribute('data-bs-target'); if (!selector || selector === '#') { let hrefAttribute = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes, // so everything starting with `#` or `.`. If a "real" URL is used as the selector, // `document.querySelector` will rightfully complain it is invalid. // See https://github.com/twbs/bootstrap/issues/32273 if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) { return null; } // Just in case some CMS puts out a full URL with the anchor appended if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) { hrefAttribute = `#${hrefAttribute.split('#')[1]}`; } selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null; } return parseSelector(selector); }; const SelectorEngine = { find(selector, element = document.documentElement) { return [].concat(...Element.prototype.querySelectorAll.call(element, selector)); }, findOne(selector, element = document.documentElement) { return Element.prototype.querySelector.call(element, selector); }, children(element, selector) { return [].concat(...element.children).filter(child => child.matches(selector)); }, parents(element, selector) { const parents = []; let ancestor = element.parentNode.closest(selector); while (ancestor) { parents.push(ancestor); ancestor = ancestor.parentNode.closest(selector); } return parents; }, prev(element, selector) { let previous = element.previousElementSibling; while (previous) { if (previous.matches(selector)) { return [previous]; } previous = previous.previousElementSibling; } return []; }, // TODO: this is now unused; remove later along with prev() next(element, selector) { let next = element.nextElementSibling; while (next) { if (next.matches(selector)) { return [next]; } next = next.nextElementSibling; } return []; }, focusableChildren(element) { const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable="true"]'].map(selector => `${selector}:not([tabindex^="-"])`).join(','); return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el)); }, getSelectorFromElement(element) { const selector = getSelector(element); if (selector) { return SelectorEngine.findOne(selector) ? selector : null; } return null; }, getElementFromSelector(element) { const selector = getSelector(element); return selector ? SelectorEngine.findOne(selector) : null; }, getMultipleElementsFromSelector(element) { const selector = getSelector(element); return selector ? SelectorEngine.find(selector) : []; } }; /** * -------------------------------------------------------------------------- * Bootstrap util/component-functions.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ const enableDismissTrigger = (component, method = 'hide') => { const clickEvent = `click.dismiss${component.EVENT_KEY}`; const name = component.NAME; EventHandler.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) { if (['A', 'AREA'].includes(this.tagName)) { event.preventDefault(); } if (isDisabled(this)) { return; } const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`); const instance = component.getOrCreateInstance(target); // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method instance[method](); }); }; /** * -------------------------------------------------------------------------- * Bootstrap alert.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$f = 'alert'; const DATA_KEY$a = 'bs.alert'; const EVENT_KEY$b = `.${DATA_KEY$a}`; const EVENT_CLOSE = `close${EVENT_KEY$b}`; const EVENT_CLOSED = `closed${EVENT_KEY$b}`; const CLASS_NAME_FADE$5 = 'fade'; const CLASS_NAME_SHOW$8 = 'show'; /** * Class definition */ class Alert extends BaseComponent { // Getters static get NAME() { return NAME$f; } // Public close() { const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE); if (closeEvent.defaultPrevented) { return; } this._element.classList.remove(CLASS_NAME_SHOW$8); const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5); this._queueCallback(() => this._destroyElement(), this._element, isAnimated); } // Private _destroyElement() { this._element.remove(); EventHandler.trigger(this._element, EVENT_CLOSED); this.dispose(); } // Static static jQueryInterface(config) { return this.each(function () { const data = Alert.getOrCreateInstance(this); if (typeof config !== 'string') { return; } if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { throw new TypeError(`No method named "${config}"`); } data[config](this); }); } } /** * Data API implementation */ enableDismissTrigger(Alert, 'close'); /** * jQuery */ defineJQueryPlugin(Alert); /** * -------------------------------------------------------------------------- * Bootstrap button.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$e = 'button'; const DATA_KEY$9 = 'bs.button'; const EVENT_KEY$a = `.${DATA_KEY$9}`; const DATA_API_KEY$6 = '.data-api'; const CLASS_NAME_ACTIVE$3 = 'active'; const SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle="button"]'; const EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`; /** * Class definition */ class Button extends BaseComponent { // Getters static get NAME() { return NAME$e; } // Public toggle() { // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3)); } // Static static jQueryInterface(config) { return this.each(function () { const data = Button.getOrCreateInstance(this); if (config === 'toggle') { data[config](); } }); } } /** * Data API implementation */ EventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => { event.preventDefault(); const button = event.target.closest(SELECTOR_DATA_TOGGLE$5); const data = Button.getOrCreateInstance(button); data.toggle(); }); /** * jQuery */ defineJQueryPlugin(Button); /** * -------------------------------------------------------------------------- * Bootstrap util/swipe.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$d = 'swipe'; const EVENT_KEY$9 = '.bs.swipe'; const EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`; const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`; const EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`; const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`; const EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`; const POINTER_TYPE_TOUCH = 'touch'; const POINTER_TYPE_PEN = 'pen'; const CLASS_NAME_POINTER_EVENT = 'pointer-event'; const SWIPE_THRESHOLD = 40; const Default$c = { endCallback: null, leftCallback: null, rightCallback: null }; const DefaultType$c = { endCallback: '(function|null)', leftCallback: '(function|null)', rightCallback: '(function|null)' }; /** * Class definition */ class Swipe extends Config { constructor(element, config) { super(); this._element = element; if (!element || !Swipe.isSupported()) { return; } this._config = this._getConfig(config); this._deltaX = 0; this._supportPointerEvents = Boolean(window.PointerEvent); this._initEvents(); } // Getters static get Default() { return Default$c; } static get DefaultType() { return DefaultType$c; } static get NAME() { return NAME$d; } // Public dispose() { EventHandler.off(this._element, EVENT_KEY$9); } // Private _start(event) { if (!this._supportPointerEvents) { this._deltaX = event.touches[0].clientX; return; } if (this._eventIsPointerPenTouch(event)) { this._deltaX = event.clientX; } } _end(event) { if (this._eventIsPointerPenTouch(event)) { this._deltaX = event.clientX - this._deltaX; } this._handleSwipe(); execute(this._config.endCallback); } _move(event) { this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX; } _handleSwipe() { const absDeltaX = Math.abs(this._deltaX); if (absDeltaX <= SWIPE_THRESHOLD) { return; } const direction = absDeltaX / this._deltaX; this._deltaX = 0; if (!direction) { return; } execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback); } _initEvents() { if (this._supportPointerEvents) { EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event)); EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event)); this._element.classList.add(CLASS_NAME_POINTER_EVENT); } else { EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event)); EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event)); EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event)); } } _eventIsPointerPenTouch(event) { return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH); } // Static static isSupported() { return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0; } } /** * -------------------------------------------------------------------------- * Bootstrap carousel.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$c = 'carousel'; const DATA_KEY$8 = 'bs.carousel'; const EVENT_KEY$8 = `.${DATA_KEY$8}`; const DATA_API_KEY$5 = '.data-api'; const ARROW_LEFT_KEY$1 = 'ArrowLeft'; const ARROW_RIGHT_KEY$1 = 'ArrowRight'; const TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch const ORDER_NEXT = 'next'; const ORDER_PREV = 'prev'; const DIRECTION_LEFT = 'left'; const DIRECTION_RIGHT = 'right'; const EVENT_SLIDE = `slide${EVENT_KEY$8}`; const EVENT_SLID = `slid${EVENT_KEY$8}`; const EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`; const EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`; const EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`; const EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`; const EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`; const EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`; const CLASS_NAME_CAROUSEL = 'carousel'; const CLASS_NAME_ACTIVE$2 = 'active'; const CLASS_NAME_SLIDE = 'slide'; const CLASS_NAME_END = 'carousel-item-end'; const CLASS_NAME_START = 'carousel-item-start'; const CLASS_NAME_NEXT = 'carousel-item-next'; const CLASS_NAME_PREV = 'carousel-item-prev'; const SELECTOR_ACTIVE = '.active'; const SELECTOR_ITEM = '.carousel-item'; const SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM; const SELECTOR_ITEM_IMG = '.carousel-item img'; const SELECTOR_INDICATORS = '.carousel-indicators'; const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'; const SELECTOR_DATA_RIDE = '[data-bs-ride="carousel"]'; const KEY_TO_DIRECTION = { [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT, [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT }; const Default$b = { interval: 5000, keyboard: true, pause: 'hover', ride: false, touch: true, wrap: true }; const DefaultType$b = { interval: '(number|boolean)', // TODO:v6 remove boolean support keyboard: 'boolean', pause: '(string|boolean)', ride: '(boolean|string)', touch: 'boolean', wrap: 'boolean' }; /** * Class definition */ class Carousel extends BaseComponent { constructor(element, config) { super(element, config); this._interval = null; this._activeElement = null; this._isSliding = false; this.touchTimeout = null; this._swipeHelper = null; this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element); this._addEventListeners(); if (this._config.ride === CLASS_NAME_CAROUSEL) { this.cycle(); } } // Getters static get Default() { return Default$b; } static get DefaultType() { return DefaultType$b; } static get NAME() { return NAME$c; } // Public next() { this._slide(ORDER_NEXT); } nextWhenVisible() { // FIXME TODO use `document.visibilityState` // Don't call next when the page isn't visible // or the carousel or its parent isn't visible if (!document.hidden && isVisible(this._element)) { this.next(); } } prev() { this._slide(ORDER_PREV); } pause() { if (this._isSliding) { triggerTransitionEnd(this._element); } this._clearInterval(); } cycle() { this._clearInterval(); this._updateInterval(); this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval); } _maybeEnableCycle() { if (!this._config.ride) { return; } if (this._isSliding) { EventHandler.one(this._element, EVENT_SLID, () => this.cycle()); return; } this.cycle(); } to(index) { const items = this._getItems(); if (index > items.length - 1 || index < 0) { return; } if (this._isSliding) { EventHandler.one(this._element, EVENT_SLID, () => this.to(index)); return; } const activeIndex = this._getItemIndex(this._getActive()); if (activeIndex === index) { return; } const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV; this._slide(order, items[index]); } dispose() { if (this._swipeHelper) { this._swipeHelper.dispose(); } super.dispose(); } // Private _configAfterMerge(config) { config.defaultInterval = config.interval; return config; } _addEventListeners() { if (this._config.keyboard) { EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event)); } if (this._config.pause === 'hover') { EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause()); EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle()); } if (this._config.touch && Swipe.isSupported()) { this._addTouchEventListeners(); } } _addTouchEventListeners() { for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) { EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault()); } const endCallBack = () => { if (this._config.pause !== 'hover') { return; } // If it's a touch-enabled device, mouseenter/leave are fired as // part of the mouse compatibility events on first tap - the carousel // would stop cycling until user tapped out of it; // here, we listen for touchend, explicitly pause the carousel // (as if it's the second time we tap on it, mouseenter compat event // is NOT fired) and after a timeout (to allow for mouse compatibility // events to fire) we explicitly restart cycling this.pause(); if (this.touchTimeout) { clearTimeout(this.touchTimeout); } this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval); }; const swipeConfig = { leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)), rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)), endCallback: endCallBack }; this._swipeHelper = new Swipe(this._element, swipeConfig); } _keydown(event) { if (/input|textarea/i.test(event.target.tagName)) { return; } const direction = KEY_TO_DIRECTION[event.key]; if (direction) { event.preventDefault(); this._slide(this._directionToOrder(direction)); } } _getItemIndex(element) { return this._getItems().indexOf(element); } _setActiveIndicatorElement(index) { if (!this._indicatorsElement) { return; } const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement); activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2); activeIndicator.removeAttribute('aria-current'); const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to="${index}"]`, this._indicatorsElement); if (newActiveIndicator) { newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2); newActiveIndicator.setAttribute('aria-current', 'true'); } } _updateInterval() { const element = this._activeElement || this._getActive(); if (!element) { return; } const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10); this._config.interval = elementInterval || this._config.defaultInterval; } _slide(order, element = null) { if (this._isSliding) { return; } const activeElement = this._getActive(); const isNext = order === ORDER_NEXT; const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap); if (nextElement === activeElement) { return; } const nextElementIndex = this._getItemIndex(nextElement); const triggerEvent = eventName => { return EventHandler.trigger(this._element, eventName, { relatedTarget: nextElement, direction: this._orderToDirection(order), from: this._getItemIndex(activeElement), to: nextElementIndex }); }; const slideEvent = triggerEvent(EVENT_SLIDE); if (slideEvent.defaultPrevented) { return; } if (!activeElement || !nextElement) { // Some weirdness is happening, so we bail // TODO: change tests that use empty divs to avoid this check return; } const isCycling = Boolean(this._interval); this.pause(); this._isSliding = true; this._setActiveIndicatorElement(nextElementIndex); this._activeElement = nextElement; const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END; const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV; nextElement.classList.add(orderClassName); reflow(nextElement); activeElement.classList.add(directionalClassName); nextElement.classList.add(directionalClassName); const completeCallBack = () => { nextElement.classList.remove(directionalClassName, orderClassName); nextElement.classList.add(CLASS_NAME_ACTIVE$2); activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName); this._isSliding = false; triggerEvent(EVENT_SLID); }; this._queueCallback(completeCallBack, activeElement, this._isAnimated()); if (isCycling) { this.cycle(); } } _isAnimated() { return this._element.classList.contains(CLASS_NAME_SLIDE); } _getActive() { return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element); } _getItems() { return SelectorEngine.find(SELECTOR_ITEM, this._element); } _clearInterval() { if (this._interval) { clearInterval(this._interval); this._interval = null; } } _directionToOrder(direction) { if (isRTL()) { return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT; } return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV; } _orderToDirection(order) { if (isRTL()) { return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT; } return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT; } // Static static jQueryInterface(config) { return this.each(function () { const data = Carousel.getOrCreateInstance(this, config); if (typeof config === 'number') { data.to(config); return; } if (typeof config === 'string') { if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { throw new TypeError(`No method named "${config}"`); } data[config](); } }); } } /** * Data API implementation */ EventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) { const target = SelectorEngine.getElementFromSelector(this); if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) { return; } event.preventDefault(); const carousel = Carousel.getOrCreateInstance(target); const slideIndex = this.getAttribute('data-bs-slide-to'); if (slideIndex) { carousel.to(slideIndex); carousel._maybeEnableCycle(); return; } if (Manipulator.getDataAttribute(this, 'slide') === 'next') { carousel.next(); carousel._maybeEnableCycle(); return; } carousel.prev(); carousel._maybeEnableCycle(); }); EventHandler.on(window, EVENT_LOAD_DATA_API$3, () => { const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE); for (const carousel of carousels) { Carousel.getOrCreateInstance(carousel); } }); /** * jQuery */ defineJQueryPlugin(Carousel); /** * -------------------------------------------------------------------------- * Bootstrap collapse.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$b = 'collapse'; const DATA_KEY$7 = 'bs.collapse'; const EVENT_KEY$7 = `.${DATA_KEY$7}`; const DATA_API_KEY$4 = '.data-api'; const EVENT_SHOW$6 = `show${EVENT_KEY$7}`; const EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`; const EVENT_HIDE$6 = `hide${EVENT_KEY$7}`; const EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`; const EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`; const CLASS_NAME_SHOW$7 = 'show'; const CLASS_NAME_COLLAPSE = 'collapse'; const CLASS_NAME_COLLAPSING = 'collapsing'; const CLASS_NAME_COLLAPSED = 'collapsed'; const CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`; const CLASS_NAME_HORIZONTAL = 'collapse-horizontal'; const WIDTH = 'width'; const HEIGHT = 'height'; const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'; const SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle="collapse"]'; const Default$a = { parent: null, toggle: true }; const DefaultType$a = { parent: '(null|element)', toggle: 'boolean' }; /** * Class definition */ class Collapse extends BaseComponent { constructor(element, config) { super(element, config); this._isTransitioning = false; this._triggerArray = []; const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4); for (const elem of toggleList) { const selector = SelectorEngine.getSelectorFromElement(elem); const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element); if (selector !== null && filterElement.length) { this._triggerArray.push(elem); } } this._initializeChildren(); if (!this._config.parent) { this._addAriaAndCollapsedClass(this._triggerArray, this._isShown()); } if (this._config.toggle) { this.toggle(); } } // Getters static get Default() { return Default$a; } static get DefaultType() { return DefaultType$a; } static get NAME() { return NAME$b; } // Public toggle() { if (this._isShown()) { this.hide(); } else { this.show(); } } show() { if (this._isTransitioning || this._isShown()) { return; } let activeChildren = []; // find active children if (this._config.parent) { activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, { toggle: false })); } if (activeChildren.length && activeChildren[0]._isTransitioning) { return; } const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6); if (startEvent.defaultPrevented) { return; } for (const activeInstance of activeChildren) { activeInstance.hide(); } const dimension = this._getDimension(); this._element.classList.remove(CLASS_NAME_COLLAPSE); this._element.classList.add(CLASS_NAME_COLLAPSING); this._element.style[dimension] = 0; this._addAriaAndCollapsedClass(this._triggerArray, true); this._isTransitioning = true; const complete = () => { this._isTransitioning = false; this._element.classList.remove(CLASS_NAME_COLLAPSING); this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7); this._element.style[dimension] = ''; EventHandler.trigger(this._element, EVENT_SHOWN$6); }; const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1); const scrollSize = `scroll${capitalizedDimension}`; this._queueCallback(complete, this._element, true); this._element.style[dimension] = `${this._element[scrollSize]}px`; } hide() { if (this._isTransitioning || !this._isShown()) { return; } const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6); if (startEvent.defaultPrevented) { return; } const dimension = this._getDimension(); this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`; reflow(this._element); this._element.classList.add(CLASS_NAME_COLLAPSING); this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7); for (const trigger of this._triggerArray) { const element = SelectorEngine.getElementFromSelector(trigger); if (element && !this._isShown(element)) { this._addAriaAndCollapsedClass([trigger], false); } } this._isTransitioning = true; const complete = () => { this._isTransitioning = false; this._element.classList.remove(CLASS_NAME_COLLAPSING); this._element.classList.add(CLASS_NAME_COLLAPSE); EventHandler.trigger(this._element, EVENT_HIDDEN$6); }; this._element.style[dimension] = ''; this._queueCallback(complete, this._element, true); } _isShown(element = this._element) { return element.classList.contains(CLASS_NAME_SHOW$7); } // Private _configAfterMerge(config) { config.toggle = Boolean(config.toggle); // Coerce string values config.parent = getElement(config.parent); return config; } _getDimension() { return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT; } _initializeChildren() { if (!this._config.parent) { return; } const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4); for (const element of children) { const selected = SelectorEngine.getElementFromSelector(element); if (selected) { this._addAriaAndCollapsedClass([element], this._isShown(selected)); } } } _getFirstLevelChildren(selector) { const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent); // remove children if greater depth return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element)); } _addAriaAndCollapsedClass(triggerArray, isOpen) { if (!triggerArray.length) { return; } for (const element of triggerArray) { element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen); element.setAttribute('aria-expanded', isOpen); } } // Static static jQueryInterface(config) { const _config = {}; if (typeof config === 'string' && /show|hide/.test(config)) { _config.toggle = false; } return this.each(function () { const data = Collapse.getOrCreateInstance(this, _config); if (typeof config === 'string') { if (typeof data[config] === 'undefined') { throw new TypeError(`No method named "${config}"`); } data[config](); } }); } } /** * Data API implementation */ EventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) { // preventDefault only for <a> elements (which change the URL) not inside the collapsible element if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') { event.preventDefault(); } for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) { Collapse.getOrCreateInstance(element, { toggle: false }).toggle(); } }); /** * jQuery */ defineJQueryPlugin(Collapse); /** * -------------------------------------------------------------------------- * Bootstrap dropdown.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$a = 'dropdown'; const DATA_KEY$6 = 'bs.dropdown'; const EVENT_KEY$6 = `.${DATA_KEY$6}`; const DATA_API_KEY$3 = '.data-api'; const ESCAPE_KEY$2 = 'Escape'; const TAB_KEY$1 = 'Tab'; const ARROW_UP_KEY$1 = 'ArrowUp'; const ARROW_DOWN_KEY$1 = 'ArrowDown'; const RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button const EVENT_HIDE$5 = `hide${EVENT_KEY$6}`; const EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`; const EVENT_SHOW$5 = `show${EVENT_KEY$6}`; const EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`; const EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`; const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`; const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`; const CLASS_NAME_SHOW$6 = 'show'; const CLASS_NAME_DROPUP = 'dropup'; const CLASS_NAME_DROPEND = 'dropend'; const CLASS_NAME_DROPSTART = 'dropstart'; const CLASS_NAME_DROPUP_CENTER = 'dropup-center'; const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'; const SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)'; const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`; const SELECTOR_MENU = '.dropdown-menu'; const SELECTOR_NAVBAR = '.navbar'; const SELECTOR_NAVBAR_NAV = '.navbar-nav'; const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'; const PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'; const PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'; const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'; const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'; const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'; const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'; const PLACEMENT_TOPCENTER = 'top'; const PLACEMENT_BOTTOMCENTER = 'bottom'; const Default$9 = { autoClose: true, boundary: 'clippingParents', display: 'dynamic', offset: [0, 2], popperConfig: null, reference: 'toggle' }; const DefaultType$9 = { autoClose: '(boolean|string)', boundary: '(string|element)', display: 'string', offset: '(array|string|function)', popperConfig: '(null|object|function)', reference: '(string|element|object)' }; /** * Class definition */ class Dropdown extends BaseComponent { constructor(element, config) { super(element, config); this._popper = null; this._parent = this._element.parentNode; // dropdown wrapper // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent); this._inNavbar = this._detectNavbar(); } // Getters static get Default() { return Default$9; } static get DefaultType() { return DefaultType$9; } static get NAME() { return NAME$a; } // Public toggle() { return this._isShown() ? this.hide() : this.show(); } show() { if (isDisabled(this._element) || this._isShown()) { return; } const relatedTarget = { relatedTarget: this._element }; const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget); if (showEvent.defaultPrevented) { return; } this._createPopper(); // If this is a touch-enabled device we add extra // empty mouseover listeners to the body's immediate children; // only needed because of broken event delegation on iOS // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) { for (const element of [].concat(...document.body.children)) { EventHandler.on(element, 'mouseover', noop); } } this._element.focus(); this._element.setAttribute('aria-expanded', true); this._menu.classList.add(CLASS_NAME_SHOW$6); this._element.classList.add(CLASS_NAME_SHOW$6); EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget); } hide() { if (isDisabled(this._element) || !this._isShown()) { return; } const relatedTarget = { relatedTarget: this._element }; this._completeHide(relatedTarget); } dispose() { if (this._popper) { this._popper.destroy(); } super.dispose(); } update() { this._inNavbar = this._detectNavbar(); if (this._popper) { this._popper.update(); } } // Private _completeHide(relatedTarget) { const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, relatedTarget); if (hideEvent.defaultPrevented) { return; } // If this is a touch-enabled device we remove the extra // empty mouseover listeners we added for iOS support if ('ontouchstart' in document.documentElement) { for (const element of [].concat(...document.body.children)) { EventHandler.off(element, 'mouseover', noop); } } if (this._popper) { this._popper.destroy(); } this._menu.classList.remove(CLASS_NAME_SHOW$6); this._element.classList.remove(CLASS_NAME_SHOW$6); this._element.setAttribute('aria-expanded', 'false'); Manipulator.removeDataAttribute(this._menu, 'popper'); EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget); } _getConfig(config) { config = super._getConfig(config); if (typeof config.reference === 'object' && !isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') { // Popper virtual elements require a getBoundingClientRect method throw new TypeError(`${NAME$a.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`); } return config; } _createPopper() { if (typeof Popper__namespace === 'undefined') { throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)'); } let referenceElement = this._element; if (this._config.reference === 'parent') { referenceElement = this._parent; } else if (isElement(this._config.reference)) { referenceElement = getElement(this._config.reference); } else if (typeof this._config.reference === 'object') { referenceElement = this._config.reference; } const popperConfig = this._getPopperConfig(); this._popper = Popper__namespace.createPopper(referenceElement, this._menu, popperConfig); } _isShown() { return this._menu.classList.contains(CLASS_NAME_SHOW$6); } _getPlacement() { const parentDropdown = this._parent; if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) { return PLACEMENT_RIGHT; } if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) { return PLACEMENT_LEFT; } if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) { return PLACEMENT_TOPCENTER; } if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) { return PLACEMENT_BOTTOMCENTER; } // We need to trim the value because custom properties can also include spaces const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'; if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) { return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP; } return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM; } _detectNavbar() { return this._element.closest(SELECTOR_NAVBAR) !== null; } _getOffset() { const { offset } = this._config; if (typeof offset === 'string') { return offset.split(',').map(value => Number.parseInt(value, 10)); } if (typeof offset === 'function') { return popperData => offset(popperData, this._element); } return offset; } _getPopperConfig() { const defaultBsPopperConfig = { placement: this._getPlacement(), modifiers: [{ name: 'preventOverflow', options: { boundary: this._config.boundary } }, { name: 'offset', options: { offset: this._getOffset() } }] }; // Disable Popper if we have a static display or Dropdown is in Navbar if (this._inNavbar || this._config.display === 'static') { Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove defaultBsPopperConfig.modifiers = [{ name: 'applyStyles', enabled: false }]; } return { ...defaultBsPopperConfig, ...execute(this._config.popperConfig, [defaultBsPopperConfig]) }; } _selectMenuItem({ key, target }) { const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element)); if (!items.length) { return; } // if target isn't included in items (e.g. when expanding the dropdown) // allow cycling to get the last item in case key equals ARROW_UP_KEY getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus(); } // Static static jQueryInterface(config) { return this.each(function () { const data = Dropdown.getOrCreateInstance(this, config); if (typeof config !== 'string') { return; } if (typeof data[config] === 'undefined') { throw new TypeError(`No method named "${config}"`); } data[config](); }); } static clearMenus(event) { if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) { return; } const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN); for (const toggle of openToggles) { const context = Dropdown.getInstance(toggle); if (!context || context._config.autoClose === false) { continue; } const composedPath = event.composedPath(); const isMenuTarget = composedPath.includes(context._menu); if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) { continue; } // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) { continue; } const relatedTarget = { relatedTarget: context._element }; if (event.type === 'click') { relatedTarget.clickEvent = event; } context._completeHide(relatedTarget); } } static dataApiKeydownHandler(event) { // If not an UP | DOWN | ESCAPE key => not a dropdown command // If input/textarea && if key is other than ESCAPE => not a dropdown command const isInput = /input|textarea/i.test(event.target.tagName); const isEscapeEvent = event.key === ESCAPE_KEY$2; const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key); if (!isUpOrDownEvent && !isEscapeEvent) { return; } if (isInput && !isEscapeEvent) { return; } event.preventDefault(); // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode); const instance = Dropdown.getOrCreateInstance(getToggleButton); if (isUpOrDownEvent) { event.stopPropagation(); instance.show(); instance._selectMenuItem(event); return; } if (instance._isShown()) { // else is escape and we check if it is shown event.stopPropagation(); instance.hide(); getToggleButton.focus(); } } } /** * Data API implementation */ EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler); EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler); EventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus); EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus); EventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) { event.preventDefault(); Dropdown.getOrCreateInstance(this).toggle(); }); /** * jQuery */ defineJQueryPlugin(Dropdown); /** * -------------------------------------------------------------------------- * Bootstrap util/backdrop.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$9 = 'backdrop'; const CLASS_NAME_FADE$4 = 'fade'; const CLASS_NAME_SHOW$5 = 'show'; const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`; const Default$8 = { className: 'modal-backdrop', clickCallback: null, isAnimated: false, isVisible: true, // if false, we use the backdrop helper without adding any element to the dom rootElement: 'body' // give the choice to place backdrop under different elements }; const DefaultType$8 = { className: 'string', clickCallback: '(function|null)', isAnimated: 'boolean', isVisible: 'boolean', rootElement: '(element|string)' }; /** * Class definition */ class Backdrop extends Config { constructor(config) { super(); this._config = this._getConfig(config); this._isAppended = false; this._element = null; } // Getters static get Default() { return Default$8; } static get DefaultType() { return DefaultType$8; } static get NAME() { return NAME$9; } // Public show(callback) { if (!this._config.isVisible) { execute(callback); return; } this._append(); const element = this._getElement(); if (this._config.isAnimated) { reflow(element); } element.classList.add(CLASS_NAME_SHOW$5); this._emulateAnimation(() => { execute(callback); }); } hide(callback) { if (!this._config.isVisible) { execute(callback); return; } this._getElement().classList.remove(CLASS_NAME_SHOW$5); this._emulateAnimation(() => { this.dispose(); execute(callback); }); } dispose() { if (!this._isAppended) { return; } EventHandler.off(this._element, EVENT_MOUSEDOWN); this._element.remove(); this._isAppended = false; } // Private _getElement() { if (!this._element) { const backdrop = document.createElement('div'); backdrop.className = this._config.className; if (this._config.isAnimated) { backdrop.classList.add(CLASS_NAME_FADE$4); } this._element = backdrop; } return this._element; } _configAfterMerge(config) { // use getElement() with the default "body" to get a fresh Element on each instantiation config.rootElement = getElement(config.rootElement); return config; } _append() { if (this._isAppended) { return; } const element = this._getElement(); this._config.rootElement.append(element); EventHandler.on(element, EVENT_MOUSEDOWN, () => { execute(this._config.clickCallback); }); this._isAppended = true; } _emulateAnimation(callback) { executeAfterTransition(callback, this._getElement(), this._config.isAnimated); } } /** * -------------------------------------------------------------------------- * Bootstrap util/focustrap.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$8 = 'focustrap'; const DATA_KEY$5 = 'bs.focustrap'; const EVENT_KEY$5 = `.${DATA_KEY$5}`; const EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`; const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`; const TAB_KEY = 'Tab'; const TAB_NAV_FORWARD = 'forward'; const TAB_NAV_BACKWARD = 'backward'; const Default$7 = { autofocus: true, trapElement: null // The element to trap focus inside of }; const DefaultType$7 = { autofocus: 'boolean', trapElement: 'element' }; /** * Class definition */ class FocusTrap extends Config { constructor(config) { super(); this._config = this._getConfig(config); this._isActive = false; this._lastTabNavDirection = null; } // Getters static get Default() { return Default$7; } static get DefaultType() { return DefaultType$7; } static get NAME() { return NAME$8; } // Public activate() { if (this._isActive) { return; } if (this._config.autofocus) { this._config.trapElement.focus(); } EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event)); EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event)); this._isActive = true; } deactivate() { if (!this._isActive) { return; } this._isActive = false; EventHandler.off(document, EVENT_KEY$5); } // Private _handleFocusin(event) { const { trapElement } = this._config; if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) { return; } const elements = SelectorEngine.focusableChildren(trapElement); if (elements.length === 0) { trapElement.focus(); } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) { elements[elements.length - 1].focus(); } else { elements[0].focus(); } } _handleKeydown(event) { if (event.key !== TAB_KEY) { return; } this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD; } } /** * -------------------------------------------------------------------------- * Bootstrap util/scrollBar.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'; const SELECTOR_STICKY_CONTENT = '.sticky-top'; const PROPERTY_PADDING = 'padding-right'; const PROPERTY_MARGIN = 'margin-right'; /** * Class definition */ class ScrollBarHelper { constructor() { this._element = document.body; } // Public getWidth() { // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes const documentWidth = document.documentElement.clientWidth; return Math.abs(window.innerWidth - documentWidth); } hide() { const width = this.getWidth(); this._disableOverFlow(); // give padding to element to balance the hidden scrollbar width this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width); // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width); this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width); } reset() { this._resetElementAttributes(this._element, 'overflow'); this._resetElementAttributes(this._element, PROPERTY_PADDING); this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING); this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN); } isOverflowing() { return this.getWidth() > 0; } // Private _disableOverFlow() { this._saveInitialAttribute(this._element, 'overflow'); this._element.style.overflow = 'hidden'; } _setElementAttributes(selector, styleProperty, callback) { const scrollbarWidth = this.getWidth(); const manipulationCallBack = element => { if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) { return; } this._saveInitialAttribute(element, styleProperty); const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty); element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`); }; this._applyManipulationCallback(selector, manipulationCallBack); } _saveInitialAttribute(element, styleProperty) { const actualValue = element.style.getPropertyValue(styleProperty); if (actualValue) { Manipulator.setDataAttribute(element, styleProperty, actualValue); } } _resetElementAttributes(selector, styleProperty) { const manipulationCallBack = element => { const value = Manipulator.getDataAttribute(element, styleProperty); // We only want to remove the property if the value is `null`; the value can also be zero if (value === null) { element.style.removeProperty(styleProperty); return; } Manipulator.removeDataAttribute(element, styleProperty); element.style.setProperty(styleProperty, value); }; this._applyManipulationCallback(selector, manipulationCallBack); } _applyManipulationCallback(selector, callBack) { if (isElement(selector)) { callBack(selector); return; } for (const sel of SelectorEngine.find(selector, this._element)) { callBack(sel); } } } /** * -------------------------------------------------------------------------- * Bootstrap modal.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$7 = 'modal'; const DATA_KEY$4 = 'bs.modal'; const EVENT_KEY$4 = `.${DATA_KEY$4}`; const DATA_API_KEY$2 = '.data-api'; const ESCAPE_KEY$1 = 'Escape'; const EVENT_HIDE$4 = `hide${EVENT_KEY$4}`; const EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`; const EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`; const EVENT_SHOW$4 = `show${EVENT_KEY$4}`; const EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`; const EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`; const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`; const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`; const EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`; const EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`; const CLASS_NAME_OPEN = 'modal-open'; const CLASS_NAME_FADE$3 = 'fade'; const CLASS_NAME_SHOW$4 = 'show'; const CLASS_NAME_STATIC = 'modal-static'; const OPEN_SELECTOR$1 = '.modal.show'; const SELECTOR_DIALOG = '.modal-dialog'; const SELECTOR_MODAL_BODY = '.modal-body'; const SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle="modal"]'; const Default$6 = { backdrop: true, focus: true, keyboard: true }; const DefaultType$6 = { backdrop: '(boolean|string)', focus: 'boolean', keyboard: 'boolean' }; /** * Class definition */ class Modal extends BaseComponent { constructor(element, config) { super(element, config); this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element); this._backdrop = this._initializeBackDrop(); this._focustrap = this._initializeFocusTrap(); this._isShown = false; this._isTransitioning = false; this._scrollBar = new ScrollBarHelper(); this._addEventListeners(); } // Getters static get Default() { return Default$6; } static get DefaultType() { return DefaultType$6; } static get NAME() { return NAME$7; } // Public toggle(relatedTarget) { return this._isShown ? this.hide() : this.show(relatedTarget); } show(relatedTarget) { if (this._isShown || this._isTransitioning) { return; } const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$4, { relatedTarget }); if (showEvent.defaultPrevented) { return; } this._isShown = true; this._isTransitioning = true; this._scrollBar.hide(); document.body.classList.add(CLASS_NAME_OPEN); this._adjustDialog(); this._backdrop.show(() => this._showElement(relatedTarget)); } hide() { if (!this._isShown || this._isTransitioning) { return; } const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4); if (hideEvent.defaultPrevented) { return; } this._isShown = false; this._isTransitioning = true; this._focustrap.deactivate(); this._element.classList.remove(CLASS_NAME_SHOW$4); this._queueCallback(() => this._hideModal(), this._element, this._isAnimated()); } dispose() { EventHandler.off(window, EVENT_KEY$4); EventHandler.off(this._dialog, EVENT_KEY$4); this._backdrop.dispose(); this._focustrap.deactivate(); super.dispose(); } handleUpdate() { this._adjustDialog(); } // Private _initializeBackDrop() { return new Backdrop({ isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value, isAnimated: this._isAnimated() }); } _initializeFocusTrap() { return new FocusTrap({ trapElement: this._element }); } _showElement(relatedTarget) { // try to append dynamic modal if (!document.body.contains(this._element)) { document.body.append(this._element); } this._element.style.display = 'block'; this._element.removeAttribute('aria-hidden'); this._element.setAttribute('aria-modal', true); this._element.setAttribute('role', 'dialog'); this._element.scrollTop = 0; const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog); if (modalBody) { modalBody.scrollTop = 0; } reflow(this._element); this._element.classList.add(CLASS_NAME_SHOW$4); const transitionComplete = () => { if (this._config.focus) { this._focustrap.activate(); } this._isTransitioning = false; EventHandler.trigger(this._element, EVENT_SHOWN$4, { relatedTarget }); }; this._queueCallback(transitionComplete, this._dialog, this._isAnimated()); } _addEventListeners() { EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => { if (event.key !== ESCAPE_KEY$1) { return; } if (this._config.keyboard) { this.hide(); return; } this._triggerBackdropTransition(); }); EventHandler.on(window, EVENT_RESIZE$1, () => { if (this._isShown && !this._isTransitioning) { this._adjustDialog(); } }); EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => { // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => { if (this._element !== event.target || this._element !== event2.target) { return; } if (this._config.backdrop === 'static') { this._triggerBackdropTransition(); return; } if (this._config.backdrop) { this.hide(); } }); }); } _hideModal() { this._element.style.display = 'none'; this._element.setAttribute('aria-hidden', true); this._element.removeAttribute('aria-modal'); this._element.removeAttribute('role'); this._isTransitioning = false; this._backdrop.hide(() => { document.body.classList.remove(CLASS_NAME_OPEN); this._resetAdjustments(); this._scrollBar.reset(); EventHandler.trigger(this._element, EVENT_HIDDEN$4); }); } _isAnimated() { return this._element.classList.contains(CLASS_NAME_FADE$3); } _triggerBackdropTransition() { const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED$1); if (hideEvent.defaultPrevented) { return; } const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; const initialOverflowY = this._element.style.overflowY; // return if the following background transition hasn't yet completed if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) { return; } if (!isModalOverflowing) { this._element.style.overflowY = 'hidden'; } this._element.classList.add(CLASS_NAME_STATIC); this._queueCallback(() => { this._element.classList.remove(CLASS_NAME_STATIC); this._queueCallback(() => { this._element.style.overflowY = initialOverflowY; }, this._dialog); }, this._dialog); this._element.focus(); } /** * The following methods are used to handle overflowing modals */ _adjustDialog() { const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; const scrollbarWidth = this._scrollBar.getWidth(); const isBodyOverflowing = scrollbarWidth > 0; if (isBodyOverflowing && !isModalOverflowing) { const property = isRTL() ? 'paddingLeft' : 'paddingRight'; this._element.style[property] = `${scrollbarWidth}px`; } if (!isBodyOverflowing && isModalOverflowing) { const property = isRTL() ? 'paddingRight' : 'paddingLeft'; this._element.style[property] = `${scrollbarWidth}px`; } } _resetAdjustments() { this._element.style.paddingLeft = ''; this._element.style.paddingRight = ''; } // Static static jQueryInterface(config, relatedTarget) { return this.each(function () { const data = Modal.getOrCreateInstance(this, config); if (typeof config !== 'string') { return; } if (typeof data[config] === 'undefined') { throw new TypeError(`No method named "${config}"`); } data[config](relatedTarget); }); } } /** * Data API implementation */ EventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) { const target = SelectorEngine.getElementFromSelector(this); if (['A', 'AREA'].includes(this.tagName)) { event.preventDefault(); } EventHandler.one(target, EVENT_SHOW$4, showEvent => { if (showEvent.defaultPrevented) { // only register focus restorer if modal will actually get shown return; } EventHandler.one(target, EVENT_HIDDEN$4, () => { if (isVisible(this)) { this.focus(); } }); }); // avoid conflict when clicking modal toggler while another one is open const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR$1); if (alreadyOpen) { Modal.getInstance(alreadyOpen).hide(); } const data = Modal.getOrCreateInstance(target); data.toggle(this); }); enableDismissTrigger(Modal); /** * jQuery */ defineJQueryPlugin(Modal); /** * -------------------------------------------------------------------------- * Bootstrap offcanvas.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$6 = 'offcanvas'; const DATA_KEY$3 = 'bs.offcanvas'; const EVENT_KEY$3 = `.${DATA_KEY$3}`; const DATA_API_KEY$1 = '.data-api'; const EVENT_LOAD_DATA_API$2 = `load${EVENT_KEY$3}${DATA_API_KEY$1}`; const ESCAPE_KEY = 'Escape'; const CLASS_NAME_SHOW$3 = 'show'; const CLASS_NAME_SHOWING$1 = 'showing'; const CLASS_NAME_HIDING = 'hiding'; const CLASS_NAME_BACKDROP = 'offcanvas-backdrop'; const OPEN_SELECTOR = '.offcanvas.show'; const EVENT_SHOW$3 = `show${EVENT_KEY$3}`; const EVENT_SHOWN$3 = `shown${EVENT_KEY$3}`; const EVENT_HIDE$3 = `hide${EVENT_KEY$3}`; const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY$3}`; const EVENT_HIDDEN$3 = `hidden${EVENT_KEY$3}`; const EVENT_RESIZE = `resize${EVENT_KEY$3}`; const EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$3}${DATA_API_KEY$1}`; const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$3}`; const SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle="offcanvas"]'; const Default$5 = { backdrop: true, keyboard: true, scroll: false }; const DefaultType$5 = { backdrop: '(boolean|string)', keyboard: 'boolean', scroll: 'boolean' }; /** * Class definition */ class Offcanvas extends BaseComponent { constructor(element, config) { super(element, config); this._isShown = false; this._backdrop = this._initializeBackDrop(); this._focustrap = this._initializeFocusTrap(); this._addEventListeners(); } // Getters static get Default() { return Default$5; } static get DefaultType() { return DefaultType$5; } static get NAME() { return NAME$6; } // Public toggle(relatedTarget) { return this._isShown ? this.hide() : this.show(relatedTarget); } show(relatedTarget) { if (this._isShown) { return; } const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$3, { relatedTarget }); if (showEvent.defaultPrevented) { return; } this._isShown = true; this._backdrop.show(); if (!this._config.scroll) { new ScrollBarHelper().hide(); } this._element.setAttribute('aria-modal', true); this._element.setAttribute('role', 'dialog'); this._element.classList.add(CLASS_NAME_SHOWING$1); const completeCallBack = () => { if (!this._config.scroll || this._config.backdrop) { this._focustrap.activate(); } this._element.classList.add(CLASS_NAME_SHOW$3); this._element.classList.remove(CLASS_NAME_SHOWING$1); EventHandler.trigger(this._element, EVENT_SHOWN$3, { relatedTarget }); }; this._queueCallback(completeCallBack, this._element, true); } hide() { if (!this._isShown) { return; } const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$3); if (hideEvent.defaultPrevented) { return; } this._focustrap.deactivate(); this._element.blur(); this._isShown = false; this._element.classList.add(CLASS_NAME_HIDING); this._backdrop.hide(); const completeCallback = () => { this._element.classList.remove(CLASS_NAME_SHOW$3, CLASS_NAME_HIDING); this._element.removeAttribute('aria-modal'); this._element.removeAttribute('role'); if (!this._config.scroll) { new ScrollBarHelper().reset(); } EventHandler.trigger(this._element, EVENT_HIDDEN$3); }; this._queueCallback(completeCallback, this._element, true); } dispose() { this._backdrop.dispose(); this._focustrap.deactivate(); super.dispose(); } // Private _initializeBackDrop() { const clickCallback = () => { if (this._config.backdrop === 'static') { EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED); return; } this.hide(); }; // 'static' option will be translated to true, and booleans will keep their value const isVisible = Boolean(this._config.backdrop); return new Backdrop({ className: CLASS_NAME_BACKDROP, isVisible, isAnimated: true, rootElement: this._element.parentNode, clickCallback: isVisible ? clickCallback : null }); } _initializeFocusTrap() { return new FocusTrap({ trapElement: this._element }); } _addEventListeners() { EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { if (event.key !== ESCAPE_KEY) { return; } if (this._config.keyboard) { this.hide(); return; } EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED); }); } // Static static jQueryInterface(config) { return this.each(function () { const data = Offcanvas.getOrCreateInstance(this, config); if (typeof config !== 'string') { return; } if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { throw new TypeError(`No method named "${config}"`); } data[config](this); }); } } /** * Data API implementation */ EventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, function (event) { const target = SelectorEngine.getElementFromSelector(this); if (['A', 'AREA'].includes(this.tagName)) { event.preventDefault(); } if (isDisabled(this)) { return; } EventHandler.one(target, EVENT_HIDDEN$3, () => { // focus on trigger when it is closed if (isVisible(this)) { this.focus(); } }); // avoid conflict when clicking a toggler of an offcanvas, while another is open const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR); if (alreadyOpen && alreadyOpen !== target) { Offcanvas.getInstance(alreadyOpen).hide(); } const data = Offcanvas.getOrCreateInstance(target); data.toggle(this); }); EventHandler.on(window, EVENT_LOAD_DATA_API$2, () => { for (const selector of SelectorEngine.find(OPEN_SELECTOR)) { Offcanvas.getOrCreateInstance(selector).show(); } }); EventHandler.on(window, EVENT_RESIZE, () => { for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) { if (getComputedStyle(element).position !== 'fixed') { Offcanvas.getOrCreateInstance(element).hide(); } } }); enableDismissTrigger(Offcanvas); /** * jQuery */ defineJQueryPlugin(Offcanvas); /** * -------------------------------------------------------------------------- * Bootstrap util/sanitizer.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ // js-docs-start allow-list const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i; const DefaultAllowlist = { // Global attributes allowed on any supplied element below. '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN], a: ['target', 'href', 'title', 'rel'], area: [], b: [], br: [], col: [], code: [], div: [], em: [], hr: [], h1: [], h2: [], h3: [], h4: [], h5: [], h6: [], i: [], img: ['src', 'srcset', 'alt', 'title', 'width', 'height'], li: [], ol: [], p: [], pre: [], s: [], small: [], span: [], sub: [], sup: [], strong: [], u: [], ul: [] }; // js-docs-end allow-list const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']); /** * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation * contexts. * * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38 */ // eslint-disable-next-line unicorn/better-regex const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i; const allowedAttribute = (attribute, allowedAttributeList) => { const attributeName = attribute.nodeName.toLowerCase(); if (allowedAttributeList.includes(attributeName)) { if (uriAttributes.has(attributeName)) { return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue)); } return true; } // Check if a regular expression validates the attribute. return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName)); }; function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) { if (!unsafeHtml.length) { return unsafeHtml; } if (sanitizeFunction && typeof sanitizeFunction === 'function') { return sanitizeFunction(unsafeHtml); } const domParser = new window.DOMParser(); const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html'); const elements = [].concat(...createdDocument.body.querySelectorAll('*')); for (const element of elements) { const elementName = element.nodeName.toLowerCase(); if (!Object.keys(allowList).includes(elementName)) { element.remove(); continue; } const attributeList = [].concat(...element.attributes); const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []); for (const attribute of attributeList) { if (!allowedAttribute(attribute, allowedAttributes)) { element.removeAttribute(attribute.nodeName); } } } return createdDocument.body.innerHTML; } /** * -------------------------------------------------------------------------- * Bootstrap util/template-factory.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$5 = 'TemplateFactory'; const Default$4 = { allowList: DefaultAllowlist, content: {}, // { selector : text , selector2 : text2 , } extraClass: '', html: false, sanitize: true, sanitizeFn: null, template: '<div></div>' }; const DefaultType$4 = { allowList: 'object', content: 'object', extraClass: '(string|function)', html: 'boolean', sanitize: 'boolean', sanitizeFn: '(null|function)', template: 'string' }; const DefaultContentType = { entry: '(string|element|function|null)', selector: '(string|element)' }; /** * Class definition */ class TemplateFactory extends Config { constructor(config) { super(); this._config = this._getConfig(config); } // Getters static get Default() { return Default$4; } static get DefaultType() { return DefaultType$4; } static get NAME() { return NAME$5; } // Public getContent() { return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean); } hasContent() { return this.getContent().length > 0; } changeContent(content) { this._checkContent(content); this._config.content = { ...this._config.content, ...content }; return this; } toHtml() { const templateWrapper = document.createElement('div'); templateWrapper.innerHTML = this._maybeSanitize(this._config.template); for (const [selector, text] of Object.entries(this._config.content)) { this._setContent(templateWrapper, text, selector); } const template = templateWrapper.children[0]; const extraClass = this._resolvePossibleFunction(this._config.extraClass); if (extraClass) { template.classList.add(...extraClass.split(' ')); } return template; } // Private _typeCheckConfig(config) { super._typeCheckConfig(config); this._checkContent(config.content); } _checkContent(arg) { for (const [selector, content] of Object.entries(arg)) { super._typeCheckConfig({ selector, entry: content }, DefaultContentType); } } _setContent(template, content, selector) { const templateElement = SelectorEngine.findOne(selector, template); if (!templateElement) { return; } content = this._resolvePossibleFunction(content); if (!content) { templateElement.remove(); return; } if (isElement(content)) { this._putElementInTemplate(getElement(content), templateElement); return; } if (this._config.html) { templateElement.innerHTML = this._maybeSanitize(content); return; } templateElement.textContent = content; } _maybeSanitize(arg) { return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg; } _resolvePossibleFunction(arg) { return execute(arg, [this]); } _putElementInTemplate(element, templateElement) { if (this._config.html) { templateElement.innerHTML = ''; templateElement.append(element); return; } templateElement.textContent = element.textContent; } } /** * -------------------------------------------------------------------------- * Bootstrap tooltip.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$4 = 'tooltip'; const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']); const CLASS_NAME_FADE$2 = 'fade'; const CLASS_NAME_MODAL = 'modal'; const CLASS_NAME_SHOW$2 = 'show'; const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'; const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`; const EVENT_MODAL_HIDE = 'hide.bs.modal'; const TRIGGER_HOVER = 'hover'; const TRIGGER_FOCUS = 'focus'; const TRIGGER_CLICK = 'click'; const TRIGGER_MANUAL = 'manual'; const EVENT_HIDE$2 = 'hide'; const EVENT_HIDDEN$2 = 'hidden'; const EVENT_SHOW$2 = 'show'; const EVENT_SHOWN$2 = 'shown'; const EVENT_INSERTED = 'inserted'; const EVENT_CLICK$1 = 'click'; const EVENT_FOCUSIN$1 = 'focusin'; const EVENT_FOCUSOUT$1 = 'focusout'; const EVENT_MOUSEENTER = 'mouseenter'; const EVENT_MOUSELEAVE = 'mouseleave'; const AttachmentMap = { AUTO: 'auto', TOP: 'top', RIGHT: isRTL() ? 'left' : 'right', BOTTOM: 'bottom', LEFT: isRTL() ? 'right' : 'left' }; const Default$3 = { allowList: DefaultAllowlist, animation: true, boundary: 'clippingParents', container: false, customClass: '', delay: 0, fallbackPlacements: ['top', 'right', 'bottom', 'left'], html: false, offset: [0, 6], placement: 'top', popperConfig: null, sanitize: true, sanitizeFn: null, selector: false, template: '<div class="tooltip" role="tooltip">' + '<div class="tooltip-arrow"></div>' + '<div class="tooltip-inner"></div>' + '</div>', title: '', trigger: 'hover focus' }; const DefaultType$3 = { allowList: 'object', animation: 'boolean', boundary: '(string|element)', container: '(string|element|boolean)', customClass: '(string|function)', delay: '(number|object)', fallbackPlacements: 'array', html: 'boolean', offset: '(array|string|function)', placement: '(string|function)', popperConfig: '(null|object|function)', sanitize: 'boolean', sanitizeFn: '(null|function)', selector: '(string|boolean)', template: 'string', title: '(string|element|function)', trigger: 'string' }; /** * Class definition */ class Tooltip extends BaseComponent { constructor(element, config) { if (typeof Popper__namespace === 'undefined') { throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)'); } super(element, config); // Private this._isEnabled = true; this._timeout = 0; this._isHovered = null; this._activeTrigger = {}; this._popper = null; this._templateFactory = null; this._newContent = null; // Protected this.tip = null; this._setListeners(); if (!this._config.selector) { this._fixTitle(); } } // Getters static get Default() { return Default$3; } static get DefaultType() { return DefaultType$3; } static get NAME() { return NAME$4; } // Public enable() { this._isEnabled = true; } disable() { this._isEnabled = false; } toggleEnabled() { this._isEnabled = !this._isEnabled; } toggle() { if (!this._isEnabled) { return; } this._activeTrigger.click = !this._activeTrigger.click; if (this._isShown()) { this._leave(); return; } this._enter(); } dispose() { clearTimeout(this._timeout); EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler); if (this._element.getAttribute('data-bs-original-title')) { this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title')); } this._disposePopper(); super.dispose(); } show() { if (this._element.style.display === 'none') { throw new Error('Please use show on visible elements'); } if (!(this._isWithContent() && this._isEnabled)) { return; } const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW$2)); const shadowRoot = findShadowRoot(this._element); const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element); if (showEvent.defaultPrevented || !isInTheDom) { return; } // TODO: v6 remove this or make it optional this._disposePopper(); const tip = this._getTipElement(); this._element.setAttribute('aria-describedby', tip.getAttribute('id')); const { container } = this._config; if (!this._element.ownerDocument.documentElement.contains(this.tip)) { container.append(tip); EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED)); } this._popper = this._createPopper(tip); tip.classList.add(CLASS_NAME_SHOW$2); // If this is a touch-enabled device we add extra // empty mouseover listeners to the body's immediate children; // only needed because of broken event delegation on iOS // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html if ('ontouchstart' in document.documentElement) { for (const element of [].concat(...document.body.children)) { EventHandler.on(element, 'mouseover', noop); } } const complete = () => { EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN$2)); if (this._isHovered === false) { this._leave(); } this._isHovered = false; }; this._queueCallback(complete, this.tip, this._isAnimated()); } hide() { if (!this._isShown()) { return; } const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE$2)); if (hideEvent.defaultPrevented) { return; } const tip = this._getTipElement(); tip.classList.remove(CLASS_NAME_SHOW$2); // If this is a touch-enabled device we remove the extra // empty mouseover listeners we added for iOS support if ('ontouchstart' in document.documentElement) { for (const element of [].concat(...document.body.children)) { EventHandler.off(element, 'mouseover', noop); } } this._activeTrigger[TRIGGER_CLICK] = false; this._activeTrigger[TRIGGER_FOCUS] = false; this._activeTrigger[TRIGGER_HOVER] = false; this._isHovered = null; // it is a trick to support manual triggering const complete = () => { if (this._isWithActiveTrigger()) { return; } if (!this._isHovered) { this._disposePopper(); } this._element.removeAttribute('aria-describedby'); EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN$2)); }; this._queueCallback(complete, this.tip, this._isAnimated()); } update() { if (this._popper) { this._popper.update(); } } // Protected _isWithContent() { return Boolean(this._getTitle()); } _getTipElement() { if (!this.tip) { this.tip = this._createTipElement(this._newContent || this._getContentForTemplate()); } return this.tip; } _createTipElement(content) { const tip = this._getTemplateFactory(content).toHtml(); // TODO: remove this check in v6 if (!tip) { return null; } tip.classList.remove(CLASS_NAME_FADE$2, CLASS_NAME_SHOW$2); // TODO: v6 the following can be achieved with CSS only tip.classList.add(`bs-${this.constructor.NAME}-auto`); const tipId = getUID(this.constructor.NAME).toString(); tip.setAttribute('id', tipId); if (this._isAnimated()) { tip.classList.add(CLASS_NAME_FADE$2); } return tip; } setContent(content) { this._newContent = content; if (this._isShown()) { this._disposePopper(); this.show(); } } _getTemplateFactory(content) { if (this._templateFactory) { this._templateFactory.changeContent(content); } else { this._templateFactory = new TemplateFactory({ ...this._config, // the `content` var has to be after `this._config` // to override config.content in case of popover content, extraClass: this._resolvePossibleFunction(this._config.customClass) }); } return this._templateFactory; } _getContentForTemplate() { return { [SELECTOR_TOOLTIP_INNER]: this._getTitle() }; } _getTitle() { return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title'); } // Private _initializeOnDelegatedTarget(event) { return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig()); } _isAnimated() { return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE$2); } _isShown() { return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW$2); } _createPopper(tip) { const placement = execute(this._config.placement, [this, tip, this._element]); const attachment = AttachmentMap[placement.toUpperCase()]; return Popper__namespace.createPopper(this._element, tip, this._getPopperConfig(attachment)); } _getOffset() { const { offset } = this._config; if (typeof offset === 'string') { return offset.split(',').map(value => Number.parseInt(value, 10)); } if (typeof offset === 'function') { return popperData => offset(popperData, this._element); } return offset; } _resolvePossibleFunction(arg) { return execute(arg, [this._element]); } _getPopperConfig(attachment) { const defaultBsPopperConfig = { placement: attachment, modifiers: [{ name: 'flip', options: { fallbackPlacements: this._config.fallbackPlacements } }, { name: 'offset', options: { offset: this._getOffset() } }, { name: 'preventOverflow', options: { boundary: this._config.boundary } }, { name: 'arrow', options: { element: `.${this.constructor.NAME}-arrow` } }, { name: 'preSetPlacement', enabled: true, phase: 'beforeMain', fn: data => { // Pre-set Popper's placement attribute in order to read the arrow sizes properly. // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement this._getTipElement().setAttribute('data-popper-placement', data.state.placement); } }] }; return { ...defaultBsPopperConfig, ...execute(this._config.popperConfig, [defaultBsPopperConfig]) }; } _setListeners() { const triggers = this._config.trigger.split(' '); for (const trigger of triggers) { if (trigger === 'click') { EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => { const context = this._initializeOnDelegatedTarget(event); context.toggle(); }); } else if (trigger !== TRIGGER_MANUAL) { const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN$1); const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT$1); EventHandler.on(this._element, eventIn, this._config.selector, event => { const context = this._initializeOnDelegatedTarget(event); context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true; context._enter(); }); EventHandler.on(this._element, eventOut, this._config.selector, event => { const context = this._initializeOnDelegatedTarget(event); context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget); context._leave(); }); } } this._hideModalHandler = () => { if (this._element) { this.hide(); } }; EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler); } _fixTitle() { const title = this._element.getAttribute('title'); if (!title) { return; } if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) { this._element.setAttribute('aria-label', title); } this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility this._element.removeAttribute('title'); } _enter() { if (this._isShown() || this._isHovered) { this._isHovered = true; return; } this._isHovered = true; this._setTimeout(() => { if (this._isHovered) { this.show(); } }, this._config.delay.show); } _leave() { if (this._isWithActiveTrigger()) { return; } this._isHovered = false; this._setTimeout(() => { if (!this._isHovered) { this.hide(); } }, this._config.delay.hide); } _setTimeout(handler, timeout) { clearTimeout(this._timeout); this._timeout = setTimeout(handler, timeout); } _isWithActiveTrigger() { return Object.values(this._activeTrigger).includes(true); } _getConfig(config) { const dataAttributes = Manipulator.getDataAttributes(this._element); for (const dataAttribute of Object.keys(dataAttributes)) { if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) { delete dataAttributes[dataAttribute]; } } config = { ...dataAttributes, ...(typeof config === 'object' && config ? config : {}) }; config = this._mergeConfigObj(config); config = this._configAfterMerge(config); this._typeCheckConfig(config); return config; } _configAfterMerge(config) { config.container = config.container === false ? document.body : getElement(config.container); if (typeof config.delay === 'number') { config.delay = { show: config.delay, hide: config.delay }; } if (typeof config.title === 'number') { config.title = config.title.toString(); } if (typeof config.content === 'number') { config.content = config.content.toString(); } return config; } _getDelegateConfig() { const config = {}; for (const [key, value] of Object.entries(this._config)) { if (this.constructor.Default[key] !== value) { config[key] = value; } } config.selector = false; config.trigger = 'manual'; // In the future can be replaced with: // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]]) // `Object.fromEntries(keysWithDifferentValues)` return config; } _disposePopper() { if (this._popper) { this._popper.destroy(); this._popper = null; } if (this.tip) { this.tip.remove(); this.tip = null; } } // Static static jQueryInterface(config) { return this.each(function () { const data = Tooltip.getOrCreateInstance(this, config); if (typeof config !== 'string') { return; } if (typeof data[config] === 'undefined') { throw new TypeError(`No method named "${config}"`); } data[config](); }); } } /** * jQuery */ defineJQueryPlugin(Tooltip); /** * -------------------------------------------------------------------------- * Bootstrap popover.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$3 = 'popover'; const SELECTOR_TITLE = '.popover-header'; const SELECTOR_CONTENT = '.popover-body'; const Default$2 = { ...Tooltip.Default, content: '', offset: [0, 8], placement: 'right', template: '<div class="popover" role="tooltip">' + '<div class="popover-arrow"></div>' + '<h3 class="popover-header"></h3>' + '<div class="popover-body"></div>' + '</div>', trigger: 'click' }; const DefaultType$2 = { ...Tooltip.DefaultType, content: '(null|string|element|function)' }; /** * Class definition */ class Popover extends Tooltip { // Getters static get Default() { return Default$2; } static get DefaultType() { return DefaultType$2; } static get NAME() { return NAME$3; } // Overrides _isWithContent() { return this._getTitle() || this._getContent(); } // Private _getContentForTemplate() { return { [SELECTOR_TITLE]: this._getTitle(), [SELECTOR_CONTENT]: this._getContent() }; } _getContent() { return this._resolvePossibleFunction(this._config.content); } // Static static jQueryInterface(config) { return this.each(function () { const data = Popover.getOrCreateInstance(this, config); if (typeof config !== 'string') { return; } if (typeof data[config] === 'undefined') { throw new TypeError(`No method named "${config}"`); } data[config](); }); } } /** * jQuery */ defineJQueryPlugin(Popover); /** * -------------------------------------------------------------------------- * Bootstrap scrollspy.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$2 = 'scrollspy'; const DATA_KEY$2 = 'bs.scrollspy'; const EVENT_KEY$2 = `.${DATA_KEY$2}`; const DATA_API_KEY = '.data-api'; const EVENT_ACTIVATE = `activate${EVENT_KEY$2}`; const EVENT_CLICK = `click${EVENT_KEY$2}`; const EVENT_LOAD_DATA_API$1 = `load${EVENT_KEY$2}${DATA_API_KEY}`; const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'; const CLASS_NAME_ACTIVE$1 = 'active'; const SELECTOR_DATA_SPY = '[data-bs-spy="scroll"]'; const SELECTOR_TARGET_LINKS = '[href]'; const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'; const SELECTOR_NAV_LINKS = '.nav-link'; const SELECTOR_NAV_ITEMS = '.nav-item'; const SELECTOR_LIST_ITEMS = '.list-group-item'; const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`; const SELECTOR_DROPDOWN = '.dropdown'; const SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle'; const Default$1 = { offset: null, // TODO: v6 @deprecated, keep it for backwards compatibility reasons rootMargin: '0px 0px -25%', smoothScroll: false, target: null, threshold: [0.1, 0.5, 1] }; const DefaultType$1 = { offset: '(number|null)', // TODO v6 @deprecated, keep it for backwards compatibility reasons rootMargin: 'string', smoothScroll: 'boolean', target: 'element', threshold: 'array' }; /** * Class definition */ class ScrollSpy extends BaseComponent { constructor(element, config) { super(element, config); // this._element is the observablesContainer and config.target the menu links wrapper this._targetLinks = new Map(); this._observableSections = new Map(); this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element; this._activeTarget = null; this._observer = null; this._previousScrollData = { visibleEntryTop: 0, parentScrollTop: 0 }; this.refresh(); // initialize } // Getters static get Default() { return Default$1; } static get DefaultType() { return DefaultType$1; } static get NAME() { return NAME$2; } // Public refresh() { this._initializeTargetsAndObservables(); this._maybeEnableSmoothScroll(); if (this._observer) { this._observer.disconnect(); } else { this._observer = this._getNewObserver(); } for (const section of this._observableSections.values()) { this._observer.observe(section); } } dispose() { this._observer.disconnect(); super.dispose(); } // Private _configAfterMerge(config) { // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case config.target = getElement(config.target) || document.body; // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin; if (typeof config.threshold === 'string') { config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value)); } return config; } _maybeEnableSmoothScroll() { if (!this._config.smoothScroll) { return; } // unregister any previous listeners EventHandler.off(this._config.target, EVENT_CLICK); EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => { const observableSection = this._observableSections.get(event.target.hash); if (observableSection) { event.preventDefault(); const root = this._rootElement || window; const height = observableSection.offsetTop - this._element.offsetTop; if (root.scrollTo) { root.scrollTo({ top: height, behavior: 'smooth' }); return; } // Chrome 60 doesn't support `scrollTo` root.scrollTop = height; } }); } _getNewObserver() { const options = { root: this._rootElement, threshold: this._config.threshold, rootMargin: this._config.rootMargin }; return new IntersectionObserver(entries => this._observerCallback(entries), options); } // The logic of selection _observerCallback(entries) { const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`); const activate = entry => { this._previousScrollData.visibleEntryTop = entry.target.offsetTop; this._process(targetElement(entry)); }; const parentScrollTop = (this._rootElement || document.documentElement).scrollTop; const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop; this._previousScrollData.parentScrollTop = parentScrollTop; for (const entry of entries) { if (!entry.isIntersecting) { this._activeTarget = null; this._clearActiveClass(targetElement(entry)); continue; } const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop; // if we are scrolling down, pick the bigger offsetTop if (userScrollsDown && entryIsLowerThanPrevious) { activate(entry); // if parent isn't scrolled, let's keep the first visible item, breaking the iteration if (!parentScrollTop) { return; } continue; } // if we are scrolling up, pick the smallest offsetTop if (!userScrollsDown && !entryIsLowerThanPrevious) { activate(entry); } } } _initializeTargetsAndObservables() { this._targetLinks = new Map(); this._observableSections = new Map(); const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target); for (const anchor of targetLinks) { // ensure that the anchor has an id and is not disabled if (!anchor.hash || isDisabled(anchor)) { continue; } const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element); // ensure that the observableSection exists & is visible if (isVisible(observableSection)) { this._targetLinks.set(decodeURI(anchor.hash), anchor); this._observableSections.set(anchor.hash, observableSection); } } } _process(target) { if (this._activeTarget === target) { return; } this._clearActiveClass(this._config.target); this._activeTarget = target; target.classList.add(CLASS_NAME_ACTIVE$1); this._activateParents(target); EventHandler.trigger(this._element, EVENT_ACTIVATE, { relatedTarget: target }); } _activateParents(target) { // Activate dropdown parents if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) { SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE$1, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE$1); return; } for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) { // Set triggered links parents as active // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor for (const item of SelectorEngine.prev(listGroup, SELECTOR_LINK_ITEMS)) { item.classList.add(CLASS_NAME_ACTIVE$1); } } } _clearActiveClass(parent) { parent.classList.remove(CLASS_NAME_ACTIVE$1); const activeNodes = SelectorEngine.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE$1}`, parent); for (const node of activeNodes) { node.classList.remove(CLASS_NAME_ACTIVE$1); } } // Static static jQueryInterface(config) { return this.each(function () { const data = ScrollSpy.getOrCreateInstance(this, config); if (typeof config !== 'string') { return; } if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { throw new TypeError(`No method named "${config}"`); } data[config](); }); } } /** * Data API implementation */ EventHandler.on(window, EVENT_LOAD_DATA_API$1, () => { for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) { ScrollSpy.getOrCreateInstance(spy); } }); /** * jQuery */ defineJQueryPlugin(ScrollSpy); /** * -------------------------------------------------------------------------- * Bootstrap tab.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$1 = 'tab'; const DATA_KEY$1 = 'bs.tab'; const EVENT_KEY$1 = `.${DATA_KEY$1}`; const EVENT_HIDE$1 = `hide${EVENT_KEY$1}`; const EVENT_HIDDEN$1 = `hidden${EVENT_KEY$1}`; const EVENT_SHOW$1 = `show${EVENT_KEY$1}`; const EVENT_SHOWN$1 = `shown${EVENT_KEY$1}`; const EVENT_CLICK_DATA_API = `click${EVENT_KEY$1}`; const EVENT_KEYDOWN = `keydown${EVENT_KEY$1}`; const EVENT_LOAD_DATA_API = `load${EVENT_KEY$1}`; const ARROW_LEFT_KEY = 'ArrowLeft'; const ARROW_RIGHT_KEY = 'ArrowRight'; const ARROW_UP_KEY = 'ArrowUp'; const ARROW_DOWN_KEY = 'ArrowDown'; const CLASS_NAME_ACTIVE = 'active'; const CLASS_NAME_FADE$1 = 'fade'; const CLASS_NAME_SHOW$1 = 'show'; const CLASS_DROPDOWN = 'dropdown'; const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'; const SELECTOR_DROPDOWN_MENU = '.dropdown-menu'; const NOT_SELECTOR_DROPDOWN_TOGGLE = ':not(.dropdown-toggle)'; const SELECTOR_TAB_PANEL = '.list-group, .nav, [role="tablist"]'; const SELECTOR_OUTER = '.nav-item, .list-group-item'; const SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role="tab"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`; const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]'; // TODO: could only be `tab` in v6 const SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`; const SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle="tab"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="pill"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="list"]`; /** * Class definition */ class Tab extends BaseComponent { constructor(element) { super(element); this._parent = this._element.closest(SELECTOR_TAB_PANEL); if (!this._parent) { return; // TODO: should throw exception in v6 // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`) } // Set up initial aria attributes this._setInitialAttributes(this._parent, this._getChildren()); EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event)); } // Getters static get NAME() { return NAME$1; } // Public show() { // Shows this elem and deactivate the active sibling if exists const innerElem = this._element; if (this._elemIsActive(innerElem)) { return; } // Search for active tab on same parent to deactivate it const active = this._getActiveElem(); const hideEvent = active ? EventHandler.trigger(active, EVENT_HIDE$1, { relatedTarget: innerElem }) : null; const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW$1, { relatedTarget: active }); if (showEvent.defaultPrevented || hideEvent && hideEvent.defaultPrevented) { return; } this._deactivate(active, innerElem); this._activate(innerElem, active); } // Private _activate(element, relatedElem) { if (!element) { return; } element.classList.add(CLASS_NAME_ACTIVE); this._activate(SelectorEngine.getElementFromSelector(element)); // Search and activate/show the proper section const complete = () => { if (element.getAttribute('role') !== 'tab') { element.classList.add(CLASS_NAME_SHOW$1); return; } element.removeAttribute('tabindex'); element.setAttribute('aria-selected', true); this._toggleDropDown(element, true); EventHandler.trigger(element, EVENT_SHOWN$1, { relatedTarget: relatedElem }); }; this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE$1)); } _deactivate(element, relatedElem) { if (!element) { return; } element.classList.remove(CLASS_NAME_ACTIVE); element.blur(); this._deactivate(SelectorEngine.getElementFromSelector(element)); // Search and deactivate the shown section too const complete = () => { if (element.getAttribute('role') !== 'tab') { element.classList.remove(CLASS_NAME_SHOW$1); return; } element.setAttribute('aria-selected', false); element.setAttribute('tabindex', '-1'); this._toggleDropDown(element, false); EventHandler.trigger(element, EVENT_HIDDEN$1, { relatedTarget: relatedElem }); }; this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE$1)); } _keydown(event) { if (![ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)) { return; } event.stopPropagation(); // stopPropagation/preventDefault both added to support up/down keys without scrolling the page event.preventDefault(); const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key); const nextActiveElement = getNextActiveElement(this._getChildren().filter(element => !isDisabled(element)), event.target, isNext, true); if (nextActiveElement) { nextActiveElement.focus({ preventScroll: true }); Tab.getOrCreateInstance(nextActiveElement).show(); } } _getChildren() { // collection of inner elements return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent); } _getActiveElem() { return this._getChildren().find(child => this._elemIsActive(child)) || null; } _setInitialAttributes(parent, children) { this._setAttributeIfNotExists(parent, 'role', 'tablist'); for (const child of children) { this._setInitialAttributesOnChild(child); } } _setInitialAttributesOnChild(child) { child = this._getInnerElement(child); const isActive = this._elemIsActive(child); const outerElem = this._getOuterElement(child); child.setAttribute('aria-selected', isActive); if (outerElem !== child) { this._setAttributeIfNotExists(outerElem, 'role', 'presentation'); } if (!isActive) { child.setAttribute('tabindex', '-1'); } this._setAttributeIfNotExists(child, 'role', 'tab'); // set attributes to the related panel too this._setInitialAttributesOnTargetPanel(child); } _setInitialAttributesOnTargetPanel(child) { const target = SelectorEngine.getElementFromSelector(child); if (!target) { return; } this._setAttributeIfNotExists(target, 'role', 'tabpanel'); if (child.id) { this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`); } } _toggleDropDown(element, open) { const outerElem = this._getOuterElement(element); if (!outerElem.classList.contains(CLASS_DROPDOWN)) { return; } const toggle = (selector, className) => { const element = SelectorEngine.findOne(selector, outerElem); if (element) { element.classList.toggle(className, open); } }; toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE); toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW$1); outerElem.setAttribute('aria-expanded', open); } _setAttributeIfNotExists(element, attribute, value) { if (!element.hasAttribute(attribute)) { element.setAttribute(attribute, value); } } _elemIsActive(elem) { return elem.classList.contains(CLASS_NAME_ACTIVE); } // Try to get the inner element (usually the .nav-link) _getInnerElement(elem) { return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem); } // Try to get the outer element (usually the .nav-item) _getOuterElement(elem) { return elem.closest(SELECTOR_OUTER) || elem; } // Static static jQueryInterface(config) { return this.each(function () { const data = Tab.getOrCreateInstance(this); if (typeof config !== 'string') { return; } if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { throw new TypeError(`No method named "${config}"`); } data[config](); }); } } /** * Data API implementation */ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { if (['A', 'AREA'].includes(this.tagName)) { event.preventDefault(); } if (isDisabled(this)) { return; } Tab.getOrCreateInstance(this).show(); }); /** * Initialize on focus */ EventHandler.on(window, EVENT_LOAD_DATA_API, () => { for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) { Tab.getOrCreateInstance(element); } }); /** * jQuery */ defineJQueryPlugin(Tab); /** * -------------------------------------------------------------------------- * Bootstrap toast.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME = 'toast'; const DATA_KEY = 'bs.toast'; const EVENT_KEY = `.${DATA_KEY}`; const EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`; const EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`; const EVENT_FOCUSIN = `focusin${EVENT_KEY}`; const EVENT_FOCUSOUT = `focusout${EVENT_KEY}`; const EVENT_HIDE = `hide${EVENT_KEY}`; const EVENT_HIDDEN = `hidden${EVENT_KEY}`; const EVENT_SHOW = `show${EVENT_KEY}`; const EVENT_SHOWN = `shown${EVENT_KEY}`; const CLASS_NAME_FADE = 'fade'; const CLASS_NAME_HIDE = 'hide'; // @deprecated - kept here only for backwards compatibility const CLASS_NAME_SHOW = 'show'; const CLASS_NAME_SHOWING = 'showing'; const DefaultType = { animation: 'boolean', autohide: 'boolean', delay: 'number' }; const Default = { animation: true, autohide: true, delay: 5000 }; /** * Class definition */ class Toast extends BaseComponent { constructor(element, config) { super(element, config); this._timeout = null; this._hasMouseInteraction = false; this._hasKeyboardInteraction = false; this._setListeners(); } // Getters static get Default() { return Default; } static get DefaultType() { return DefaultType; } static get NAME() { return NAME; } // Public show() { const showEvent = EventHandler.trigger(this._element, EVENT_SHOW); if (showEvent.defaultPrevented) { return; } this._clearTimeout(); if (this._config.animation) { this._element.classList.add(CLASS_NAME_FADE); } const complete = () => { this._element.classList.remove(CLASS_NAME_SHOWING); EventHandler.trigger(this._element, EVENT_SHOWN); this._maybeScheduleHide(); }; this._element.classList.remove(CLASS_NAME_HIDE); // @deprecated reflow(this._element); this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING); this._queueCallback(complete, this._element, this._config.animation); } hide() { if (!this.isShown()) { return; } const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE); if (hideEvent.defaultPrevented) { return; } const complete = () => { this._element.classList.add(CLASS_NAME_HIDE); // @deprecated this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW); EventHandler.trigger(this._element, EVENT_HIDDEN); }; this._element.classList.add(CLASS_NAME_SHOWING); this._queueCallback(complete, this._element, this._config.animation); } dispose() { this._clearTimeout(); if (this.isShown()) { this._element.classList.remove(CLASS_NAME_SHOW); } super.dispose(); } isShown() { return this._element.classList.contains(CLASS_NAME_SHOW); } // Private _maybeScheduleHide() { if (!this._config.autohide) { return; } if (this._hasMouseInteraction || this._hasKeyboardInteraction) { return; } this._timeout = setTimeout(() => { this.hide(); }, this._config.delay); } _onInteraction(event, isInteracting) { switch (event.type) { case 'mouseover': case 'mouseout': { this._hasMouseInteraction = isInteracting; break; } case 'focusin': case 'focusout': { this._hasKeyboardInteraction = isInteracting; break; } } if (isInteracting) { this._clearTimeout(); return; } const nextElement = event.relatedTarget; if (this._element === nextElement || this._element.contains(nextElement)) { return; } this._maybeScheduleHide(); } _setListeners() { EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true)); EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false)); EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true)); EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false)); } _clearTimeout() { clearTimeout(this._timeout); this._timeout = null; } // Static static jQueryInterface(config) { return this.each(function () { const data = Toast.getOrCreateInstance(this, config); if (typeof config === 'string') { if (typeof data[config] === 'undefined') { throw new TypeError(`No method named "${config}"`); } data[config](this); } }); } } /** * Data API implementation */ enableDismissTrigger(Toast); /** * jQuery */ defineJQueryPlugin(Toast); /** * -------------------------------------------------------------------------- * Bootstrap index.umd.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ const index_umd = { Alert, Button, Carousel, Collapse, Dropdown, Modal, Offcanvas, Popover, ScrollSpy, Tab, Toast, Tooltip }; return index_umd; })); //# sourceMappingURL=bootstrap.js.map ; //# sourceMappingURL=scripts.js.map
Evidence later
Solution Remove all comments that return information that may help an attacker and fix any underlying problems they refer to.
-
-
Appendix
Alert types
This section contains additional information on the types of alerts in the report.
-
Cloud Metadata Potentially Exposed
Source raised by an active scanner (Cloud Metadata Potentially Exposed) Reference -
CSP: Wildcard Directive
Source raised by a passive scanner (CSP) CWE ID 693 WASC ID 15 Reference -
Content Security Policy (CSP) Header Not Set
-
Cross-Domain Misconfiguration
Source raised by a passive scanner (Cross-Domain Misconfiguration) CWE ID 264 WASC ID 14 Reference -
Directory Browsing
Source raised by an active scanner (Directory Browsing) CWE ID 548 WASC ID 48 Reference -
Missing Anti-clickjacking Header
Source raised by a passive scanner (Anti-clickjacking Header) CWE ID 1021 WASC ID 15 Reference -
Server Leaks Information via "X-Powered-By" HTTP Response Header Field(s)
Source raised by a passive scanner (Server Leaks Information via "X-Powered-By" HTTP Response Header Field(s)) CWE ID 200 WASC ID 13 Reference -
X-Content-Type-Options Header Missing
Source raised by a passive scanner (X-Content-Type-Options Header Missing) CWE ID 693 WASC ID 15 Reference -
Information Disclosure - Suspicious Comments
Source raised by a passive scanner (Information Disclosure - Suspicious Comments) CWE ID 200 WASC ID 13 -
Modern Web Application
Source raised by a passive scanner (Modern Web Application)