@jinntec/fore 2.1.1 → 2.3.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 (88) hide show
  1. package/README.md +2 -2
  2. package/dist/fore-dev.js +2 -2
  3. package/dist/fore-dev.js.map +1 -1
  4. package/dist/fore.js +2 -2
  5. package/dist/fore.js.map +1 -1
  6. package/index.js +4 -2
  7. package/package.json +10 -38
  8. package/resources/fore.css +263 -262
  9. package/resources/vars.css +8 -0
  10. package/src/DependencyNotifyingDomFacade.js +1 -0
  11. package/src/ForeElementMixin.js +243 -228
  12. package/src/actions/abstract-action.js +407 -398
  13. package/src/actions/fx-action.js +6 -6
  14. package/src/actions/fx-append.js +1 -1
  15. package/src/actions/fx-call.js +63 -62
  16. package/src/actions/fx-confirm.js +11 -11
  17. package/src/actions/fx-construct-done.js +4 -4
  18. package/src/actions/fx-copy.js +36 -36
  19. package/src/actions/fx-delete.js +77 -79
  20. package/src/actions/fx-dispatch.js +14 -14
  21. package/src/actions/fx-hide.js +15 -15
  22. package/src/actions/fx-insert.js +9 -12
  23. package/src/actions/fx-insertchild.js +88 -0
  24. package/src/actions/fx-load.js +188 -185
  25. package/src/actions/fx-message.js +18 -19
  26. package/src/actions/fx-refresh.js +10 -10
  27. package/src/actions/fx-reload.js +16 -14
  28. package/src/actions/fx-replace.js +0 -1
  29. package/src/actions/fx-reset.js +31 -31
  30. package/src/actions/fx-send.js +59 -52
  31. package/src/actions/fx-setattribute.js +48 -49
  32. package/src/actions/fx-setfocus.js +54 -58
  33. package/src/actions/fx-setvalue.js +85 -81
  34. package/src/actions/fx-show.js +11 -11
  35. package/src/actions/fx-toggle.js +2 -2
  36. package/src/actions/fx-toggleboolean.js +38 -39
  37. package/src/actions/fx-unmodified.js +27 -0
  38. package/src/actions/fx-update.js +6 -6
  39. package/src/dep_graph.js +1 -1
  40. package/src/drawdown.js +23 -30
  41. package/src/events.js +19 -19
  42. package/src/fore.js +143 -125
  43. package/src/functions/common-function.js +24 -25
  44. package/src/functions/fx-function.js +6 -99
  45. package/src/functions/fx-functionlib.js +56 -0
  46. package/src/functions/registerFunction.js +112 -0
  47. package/src/fx-bind.js +19 -24
  48. package/src/fx-connection.js +226 -0
  49. package/src/fx-fore.js +844 -815
  50. package/src/fx-header.js +4 -4
  51. package/src/fx-instance.js +25 -29
  52. package/src/fx-model.js +405 -380
  53. package/src/fx-submission.js +399 -397
  54. package/src/fx-var.js +13 -12
  55. package/src/getInScopeContext.js +102 -89
  56. package/src/json-util.js +24 -24
  57. package/src/lab/fore-component.js +48 -52
  58. package/src/lab/instance-inspector.js +13 -16
  59. package/src/modelitem.js +48 -10
  60. package/src/relevance.js +12 -16
  61. package/src/tools/adi.js +858 -812
  62. package/src/tools/fx-action-log.js +377 -295
  63. package/src/tools/fx-devtools.js +210 -210
  64. package/src/tools/fx-dom-inspector.js +123 -122
  65. package/src/tools/fx-json-instance.js +262 -253
  66. package/src/tools/fx-log-item.js +6 -11
  67. package/src/tools/fx-log-settings.js +358 -301
  68. package/src/tools/fx-minimap.js +186 -177
  69. package/src/tools/helpers.js +1 -1
  70. package/src/ui/abstract-control.js +73 -53
  71. package/src/ui/fx-case.js +59 -7
  72. package/src/ui/fx-container.js +13 -14
  73. package/src/ui/fx-control.js +572 -538
  74. package/src/ui/fx-dialog.js +3 -3
  75. package/src/ui/fx-droptarget.js +3 -4
  76. package/src/ui/fx-group.js +4 -2
  77. package/src/ui/fx-hint.js +3 -0
  78. package/src/ui/fx-inspector.js +5 -5
  79. package/src/ui/fx-items.js +11 -11
  80. package/src/ui/fx-output.js +10 -12
  81. package/src/ui/fx-repeat-attributes.js +23 -27
  82. package/src/ui/fx-repeat.js +347 -344
  83. package/src/ui/fx-repeatitem.js +75 -68
  84. package/src/ui/fx-switch.js +39 -14
  85. package/src/ui/fx-trigger.js +4 -4
  86. package/src/withDraggability.js +193 -191
  87. package/src/xpath-evaluation.js +969 -958
  88. package/src/xpath-util.js +161 -134
