@awesomeness-js/server 1.1.10 → 1.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/build/build.js +4 -1
  2. package/build/postBuild.js +4 -1
  3. package/index.js +6 -4
  4. package/package.json +1 -1
  5. package/src/componentAndPageMemory.js +297 -0
  6. package/src/componentDependencies.js +7 -19
  7. package/src/extractUiComponentRefs.js +55 -0
  8. package/src/fetchPage.js +79 -4
  9. package/src/resolveRealCasePath.js +0 -4
  10. package/tests/componentAndPageMemory.test.js +172 -0
  11. package/tests/fetchPage.test.js +259 -0
  12. package/tests/fixtures/site-and-components/components/app/cleanMain/index.js +26 -0
  13. package/tests/fixtures/site-and-components/components/app/cleanMain.js +14 -0
  14. package/tests/fixtures/site-and-components/components/app/index.css +4 -0
  15. package/tests/fixtures/site-and-components/components/app/index.js +42 -0
  16. package/tests/fixtures/site-and-components/components/app/insertIntoList.jquery.js +150 -0
  17. package/tests/fixtures/site-and-components/components/app/keyUpWithTimeout.jQuery.js +26 -0
  18. package/tests/fixtures/site-and-components/components/app/onEnter.jQuery.js +39 -0
  19. package/tests/fixtures/site-and-components/components/app/onResize.jQuery.js +64 -0
  20. package/tests/fixtures/site-and-components/components/app/pwa/_.css +305 -0
  21. package/tests/fixtures/site-and-components/components/app/pwa/index.js +235 -0
  22. package/tests/fixtures/site-and-components/components/app/pwa/updateProfileImage.js +7 -0
  23. package/tests/fixtures/site-and-components/components/app/shapes.css +3 -0
  24. package/tests/fixtures/site-and-components/components/app/simple/_.css +151 -0
  25. package/tests/fixtures/site-and-components/components/app/simple/index.js +170 -0
  26. package/tests/fixtures/site-and-components/components/app/start.js +165 -0
  27. package/tests/fixtures/site-and-components/components/app/vanilla/_.css +1 -0
  28. package/tests/fixtures/site-and-components/components/app/vanilla/index.js +27 -0
  29. package/tests/fixtures/site-and-components/components/card/index.js +88 -0
  30. package/tests/fixtures/site-and-components/components/cardMain/index.js +86 -0
  31. package/tests/fixtures/site-and-components/components/cardMount/index.js +86 -0
  32. package/tests/fixtures/site-and-components/components/pageInit/index.js +86 -0
  33. package/tests/fixtures/site-and-components/components/pageScript/index.js +86 -0
  34. package/tests/fixtures/site-and-components/components/pageWidget/index.js +86 -0
  35. package/tests/fixtures/site-and-components/components/scrollSpy/elm.js +172 -0
  36. package/tests/fixtures/site-and-components/components/scrollSpy/index.js +63 -0
  37. package/tests/fixtures/site-and-components/components/scrollSpy/observerPoolGet.js +91 -0
  38. package/tests/fixtures/site-and-components/components/scrollSpy/observerPoolRegistry.js +18 -0
  39. package/tests/fixtures/site-and-components/components/scrollSpy/observerPoolSubscribe.js +37 -0
  40. package/tests/fixtures/site-and-components/components/scrollSpy/observerPoolUnsubscribe.js +44 -0
  41. package/tests/fixtures/site-and-components/components/scrollSpy/top.js +86 -0
  42. package/tests/fixtures/site-and-components/sites/site-a/pages/home/init.js +78 -0
  43. package/tests/fixtures/site-and-components/sites/site-a/pages/home/js/index.js +93 -0
  44. package/tests/setup.js +1 -0
  45. package/tsconfig.json +18 -0
  46. package/types/errors.d.ts +0 -0
  47. package/types/index.d.ts +109 -0
  48. package/types/src/applicationMap.d.ts +1 -0
  49. package/types/src/awesomenessNormalizeRequest.d.ts +27 -0
  50. package/types/src/brotliJsonResponse.d.ts +10 -0
  51. package/types/src/checkAccess.d.ts +5 -0
  52. package/types/src/componentAndPageMemory.d.ts +75 -0
  53. package/types/src/componentDependencies.d.ts +6 -0
  54. package/types/src/config.d.ts +7 -0
  55. package/types/src/extractUiComponentRefs.d.ts +5 -0
  56. package/types/src/fetchPage.d.ts +4 -0
  57. package/types/src/getConfig.d.ts +2 -0
  58. package/types/src/getMD.d.ts +1 -0
  59. package/types/src/init.d.ts +2 -0
  60. package/types/src/koa/attachAwesomenessRequest.d.ts +1 -0
  61. package/types/src/koa/cors.d.ts +1 -0
  62. package/types/src/koa/errorHandler.d.ts +1 -0
  63. package/types/src/koa/finalFormat.d.ts +1 -0
  64. package/types/src/koa/jsonBodyParser.d.ts +1 -0
  65. package/types/src/koa/routeRequest.d.ts +1 -0
  66. package/types/src/koa/serverUp.d.ts +1 -0
  67. package/types/src/koa/staticFiles.d.ts +1 -0
  68. package/types/src/koa/timeout.d.ts +1 -0
  69. package/types/src/pageInfo.d.ts +8 -0
  70. package/types/src/reRoute.d.ts +7 -0
  71. package/types/src/resolveRealCasePath.d.ts +1 -0
  72. package/types/src/specialPaths.d.ts +3 -0
  73. package/types/src/start.d.ts +1 -0
  74. package/types/src/validateRequest.d.ts +2 -0
  75. package/types/src/ws/handlers.d.ts +4 -0
  76. package/types/src/ws/index.d.ts +1 -0
