pinstripe 0.33.0 → 0.35.0

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 (187) hide show
  1. package/README.md +1 -1
  2. package/lib/class.js +1 -63
  3. package/lib/component.js +146 -100
  4. package/lib/components/helpers.js +6 -5
  5. package/lib/components/index.js +14 -0
  6. package/lib/components/pinstripe_anchor.js +5 -4
  7. package/lib/components/pinstripe_body.js +13 -4
  8. package/lib/components/pinstripe_document.js +36 -2
  9. package/lib/components/pinstripe_form.js +36 -5
  10. package/lib/components/pinstripe_frame.js +38 -20
  11. package/lib/components/pinstripe_global_styles.js +13 -0
  12. package/lib/components/pinstripe_menu.js +33 -0
  13. package/lib/components/pinstripe_modal.js +98 -31
  14. package/lib/components/pinstripe_overlay.js +5 -7
  15. package/lib/components/pinstripe_popover.js +86 -0
  16. package/lib/components/pinstripe_progress_bar.js +6 -4
  17. package/lib/components/pinstripe_redirect.js +18 -0
  18. package/lib/components/pinstripe_script.js +6 -3
  19. package/lib/components/pinstripe_skeleton.js +4 -2
  20. package/lib/constants.js +1 -26
  21. package/lib/index.js +0 -12
  22. package/lib/inflector.js +1 -187
  23. package/lib/initialize.js +9 -3
  24. package/lib/lru_cache.js +1 -52
  25. package/lib/proof_of_work.js +1 -83
  26. package/lib/registry.js +1 -137
  27. package/lib/trapify.js +1 -31
  28. package/lib/virtual_node.js +1 -171
  29. package/package.json +9 -41
  30. package/babel.config.cjs +0 -18
  31. package/cli.js +0 -36
  32. package/jest.config.cjs +0 -6
  33. package/lib/app.js +0 -37
  34. package/lib/apps/_file_importer.js +0 -1
  35. package/lib/apps/docs.js +0 -6
  36. package/lib/apps/main.js +0 -6
  37. package/lib/background_job.js +0 -42
  38. package/lib/background_jobs/_file_importer.js +0 -1
  39. package/lib/background_jobs/purge_used_hashes.js +0 -12
  40. package/lib/client.js +0 -20
  41. package/lib/command.js +0 -68
  42. package/lib/command.test.js +0 -32
  43. package/lib/commands/_file_importer.js +0 -1
  44. package/lib/commands/drop_database.js +0 -6
  45. package/lib/commands/generate_app.js +0 -43
  46. package/lib/commands/generate_background_job.js +0 -47
  47. package/lib/commands/generate_command.js +0 -39
  48. package/lib/commands/generate_component.js +0 -50
  49. package/lib/commands/generate_migration.js +0 -55
  50. package/lib/commands/generate_model.js +0 -40
  51. package/lib/commands/generate_project.js +0 -147
  52. package/lib/commands/generate_service.js +0 -34
  53. package/lib/commands/generate_static_site.js +0 -92
  54. package/lib/commands/generate_view.js +0 -80
  55. package/lib/commands/init_database.js +0 -9
  56. package/lib/commands/list_apps.js +0 -15
  57. package/lib/commands/list_background_jobs.js +0 -15
  58. package/lib/commands/list_commands.js +0 -21
  59. package/lib/commands/list_components.js +0 -16
  60. package/lib/commands/list_migrations.js +0 -15
  61. package/lib/commands/list_models.js +0 -15
  62. package/lib/commands/list_services.js +0 -15
  63. package/lib/commands/list_views.js +0 -19
  64. package/lib/commands/migrate_database.js +0 -6
  65. package/lib/commands/reset_database.js +0 -9
  66. package/lib/commands/run_background_job.js +0 -11
  67. package/lib/commands/seed_database.js +0 -6
  68. package/lib/commands/show_config.js +0 -6
  69. package/lib/commands/start_repl.js +0 -6
  70. package/lib/commands/start_server.js +0 -28
  71. package/lib/components/_file_importer.js +0 -1
  72. package/lib/context.js +0 -40
  73. package/lib/database/client.js +0 -322
  74. package/lib/database/column_reference.js +0 -13
  75. package/lib/database/constants.js +0 -99
  76. package/lib/database/index.js +0 -7
  77. package/lib/database/migration.js +0 -32
  78. package/lib/database/migrator.js +0 -28
  79. package/lib/database/row.js +0 -387
  80. package/lib/database/singleton.js +0 -12
  81. package/lib/database/table.js +0 -518
  82. package/lib/database/table_reference.js +0 -33
  83. package/lib/database/union.js +0 -130
  84. package/lib/database.js +0 -147
  85. package/lib/defer.js +0 -35
  86. package/lib/defer.test.js +0 -48
  87. package/lib/escape_html.js +0 -2
  88. package/lib/extensions/_file_importer.js +0 -1
  89. package/lib/extensions/multi-tenant/database/row.js +0 -48
  90. package/lib/extensions/multi-tenant/database/table.js +0 -30
  91. package/lib/extensions/multi-tenant/database.js +0 -18
  92. package/lib/extensions/multi-tenant/index.js +0 -4
  93. package/lib/extensions/multi-tenant/migrations/1627976174_create_tenant_table_and_add_tenant_id_to_existing_tables.js +0 -20
  94. package/lib/extensions/multi-tenant/migrations/_file_importer.js +0 -2
  95. package/lib/extensions/multi-tenant/models/_file_importer.js +0 -1
  96. package/lib/extensions/multi-tenant/models/tenant.js +0 -14
  97. package/lib/extensions/multi-tenant/services/_file_importer.js +0 -2
  98. package/lib/extensions/multi-tenant/services/database.js +0 -35
  99. package/lib/extensions/multi-tenant/services/run_background_job.js +0 -27
  100. package/lib/html.js +0 -72
  101. package/lib/import_all.js +0 -94
  102. package/lib/lru_cache.test.js +0 -45
  103. package/lib/markdown.js +0 -66
  104. package/lib/migrations/1708772281_create_used_hash.js +0 -9
  105. package/lib/migrations/_file_importer.js +0 -2
  106. package/lib/model.js +0 -110
  107. package/lib/models/_file_importer.js +0 -2
  108. package/lib/models/used_hash.js +0 -7
  109. package/lib/project.js +0 -70
  110. package/lib/service_consumer.js +0 -16
  111. package/lib/service_factory.js +0 -22
  112. package/lib/services/_file_importer.js +0 -1
  113. package/lib/services/app.js +0 -11
  114. package/lib/services/bot.js +0 -70
  115. package/lib/services/cli_utils.js +0 -25
  116. package/lib/services/client_builder.js +0 -70
  117. package/lib/services/config.js +0 -66
  118. package/lib/services/cookies.js +0 -19
  119. package/lib/services/create_model.js +0 -8
  120. package/lib/services/css_classes_for.js +0 -13
  121. package/lib/services/database.js +0 -14
  122. package/lib/services/defer.js +0 -8
  123. package/lib/services/docs.js +0 -93
  124. package/lib/services/fetch.js +0 -86
  125. package/lib/services/format_date.js +0 -8
  126. package/lib/services/fs_builder.js +0 -132
  127. package/lib/services/inflector.js +0 -8
  128. package/lib/services/initial_params.js +0 -13
  129. package/lib/services/only_once.js +0 -24
  130. package/lib/services/params.js +0 -13
  131. package/lib/services/parse_html.js +0 -8
  132. package/lib/services/project.js +0 -8
  133. package/lib/services/render_form.js +0 -120
  134. package/lib/services/render_html.js +0 -8
  135. package/lib/services/render_markdown.js +0 -9
  136. package/lib/services/render_table.js +0 -48
  137. package/lib/services/render_view.js +0 -6
  138. package/lib/services/repl.js +0 -54
  139. package/lib/services/run_background_job.js +0 -8
  140. package/lib/services/run_command.js +0 -8
  141. package/lib/services/run_in_new_workspace.js +0 -11
  142. package/lib/services/send_mail.js +0 -47
  143. package/lib/services/server.js +0 -105
  144. package/lib/services/trapify.js +0 -8
  145. package/lib/services/view.js +0 -6
  146. package/lib/singleton.js +0 -13
  147. package/lib/string_reader.js +0 -22
  148. package/lib/unescape_html.js +0 -2
  149. package/lib/unescape_html.test.js +0 -9
  150. package/lib/util.js +0 -12
  151. package/lib/validation_error.js +0 -7
  152. package/lib/view.js +0 -120
  153. package/lib/view_file_importers/js.js +0 -38
  154. package/lib/view_file_importers/md.js +0 -50
  155. package/lib/views/_file_importer.js +0 -1
  156. package/lib/views/docs/_404.js +0 -16
  157. package/lib/views/docs/_footer.js +0 -67
  158. package/lib/views/docs/_header.js +0 -17
  159. package/lib/views/docs/_hero.js +0 -59
  160. package/lib/views/docs/_layout.js +0 -118
  161. package/lib/views/docs/_navbar.js +0 -99
  162. package/lib/views/docs/_sidebar.js +0 -31
  163. package/lib/views/docs/default.js +0 -6
  164. package/lib/views/docs/docs/default.js +0 -24
  165. package/lib/views/docs/docs/guides/introduction.md +0 -18
  166. package/lib/views/docs/images/logo.svg +0 -7
  167. package/lib/views/docs/index.js +0 -16
  168. package/lib/views/shared/_button.js +0 -100
  169. package/lib/views/shared/_content.js +0 -81
  170. package/lib/views/shared/_danger_area.js +0 -78
  171. package/lib/views/shared/_editable_area.js +0 -37
  172. package/lib/views/shared/_form.js +0 -273
  173. package/lib/views/shared/_navbar.js +0 -110
  174. package/lib/views/shared/_pagination.js +0 -47
  175. package/lib/views/shared/_panel.js +0 -69
  176. package/lib/views/shared/_section.js +0 -45
  177. package/lib/views/shared/_shell/index.js +0 -26
  178. package/lib/views/shared/_shell/javascripts/all.js.js +0 -7
  179. package/lib/views/shared/_shell/javascripts/all.js.map.js +0 -7
  180. package/lib/views/shared/_shell/stylesheets/all.css +0 -4
  181. package/lib/views/shared/_shell/stylesheets/global.css +0 -126
  182. package/lib/views/shared/_shell/stylesheets/reset.css +0 -74
  183. package/lib/views/shared/_shell/stylesheets/vars.css +0 -25
  184. package/lib/views/shared/_shell/stylesheets/view.css.js +0 -43
  185. package/lib/views/shared/_table.js +0 -139
  186. package/lib/virtual_node.test.js +0 -28
  187. package/lib/workspace.js +0 -21
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  # Welcome to Pinstripe
3
3
 
