source: frontend/node_modules/react-refresh/cjs/react-refresh-babel.development.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 26.2 KB
RevLine 
[9af201e]1/** @license React vundefined
2 * react-refresh-babel.development.js
3 *
4 * Copyright (c) Facebook, Inc. and its affiliates.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE file in the root directory of this source tree.
8 */
9
10'use strict';
11
12if (process.env.NODE_ENV !== "production") {
13 (function() {
14'use strict';
15
16function ReactFreshBabelPlugin (babel) {
17 var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
18
19 if (typeof babel.env === 'function') {
20 // Only available in Babel 7.
21 var env = babel.env();
22
23 if (env !== 'development' && !opts.skipEnvCheck) {
24 throw new Error('React Refresh Babel transform should only be enabled in development environment. ' + 'Instead, the environment is: "' + env + '". If you want to override this check, pass {skipEnvCheck: true} as plugin options.');
25 }
26 }
27
28 var t = babel.types;
29 var refreshReg = t.identifier(opts.refreshReg || '$RefreshReg$');
30 var refreshSig = t.identifier(opts.refreshSig || '$RefreshSig$');
31 var registrationsByProgramPath = new Map();
32
33 function createRegistration(programPath, persistentID) {
34 var handle = programPath.scope.generateUidIdentifier('c');
35
36 if (!registrationsByProgramPath.has(programPath)) {
37 registrationsByProgramPath.set(programPath, []);
38 }
39
40 var registrations = registrationsByProgramPath.get(programPath);
41 registrations.push({
42 handle: handle,
43 persistentID: persistentID
44 });
45 return handle;
46 }
47
48 function isComponentishName(name) {
49 return typeof name === 'string' && name[0] >= 'A' && name[0] <= 'Z';
50 }
51
52 function findInnerComponents(inferredName, path, callback) {
53 var node = path.node;
54
55 switch (node.type) {
56 case 'Identifier':
57 {
58 if (!isComponentishName(node.name)) {
59 return false;
60 } // export default hoc(Foo)
61 // const X = hoc(Foo)
62
63
64 callback(inferredName, node, null);
65 return true;
66 }
67
68 case 'FunctionDeclaration':
69 {
70 // function Foo() {}
71 // export function Foo() {}
72 // export default function Foo() {}
73 callback(inferredName, node.id, null);
74 return true;
75 }
76
77 case 'ArrowFunctionExpression':
78 {
79 if (node.body.type === 'ArrowFunctionExpression') {
80 return false;
81 } // let Foo = () => {}
82 // export default hoc1(hoc2(() => {}))
83
84
85 callback(inferredName, node, path);
86 return true;
87 }
88
89 case 'FunctionExpression':
90 {
91 // let Foo = function() {}
92 // const Foo = hoc1(forwardRef(function renderFoo() {}))
93 // export default memo(function() {})
94 callback(inferredName, node, path);
95 return true;
96 }
97
98 case 'CallExpression':
99 {
100 var argsPath = path.get('arguments');
101
102 if (argsPath === undefined || argsPath.length === 0) {
103 return false;
104 }
105
106 var calleePath = path.get('callee');
107
108 switch (calleePath.node.type) {
109 case 'MemberExpression':
110 case 'Identifier':
111 {
112 var calleeSource = calleePath.getSource();
113 var firstArgPath = argsPath[0];
114 var innerName = inferredName + '$' + calleeSource;
115 var foundInside = findInnerComponents(innerName, firstArgPath, callback);
116
117 if (!foundInside) {
118 return false;
119 } // const Foo = hoc1(hoc2(() => {}))
120 // export default memo(React.forwardRef(function() {}))
121
122
123 callback(inferredName, node, path);
124 return true;
125 }
126
127 default:
128 {
129 return false;
130 }
131 }
132 }
133
134 case 'VariableDeclarator':
135 {
136 var init = node.init;
137
138 if (init === null) {
139 return false;
140 }
141
142 var name = node.id.name;
143
144 if (!isComponentishName(name)) {
145 return false;
146 }
147
148 switch (init.type) {
149 case 'ArrowFunctionExpression':
150 case 'FunctionExpression':
151 // Likely component definitions.
152 break;
153
154 case 'CallExpression':
155 {
156 // Maybe a HOC.
157 // Try to determine if this is some form of import.
158 var callee = init.callee;
159 var calleeType = callee.type;
160
161 if (calleeType === 'Import') {
162 return false;
163 } else if (calleeType === 'Identifier') {
164 if (callee.name.indexOf('require') === 0) {
165 return false;
166 } else if (callee.name.indexOf('import') === 0) {
167 return false;
168 } // Neither require nor import. Might be a HOC.
169 // Pass through.
170
171 }
172
173 break;
174 }
175
176 case 'TaggedTemplateExpression':
177 // Maybe something like styled.div`...`
178 break;
179
180 default:
181 return false;
182 }
183
184 var initPath = path.get('init');
185
186 var _foundInside = findInnerComponents(inferredName, initPath, callback);
187
188 if (_foundInside) {
189 return true;
190 } // See if this identifier is used in JSX. Then it's a component.
191
192
193 var binding = path.scope.getBinding(name);
194
195 if (binding === undefined) {
196 return;
197 }
198
199 var isLikelyUsedAsType = false;
200 var referencePaths = binding.referencePaths;
201
202 for (var i = 0; i < referencePaths.length; i++) {
203 var ref = referencePaths[i];
204
205 if (ref.node && ref.node.type !== 'JSXIdentifier' && ref.node.type !== 'Identifier') {
206 continue;
207 }
208
209 var refParent = ref.parent;
210
211 if (refParent.type === 'JSXOpeningElement') {
212 isLikelyUsedAsType = true;
213 } else if (refParent.type === 'CallExpression') {
214 var _callee = refParent.callee;
215 var fnName = void 0;
216
217 switch (_callee.type) {
218 case 'Identifier':
219 fnName = _callee.name;
220 break;
221
222 case 'MemberExpression':
223 fnName = _callee.property.name;
224 break;
225 }
226
227 switch (fnName) {
228 case 'createElement':
229 case 'jsx':
230 case 'jsxDEV':
231 case 'jsxs':
232 isLikelyUsedAsType = true;
233 break;
234 }
235 }
236
237 if (isLikelyUsedAsType) {
238 // const X = ... + later <X />
239 callback(inferredName, init, initPath);
240 return true;
241 }
242 }
243 }
244 }
245
246 return false;
247 }
248
249 function isBuiltinHook(hookName) {
250 switch (hookName) {
251 case 'useState':
252 case 'React.useState':
253 case 'useReducer':
254 case 'React.useReducer':
255 case 'useEffect':
256 case 'React.useEffect':
257 case 'useLayoutEffect':
258 case 'React.useLayoutEffect':
259 case 'useMemo':
260 case 'React.useMemo':
261 case 'useCallback':
262 case 'React.useCallback':
263 case 'useRef':
264 case 'React.useRef':
265 case 'useContext':
266 case 'React.useContext':
267 case 'useImperativeHandle':
268 case 'React.useImperativeHandle':
269 case 'useDebugValue':
270 case 'React.useDebugValue':
271 return true;
272
273 default:
274 return false;
275 }
276 }
277
278 function getHookCallsSignature(functionNode) {
279 var fnHookCalls = hookCalls.get(functionNode);
280
281 if (fnHookCalls === undefined) {
282 return null;
283 }
284
285 return {
286 key: fnHookCalls.map(function (call) {
287 return call.name + '{' + call.key + '}';
288 }).join('\n'),
289 customHooks: fnHookCalls.filter(function (call) {
290 return !isBuiltinHook(call.name);
291 }).map(function (call) {
292 return t.cloneDeep(call.callee);
293 })
294 };
295 }
296
297 var hasForceResetCommentByFile = new WeakMap(); // We let user do /* @refresh reset */ to reset state in the whole file.
298
299 function hasForceResetComment(path) {
300 var file = path.hub.file;
301 var hasForceReset = hasForceResetCommentByFile.get(file);
302
303 if (hasForceReset !== undefined) {
304 return hasForceReset;
305 }
306
307 hasForceReset = false;
308 var comments = file.ast.comments;
309
310 for (var i = 0; i < comments.length; i++) {
311 var cmt = comments[i];
312
313 if (cmt.value.indexOf('@refresh reset') !== -1) {
314 hasForceReset = true;
315 break;
316 }
317 }
318
319 hasForceResetCommentByFile.set(file, hasForceReset);
320 return hasForceReset;
321 }
322
323 function createArgumentsForSignature(node, signature, scope) {
324 var key = signature.key,
325 customHooks = signature.customHooks;
326 var forceReset = hasForceResetComment(scope.path);
327 var customHooksInScope = [];
328 customHooks.forEach(function (callee) {
329 // Check if a corresponding binding exists where we emit the signature.
330 var bindingName;
331
332 switch (callee.type) {
333 case 'MemberExpression':
334 if (callee.object.type === 'Identifier') {
335 bindingName = callee.object.name;
336 }
337
338 break;
339
340 case 'Identifier':
341 bindingName = callee.name;
342 break;
343 }
344
345 if (scope.hasBinding(bindingName)) {
346 customHooksInScope.push(callee);
347 } else {
348 // We don't have anything to put in the array because Hook is out of scope.
349 // Since it could potentially have been edited, remount the component.
350 forceReset = true;
351 }
352 });
353 var finalKey = key;
354
355 if (typeof require === 'function' && !opts.emitFullSignatures) {
356 // Prefer to hash when we can (e.g. outside of ASTExplorer).
357 // This makes it deterministically compact, even if there's
358 // e.g. a useState initializer with some code inside.
359 // We also need it for www that has transforms like cx()
360 // that don't understand if something is part of a string.
361 finalKey = require('crypto').createHash('sha1').update(key).digest('base64');
362 }
363
364 var args = [node, t.stringLiteral(finalKey)];
365
366 if (forceReset || customHooksInScope.length > 0) {
367 args.push(t.booleanLiteral(forceReset));
368 }
369
370 if (customHooksInScope.length > 0) {
371 args.push( // TODO: We could use an arrow here to be more compact.
372 // However, don't do it until AMA can run them natively.
373 t.functionExpression(null, [], t.blockStatement([t.returnStatement(t.arrayExpression(customHooksInScope))])));
374 }
375
376 return args;
377 }
378
379 function findHOCCallPathsAbove(path) {
380 var calls = [];
381
382 while (true) {
383 if (!path) {
384 return calls;
385 }
386
387 var parentPath = path.parentPath;
388
389 if (!parentPath) {
390 return calls;
391 }
392
393 if ( // hoc(_c = function() { })
394 parentPath.node.type === 'AssignmentExpression' && path.node === parentPath.node.right) {
395 // Ignore registrations.
396 path = parentPath;
397 continue;
398 }
399
400 if ( // hoc1(hoc2(...))
401 parentPath.node.type === 'CallExpression' && path.node !== parentPath.node.callee) {
402 calls.push(parentPath);
403 path = parentPath;
404 continue;
405 }
406
407 return calls; // Stop at other types.
408 }
409 }
410
411 var seenForRegistration = new WeakSet();
412 var seenForSignature = new WeakSet();
413 var seenForOutro = new WeakSet();
414 var hookCalls = new WeakMap();
415 var HookCallsVisitor = {
416 CallExpression: function (path) {
417 var node = path.node;
418 var callee = node.callee; // Note: this visitor MUST NOT mutate the tree in any way.
419 // It runs early in a separate traversal and should be very fast.
420
421 var name = null;
422
423 switch (callee.type) {
424 case 'Identifier':
425 name = callee.name;
426 break;
427
428 case 'MemberExpression':
429 name = callee.property.name;
430 break;
431 }
432
433 if (name === null || !/^use[A-Z]/.test(name)) {
434 return;
435 }
436
437 var fnScope = path.scope.getFunctionParent();
438
439 if (fnScope === null) {
440 return;
441 } // This is a Hook call. Record it.
442
443
444 var fnNode = fnScope.block;
445
446 if (!hookCalls.has(fnNode)) {
447 hookCalls.set(fnNode, []);
448 }
449
450 var hookCallsForFn = hookCalls.get(fnNode);
451 var key = '';
452
453 if (path.parent.type === 'VariableDeclarator') {
454 // TODO: if there is no LHS, consider some other heuristic.
455 key = path.parentPath.get('id').getSource();
456 } // Some built-in Hooks reset on edits to arguments.
457
458
459 var args = path.get('arguments');
460
461 if (name === 'useState' && args.length > 0) {
462 // useState second argument is initial state.
463 key += '(' + args[0].getSource() + ')';
464 } else if (name === 'useReducer' && args.length > 1) {
465 // useReducer second argument is initial state.
466 key += '(' + args[1].getSource() + ')';
467 }
468
469 hookCallsForFn.push({
470 callee: path.node.callee,
471 name: name,
472 key: key
473 });
474 }
475 };
476 return {
477 visitor: {
478 ExportDefaultDeclaration: function (path) {
479 var node = path.node;
480 var decl = node.declaration;
481 var declPath = path.get('declaration');
482
483 if (decl.type !== 'CallExpression') {
484 // For now, we only support possible HOC calls here.
485 // Named function declarations are handled in FunctionDeclaration.
486 // Anonymous direct exports like export default function() {}
487 // are currently ignored.
488 return;
489 } // Make sure we're not mutating the same tree twice.
490 // This can happen if another Babel plugin replaces parents.
491
492
493 if (seenForRegistration.has(node)) {
494 return;
495 }
496
497 seenForRegistration.add(node); // Don't mutate the tree above this point.
498 // This code path handles nested cases like:
499 // export default memo(() => {})
500 // In those cases it is more plausible people will omit names
501 // so they're worth handling despite possible false positives.
502 // More importantly, it handles the named case:
503 // export default memo(function Named() {})
504
505 var inferredName = '%default%';
506 var programPath = path.parentPath;
507 findInnerComponents(inferredName, declPath, function (persistentID, targetExpr, targetPath) {
508 if (targetPath === null) {
509 // For case like:
510 // export default hoc(Foo)
511 // we don't want to wrap Foo inside the call.
512 // Instead we assume it's registered at definition.
513 return;
514 }
515
516 var handle = createRegistration(programPath, persistentID);
517 targetPath.replaceWith(t.assignmentExpression('=', handle, targetExpr));
518 });
519 },
520 FunctionDeclaration: {
521 enter: function (path) {
522 var node = path.node;
523 var programPath;
524 var insertAfterPath;
525 var modulePrefix = '';
526
527 switch (path.parent.type) {
528 case 'Program':
529 insertAfterPath = path;
530 programPath = path.parentPath;
531 break;
532
533 case 'TSModuleBlock':
534 insertAfterPath = path;
535 programPath = insertAfterPath.parentPath.parentPath;
536 break;
537
538 case 'ExportNamedDeclaration':
539 insertAfterPath = path.parentPath;
540 programPath = insertAfterPath.parentPath;
541 break;
542
543 case 'ExportDefaultDeclaration':
544 insertAfterPath = path.parentPath;
545 programPath = insertAfterPath.parentPath;
546 break;
547
548 default:
549 return;
550 } // These types can be nested in typescript namespace
551 // We need to find the export chain
552 // Or return if it stays local
553
554
555 if (path.parent.type === 'TSModuleBlock' || path.parent.type === 'ExportNamedDeclaration') {
556 while (programPath.type !== 'Program') {
557 if (programPath.type === 'TSModuleDeclaration') {
558 if (programPath.parentPath.type !== 'Program' && programPath.parentPath.type !== 'ExportNamedDeclaration') {
559 return;
560 }
561
562 modulePrefix = programPath.node.id.name + '$' + modulePrefix;
563 }
564
565 programPath = programPath.parentPath;
566 }
567 }
568
569 var id = node.id;
570
571 if (id === null) {
572 // We don't currently handle anonymous default exports.
573 return;
574 }
575
576 var inferredName = id.name;
577
578 if (!isComponentishName(inferredName)) {
579 return;
580 } // Make sure we're not mutating the same tree twice.
581 // This can happen if another Babel plugin replaces parents.
582
583
584 if (seenForRegistration.has(node)) {
585 return;
586 }
587
588 seenForRegistration.add(node); // Don't mutate the tree above this point.
589
590 var innerName = modulePrefix + inferredName; // export function Named() {}
591 // function Named() {}
592
593 findInnerComponents(innerName, path, function (persistentID, targetExpr) {
594 var handle = createRegistration(programPath, persistentID);
595 insertAfterPath.insertAfter(t.expressionStatement(t.assignmentExpression('=', handle, targetExpr)));
596 });
597 },
598 exit: function (path) {
599 var node = path.node;
600 var id = node.id;
601
602 if (id === null) {
603 return;
604 }
605
606 var signature = getHookCallsSignature(node);
607
608 if (signature === null) {
609 return;
610 } // Make sure we're not mutating the same tree twice.
611 // This can happen if another Babel plugin replaces parents.
612
613
614 if (seenForSignature.has(node)) {
615 return;
616 }
617
618 seenForSignature.add(node); // Don't mutate the tree above this point.
619
620 var sigCallID = path.scope.generateUidIdentifier('_s');
621 path.scope.parent.push({
622 id: sigCallID,
623 init: t.callExpression(refreshSig, [])
624 }); // The signature call is split in two parts. One part is called inside the function.
625 // This is used to signal when first render happens.
626
627 path.get('body').unshiftContainer('body', t.expressionStatement(t.callExpression(sigCallID, []))); // The second call is around the function itself.
628 // This is used to associate a type with a signature.
629 // Unlike with $RefreshReg$, this needs to work for nested
630 // declarations too. So we need to search for a path where
631 // we can insert a statement rather than hard coding it.
632
633 var insertAfterPath = null;
634 path.find(function (p) {
635 if (p.parentPath.isBlock()) {
636 insertAfterPath = p;
637 return true;
638 }
639 });
640
641 if (insertAfterPath === null) {
642 return;
643 }
644
645 insertAfterPath.insertAfter(t.expressionStatement(t.callExpression(sigCallID, createArgumentsForSignature(id, signature, insertAfterPath.scope))));
646 }
647 },
648 'ArrowFunctionExpression|FunctionExpression': {
649 exit: function (path) {
650 var node = path.node;
651 var signature = getHookCallsSignature(node);
652
653 if (signature === null) {
654 return;
655 } // Make sure we're not mutating the same tree twice.
656 // This can happen if another Babel plugin replaces parents.
657
658
659 if (seenForSignature.has(node)) {
660 return;
661 }
662
663 seenForSignature.add(node); // Don't mutate the tree above this point.
664
665 var sigCallID = path.scope.generateUidIdentifier('_s');
666 path.scope.parent.push({
667 id: sigCallID,
668 init: t.callExpression(refreshSig, [])
669 }); // The signature call is split in two parts. One part is called inside the function.
670 // This is used to signal when first render happens.
671
672 if (path.node.body.type !== 'BlockStatement') {
673 path.node.body = t.blockStatement([t.returnStatement(path.node.body)]);
674 }
675
676 path.get('body').unshiftContainer('body', t.expressionStatement(t.callExpression(sigCallID, []))); // The second call is around the function itself.
677 // This is used to associate a type with a signature.
678
679 if (path.parent.type === 'VariableDeclarator') {
680 var insertAfterPath = null;
681 path.find(function (p) {
682 if (p.parentPath.isBlock()) {
683 insertAfterPath = p;
684 return true;
685 }
686 });
687
688 if (insertAfterPath === null) {
689 return;
690 } // Special case when a function would get an inferred name:
691 // let Foo = () => {}
692 // let Foo = function() {}
693 // We'll add signature it on next line so that
694 // we don't mess up the inferred 'Foo' function name.
695
696
697 insertAfterPath.insertAfter(t.expressionStatement(t.callExpression(sigCallID, createArgumentsForSignature(path.parent.id, signature, insertAfterPath.scope)))); // Result: let Foo = () => {}; __signature(Foo, ...);
698 } else {
699 // let Foo = hoc(() => {})
700 var paths = [path].concat(findHOCCallPathsAbove(path));
701 paths.forEach(function (p) {
702 p.replaceWith(t.callExpression(sigCallID, createArgumentsForSignature(p.node, signature, p.scope)));
703 }); // Result: let Foo = __signature(hoc(__signature(() => {}, ...)), ...)
704 }
705 }
706 },
707 VariableDeclaration: function (path) {
708 var node = path.node;
709 var programPath;
710 var insertAfterPath;
711 var modulePrefix = '';
712
713 switch (path.parent.type) {
714 case 'Program':
715 insertAfterPath = path;
716 programPath = path.parentPath;
717 break;
718
719 case 'TSModuleBlock':
720 insertAfterPath = path;
721 programPath = insertAfterPath.parentPath.parentPath;
722 break;
723
724 case 'ExportNamedDeclaration':
725 insertAfterPath = path.parentPath;
726 programPath = insertAfterPath.parentPath;
727 break;
728
729 case 'ExportDefaultDeclaration':
730 insertAfterPath = path.parentPath;
731 programPath = insertAfterPath.parentPath;
732 break;
733
734 default:
735 return;
736 } // These types can be nested in typescript namespace
737 // We need to find the export chain
738 // Or return if it stays local
739
740
741 if (path.parent.type === 'TSModuleBlock' || path.parent.type === 'ExportNamedDeclaration') {
742 while (programPath.type !== 'Program') {
743 if (programPath.type === 'TSModuleDeclaration') {
744 if (programPath.parentPath.type !== 'Program' && programPath.parentPath.type !== 'ExportNamedDeclaration') {
745 return;
746 }
747
748 modulePrefix = programPath.node.id.name + '$' + modulePrefix;
749 }
750
751 programPath = programPath.parentPath;
752 }
753 } // Make sure we're not mutating the same tree twice.
754 // This can happen if another Babel plugin replaces parents.
755
756
757 if (seenForRegistration.has(node)) {
758 return;
759 }
760
761 seenForRegistration.add(node); // Don't mutate the tree above this point.
762
763 var declPaths = path.get('declarations');
764
765 if (declPaths.length !== 1) {
766 return;
767 }
768
769 var declPath = declPaths[0];
770 var inferredName = declPath.node.id.name;
771 var innerName = modulePrefix + inferredName;
772 findInnerComponents(innerName, declPath, function (persistentID, targetExpr, targetPath) {
773 if (targetPath === null) {
774 // For case like:
775 // export const Something = hoc(Foo)
776 // we don't want to wrap Foo inside the call.
777 // Instead we assume it's registered at definition.
778 return;
779 }
780
781 var handle = createRegistration(programPath, persistentID);
782
783 if (targetPath.parent.type === 'VariableDeclarator') {
784 // Special case when a variable would get an inferred name:
785 // let Foo = () => {}
786 // let Foo = function() {}
787 // let Foo = styled.div``;
788 // We'll register it on next line so that
789 // we don't mess up the inferred 'Foo' function name.
790 // (eg: with @babel/plugin-transform-react-display-name or
791 // babel-plugin-styled-components)
792 insertAfterPath.insertAfter(t.expressionStatement(t.assignmentExpression('=', handle, declPath.node.id))); // Result: let Foo = () => {}; _c1 = Foo;
793 } else {
794 // let Foo = hoc(() => {})
795 targetPath.replaceWith(t.assignmentExpression('=', handle, targetExpr)); // Result: let Foo = hoc(_c1 = () => {})
796 }
797 });
798 },
799 Program: {
800 enter: function (path) {
801 // This is a separate early visitor because we need to collect Hook calls
802 // and "const [foo, setFoo] = ..." signatures before the destructuring
803 // transform mangles them. This extra traversal is not ideal for perf,
804 // but it's the best we can do until we stop transpiling destructuring.
805 path.traverse(HookCallsVisitor);
806 },
807 exit: function (path) {
808 var registrations = registrationsByProgramPath.get(path);
809
810 if (registrations === undefined) {
811 return;
812 } // Make sure we're not mutating the same tree twice.
813 // This can happen if another Babel plugin replaces parents.
814
815
816 var node = path.node;
817
818 if (seenForOutro.has(node)) {
819 return;
820 }
821
822 seenForOutro.add(node); // Don't mutate the tree above this point.
823
824 registrationsByProgramPath.delete(path);
825 var declarators = [];
826 path.pushContainer('body', t.variableDeclaration('var', declarators));
827 registrations.forEach(function (_ref) {
828 var handle = _ref.handle,
829 persistentID = _ref.persistentID;
830 path.pushContainer('body', t.expressionStatement(t.callExpression(refreshReg, [handle, t.stringLiteral(persistentID)])));
831 declarators.push(t.variableDeclarator(handle));
832 });
833 }
834 }
835 }
836 };
837}
838
839module.exports = ReactFreshBabelPlugin;
840 })();
841}
Note: See TracBrowser for help on using the repository browser.