@@ -0,0 +1,151 @@
1
+ /* ================================
2
+ Awesomeness App Simple - NAV ONLY
3
+ ================================ */
4
+
5
+ .awesomeness-app-simple-main {
6
+ opacity: 1;
7
+ position: absolute;
8
+ top: 0;
9
+ right: 0;
10
+ left: 0;
11
+ bottom: 0;
12
+ width: 100%;
13
+ min-height: 100%;
14
+ margin: 0 auto;
15
+ padding-top: 65px; /* account for fixed nav bar */
16
+ }
17
+
18
+ .awesomeness-app-simple-main.withFullScreenHero {
19
+ padding-top: 0px; /* account for fixed nav bar */
20
+ }
21
+
22
+ .awesomeness-app-simple-navBar {
23
+ position: fixed;
24
+ top: 0;
25
+ left: 0;
26
+ right: 0;
27
+ width: 100%;
28
+ height: 65px;
29
+ z-index: 5;
30
+ }
31
+
32
+ /* NEW: interior wrapper */
33
+ .awesomeness-app-simple-navBar-interior{
34
+ margin: 0 auto;
35
+ height: 65px;
36
+ padding: 0 16px;
37
+
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: space-between;
41
+ }
42
+
43
+ .awesomeness-app-simple-logo{
44
+ cursor: pointer;
45
+ display: flex;
46
+ align-items: center;
47
+ white-space: nowrap;
48
+ user-select: none;
49
+ }
50
+
51
+ .awesomeness-app-simple-top-navItems{
52
+ margin-left: auto;
53
+ display: flex;
54
+ align-items: center;
55
+ height: 100%;
56
+ }
57
+
58
+ .awesomeness-app-simple-nav-item{
59
+ display: inline-flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ height: 65px;
63
+ padding: 0 14px;
64
+ white-space: nowrap;
65
+ cursor: pointer;
66
+ }
67
+
68
+ /* Menu button */
69
+ .awesomeness-app-simple-menu-button{
70
+ margin-left: auto;
71
+ display: inline-flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ height: 65px;
75
+ padding: 0 12px;
76
+ cursor: pointer;
77
+ -webkit-tap-highlight-color: transparent;
78
+ font-size: 1.2em;
79
+ cursor: pointer;
80
+ }
81
+
82
+ /* Backdrop (z-index 2) */
83
+ .awesomeness-app-simple-menu-backdrop{
84
+ position: fixed;
85
+ inset: 0;
86
+ z-index: 2;
87
+ }
88
+
89
+ /* Phone slide-down menu (z-index 3) */
90
+ .awesomeness-app-simple-phone-menu{
91
+ position: fixed;
92
+ top: 0;
93
+ left: 0;
94
+ width: 100%;
95
+ z-index: 4;
96
+
97
+ transform: translate3d(0, -100%, 0);
98
+ transition: transform 220ms ease;
99
+
100
+ padding-top: 65px;
101
+ padding-bottom: 30px;
102
+ }
103
+
104
+ /* open state */
105
+ .awesomeness-app-simple-phone-menu.is-open{
106
+ transform: translate3d(0, 0, 0);
107
+ }
108
+
109
+ /* Menu rows */
110
+ .awesomeness-app-simple-phone-menu a,
111
+ .awesomeness-app-simple-phone-menu .awesomeness-app-simple-nav-item{
112
+ display: flex;
113
+ align-items: center;
114
+ justify-content: flex-start;
115
+ width: 100%;
116
+ height: 56px;
117
+ padding: 0 16px;
118
+ }
119
+
120
+
121
+ .awesomeness-app-simple-nav-item span {
122
+ position: relative;
123
+ display: inline-block;
124
+ padding-bottom: 4px;
125
+ }
126
+
127
+ .awesomeness-app-simple-nav-item span::after {
128
+ content: "";
129
+ position: absolute;
130
+ left: 0;
131
+ bottom: 0;
132
+
133
+ height: 2px;
134
+ width: 100%;
135
+
136
+ background: currentColor;
137
+
138
+ transform: scaleX(0);
139
+ transform-origin: right center;
140
+
141
+ transition: transform 420ms ease !important;
142
+ }
143
+
144
+ .awesomeness-app-simple-nav-item:hover span::after {
145
+ transform: scaleX(1);
146
+ transform-origin: left center;
147
+ }
148
+
149
+ .awesomeness-app-simple-nav-item span::after {
150
+ transition: transform 420ms cubic-bezier(.2,.8,.2,1);
151
+ }
@@ -0,0 +1,170 @@
1
+ import ui from '#ui';
2
+
3
+ export default ({
4
+ replaceApp = true,
5
+ navItems = [],
6
+ logo = 'AwesomenessJS.com',
7
+ fancyNavUnderline = true,
8
+ maxWidth = null,
9
+ navMaxWidth = null,
10
+ } = {}) => {
11
+
12
+ console.log({ navMaxWidth });
13
+
14
+ let $app = ui.app.vanilla({ replaceApp });
15
+
16
+ const themeProps = ui.theme();
17
+
18
+ const $topNavBar = $(`<div class="awesomeness-app-simple-navBar ${themeProps.text.secondary}"></div>`)
19
+ .appendTo($app);
20
+
21
+ const $navBarInterior = $(`<div class="awesomeness-app-simple-navBar-interior"></div>`)
22
+ .appendTo($topNavBar);
23
+
24
+
25
+ if(navMaxWidth){
26
+
27
+ $navBarInterior.css('max-width', `${navMaxWidth}px`);
28
+
29
+ }
30
+
31
+ const $logo = $(`<div class="awesomeness-app-simple-logo ${themeProps.text.accent}--hover">${logo}</div>`)
32
+ .appendTo($navBarInterior)
33
+ .on('click', () => {
34
+
35
+ window.location.href = '/';
36
+
37
+ });
38
+
39
+ const $topNavItems = $(`<div class="hidden-t hidden-p awesomeness-app-simple-top-navItems"></div>`)
40
+ .appendTo($navBarInterior);
41
+
42
+ const $main = $(`<div id="main" class="awesomeness-app-simple-main ${themeProps.surface.base}"></div>`)
43
+ .appendTo($app);
44
+
45
+ if(maxWidth){
46
+
47
+ $main.css('max-width', `${maxWidth}px`);
48
+
49
+ }
50
+
51
+ const $menuNav = $(`<i class="ico-menu awesomeness-app-simple-menu-button hidden-xl hidden-d ${themeProps.text.secondary} ${themeProps.text.accent}--hover"></i>`)
52
+ .appendTo($navBarInterior);
53
+
54
+ const $phoneMenu = $(`<div class="awesomeness-app-simple-phone-menu hidden-xl hidden-d ${themeProps.surface.base} ${themeProps.text.secondary} ${themeProps.text.accent}--hover pt65"></div>`);
55
+
56
+
57
+ if(navItems.length > 0){
58
+
59
+ const $navItemsContainer = $(`<div class="flex gap-10 height-65"></div>`)
60
+ .appendTo($topNavItems);
61
+
62
+ navItems.forEach((item) => {
63
+
64
+ let thisFancyNavUnderline = fancyNavUnderline;
65
+
66
+ if(!item.name && item.ico) {
67
+
68
+ item.name = `<i class="ico-${item.ico}"></i>`;
69
+ thisFancyNavUnderline = false;
70
+
71
+ }
72
+
73
+
74
+ const itemContent = thisFancyNavUnderline ? `<span class="underline">${item.name}</span>` : item.name;
75
+
76
+ const $link = ui.link({
77
+ link: item.url,
78
+ }).appendTo($navItemsContainer)
79
+ .addClass(`flex justify-center align-center`);
80
+
81
+ const $item = $(`<div class="awesomeness-app-simple-nav-item ${themeProps.text.secondary} ${themeProps.text.accent}--hover">${itemContent}</div>`)
82
+ .appendTo($link);
83
+
84
+
85
+ $link.clone(true).appendTo($phoneMenu);
86
+
87
+ });
88
+
89
+ }
90
+
91
+ // Put this after you create $menuNav and $phoneMenu
92
+
93
+ let menuOpen = false;
94
+
95
+ // Ensure phone menu is in the DOM
96
+ $phoneMenu.appendTo($app);
97
+
98
+ // Backdrop (z-index 2)
99
+ const $menuBackdrop = $('<div class="awesomeness-app-simple-menu-backdrop clearfix hidden"></div>')
100
+ .appendTo($app);
101
+
102
+ // icon swap helpers
103
+ const setMenuIconOpen = () => {
104
+
105
+ $menuNav.removeClass('ico-menu').addClass('ico-x');
106
+
107
+ };
108
+
109
+ const setMenuIconClosed = () => {
110
+
111
+ $menuNav.removeClass('ico-x').addClass('ico-menu');
112
+
113
+ };
114
+
115
+ // open/close
116
+ const openMenu = () => {
117
+
118
+ if (menuOpen) return;
119
+ menuOpen = true;
120
+
121
+ $menuBackdrop.removeClass('hidden'); // show backdrop
122
+ $phoneMenu.addClass('is-open'); // slide down
123
+ setMenuIconOpen();
124
+
125
+ };
126
+
127
+ const closeMenu = () => {
128
+
129
+ if (!menuOpen) return;
130
+ menuOpen = false;
131
+
132
+ $phoneMenu.removeClass('is-open'); // slide up
133
+ $menuBackdrop.addClass('hidden'); // hide backdrop
134
+ setMenuIconClosed();
135
+
136
+ };
137
+
138
+ const toggleMenu = () => (menuOpen ? closeMenu() : openMenu());
139
+
140
+ // button click
141
+ $menuNav.on('click', (e) => {
142
+
143
+ e.preventDefault();
144
+ toggleMenu();
145
+
146
+ });
147
+
148
+ // backdrop click closes
149
+ $menuBackdrop.on('click', (e) => {
150
+
151
+ e.preventDefault();
152
+ closeMenu();
153
+
154
+ });
155
+
156
+ // clicking a link closes
157
+ $phoneMenu.on('click', 'a', () => closeMenu());
158
+
159
+ // defensive: if items aren't anchors
160
+ $phoneMenu.find('.awesomeness-app-simple-nav-item').on('click', () => closeMenu());
161
+
162
+ ui.scrollSpy.top({
163
+ threshold: 120,
164
+ className: themeProps.surface.base,
165
+ $applyTo: $topNavBar
166
+ });
167
+
168
+ return $app;
169
+
170
+ };
@@ -0,0 +1,165 @@
1
+ import ui from '#ui';
2
+
3
+ export default function startApp({
4
+ data = {},
5
+ style = 'simple',
6
+ theme = {
7
+ name: 'light',
8
+ neutralColor: 'zinc',
9
+ accentColor: 'cyan',
10
+ customColors: {},
11
+ },
12
+ navItems = [],
13
+ logo = null,
14
+ styleConfig = {
15
+ },
16
+ postSetupHook = null,
17
+ home = null,
18
+ }) {
19
+
20
+ if(!ui.app[style]){
21
+
22
+ throw new Error(`App style ${style} not found`);
23
+
24
+ }
25
+
26
+ app.theme = theme;
27
+
28
+ if(theme.customColors){
29
+
30
+ $.each(theme.customColors, (name, hexValue) => {
31
+
32
+ let inputColor;
33
+ let anchorShade = 600;
34
+
35
+ if(typeof hexValue === 'string'){
36
+
37
+ inputColor = hexValue;
38
+
39
+ } else if(typeof hexValue === 'object' && hexValue !== null){
40
+
41
+ inputColor = hexValue.inputColor;
42
+ anchorShade = hexValue.anchorShade || anchorShade;
43
+
44
+ }
45
+
46
+ ui.colors.custom({
47
+ inputColor: inputColor,
48
+ anchorShade: 600,
49
+ name
50
+ });
51
+
52
+ });
53
+
54
+ }
55
+
56
+ // create HTML first
57
+ // could be done in parts broken down in the scripts folder
58
+ var $body = $('body');
59
+
60
+ $body.empty();
61
+
62
+ var $app = $('<div id="app"></div>').appendTo($body);
63
+
64
+ ui.app[style]({
65
+ logo,
66
+ navItems,
67
+ ...styleConfig
68
+ });
69
+
70
+ app.$app = $app;
71
+
72
+ if(postSetupHook && typeof postSetupHook === 'function'){
73
+
74
+ postSetupHook({ $app });
75
+
76
+ }
77
+
78
+
79
+ const $main = ui.app.cleanMain();
80
+
81
+
82
+ if(
83
+ data?.goToPage
84
+ && data.goToPage != 'start'
85
+ ){
86
+
87
+ let place = app.pages;
88
+ const trimmedPlace = data.goToPage.replace(/^\/+|\/+$/g, '');
89
+ const placeParts = trimmedPlace.split('/');
90
+
91
+ for(const part of placeParts){
92
+
93
+ if(!place[part]){
94
+
95
+ place[part] = {};
96
+
97
+ }
98
+
99
+ place = place[part];
100
+
101
+ }
102
+
103
+ if(place.init){
104
+
105
+ if(data.goToPage == "_md"){
106
+
107
+ // state management
108
+ app.state.create({
109
+ title: data?.pageData?.metadata?.title ?? '👋',
110
+ url: data?.pageData?.metadata?.url ?? data?.origLocation ?? '/' + data.goToPage,
111
+ });
112
+
113
+ } else {
114
+
115
+ // state management
116
+ app.state.create({
117
+ title: place?.about?.title ?? '👋',
118
+ url: place?.about?.url ?? data?.origLocation ?? '/' + data.goToPage,
119
+ });
120
+
121
+ }
122
+
123
+ place.init(data.pageData);
124
+
125
+ } else {
126
+
127
+ console.warn('No page found for', data);
128
+
129
+ if(data.errorFetchingPage?.code){
130
+
131
+ ui.statusPage(data.errorFetchingPage?.code).appendTo($app);
132
+
133
+ } else {
134
+
135
+ ui.statusPage(401).appendTo($app);
136
+
137
+ }
138
+
139
+
140
+ }
141
+
142
+
143
+ } else {
144
+
145
+ if(home && typeof home === 'function'){
146
+
147
+ home({
148
+ $app,
149
+ data,
150
+ $main
151
+ });
152
+
153
+ }
154
+
155
+ }
156
+
157
+
158
+ setTimeout(function(){
159
+
160
+ app.initialScroll();
161
+
162
+ }, 10);
163
+
164
+
165
+ }
@@ -0,0 +1,27 @@
1
+ import ui from '#ui';
2
+
3
+ export default ({
4
+ replaceApp = true
5
+ } = {}) => {
6
+
7
+ let $app;
8
+
9
+ if (replaceApp) {
10
+
11
+ $app = $('#app');
12
+ $app.empty().addClass('awesomeness-app-simple');
13
+
14
+ } else {
15
+
16
+ $app = $('<div class="awesomeness-app-simple"></div>');
17
+
18
+ }
19
+
20
+ const themeProps = ui.theme();
21
+
22
+ $app.addClass(themeProps.surface.base);
23
+ $('body').addClass(themeProps.surface.base);
24
+
25
+ return $app;
26
+
27
+ };
@@ -0,0 +1,88 @@
1
+ export default function card() {
2
+
3
+ const $card = ui.cardMain();
4
+ const mountRef = ui.cardMount.mount();
5
+
6
+ const items = [
7
+ {
8
+ id: "itm-1",
9
+ title: "First",
10
+ score: 11
11
+ },
12
+ {
13
+ id: "itm-2",
14
+ title: "Second",
15
+ score: 27
16
+ },
17
+ {
18
+ id: "itm-3",
19
+ title: "Third",
20
+ score: 42
21
+ },
22
+ {
23
+ id: "itm-4",
24
+ title: "Fourth",
25
+ score: 31
26
+ },
27
+ {
28
+ id: "itm-5",
29
+ title: "Fifth",
30
+ score: 18
31
+ },
32
+ {
33
+ id: "itm-6",
34
+ title: "Sixth",
35
+ score: 54
36
+ },
37
+ {
38
+ id: "itm-7",
39
+ title: "Seventh",
40
+ score: 39
41
+ },
42
+ {
43
+ id: "itm-8",
44
+ title: "Eighth",
45
+ score: 23
46
+ },
47
+ ];
48
+
49
+ const grouped = items.reduce((acc, item) => {
50
+
51
+ const bucket = item.score >= 40 ? "high" : item.score >= 25 ? "medium" : "low";
52
+
53
+ acc[bucket] = acc[bucket] || [];
54
+ acc[bucket].push(item);
55
+
56
+ return acc;
57
+
58
+ }, {});
59
+
60
+ const sorted = [ ...items ].sort((a, b) => b.score - a.score);
61
+ const topThree = sorted.slice(0, 3);
62
+
63
+ const details = {
64
+ count: items.length,
65
+ topThree,
66
+ grouped,
67
+ mountRef,
68
+ signature: `${topThree.map((item) => item.id).join("|")}:${items.length}`,
69
+ };
70
+
71
+ if (details.count > 5) {
72
+
73
+ ui.cardMain();
74
+
75
+ }
76
+
77
+ if (details.topThree.length === 3) {
78
+
79
+ ui.cardMount.mount();
80
+
81
+ }
82
+
83
+ return {
84
+ $card,
85
+ details,
86
+ };
87
+
88
+ }
@@ -0,0 +1,86 @@
1
+ export default function() {
2
+
3
+ const items = [
4
+ {
5
+ id: "itm-1",
6
+ title: "First",
7
+ score: 11
8
+ },
9
+ {
10
+ id: "itm-2",
11
+ title: "Second",
12
+ score: 27
13
+ },
14
+ {
15
+ id: "itm-3",
16
+ title: "Third",
17
+ score: 42
18
+ },
19
+ {
20
+ id: "itm-4",
21
+ title: "Fourth",
22
+ score: 31
23
+ },
24
+ {
25
+ id: "itm-5",
26
+ title: "Fifth",
27
+ score: 18
28
+ },
29
+ {
30
+ id: "itm-6",
31
+ title: "Sixth",
32
+ score: 54
33
+ },
34
+ {
35
+ id: "itm-7",
36
+ title: "Seventh",
37
+ score: 39
38
+ },
39
+ {
40
+ id: "itm-8",
41
+ title: "Eighth",
42
+ score: 23
43
+ },
44
+ ];
45
+
46
+ const grouped = items.reduce((acc, item) => {
47
+
48
+ const bucket = item.score >= 40 ? "high" : item.score >= 25 ? "medium" : "low";
49
+
50
+ acc[bucket] = acc[bucket] || [];
51
+ acc[bucket].push(item);
52
+
53
+ return acc;
54
+
55
+ }, {});
56
+
57
+ const sorted = [ ...items ].sort((a, b) => b.score - a.score);
58
+ const topThree = sorted.slice(0, 3);
59
+
60
+ const details = {
61
+ count: items.length,
62
+ topThree,
63
+ grouped,
64
+ mountRef,
65
+ signature: `${topThree.map((item) => item.id).join("|")}:${items.length}`,
66
+ };
67
+
68
+ if (details.count > 5) {
69
+
70
+ ui.cardMain();
71
+
72
+ }
73
+
74
+ if (details.topThree.length === 3) {
75
+
76
+ ui.cardMount.mount();
77
+
78
+ }
79
+
80
+ return {
81
+ $card,
82
+ details,
83
+ };
84
+
85
+
86
+ }