4
- A slick web framework for Node.js.
4
+ A slick web framework for your browser.
5
5
 
6
6
  ## License
7
7
 
package/lib/class.js CHANGED
@@ -1,64 +1,2 @@
1
1
 
2
- export class Class {
3
-
4
- static extend(){
5
- return class extends this {};
6
- }
7
-
8
- static include(...includes){
9
- includes.forEach(include => {
10
- if(typeof include.meta == 'function') include.meta.call(this);
11
- this.prototype.assignProps(include, name => name != 'meta');
12
- });
13
- return this;
14
- }
15
-
16
- static assignProps(...sources){
17
- return assignProps(this, ...sources);
18
- }
19
-
20
- static new(...args){
21
- return new this(...args);
22
- }
23
-
24
- static get parent(){
25
- return this.__proto__;
26
- }
27
-
28
- constructor(...args){
29
- let out = this.initialize(...args);
30
- if(typeof out?.then == 'function'){
31
- return out.then(out => out || this);
32
- }
33
- return out || this;
34
- }
35
-
36
- initialize(){
37
-
38
- }
39
-
40
- assignProps(...sources){
41
- return assignProps(this, ...sources);
42
- }
43
- }
44
-
45
- const assignProps = (target, ...sources) => {
46
- const fn = typeof sources[sources.length - 1] == 'function' ? sources.pop() : () => true;
47
-
48
- sources.forEach(source => {
49
- Object.getOwnPropertyNames(source).forEach(name => {
50
- if(!fn(name)){
51
- return;
52
- }
53
- const descriptor = { ...Object.getOwnPropertyDescriptor(source, name) };
54
- const { get: targetGet, set: targetSet } = (Object.getOwnPropertyDescriptor(target, name) || {});
55
- const { get = targetGet, set = targetSet } = descriptor;
56
-
57
- if(get) descriptor.get = get;
58
- if(set) descriptor.set = set;
59
-
60
- Object.defineProperty(target, name, descriptor);
61
- });
62
- });
63
- return target;
64
- };
2
+ export { Class } from '@sintra/utils';
package/lib/component.js CHANGED
@@ -1,13 +1,10 @@
1
1
 
