@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,172 @@
1
+ import path from "path";
2
+ import { performance } from "perf_hooks";
3
+ import { describe, expect, it } from "vitest";
4
+
5
+ import componentAndPageMemory, { clearComponentAndPageMemory,
6
+ extractUiRefsFromFileMemoized,
7
+ extractUiRefsMemoized,
8
+ getComponentAndPageMemoryStatus, } from "../src/componentAndPageMemory.js";
9
+
10
+ describe("componentAndPageMemory", () => {
11
+
12
+ const fixturesRoot = path.join(
13
+ process.cwd(),
14
+ "tests",
15
+ "fixtures",
16
+ "site-and-components"
17
+ );
18
+ const siteInitPath = path.join(
19
+ fixturesRoot,
20
+ "sites",
21
+ "site-a",
22
+ "pages",
23
+ "home",
24
+ "init.js"
25
+ );
26
+ const siteJsPath = path.join(
27
+ fixturesRoot,
28
+ "sites",
29
+ "site-a",
30
+ "pages",
31
+ "home",
32
+ "js",
33
+ "index.js"
34
+ );
35
+ const componentJsPath = path.join(
36
+ fixturesRoot,
37
+ "components",
38
+ "card",
39
+ "index.js"
40
+ );
41
+
42
+ it("exports a default API object", () => {
43
+
44
+ expect(componentAndPageMemory).toBeTruthy();
45
+ expect(typeof componentAndPageMemory.readFileMemoized).toBe("function");
46
+ expect(typeof componentAndPageMemory.extractUiRefsMemoized).toBe("function");
47
+ expect(typeof componentAndPageMemory.extractUiRefsFromFileMemoized).toBe("function");
48
+ expect(typeof componentAndPageMemory.clearComponentAndPageMemory).toBe("function");
49
+ expect(typeof componentAndPageMemory.getComponentAndPageMemoryStatus).toBe("function");
50
+
51
+ });
52
+
53
+ it("returns useful status/debug information", () => {
54
+
55
+ clearComponentAndPageMemory();
56
+ const initComponents = extractUiRefsFromFileMemoized(siteInitPath, {
57
+ namespace: "ui",
58
+ includeDotAccess: true,
59
+ });
60
+ const pageJsComponents = extractUiRefsFromFileMemoized(siteJsPath, {
61
+ namespace: "ui",
62
+ includeDotAccess: true,
63
+ });
64
+ const cardComponents = extractUiRefsFromFileMemoized(componentJsPath, {
65
+ namespace: "ui",
66
+ includeDotAccess: true,
67
+ });
68
+ const gatheredComponents = [ ...new Set([
69
+ "card",
70
+ ...initComponents,
71
+ ...pageJsComponents,
72
+ ...cardComponents,
73
+ ]) ].sort();
74
+
75
+ expect(initComponents).toEqual(expect.arrayContaining([ "pageInit", "pageWidget" ]));
76
+ expect(pageJsComponents).toEqual(expect.arrayContaining([ "pageScript" ]));
77
+ expect(cardComponents).toEqual(expect.arrayContaining([ "cardMain", "cardMount" ]));
78
+ expect(extractUiRefsMemoized("ui.inlineOnly();", { namespace: "ui" })).toEqual([ "inlineOnly" ]);
79
+
80
+ console.log("components gathered", gatheredComponents);
81
+ expect(gatheredComponents).toEqual([
82
+ "app",
83
+ "card",
84
+ "cardMain",
85
+ "cardMount",
86
+ "pageInit",
87
+ "pageScript",
88
+ "pageWidget",
89
+ ]);
90
+
91
+ const status = getComponentAndPageMemoryStatus({
92
+ includeKeys: true,
93
+ sampleSize: 10,
94
+ });
95
+
96
+ console.log("componentAndPageMemory status", status);
97
+
98
+ expect(status.counts.fileCacheEntries).toBeGreaterThanOrEqual(1);
99
+ expect(status.counts.refsCacheEntries).toBeGreaterThanOrEqual(4);
100
+ expect(status.approximateBytes.totalCacheBytes).toBeGreaterThan(0);
101
+ expect(status.processMemoryUsage).toBeTruthy();
102
+ expect(status.keys.fileCacheKeys).toEqual(expect.arrayContaining([
103
+ siteInitPath,
104
+ siteJsPath,
105
+ componentJsPath,
106
+ ]));
107
+ expect(status.keys.refsCacheKeys.length).toBeGreaterThan(0);
108
+ clearComponentAndPageMemory();
109
+
110
+ });
111
+
112
+ it("shows cold hit vs warm hit timing", () => {
113
+
114
+ const coldRuns = 25;
115
+ const warmRuns = 250;
116
+
117
+ let coldTotalMs = 0;
118
+
119
+ for (let i = 0; i < coldRuns; i++) {
120
+
121
+ clearComponentAndPageMemory();
122
+
123
+ const coldStart = performance.now();
124
+
125
+ extractUiRefsFromFileMemoized(componentJsPath, {
126
+ namespace: "ui",
127
+ includeDotAccess: true,
128
+ });
129
+
130
+ coldTotalMs += performance.now() - coldStart;
131
+
132
+ }
133
+
134
+ clearComponentAndPageMemory();
135
+ extractUiRefsFromFileMemoized(componentJsPath, {
136
+ namespace: "ui",
137
+ includeDotAccess: true,
138
+ });
139
+
140
+ const warmStart = performance.now();
141
+
142
+ for (let i = 0; i < warmRuns; i++) {
143
+
144
+ extractUiRefsFromFileMemoized(componentJsPath, {
145
+ namespace: "ui",
146
+ includeDotAccess: true,
147
+ });
148
+
149
+ }
150
+
151
+ const warmTotalMs = performance.now() - warmStart;
152
+ const coldAvgMs = coldTotalMs / coldRuns;
153
+ const warmAvgMs = warmTotalMs / warmRuns;
154
+
155
+ console.log("componentAndPageMemory cold vs warm", {
156
+ coldRuns,
157
+ warmRuns,
158
+ coldTotalMs,
159
+ warmTotalMs,
160
+ coldAvgMs,
161
+ warmAvgMs,
162
+ ratioWarmToCold: warmAvgMs / coldAvgMs,
163
+ });
164
+
165
+ // Warm hits should typically be faster or close to cold hits in CI/VM environments.
166
+ expect(warmAvgMs).toBeLessThanOrEqual(coldAvgMs * 1.5);
167
+
168
+ clearComponentAndPageMemory();
169
+
170
+ });
171
+
172
+ });
@@ -0,0 +1,259 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import path from "path";
3
+ import { performance } from "perf_hooks";
4
+ import { clearComponentAndPageMemory } from "../src/componentAndPageMemory.js";
5
+
6
+ const mocks = vi.hoisted(() => ({
7
+ componentDependencies: vi.fn(),
8
+ pageInfo: vi.fn(),
9
+ getConfig: vi.fn(),
10
+ }));
11
+
12
+ vi.mock("../src/componentDependencies.js", () => ({
13
+ default: mocks.componentDependencies,
14
+ }));
15
+
16
+ vi.mock("../src/pageInfo.js", () => ({
17
+ default: mocks.pageInfo,
18
+ }));
19
+
20
+ vi.mock("../src/getConfig.js", () => ({
21
+ default: mocks.getConfig,
22
+ }));
23
+
24
+ import fetchPage from "../src/fetchPage.js";
25
+
26
+ describe("fetchPage component inference", () => {
27
+
28
+ const fixturesRoot = path.join(
29
+ process.cwd(),
30
+ "tests",
31
+ "fixtures",
32
+ "site-and-components"
33
+ );
34
+ const jsPath = path.join(
35
+ fixturesRoot,
36
+ "sites",
37
+ "site-a",
38
+ "pages",
39
+ "home",
40
+ "js"
41
+ );
42
+ const cssPath = path.join(
43
+ fixturesRoot,
44
+ "sites",
45
+ "site-a",
46
+ "pages",
47
+ "home",
48
+ "css"
49
+ );
50
+ const expectedComponents = [
51
+ "app",
52
+ "card",
53
+ "cardMain",
54
+ "cardMount",
55
+ "pageInit",
56
+ "pageScript",
57
+ "pageWidget",
58
+ ];
59
+
60
+ function buildRequest() {
61
+
62
+ return {
63
+ site: "site-a",
64
+ pageRoute: "home",
65
+ testing: true,
66
+ meta: {
67
+ pages: {},
68
+ components: {},
69
+ },
70
+ updatedMeta: {
71
+ pages: {},
72
+ components: {},
73
+ },
74
+ user: {
75
+ permissions: [ "*" ],
76
+ },
77
+ };
78
+
79
+ }
80
+
81
+ beforeEach(() => {
82
+
83
+ vi.clearAllMocks();
84
+
85
+ mocks.getConfig.mockReturnValue({
86
+ siteDir__URL: path.join(fixturesRoot, "sites"),
87
+ componentLocations: () => [ new URL("file:///tmp/components/") ],
88
+ });
89
+
90
+ mocks.pageInfo.mockResolvedValue({
91
+ about: {
92
+ version: "1.0.0",
93
+ permissions: [],
94
+ components: [ "card" ],
95
+ },
96
+ cssPath,
97
+ jsPath,
98
+ getData: async () => ({ ok: true }),
99
+ });
100
+
101
+ mocks.componentDependencies.mockImplementation((components) => {
102
+
103
+ return components.reduce((acc, component) => {
104
+
105
+ acc[component] = {
106
+ css: `/* ${component} css */`,
107
+ js: `/* ${component} js */`,
108
+ };
109
+
110
+ return acc;
111
+
112
+ }, {});
113
+
114
+ });
115
+
116
+ });
117
+
118
+ it("infers components from init.js and page js files", async () => {
119
+
120
+ const awesomenessRequest = {
121
+ site: "site-a",
122
+ pageRoute: "home",
123
+ meta: {
124
+ pages: {},
125
+ components: {},
126
+ },
127
+ updatedMeta: {
128
+ pages: {},
129
+ components: {},
130
+ },
131
+ user: {
132
+ permissions: [ "*" ],
133
+ },
134
+ };
135
+
136
+ const out = await fetchPage(awesomenessRequest, {});
137
+
138
+ expect(out).toEqual({ ok: true });
139
+ expect(mocks.componentDependencies).toHaveBeenCalledTimes(1);
140
+
141
+ const inferred = [ ...new Set(mocks.componentDependencies.mock.calls[0][0]) ].sort();
142
+
143
+ console.log("fetchPage inferred components", inferred);
144
+
145
+ expect(inferred).toEqual(expectedComponents);
146
+
147
+ });
148
+
149
+ it("still infers init.js components when page js rebuild is skipped", async () => {
150
+
151
+ const awesomenessRequest = {
152
+ site: "site-a",
153
+ pageRoute: "home",
154
+ meta: {
155
+ pages: {
156
+ home: "1.0.0",
157
+ },
158
+ components: {},
159
+ },
160
+ updatedMeta: {
161
+ pages: {},
162
+ components: {},
163
+ },
164
+ user: {
165
+ permissions: [ "*" ],
166
+ },
167
+ };
168
+
169
+ const out = await fetchPage(awesomenessRequest, {});
170
+
171
+ expect(out).toEqual({ ok: true });
172
+ expect(mocks.componentDependencies).toHaveBeenCalledTimes(1);
173
+
174
+ const inferred = [ ...new Set(mocks.componentDependencies.mock.calls[0][0]) ].sort();
175
+
176
+ console.log("fetchPage inferred components (cached page)", inferred);
177
+
178
+ expect(inferred).toEqual(expectedComponents);
179
+
180
+ });
181
+
182
+ it("works when about.components is omitted and still infers from page files", async () => {
183
+
184
+ mocks.pageInfo.mockResolvedValueOnce({
185
+ about: {
186
+ version: "1.0.0",
187
+ permissions: [],
188
+ },
189
+ cssPath,
190
+ jsPath,
191
+ getData: async () => ({ ok: true }),
192
+ });
193
+
194
+ const awesomenessRequest = buildRequest();
195
+ const out = await fetchPage(awesomenessRequest, {});
196
+
197
+ expect(out).toEqual({ ok: true });
198
+ expect(mocks.componentDependencies).toHaveBeenCalledTimes(1);
199
+
200
+ const inferred = [ ...new Set(mocks.componentDependencies.mock.calls[0][0]) ].sort();
201
+
202
+ console.log("fetchPage inferred components (no about.components)", inferred);
203
+
204
+ expect(inferred).toEqual([
205
+ "app",
206
+ "cardMain",
207
+ "cardMount",
208
+ "pageInit",
209
+ "pageScript",
210
+ "pageWidget",
211
+ ]);
212
+ expect(inferred).not.toContain("card");
213
+
214
+ });
215
+
216
+ it("shows batch timing with cache retained vs cache cleared", async () => {
217
+
218
+ const requestRuns = 75;
219
+
220
+ clearComponentAndPageMemory();
221
+ mocks.componentDependencies.mockClear();
222
+
223
+ const retainedStart = performance.now();
224
+
225
+ for (let i = 0; i < requestRuns; i++) {
226
+
227
+ await fetchPage(buildRequest(), {});
228
+
229
+ }
230
+
231
+ const retainedMs = performance.now() - retainedStart;
232
+
233
+ clearComponentAndPageMemory();
234
+ mocks.componentDependencies.mockClear();
235
+
236
+ const clearedStart = performance.now();
237
+
238
+ for (let i = 0; i < requestRuns; i++) {
239
+
240
+ clearComponentAndPageMemory();
241
+ await fetchPage(buildRequest(), {});
242
+
243
+ }
244
+
245
+ const clearedMs = performance.now() - clearedStart;
246
+ const ratioRetainedToCleared = retainedMs / clearedMs;
247
+
248
+ console.log("fetchPage batch cache benchmark", {
249
+ requestRuns,
250
+ retainedMs,
251
+ clearedMs,
252
+ ratioRetainedToCleared,
253
+ });
254
+
255
+ expect(retainedMs).toBeLessThanOrEqual(clearedMs * 1.5);
256
+
257
+ });
258
+
259
+ });
@@ -0,0 +1,26 @@
1
+ export default ({
2
+ withFullScreenHero = false
3
+ } = {}) => {
4
+
5
+ if(!app.$main){
6
+
7
+ app.$main = $('#main');
8
+
9
+ }
10
+
11
+ if(withFullScreenHero){
12
+
13
+ app.$main.addClass('withFullScreenHero');
14
+
15
+ } else {
16
+
17
+ app.$main.removeClass('withFullScreenHero');
18
+
19
+ }
20
+
21
+ app.$main.empty();
22
+ $(document).scrollTop(0);
23
+
24
+ return app.$main;
25
+
26
+ };
@@ -0,0 +1,14 @@
1
+ export default () => {
2
+
3
+ if(!app.$main){
4
+
5
+ app.$main = $('#main');
6
+
7
+ }
8
+
9
+ app.$main.empty();
10
+ $(document).scrollTop(0);
11
+
12
+ return app.$main;
13
+
14
+ };
@@ -0,0 +1,4 @@
1
+ #app {
2
+ margin: 0;
3
+ padding: 0;
4
+ }
@@ -0,0 +1,42 @@
1
+ import ui from '#ui';
2
+
3
+ export default function silentStart({
4
+ theme = {
5
+ name: 'light',
6
+ neutralColor: 'zinc',
7
+ accentColor: 'cyan',
8
+ customColors: {},
9
+ }
10
+ }) {
11
+
12
+ app.theme = theme;
13
+
14
+ if(theme.customColors){
15
+
16
+ $.each(theme.customColors, (name, hexValue) => {
17
+
18
+ let inputColor;
19
+ let anchorShade = 600;
20
+
21
+ if(typeof hexValue === 'string'){
22
+
23
+ inputColor = hexValue;
24
+
25
+ } else if(typeof hexValue === 'object' && hexValue !== null){
26
+
27
+ inputColor = hexValue.inputColor;
28
+ anchorShade = hexValue.anchorShade || anchorShade;
29
+
30
+ }
31
+
32
+ ui.colors.custom({
33
+ inputColor: inputColor,
34
+ anchorShade: 600,
35
+ name
36
+ });
37
+
38
+ });
39
+
40
+ }
41
+
42
+ }
@@ -0,0 +1,150 @@
1
+ (function(){
2
+
3
+ // IMPORTANT NOTE: with custom sortFn's use toLowerCase !!
4
+ $.fn.extend({
5
+
6
+ //Name the function
7
+ insertIntoList: function($ul, options) {
8
+
9
+ var defaults = {
10
+ 'sortFn':function($elm){
11
+
12
+ return $elm.html().toLowerCase();
13
+
14
+ },
15
+ 'insertBeforeLast':false,
16
+ 'za':false,
17
+ 'selector':null
18
+ };
19
+
20
+ var options = $.extend(defaults, options); // jshint ignore:line
21
+
22
+ return this.each(function() {
23
+
24
+ if(!$ul){
25
+
26
+ return false;
27
+
28
+ }
29
+
30
+ // From here on in, it's "normal" jQuery
31
+
32
+ var $li = $(this); // new item
33
+ var $lis;
34
+
35
+
36
+ if(options.selector){
37
+
38
+ $lis = $ul.children(options.selector); // existing items
39
+
40
+ } else {
41
+
42
+ $lis = $ul.children(); // existing items
43
+
44
+ }
45
+
46
+ var newHtml = options.sortFn($li);
47
+
48
+ if($lis.length === 0){
49
+
50
+ $li.appendTo($ul);
51
+
52
+ return;
53
+
54
+ }
55
+
56
+ $lis.each(function(k,e){
57
+
58
+ var $t = $(this);
59
+
60
+ // meta hack for skip
61
+ if($t.data('insertIntoListSkip') === true){
62
+
63
+ return true;
64
+
65
+ }
66
+
67
+ var isLast = $t.is( ":last-child" );
68
+ var current = options.sortFn($t);
69
+
70
+ if(options.insertBeforeLast && isLast){
71
+
72
+ // insert before last child
73
+ $li.insertBefore($t);
74
+
75
+ return false;
76
+
77
+ }
78
+
79
+ if(options.za){
80
+
81
+ if(current < newHtml){
82
+
83
+ $li.insertBefore($t);
84
+
85
+ return false;
86
+
87
+ } else {
88
+
89
+ // continue if it is not the last item
90
+ if(!isLast){
91
+
92
+ return true;
93
+
94
+ }
95
+
96
+ // it is the last item
97
+ if(options.insertBeforeLast){
98
+
99
+ $li.insertBefore($t);
100
+
101
+ } else {
102
+
103
+ $li.insertAfter($t);
104
+
105
+ }
106
+
107
+ }
108
+
109
+ } else {
110
+
111
+ if(current > newHtml){
112
+
113
+ $li.insertBefore($t);
114
+
115
+ return false;
116
+
117
+ } else {
118
+
119
+ // continue if it is not the last item
120
+ if(!isLast){
121
+
122
+ return true;
123
+
124
+ }
125
+
126
+ // it is the last item
127
+ if(options.insertBeforeLast){
128
+
129
+ $li.insertBefore($t);
130
+
131
+ } else {
132
+
133
+ $li.insertAfter($t);
134
+
135
+ }
136
+
137
+ }
138
+
139
+ }
140
+
141
+
142
+ });
143
+
144
+ }); // each CORE
145
+
146
+ } // xoSelect Function
147
+
148
+ }); // extend
149
+
150
+ })();
@@ -0,0 +1,26 @@
1
+ (function() {
2
+
3
+ $.fn.keyUpWithTimeout = function(callback, timeout) {
4
+
5
+ var timer;
6
+
7
+ return this.each(function() {
8
+
9
+ let $this = $(this);
10
+
11
+ $this.on('keyup', function() {
12
+
13
+ clearTimeout(timer);
14
+ timer = setTimeout(function() {
15
+
16
+ callback($this.val(), this);
17
+
18
+ }, timeout);
19
+
20
+ });
21
+
22
+ });
23
+
24
+ };
25
+
26
+ })();