@@ -0,0 +1,56 @@
1
+ import ForeElementMixin from '../ForeElementMixin.js';
2
+ import registerFunction from './registerFunction.js';
3
+
4
+ /**
5
+ * Allows to extend a form with remote custom functions.
6
+ *
7
+ */
8
+ export class FxFunctionlib extends ForeElementMixin {
9
+ constructor() {
10
+ super();
11
+
12
+ /**
13
+ * @type {Function}
14
+ */
15
+ this._resolve = null;
16
+
17
+ /**
18
+ * @type {Promise<undefined>}
19
+ */
20
+ this.readyPromise = new Promise(resolve => (this._resolveLoading = resolve));
21
+ }
22
+
23
+ async connectedCallback() {
24
+ this.style.display = 'none';
25
+
26
+ const src = this.getAttribute('src');
27
+
28
+ const result = await fetch(src);
29
+ if (!result.ok) {
30
+ console.error(`Loading function library at ${src} failed.`);
31
+ }
32
+
33
+ const body = await result.text();
34
+ const document = new DOMParser().parseFromString(body, 'text/html');
35
+
36
+ /**
37
+ * @type {HTMLElement[]}
38
+ */
39
+ const functions = Array.from(document.querySelectorAll('fx-function'));
40
+ // TODO: also recurse into new function libraries here?
41
+ for (const func of functions) {
42
+ const functionObject = {
43
+ type: func.getAttribute('type'),
44
+ signature: func.getAttribute('signature'),
45
+ functionBody: func.innerText,
46
+ };
47
+
48
+ registerFunction(functionObject, this);
49
+ }
50
+ this._resolveLoading(undefined);
51
+ }
52
+ }
53
+
54
+ if (!customElements.get('fx-functionlib')) {
55
+ customElements.define('fx-functionlib', FxFunctionlib);
56
+ }
@@ -0,0 +1,112 @@
1
+ import { createTypedValueFactory, registerCustomXPathFunction } from 'fontoxpath';
2
+ import { evaluateXPath, globallyDeclaredFunctionLocalNames } from '../xpath-evaluation';
3
+
4
+ /**
5
+ * @param functionObject {{signature: string, type: string|null, functionBody: string}}
6
+ * @param formElement {HTMLElement} The form element connected to this function. Used to determine inscope context
7
+ * @returns {undefined}
8
+ */
9
+ export default function registerFunction(functionObject, formElement) {
10
+ if (functionObject.signature === null) {
11
+ console.error('signature is a required attribute');
12
+ }
13
+
14
+ const type = functionObject.type ?? 'text/xpath';
15
+
16
+ // Parse the signature to something useful
17
+ // Signature is of the form `my:sumproduct($p as xs:decimal*, $q as xs:decimal*) as xs:decimal` or local:something($a as item()*) as item()*
18
+ const signatureParseResult = functionObject.signature.match(
19
+ /(?:(?<prefix>[a-zA-Z_\-][a-zA-Z0-9_\-]*):)?(?<localName>[a-zA-Z_\-][a-zA-Z0-9_\-]*\s*)\((?<params>(?:[^()]*|\([^()]*\))*)\)\s*(?:as\s+(?<returnType>.*))?/,
20
+ );
21
+
22
+ if (!signatureParseResult) {
23
+ throw new Error(`Function signature ${functionObject.signature} could not be parsed`);
24
+ }
25
+
26
+ const { prefix, localName, params, returnType } = signatureParseResult.groups;
27
+
28
+ // TODO: lookup prefix
29
+ const functionIdentifier =
30
+ prefix === 'local' || !prefix
31
+ ? { namespaceURI: 'http://www.w3.org/2005/xquery-local-functions', localName }
32
+ : `${prefix}:${localName}`;
33
+
34
+ // Make the function available globally w/o a prefix. See the functionNameResolver for for how
35
+ // functionObject is picked up
36
+ if (!prefix) {
37
+ globallyDeclaredFunctionLocalNames.push(localName);
38
+ }
39
+
40
+ const paramParts = params
41
+ ? params.split(',').map(param => {
42
+ const match = param.match(/(?<variableName>\$[^\s]+)(?:\sas\s(?<varType>[^\s]+))/);
43
+ if (!match) {
44
+ throw new Error(`Param ${param} could not be parsed`);
45
+ }
46
+ const { variableName, varType } = match.groups;
47
+ return {
48
+ variableName,
49
+ variableType: varType || 'item()*',
50
+ };
51
+ })
52
+ : [];
53
+
54
+ switch (type) {
55
+ case 'text/javascript': {
56
+ // eslint-disable-next-line no-new-func
57
+ const fun = new Function(
58
+ '_domFacade',
59
+ ...paramParts.map(paramPart => paramPart.variableName),
60
+ 'form',
61
+ functionObject.functionBody,
62
+ );
63
+ registerCustomXPathFunction(
64
+ functionIdentifier,
65
+ paramParts.map(paramPart => paramPart.variableType),
66
+ returnType || 'item()*',
67
+ (...args) =>
68
+ fun.apply(formElement.getInScopeContext(), [...args, formElement.getOwnerForm()]),
69
+ );
70
+ break;
71
+ }
72
+
73
+ case 'text/xquf':
74
+ case 'text/xquery':
75
+ case 'text/xpath': {
76
+ const typedValueFactories = paramParts.map(param =>
77
+ createTypedValueFactory(param.variableType),
78
+ );
79
+ const language =
80
+ type === 'text/xpath'
81
+ ? 'XPath3.1'
82
+ : type === 'text/xquery'
83
+ ? 'XQuery3.1'
84
+ : 'XQueryUpdate3.1';
85
+ const fun = (domFacade, ...args) =>
86
+ evaluateXPath(
87
+ functionObject.functionBody,
88
+ formElement.getInScopeContext(),
89
+ formElement.getOwnerForm(),
90
+ paramParts.reduce((variablesByName, paramPart, i) => {
91
+ // Because we know the XPath type here (from the function declaration) we do not have to depend on the implicit typings
92
+ variablesByName[paramPart.variableName.replace('$', '')] = typedValueFactories[i](
93
+ args[i],
94
+ domFacade,
95
+ );
96
+ return variablesByName;
97
+ }, {}),
98
+ { language },
99
+ );
100
+ registerCustomXPathFunction(
101
+ functionIdentifier,
102
+ paramParts.map(paramPart => paramPart.variableType),
103
+ returnType || 'item()*',
104
+ fun,
105
+ );
106
+ break;
107
+ }
108
+
109
+ default:
110
+ throw new Error(`Unexpected mimetype ${type} for function`);
111
+ }
112
+ }
package/src/fx-bind.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DependencyNotifyingDomFacade } from './DependencyNotifyingDomFacade.js';
2
- import { foreElementMixin } from './ForeElementMixin.js';
2
+ import ForeElementMixin from './ForeElementMixin.js';
3
3
  import { ModelItem } from './modelitem.js';