2
- import { fileURLToPath } from 'url'; // pinstripe-if-client: const fileURLToPath = undefined;
3
-
4
2
  import { Class } from './class.js';
5
3
  import { TEXT_ONLY_TAGS } from './constants.js';
6
4
  import { Inflector } from './inflector.js';
7
5
  import { VirtualNode } from './virtual_node.js';
8
6
  import { Registry } from './registry.js';
9
7
  import { ComponentEvent } from './component_event.js';
10
- import { Client } from './client.js'; // pinstripe-if-client: const Client = undefined;
11
8
  import { generateProofOfWork } from './proof_of_work.js';
12
9
 
13
10
  export const Component = Class.extend().include({
@@ -24,7 +21,7 @@ export const Component = Class.extend().include({
24
21
  node._component.attributes['data-component'] || (node._component.type == '#document' ? 'pinstripe-document' : node._component.type),
25
22
  node
26
23
  );
27
- (node._component.attributes.class || '').trim().split(/\s+/).forEach((className) => {
24
+ if(!this.isFromCachedHtml) (node._component.attributes.class || '').trim().split(/\s+/).forEach((className) => {
28
25
  const decoratorMethodName = `.${className}`;
29
26
  if(typeof node._component[decoratorMethodName] == 'function'){
30
27
  node._component[decoratorMethodName]();
@@ -39,38 +36,11 @@ export const Component = Class.extend().include({
39
36
  return Inflector.instance.dasherize(name);
40
37
  }
41
38
  });
42
-
43
- this.FileImporter.register('js', {
44
- meta(){
45
- const { importFile } = this.prototype;
46
-
47
- this.include({
48
- async importFile(params){
49
- const { filePath, relativeFilePathWithoutExtension } = params;
50
- if((await import(filePath)).default){
51
- Client.instance.addModule(`
52
- import { Component } from ${JSON.stringify(fileURLToPath(`${import.meta.url}/../index.js`))};
53
- import include from ${JSON.stringify(filePath)};
54
- Component.register(${JSON.stringify(relativeFilePathWithoutExtension)}, include);
55
- `);
56
- } else {
57
- Client.instance.addModule(`
58
- import ${JSON.stringify(filePath)};
59
- `);
60
- }
61
- return importFile.call(this, params);
62
- }
63
- });
64
- }
65
- });
66
39
  },
67
40
 
68
41
  initialize(node, skipInit = false){
69
42
  this.node = node;
70
- this._registeredEventListeners = [];
71
- this._registeredObservers = [];
72
- this._registeredTimers = [];
73
- this._registeredAbortControllers = [];
43
+ this._managedResources = [];
74
44
  this._virtualNodeFilters = [];
75
45
 
76
46
  this.addVirtualNodeFilter(function(){
@@ -84,6 +54,28 @@ export const Component = Class.extend().include({
84
54
  if(autofocus){
85
55
  this.setTimeout(() => this.node.focus());
86
56
  }
57
+
58
+ const { name } = this.constructor;
59
+
60
+ if(this.constructor.mixins[name]) return;
61
+
62
+ if(name.match(/^([a-z0-9]+|document-fragment)$/)) return;
63
+
64
+ const { componentBase } = this.document;
65
+ if(!componentBase) return;
66
+
67
+ this.shadow.patch(`
68
+ <pinstripe-global-styles></pinstripe-global-styles>
69
+ <pinstripe-frame use-cache="true" load-on-init="false"></pinstripe-frame>
70
+ `);
71
+ const frame = this.shadow.find('pinstripe-frame');
72
+
73
+ const url = new URL(`${componentBase}/${name}`, frame.url);
74
+ const { params } = this;
75
+ for(const [key, value] of Object.entries(params)){
76
+ url.searchParams.set(key, value);
77
+ }
78
+ frame.load(url);
87
79
  },
88
80
 
89
81
  get type(){
@@ -290,6 +282,14 @@ export const Component = Class.extend().include({
290
282
  return Component.instanceFor(this.node.shadowRoot);
291
283
  },
292
284
 
285
+ get isFromPlaceholderHtml(){
286
+ return this.frame?.status == 'using-placeholder-html';
287
+ },
288
+
289
+ get isFromCachedHtml(){
290
+ return this.frame?.status == 'using-cached-html';
291
+ },
292
+
293
293
  focus(){
294
294
  this.node.focus();
295
295
  return this;
@@ -306,6 +306,20 @@ export const Component = Class.extend().include({
306
306
  }
307
307
  },
308
308
 
309
+ manage(resource){
310
+ const destroy = resource.destroy;
311
+ const that = this;
312
+ Object.assign(resource, {
313
+ destroy(){
314
+ const out = destroy.call(this);
315
+ that._managedResources = that._managedResources.filter(item => item !== this);
316
+ return out;
317
+ }
318
+ });
319
+ this._managedResources.push(resource);
320
+ return resource;
321
+ },
322
+
309
323
  on(name, ...args){
310
324
  const fn = args.pop()
311
325
  const selector = args.pop()
@@ -323,9 +337,9 @@ export const Component = Class.extend().include({
323
337
 
324
338
  this.node.addEventListener(name, wrapperFn);
325
339
 
326
- this._registeredEventListeners.push([name, wrapperFn]);
327
-
328
- return this;
340
+ return this.manage({
341
+ destroy: () => this.node.removeEventListener(name, wrapperFn)
342
+ });
329
343
  },
330
344
 
331
345
  trigger(name, options = {}){
@@ -345,16 +359,22 @@ export const Component = Class.extend().include({
345
359
  return this;
346
360
  },
347
361
 
348
- setTimeout(...args){
349
- const out = setTimeout(...args);
350
- this._registeredTimers.push(out);
362
+ setTimeout(fn, ...args){
363
+ const timeout = setTimeout(() => {
364
+ fn();
365
+ out.destroy();
366
+ }, ...args);
367
+ const out = this.manage({
368
+ destroy: () => clearTimeout(timeout)
369
+ });
351
370
  return out;
352
371
  },
353
372
 
354
373
  setInterval(...args){
355
- const out = setInterval(...args);
356
- this._registeredTimers.push(out);
357
- return out;
374
+ const interval = setInterval(...args);
375
+ return this.manage({
376
+ destroy: () => clearInterval(interval)
377
+ });
358
378
  },
359
379
 
360
380
  remove(){
@@ -442,53 +462,57 @@ export const Component = Class.extend().include({
442
462
  subtree: true
443
463
  });
444
464
 
445
- this._registeredObservers.push(observer);
446
-
447
- return this;
465
+ return this.manage({
466
+ destroy: () => observer.disconnect()
467
+ });
448
468
  },
449
469
 
450
- async fetch(url, options = {}){
451
- const { minimumDelay = 0, requiresProofOfWork = false, ...otherOptions } = options;
452
- const { progressBar } = this.document;
453
- const frame = this.frame || this;
454
- const normalizedUrl = new URL(url, frame.url);
470
+ fetch(url, options = {}){
455
471
  const abortController = new AbortController();
456
- this._registeredAbortControllers.push(abortController);
457
- progressBar.start();
458
472
  let minimumDelayTimeout;
459
- const cleanUp = () => {
460
- clearTimeout(minimumDelayTimeout);
461
- this._registeredAbortControllers = this._registeredAbortControllers.filter(item => item !== abortController);
462
- progressBar.stop();
463
- };
464
- try {
465
- if(requiresProofOfWork){
466
- if(!(otherOptions.body instanceof FormData)) throw new Error(`Proof of work requires form data to be present`);
467
- const values = {};
468
- otherOptions.body.forEach((value, key) => values[key] = value);
469
- otherOptions.body.append('_proofOfWork', await generateProofOfWork(values, {
470
- abortSignal: abortController.signal,
471
- onProgress: progress => this.trigger('proofOfWorkProgress', { data: progress, bubbles: false })
472
- }))
473
+ let rejectMinimumDelay;
474
+ let isSuccess = false;
475
+
476
+ const promise = (async () => {
477
+ const { minimumDelay = 0, requiresProofOfWork = false, ...otherOptions } = options;
478
+ const frame = this.frame || this;
479
+ const normalizedUrl = new URL(url, frame.url);
480
+
481
+ try {
482
+ if(requiresProofOfWork){
483
+ if(!(otherOptions.body instanceof FormData)) throw new Error(`Proof of work requires form data to be present`);
484
+ const values = {};
485
+ otherOptions.body.forEach((value, key) => values[key] = value);
486
+ otherOptions.body.append('_proofOfWork', await generateProofOfWork(values, {
487
+ abortSignal: abortController.signal,
488
+ onProgress: progress => this.trigger('proofOfWorkProgress', { data: progress, bubbles: false })
489
+ }))
490
+ }
491
+ const promises = [
492
+ fetch(normalizedUrl, { signal: abortController.signal, ...otherOptions }),
493
+ new Promise((resolve, reject) => {
494
+ minimumDelayTimeout = setTimeout(resolve, minimumDelay);
495
+ rejectMinimumDelay = reject;
496
+ })
497
+ ];
498
+ const [ out ] = await Promise.all(promises);
499
+ isSuccess = true;
500
+ promise.destroy();
501
+ return out;
502
+ } catch(e){
503
+ promise.destroy();
504
+ throw e;
473
505
  }
474
- const promises = [
475
- fetch(normalizedUrl, { signal: abortController.signal, ...otherOptions }),
476
- new Promise(resolve => minimumDelayTimeout = setTimeout(resolve, minimumDelay))
477
- ];
478
- const [ out ] = await Promise.all(promises);
479
- cleanUp();
480
- return out;
481
- } catch(e){
482
- cleanUp();
483
- throw e;
484
- }
485
- },
486
-
487
- abort(){
488
- while(this._registeredAbortControllers.length){
489
- this._registeredAbortControllers.pop().abort();
490
- }
491
- return this;
506
+ })();
507
+
508
+ return this.manage(Object.assign(promise, {
509
+ destroy: () => {
510
+ if(isSuccess) return;
511
+ if(!abortController.signal.aborted) abortController.abort(`Request aborted`);
512
+ clearTimeout(minimumDelayTimeout);
513
+ if(rejectMinimumDelay) rejectMinimumDelay();
514
+ }
515
+ }));
492
516
  },
493
517
 
494
518
  find(...args){
@@ -526,29 +550,15 @@ function clean(){
526
550
 
527
551
  [...this.node.childNodes].forEach(node => node._component && clean.call(node._component));
528
552
 
529
- while(this._registeredEventListeners.length){
530
- this.node.removeEventListener(...this._registeredEventListeners.pop());
531
- }
532
-
533
- while(this._registeredObservers.length){
534
- this._registeredObservers.pop().disconnect();
553
+ while(this._managedResources.length){
554
+ this._managedResources.pop().destroy();
535
555
  }
536
556
 
537
- clearTimers.call(this);
538
-
539
- this.abort();
540
-
541
557
  if(this._overlayChild) this._overlayChild.remove();
542
558
 
543
559
  delete this.node._component;
544
560
  }
545
561
 
546
- function clearTimers(){
547
- while(this._registeredTimers.length){
548
- clearTimeout(this._registeredTimers.pop());
549
- }
550
- }
551
-
552
562
  function initChildren(){
553
563
  if(this.node.shadowRoot){
554
564
  this.shadow.children.forEach(child => initChildren.call(child));
@@ -577,7 +587,7 @@ function createVirtualNode(html){
577
587
  function patch(attributes, virtualChildren){
578
588
  const isFrame = this.type == 'pinstripe-frame' || this.attributes['data-component'] == 'pinstripe-frame';
579
589
  const isEmptyFrame = isFrame && virtualChildren.length == 0;
580
- if(isEmptyFrame && attributes['data-load-on-init'] === undefined){
590
+ if(isEmptyFrame && attributes['load-on-init'] === undefined && attributes['data-load-on-init'] === undefined){
581
591
  attributes['data-load-on-init'] = 'true';
582
592
  }
583
593
  patchAttributes.call(this, attributes);
@@ -614,6 +624,8 @@ function patchAttributes(attributes){
614
624
  }
615
625
 
616
626
  function patchChildren(virtualChildren){
627
+ if(this.type == 'head') return patchHeadChildren.call(this, virtualChildren);
628
+
617
629
  const children = [...this.node.childNodes].map(
618
630
  node => new Component(node, true)
619
631
  );
@@ -646,6 +658,40 @@ function patchChildren(virtualChildren){
646
658
  }
647
659
  }
648
660
 
661
+ function patchHeadChildren(virtualChildren){
662
+ const children = [...this.node.childNodes].map(
663
+ node => new Component(node, true)
664
+ );
665
+
666
+ const stylesheets = {};
667
+ for(let i = 0; i < virtualChildren.length; i++){
668
+ const virtualChild = virtualChildren[i];
669
+ if(virtualChild.type != 'link') continue;
670
+ if(virtualChild.attributes.rel != 'stylesheet') continue;
671
+ const href = virtualChild.attributes.href;
672
+ stylesheets[href] = virtualChild;
673
+ }
674
+ while(children.length > 0){
675
+ const child = children.shift();
676
+ if(child.type == 'link' && child.attributes.rel == 'stylesheet'){
677
+ const { href } = child.attributes;
678
+ const stylesheet = stylesheets[href];
679
+ if(stylesheet){
680
+ patch.call(child, stylesheet.attributes, stylesheet.children);
681
+ continue;
682
+ }
683
+ }
684
+ remove.call(child);
685
+ }
686
+
687
+ for(let i = 0; i < virtualChildren.length; i++){
688
+ const virtualChild = virtualChildren[i];
689
+ if(virtualChild.type == 'script') continue;
690
+ if(virtualChild.type == 'link' && virtualChild.attributes.rel == 'stylesheet' && stylesheets[virtualChild.attributes.href]) continue;
691
+ insert.call(this, virtualChild, null, false);
692
+ }
693
+ }
694
+
649
695
  function insert(virtualNode, referenceChild, returnComponent = true){
650
696
  const { type, attributes, children } = virtualNode;
651
697
 
@@ -3,7 +3,7 @@ import { LruCache } from '../lru_cache.js';
3
3
 
4
4
  export const loadCache = LruCache.new();
5
5
 
6
- export function loadFrame(confirm, target, method, url, placeholderUrl, requiresProofOfWork = false){
6
+ export function loadFrame({ confirm, target, method, url, placeholderUrl, requiresProofOfWork = false, values = this.values }){
7
7
  if(confirm && !window.confirm(confirm)){
8
8
  return;
9
9
  }
@@ -11,15 +11,17 @@ export function loadFrame(confirm, target, method, url, placeholderUrl, requires
11
11
  let frame;
12
12
 
13
13
  if(target == '_overlay'){
14
- this.document.body.clip();
15
14
  frame = this.document.descendants.find(node => node.is('body')).append(`<pinstripe-overlay load-on-init="false"></pinstripe-overlay>`).pop();
16
15
  frame._parent = this;
17
16
  this._overlayChild = frame;
18
- } else {
17
+ } else if(target) {
19
18
  frame = getFrame.call(this, target);
20
- if(!frame) return;
19
+ } else if(this.isFrame) {
20
+ frame = this;
21
21
  }
22
22
 
23
+ if(!frame) return;
24
+
23
25
  url = normalizeUrl(url || frame.url, this.frame.url);
24
26
  if(url.protocol != 'data:' && (url.host != frame.url.host || url.port != frame.url.port)){
25
27
  return;
@@ -29,7 +31,6 @@ export function loadFrame(confirm, target, method, url, placeholderUrl, requires
29
31
 
30
32
  if(method.match(/POST|PUT|PATCH/i)){
31
33
  const formData = new FormData();
32
- const values = this.values;
33
34
  Object.keys(values).forEach((name) => formData.append(name, values[name]));
34
35
  frame.load(url, { method, body: formData, placeholderUrl, requiresProofOfWork });
35
36
  } else {
@@ -1 +1,15 @@
1
1
 
2
+ import './pinstripe_anchor.js';
3
+ import './pinstripe_body.js';
4
+ import './pinstripe_document.js';
5
+ import './pinstripe_form.js';
6
+ import './pinstripe_frame.js';
7
+ import './pinstripe_global_styles.js';
8
+ import './pinstripe_menu.js';
9
+ import './pinstripe_modal.js';
10
+ import './pinstripe_overlay.js';
11
+ import './pinstripe_popover.js';
12
+ import './pinstripe_progress_bar.js';
13
+ import './pinstripe_redirect.js';
14
+ import './pinstripe_script.js';
15
+ import './pinstripe_skeleton.js';
@@ -1,15 +1,16 @@
1
1
 
2
+ import { Component } from "../component.js";
2
3
  import { loadFrame, getFrame, normalizeUrl } from "./helpers.js";
3
4
 
4
- export default {
5
+ Component.register('pinstripe-anchor', {
5
6
  initialize(...args){
6
7
  this.constructor.parent.prototype.initialize.call(this, ...args);
7
8
  this.on('click', (event) => {
8
9
  const { confirm, target = '_self', method = 'GET', href, placeholder } = this.params;
10
+ if(target == '_blank') return;
9
11
  if(normalizeUrl(href, window.location.href).host != window.location.host) return;
10
12
  event.preventDefault();
11
- event.stopPropagation();
12
- loadFrame.call(this, confirm, target, method, href, placeholder);
13
+ loadFrame.call(this, { confirm, target, method, url: href, placeholderUrl: placeholder });
13
14
  });
14
15
 
15
16
  const { target = '_self', method = 'GET', href, placeholder, preload } = this.params;
@@ -21,4 +22,4 @@ export default {
21
22
 
22
23
  if(this.is('input, textarea')) this.on('keyup', (event) => this.trigger('click'));
23
24
  }
24
- };
25
+ });
@@ -1,5 +1,7 @@
1
1
 
2
- export default {
2
+ import { Component } from "../component.js";
3
+
4
+ Component.register('pinstripe-body', {
3
5
  initialize(...args){
4
6
  this.constructor.parent.prototype.initialize.call(this, ...args);
5
7
 
@@ -8,6 +10,8 @@ export default {
8
10
  <slot>
9
11
  <div class="styles"></div>
10
12
  `);
13
+
14
+ this.clipCounter = 0;
11
15
  },
12
16
 
13
17
  get progressBar(){
@@ -18,7 +22,8 @@ export default {
18
22
  },
19
23
 
20
24
  clip(){
21
- this.shadow.find('.styles').patch(`
25
+ this.clipCounter++;
26
+ if(this.clipCounter == 1) this.shadow.find('.styles').patch(`
22
27
  <style>
23
28
  :host {
24
29
  overflow: hidden !important;
@@ -28,6 +33,10 @@ export default {
28
33
  },
29
34
 
30
35
  unclip(){
31
- this.shadow.find('.styles').patch('');
36
+ this.clipCounter--;
37
+ if(this.clipCounter <= 0){
38
+ this.clipCounter = 0;
39
+ this.shadow.find('.styles').patch('');
40
+ }
32
41
  }
33
- };
42
+ });
@@ -1,9 +1,10 @@
1
1
 
2
+ import { Component } from "../component.js";
2
3
  import { loadCache, normalizeUrl } from "./helpers.js";
3
4
 
4
5
  const preloading = {};
5
6
 
6
- export default {
7
+ Component.register('pinstripe-document', {
7
8
  meta(){
8
9
  this.include('pinstripe-frame');
9
10
 
@@ -51,6 +52,21 @@ export default {
51
52
  return this.head.find('meta[name="pinstripe-load-cache-namespace"]')?.params.content ?? 'default';
52
53
  },
53
54
 
55
+ get globalStyles(){
56
+ if(!this._globalStyles){
57
+ try {
58
+ this._globalStyles = mergeCssStylesheets(this.node.styleSheets ?? []);
59
+ } catch (e) {
60
+ this._globalStyles = undefined;
61
+ }
62
+ }
63
+ return this._globalStyles;
64
+ },
65
+
66
+ get componentBase(){
67
+ return this.head.find('meta[name="pinstripe-component-base"]')?.params.content;
68
+ },
69
+
54
70
  async load(url = this.url.toString(), options = {}){
55
71
  const { replace, method = 'GET' } = options;
56
72
  const previousUrl = this.url.toString();
@@ -77,4 +93,22 @@ export default {
77
93
  loadCache.put(`${this.document.loadCacheNamespace}:${url}`, html);
78
94
  delete preloading[url.toString()];
79
95
  }
80
- };
96
+ });
97
+
98
+
99
+ export function mergeCssStylesheets(stylesheets, out = new CSSStyleSheet()) {
100
+ for(const sheet of stylesheets){
101
+ try {
102
+ for(const rule of sheet.cssRules) {
103
+ if(rule instanceof CSSImportRule) {
104
+ mergeCssStylesheets([ rule.styleSheet ], out);
105
+ } else {
106
+ out.insertRule(rule.cssText);
107
+ }
108
+ }
109
+ } catch (e) {
110
+ // Ignore errors from stylesheets that cannot be merged
111
+ }
112
+ }
113
+ return out;
114
+ }
@@ -1,12 +1,14 @@
1
1
 
2
+ import { Component } from "../component.js";
2
3
  import { loadFrame, getFrame, normalizeUrl } from "./helpers.js";
3
4
 
4
- export default {
5
+ Component.register('pinstripe-form', {
5
6
  initialize(...args){
6
7
  this.constructor.parent.prototype.initialize.call(this, ...args);
7
8
 
8
9
  const { confirm, target = '_self', method = 'GET', action, placeholder, requiresProofOfWork, disabled } = this.params;
9
-
10
+ const frame = target == '_overlay' ? this.frame : getFrame.call(this, target);
11
+
10
12
  this.on('submit', (event) => {
11
13
  event.preventDefault();
12
14
  event.stopPropagation();
@@ -16,14 +18,31 @@ export default {
16
18
  ...this.attributes,
17
19
  [this.params.component ? 'data-loading' : 'loading']: 'true'
18
20
  });
21
+
22
+ delete frame._previousHash;
23
+ this._watchInterval?.destroy();
19
24
 
20
- loadFrame.call(this, confirm, target, method, action, placeholder, requiresProofOfWork == 'true');
25
+ loadFrame.call(this, { confirm, target, method, url: action, placeholderUrl: placeholder, requiresProofOfWork: requiresProofOfWork == 'true' });
21
26
  });
22
27
 
23
28
  this._initialHash = JSON.stringify(this.values);
24
29
 
25
- const frame = target == '_overlay' ? this.frame : getFrame.call(this, target);
30
+ if(this.isFromPlaceholderHtml) return;
31
+
26
32
  if(placeholder != undefined) this.document.preload(normalizeUrl(placeholder, frame.url));
33
+
34
+ const hasValuesToWatch = Object.keys(valuesToWatch.call(this)).length > 0;
35
+ if(!hasValuesToWatch) return;
36
+
37
+ frame._previousHash ||= JSON.stringify(valuesToWatch.call(this));
38
+
39
+ this._watchInterval = this.setInterval(() => {
40
+ const currentHash = JSON.stringify(valuesToWatch.call(this));
41
+ if(frame._previousHash == currentHash) return;
42
+ console.log(`${frame._previousHash} != ${currentHash}`)
43
+ frame._previousHash = currentHash;
44
+ loadFrame.call(this, { target, method: 'PATCH', url: action });
45
+ }, 100);
27
46
  },
28
47
 
29
48
  isForm: true,
@@ -31,4 +50,16 @@ export default {
31
50
  get hasUnsavedChanges(){
32
51
  return this.params.hasUnsavedChanges == 'true' || JSON.stringify(this.values) != this._initialHash;
33
52
  }
34
- };
53
+ });
54
+
55
+ function valuesToWatch(){
56
+ const out = {}
57
+ this.inputs.forEach(input => {
58
+ if(input.params.watch != 'true') return;
59
+ const value = input.value;
60
+ if(value !== undefined){
61
+ out[input.name] = value
62
+ }
63
+ })
64
+ return out;
65
+ }