4
4
  import {
5
5
  evaluateXPathToBoolean,
@@ -22,7 +22,7 @@ import getInScopeContext from './getInScopeContext.js';
22
22
  *
23
23
  * @customElements
24
24
  */
25
- export class FxBind extends foreElementMixin(HTMLElement) {
25
+ export class FxBind extends ForeElementMixin {
26
26
  static READONLY_DEFAULT = false;
27
27
 
28
28
  static REQUIRED_DEFAULT = false;
@@ -80,10 +80,10 @@ export class FxBind extends foreElementMixin(HTMLElement) {
80
80
 
81
81
  _buildBindGraph() {
82
82
  if (this.bindType === 'xml') {
83
- this.nodeset.forEach(node => {
84
- const instance = XPathUtil.resolveInstance(this,this.ref);
83
+ this.nodeset.forEach((node) => {
84
+ const instance = XPathUtil.resolveInstance(this, this.ref);
85
85
 
86
- const path = XPathUtil.getPath(node, instance);
86
+ const path = XPathUtil.getPath(node, instance);
87
87
  this.model.mainGraph.addNode(path, node);
88
88
 
89
89
  /* ### catching references in the 'ref' itself...
@@ -93,7 +93,6 @@ export class FxBind extends foreElementMixin(HTMLElement) {
93
93
  General question: are there valid use-cases for using a 'filter' expression to narrow the nodeset
94
94
  where to apply constraints? Guess yes and if it's 'just' for reducing the amount of necessary modelItem objects.
95
95
 
96
-
97
96
  */
98
97
  // const foreignRefs = this.getReferences(this.ref);
99
98
 
@@ -159,10 +158,10 @@ export class FxBind extends foreElementMixin(HTMLElement) {
159
158
  if (!this.model.mainGraph.hasNode(nodeHash)) {
160
159
  this.model.mainGraph.addNode(nodeHash, node);
161
160
  }
162
- refs.forEach(ref => {
163
- const instance = XPathUtil.resolveInstance(this, path);
161
+ refs.forEach((ref) => {
162
+ const instance = XPathUtil.resolveInstance(this, path);
164
163
 
165
- const otherPath = XPathUtil.getPath(ref, instance);
164
+ const otherPath = XPathUtil.getPath(ref, instance);
166
165
  // console.log('otherPath', otherPath)
167
166
 
168
167
  // todo: nasty hack to prevent duplicate pathes like 'a[1]' and 'a[1]/text()[1]' to end up as separate nodes in the graph
@@ -180,7 +179,7 @@ export class FxBind extends foreElementMixin(HTMLElement) {
180
179
 
181
180
  _processChildren(model) {
182
181
  const childbinds = this.querySelectorAll(':scope > fx-bind');
183
- Array.from(childbinds).forEach(bind => {
182
+ Array.from(childbinds).forEach((bind) => {
184
183
  // console.log('init child bind ', bind);
185
184
  bind.init(model);
186
185
  });
@@ -209,14 +208,14 @@ export class FxBind extends foreElementMixin(HTMLElement) {
209
208
  if (this.ref === '' || this.ref === null) {
210
209
  this.nodeset = inscopeContext;
211
210
  } else if (Array.isArray(inscopeContext)) {
212
- inscopeContext.forEach(n => {
211
+ inscopeContext.forEach((n) => {
213
212
  if (XPathUtil.isSelfReference(this.ref)) {
214
213
  this.nodeset = inscopeContext;
215
214
  } else {
216
215
  // eslint-disable-next-line no-lonely-if
217
216
  if (this.ref) {
218
217
  const localResult = evaluateXPathToNodes(this.ref, n, this);
219
- localResult.forEach(item => {
218
+ localResult.forEach((item) => {
220
219
  this.nodeset.push(item);
221
220
  });
222
221
  /*
@@ -244,7 +243,7 @@ export class FxBind extends foreElementMixin(HTMLElement) {
244
243
  if (Array.isArray(this.nodeset)) {
245
244
  // console.log('################################################ ', this.nodeset);
246
245
  // Array.from(this.nodeset).forEach((n, index) => {
247
- Array.from(this.nodeset).forEach(n => {
246
+ Array.from(this.nodeset).forEach((n) => {
248
247
  // console.log('node ',n);
249
248
  // this._createModelItem(n, index);
250
249
  this._createModelItem(n);
@@ -306,10 +305,10 @@ export class FxBind extends foreElementMixin(HTMLElement) {
306
305
  const targetNode = node;
307
306
 
308
307
  // const path = fx.evaluateXPath('path()',node);
309
- // const path = this.getPath(node);
310
- const instance = XPathUtil.resolveInstance(this, this.ref);
308
+ // const path = this.getPath(node);
309
+ const instance = XPathUtil.resolveInstance(this, this.ref);
311
310
 
312
- const path = XPathUtil.getPath(node, instance);
311
+ const path = XPathUtil.getPath(node, instance);
313
312
  // const shortPath = this.shortenPath(path);
314
313
 
315
314
  // ### constructing default modelitem - will get evaluated during recalculate()
@@ -333,7 +332,6 @@ export class FxBind extends foreElementMixin(HTMLElement) {
333
332
  newItem.addAlert(alert);
334
333
  }
335
334
 
336
-
337
335
  this.getModel().registerModelItem(newItem);
338
336
  }
339
337
 
@@ -356,7 +354,7 @@ export class FxBind extends foreElementMixin(HTMLElement) {
356
354
  getReferences(propertyExpr) {
357
355
  const touchedNodes = new Set();
358
356
  const domFacade = new DependencyNotifyingDomFacade(otherNode => touchedNodes.add(otherNode));
359
- this.nodeset.forEach(node => {
357
+ this.nodeset.forEach((node) => {
360
358
  evaluateXPathToString(propertyExpr, node, this, domFacade);
361
359
  });
362
360
  return Array.from(touchedNodes.values());
@@ -412,15 +410,15 @@ export class FxBind extends foreElementMixin(HTMLElement) {
412
410
  this.instanceId = XPathUtil.getInstanceId(bindExpr);
413
411
  return;
414
412
  }
415
- if(!this.instanceId && this.parentNode.nodeName === 'FX-BIND'){
413
+ if (!this.instanceId && this.parentNode.nodeName === 'FX-BIND') {
416
414
  let parent = this.parentNode;
417
- while(parent && !this.instanceId){
415
+ while (parent && !this.instanceId) {
418
416
  const ref = parent.getBindingExpr();
419
417
  if (ref.startsWith('instance(')) {
420
418
  this.instanceId = XPathUtil.getInstanceId(ref);
421
419
  return;
422
420
  }
423
- if(parent.parentNode.nodeName !== 'FX-BIND'){
421
+ if (parent.parentNode.nodeName !== 'FX-BIND') {
424
422
  this.instanceId = 'default';
425
423
  break;
426
424
  }
@@ -429,9 +427,6 @@ export class FxBind extends foreElementMixin(HTMLElement) {
429
427
  }
430
428
  this.instanceId = 'default';
431
429
  }
432
-
433
-
434
-
435
430
  }
436
431
 
437
432
  if (!customElements.get('fx-bind')) {
@@ -0,0 +1,226 @@
1
+ import ForeElementMixin from './ForeElementMixin.js';
2
+ import { Fore } from './fore.js';
3
+ import { FxModel } from './fx-model.js';
4
+
5
+ class FxConnection extends ForeElementMixin {
6
+ constructor() {
7
+ super();
8
+ this.attachShadow({ mode: 'open' });
9
+ this._url = '';
10
+ this._socket = null;
11
+ this._heartbeatInterval = FxConnection._defaultHeartbeatInterval;
12
+ this._heartbeatTimer = null;
13
+ this._messageFormat = FxConnection._defaultMessageFormat;
14
+ this._onMessage = this._onMessage.bind(this);
15
+ }
16
+
17
+ _render() {
18
+ const style = `
19
+ :host {
20
+ display: none;
21
+ }
22
+ `;
23
+
24
+ const html = `
25
+ <slot></slot>
26
+ ${
27
+ this._messageFormat === 'xml'
28
+ ? '<fx-replace id="replace" ref="."></fx-replace>'
29
+ : '<fx-setvalue id="setvalue" ref="."></fx-setvalue>'
30
+ }
31
+ `;
32
+ this.shadowRoot.innerHTML = `
33
+ <style>
34
+ ${style}
35
+ </style>
36
+ ${html}
37
+ `;
38
+ }
39
+
40
+ static get observedAttributes() {
41
+ return ['url', 'heartbeat', 'message-format'];
42
+ }
43
+
44
+ connectedCallback() {
45
+ if (this.hasAttribute('url')) {
46
+ this._url = this.getAttribute('url');
47
+ }
48
+ if (this.hasAttribute('heartbeat')) {
49
+ this._heartbeatInterval = parseInt(this.getAttribute('heartbeat'));
50
+ this._setupHeartbeat();
51
+ }
52
+ if (this.hasAttribute('message-format')) {
53
+ this._messageFormat = this.getAttribute('message-format');
54
+ }
55
+
56
+ this._render();
57
+
58
+ this.getOwnerForm().addEventListener('model-construct-done', e => {
59
+ console.log('Fore model ready');
60
+ this._connect();
61
+
62
+ this.evalInContext();
63
+ });
64
+ }
65
+
66
+ disconnectedCallback() {
67
+ this._disconnect();
68
+ }
69
+
70
+ attributeChangedCallback(name, oldValue, newValue) {
71
+ switch (name) {
72
+ case 'url':
73
+ this._url = newValue;
74
+ this._connect();
75
+ break;
76
+ case 'heartbeat':
77
+ this._heartbeatInterval = parseInt(newValue);
78
+ this._setupHeartbeat();
79
+ break;
80
+ case 'messageformat':
81
+ this._messageFormat = newValue;
82
+ break;
83
+ default:
84
+ // Do nothing for other attributes
85
+ break;
86
+ }
87
+ }
88
+
89
+ send(data) {
90
+ this.evalInContext();
91
+ data = this.nodeset;
92
+ if (this._socket && this._socket.readyState === WebSocket.OPEN) {
93
+ let message;
94
+ switch (this._messageFormat) {
95
+ case 'json':
96
+ message = JSON.stringify(data);
97
+ break;
98
+ case 'xml':
99
+ message = new XMLSerializer().serializeToString(data);
100
+ break;
101
+ case 'text':
102
+ message = data.textContent;
103
+ break;
104
+ default:
105
+ throw new Error(`Unsupported message format: ${this._messageFormat}`);
106
+ }
107
+ this._sendMessage(message);
108
+ } else {
109
+ throw new Error('WebSocket is not connected');
110
+ }
111
+ }
112
+
113
+ _connect() {
114
+ this._disconnect();
115
+ if (this._url) {
116
+ this._socket = new WebSocket(this._url);
117
+ this._socket.addEventListener('open', this._onOpen.bind(this));
118
+ this._socket.addEventListener('message', this._onMessage);
119
+ this._socket.addEventListener('close', this._onClose.bind(this));
120
+ }
121
+ }
122
+
123
+ _disconnect() {
124
+ if (this._socket) {
125
+ this._socket.removeEventListener('open', this._onOpen.bind(this));
126
+ this._socket.removeEventListener('message', event => this._onMessage(event));
127
+ this._socket.removeEventListener('close', this._onClose.bind(this));
128
+ this._socket.close();
129
+ this._socket = null;
130
+ }
131
+ if (this._heartbeatTimer) {
132
+ clearInterval(this._heartbeatTimer);
133
+ this._heartbeatTimer = null;
134
+ }
135
+ }
136
+
137
+ _onOpen(event) {
138
+ this.dispatchEvent(new CustomEvent('open', { detail: event }));
139
+ }
140
+
141
+ _onMessage(event) {
142
+ let message;
143
+ switch (this._messageFormat) {
144
+ case 'json':
145
+ message = JSON.parse(event.data);
146
+ break;
147
+ case 'xml':
148
+ const parser = new DOMParser();
149
+ message = parser.parseFromString(event.data, 'application/xml');
150
+ this.getModelItem().value = message;
151
+ FxModel.dataChanged = true;
152
+ this.getModel().changed.push(this.modelItem);
153
+ this.getModel().updateModel();
154
+ this.getOwnerForm().refresh(true);
155
+ break;
156
+ case 'text':
157
+ message = event.data;
158
+ break;
159
+ default:
160
+ throw new Error(`Unsupported message format: ${this._messageFormat}`);
161
+ }
162
+ console.log('dispatching channel-message', message);
163
+
164
+ Fore.dispatch(this, 'channel-message', { message });
165
+ }
166
+
167
+ _onClose(event) {
168
+ this.dispatchEvent(new CustomEvent('close', { detail: event }));
169
+ }
170
+
171
+ _setupHeartbeat() {
172
+ if (this._heartbeatInterval > 0) {
173
+ if (this._heartbeatTimer) {
174
+ clearInterval(this._heartbeatTimer);
175
+ }
176
+ this._heartbeatTimer = setInterval(() => {
177
+ this._sendMessage('');
178
+ }, this._heartbeatInterval);
179
+ } else if (this._heartbeatTimer) {
180
+ clearInterval(this._heartbeatTimer);
181
+ this._heartbeatTimer = null;
182
+ }
183
+ }
184
+
185
+ _sendMessage(message) {
186
+ if (this._socket && this._socket.readyState === WebSocket.OPEN) {
187
+ this._socket.send(message);
188
+ }
189
+ }
190
+
191
+ static get _defaultHeartbeatInterval() {
192
+ return 0;
193
+ }
194
+
195
+ static get _defaultMessageFormat() {
196
+ return 'json';
197
+ }
198
+
199
+ get url() {
200
+ return this._url;
201
+ }
202
+
203
+ set url(value) {
204
+ this.setAttribute('url', value);
205
+ }
206
+
207
+ get heartbeat() {
208
+ return this._heartbeatInterval;
209
+ }
210
+
211
+ set heartbeat(value) {
212
+ this.setAttribute('heartbeat', value);
213
+ }
214
+
215
+ get messageformat() {
216
+ return this._messageFormat;
217
+ }
218
+
219
+ set messageformat(value) {
220
+ this.setAttribute('messageformat', value);
221
+ }
222
+ }
223
+
224
+ if (!customElements.get('fx-connection')) {
225
+ customElements.define('fx-connection', FxConnection);
226
+ }