glib-web 2.6.3 → 2.6.5

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.
package/action.js CHANGED
@@ -52,7 +52,6 @@ import ActionsAnalyticsLogEvent from "./actions/analytics/logEvent";
52
52
 
53
53
  import ActionCommandsCopy from "./actions/commands/copy";
54
54
  import ActionCommandsCustom from "./actions/commands/custom";
55
- import ActionCommandsEnqueue from "./actions/commands/enqueue";
56
55
 
57
56
  import ActionToursStart from "./actions/tours/start";
58
57
  import ActionToursStop from "./actions/tours/stop";
@@ -112,7 +111,6 @@ const actions = {
112
111
 
113
112
  "commands/copy": ActionCommandsCopy,
114
113
  "commands/custom": ActionCommandsCustom,
115
- "commands/enqueue": ActionCommandsEnqueue,
116
114
 
117
115
  "tours/start": ActionToursStart,
118
116
  "tours/stop": ActionToursStop,
@@ -126,6 +124,10 @@ export default class Action {
126
124
  if (!TypeUtils.isObject(spec)) {
127
125
  return;
128
126
  }
127
+ if (!TypeUtils.isObject(component)) {
128
+ console.warn("Action requires a component");
129
+ return;
130
+ }
129
131
 
130
132
  const name = spec.action;
131
133
  if (TypeUtils.isString(name)) {
@@ -134,28 +136,15 @@ export default class Action {
134
136
  } else {
135
137
  this.executeGlobal(name, spec, component, params);
136
138
  }
137
-
138
- // const actionName = name.replace(/-v1$/, "");
139
-
140
- // try {
141
- // const action = new actions[actionName]();
142
- // const logDisabled = action.logDisabled && action.logDisabled();
143
- // if (!logDisabled) {
144
- // console.log(`Executing "${actionName}"`);
145
- // }
146
- // action.execute(spec, null, component, params);
147
- // } catch (e) {
148
- // console.log(`Failed executing command: "${e.message}"`);
149
- // }
150
139
  } else {
151
- console.log(`Invalid action: "${name}"`);
140
+ console.warn(`Invalid action: "${name}"`);
152
141
  }
153
142
  }
154
143
 
155
144
  static executeLocal(name, spec, component) {
156
145
  const methodName = name.replace(/^component\//, "");
157
146
  const componentName = GLib.component.vueName(component);
158
- console.log(
147
+ console.debug(
159
148
  `Executing component action on ${componentName}: ${methodName}`
160
149
  );
161
150
  component[`action_${methodName}`](spec);
@@ -172,7 +161,7 @@ export default class Action {
172
161
  const action = new registry[actionName]();
173
162
  const logDisabled = action.logDisabled && action.logDisabled();
174
163
  if (!logDisabled) {
175
- console.log(`Executing "${actionName}"`);
164
+ console.debug(`Executing "${actionName}"`);
176
165
  }
177
166
  action.execute(spec, component, params);
178
167
  } catch (e) {
@@ -14,10 +14,10 @@ export default class {
14
14
  const suffix = params.referer_group ? "w/ referer" : "w/o referer";
15
15
  params.build_env = Utils.settings.env;
16
16
  // params.build_version = Utils.settings.appVersion;
17
- console.log(`Logging ${suffix}:`, properties.name, params);
17
+ console.debug(`Logging ${suffix}:`, properties.name, params);
18
18
  firebase.analytics().logEvent(properties.name, params);
19
19
  } else {
20
- console.log("Waiting for firebase to be initialized...");
20
+ console.debug("Waiting for firebase to be initialized...");
21
21
  setTimeout(() => {
22
22
  this.execute(properties);
23
23
  }, 100);
@@ -1,11 +1,5 @@
1
- // import Storage from '../../utils/storage'
2
- // import Keys from '../../keys'
3
-
4
1
  export default class {
5
2
  execute(properties, component) {
6
- // console.log(properties['token'])
7
- // Storage.setLocal(Keys.Db.csrfToken, properties['token'])
8
-
9
3
  Utils.dom.setCsrf(properties["token"]);
10
4
  Action.execute(properties["onSave"], component);
11
5
  }
@@ -4,7 +4,9 @@ export default class {
4
4
  const ws = window.vueApp.actionCable;
5
5
  const channel = ws.channels[channelName];
6
6
 
7
- console.log("Pushing to", channel);
7
+ // TODO: Use logDisabled() that reads params from server to decide whether we want to print the log or not.
8
+ // This is because `push` action's logs are a bit sensitive.
9
+ console.debug("Pushing to", channel);
8
10
 
9
11
  Utils.type.ifString(properties.event, eventName => {
10
12
  if (channel) {
@@ -1,18 +1,15 @@
1
1
  export default class {
2
2
  execute(properties, controller) {
3
3
  const currentUrl = window.location.href;
4
- // const htmlUrl = Utils.url.htmlUrl(properties["url"]);
5
4
 
6
5
  GLib.http.execute(properties, "GET", controller, (page, response) => {
7
6
  Utils.type.ifString(properties.historyUrl, historyUrl => {
8
7
  const cleanUrl = Utils.url.htmlUrl(historyUrl);
9
8
  if (cleanUrl !== currentUrl) {
10
- console.log("URL", cleanUrl, response.url, response);
11
9
  const data = Object.assign({}, window.vueApp.page, {
12
10
  replayGetResponse: page.onResponse
13
11
  });
14
12
 
15
- // Utils.history.pushPage(data, historyUrl);
16
13
  Utils.history.pushPage(data, cleanUrl);
17
14
  }
18
15
  });
@@ -5,7 +5,7 @@ export default class {
5
5
  Utils.launch.dialog.closestBody(component) || Utils.history._pageBody;
6
6
 
7
7
  const selector = `#${properties.viewId}`;
8
- console.log("Scrolling to", selector);
8
+ console.debug("Scrolling to", selector);
9
9
  const element = pageBody.querySelector(selector);
10
10
  element.scrollIntoView({
11
11
  behavior: properties["animate"] ? "smooth" : "auto"
@@ -4,7 +4,7 @@ export default class {
4
4
  // TODO: make this support any panelId
5
5
  const target = properties.panelId ? `_innerBody` : "_pageBody";
6
6
  const pageBody = Utils.history[target];
7
- console.log(`Scrolling ${target} to bottom`);
7
+ console.debug(`Scrolling ${target} to bottom`);
8
8
  pageBody.scrollTo({
9
9
  top: pageBody.scrollHeight,
10
10
  behavior: properties["animate"] ? "smooth" : "auto"
@@ -6,7 +6,7 @@ export default class {
6
6
  const data = Object.assign({}, properties);
7
7
  if (!Utils.history.back()) {
8
8
  data.url = fallbackUrl;
9
- console.log("Use fallbackUrl", data.url);
9
+ console.debug("Use fallbackUrl", data.url);
10
10
  }
11
11
 
12
12
  // Allow time for history.back() to complete, which is important for actions that need
@@ -13,14 +13,14 @@ export default class {
13
13
  channel
14
14
  .push(eventName, payload)
15
15
  .receive("ok", resp => {
16
- console.log(
16
+ console.debug(
17
17
  `Push to '${topicName}/${eventName}' succeeded`,
18
18
  resp
19
19
  );
20
20
  Utils.ws.handleResponse(resp.onResponse, component);
21
21
  })
22
22
  .receive("error", resp => {
23
- console.log(`Push to '${topicName}/${eventName}' failed`, resp);
23
+ console.debug(`Push to '${topicName}/${eventName}' failed`, resp);
24
24
  Utils.ws.handleResponse(resp.onResponse, component);
25
25
  });
26
26
  } else {
package/app.vue CHANGED
@@ -91,7 +91,7 @@ export default {
91
91
  }
92
92
  },
93
93
  created() {
94
- console.log(
94
+ console.debug(
95
95
  `Version: ${Utils.settings.appVersion} (${Utils.settings.env})`
96
96
  );
97
97
  Utils.history.saveInitialContent(this.page);
@@ -145,7 +145,6 @@ export default {
145
145
  });
146
146
  },
147
147
  updateMainHeight() {
148
- console.debug("Setting body height");
149
148
  this.mainHeight = window.innerHeight - this.$refs.appBar.$el.offsetHeight;
150
149
  }
151
150
  }
@@ -196,7 +195,7 @@ body,
196
195
  background: yellow !important;
197
196
  }
198
197
  // Don't override the default button's effect.
199
- .glib-clickable:not(.v-btn) {
198
+ .glib-clickable:not(.v-btn):not(.disabled) {
200
199
  cursor: pointer;
201
200
 
202
201
  // // So that we can display a semi-transparent layer on hover (see below)
@@ -34,10 +34,8 @@ export default {
34
34
  }
35
35
  },
36
36
  $tearDown() {
37
- console.log("TimeTicker destroyed");
38
37
  if (this.timer != null) {
39
38
  clearInterval(this.timer);
40
- console.log(`Timer stopped: ${this.timer}`);
41
39
  }
42
40
  },
43
41
  tick() {
@@ -147,7 +147,7 @@ export default {
147
147
  upload.start((error, blob) => {
148
148
  if (error) {
149
149
  // Handle the error
150
- console.log("Failed uploading image!");
150
+ console.warn("Failed uploading image!");
151
151
  } else {
152
152
  this.insertImage(file, blob, onComplete);
153
153
  }
@@ -108,10 +108,9 @@ export default {
108
108
  }
109
109
  },
110
110
  $tearDown() {
111
- console.log("Cleaning up timer...");
112
111
  if (this.timer != null) {
113
112
  clearInterval(this.timer);
114
- console.log(`Timer stopped: ${this.timer}`);
113
+ console.debug(`Timer stopped: ${this.timer}`);
115
114
  this.timer == null;
116
115
  }
117
116
  },
@@ -57,23 +57,15 @@ export default {
57
57
  let width, height;
58
58
  const aspectRatio = image.naturalWidth / image.naturalHeight;
59
59
 
60
- console.log("FIT1");
61
-
62
60
  if (this.spec.width == "matchParent") {
63
- console.log("FIT2");
64
-
65
61
  width = "100%";
66
62
  // This breaks the image height when displayed in a badge.
67
63
  // height = `${100 / aspectRatio}%`;
68
64
  } else {
69
65
  if (this.spec.width) {
70
- console.log("FIT3");
71
-
72
66
  width = `${this.spec.width}px`;
73
67
  height = `${this.spec.width / aspectRatio}px`;
74
68
  } else if (this.spec.height) {
75
- console.log("FIT4", this.spec.height);
76
-
77
69
  width = `${this.spec.height * aspectRatio}px`;
78
70
  height = `${this.spec.height}px`;
79
71
  }
@@ -31,10 +31,14 @@ export default {
31
31
 
32
32
  this.text = this.spec.text;
33
33
  },
34
- action_set(spec) {
35
- if (spec.user_id !== spec.filterKey) {
36
- this.text = spec.text;
37
- }
34
+ // action_set(spec) {
35
+ // if (spec.user_id !== spec.filterKey) {
36
+ // this.text = spec.text;
37
+ // }
38
+ // },
39
+ action_merge(mergedSpec) {
40
+ Object.assign(this.spec, mergedSpec);
41
+ this.$ready();
38
42
  }
39
43
  }
40
44
  };
@@ -30,7 +30,7 @@ export default {
30
30
  $ready() {
31
31
  if (this.spec.previewVideo) {
32
32
  this.youtubeId = this.extractYoutubeId(this.spec.text);
33
- console.log("Detected Youtube ID", this.youtubeId);
33
+ console.debug("Detected Youtube ID", this.youtubeId);
34
34
  }
35
35
  // Set all links to be openWeb or open in a new tab by default
36
36
  if (!this.spec.openWeb) {
@@ -54,7 +54,7 @@ export default {
54
54
  const channel = ws.channels[topicName];
55
55
 
56
56
  if (!channel) {
57
- console.log(`Topic not ready: '${topicName}'`);
57
+ console.debug(`Topic not ready: '${topicName}'`);
58
58
  setTimeout(() => {
59
59
  // Wait until $wsInitPhoenixSocket() has executed.
60
60
  this.$wsSubscribeEvents(spec);
@@ -63,7 +63,7 @@ export default {
63
63
  }
64
64
 
65
65
  for (const eventName of events) {
66
- console.log(`Registering event '${eventName}'`);
66
+ console.debug(`Registering event '${eventName}'`);
67
67
  channel.on(eventName, payload => {
68
68
  console.debug(`Received '${eventName}' event`, payload);
69
69
  Utils.ws.handleResponse(payload.onResponse, this);
@@ -65,12 +65,12 @@ export default {
65
65
  const topDialog = Utils.launch.dialog.topDialog();
66
66
  if (topDialog) {
67
67
  if (!topDialog.isFormDirty) {
68
- console.log("Dialog form is now dirty");
68
+ console.debug("Dialog form is now dirty");
69
69
  topDialog.isFormDirty = true;
70
70
  }
71
71
  } else {
72
72
  if (!window.vueApp.isFormDirty) {
73
- console.log("Window form is now dirty");
73
+ console.debug("Window form is now dirty");
74
74
  window.vueApp.isFormDirty = true;
75
75
  }
76
76
  }
@@ -19,13 +19,13 @@ export default {
19
19
  channel: channelName
20
20
  });
21
21
 
22
- console.log("Connecting to channel", subscription);
22
+ console.debug("Connecting to channel", subscription);
23
23
 
24
24
  consumer.subscriptions.create(subscription, {
25
25
  connected() {
26
26
  const ws = window.vueApp.actionCable;
27
27
  ws.channels[channelName] = this;
28
- console.log("Connected to channel", channelName);
28
+ console.debug("Connected to channel", channelName);
29
29
  },
30
30
 
31
31
  disconnected() {},
@@ -12,7 +12,6 @@ export default {
12
12
  Utils.type.ifObject(
13
13
  spec,
14
14
  ws => {
15
- console.log("Connecting socket...");
16
15
  Utils.launch.snackbar.indicator("Connecting...");
17
16
  Utils.http.startIndicator(this);
18
17
 
@@ -54,14 +53,14 @@ export default {
54
53
 
55
54
  this._wsSocket = socket;
56
55
  socket.onOpen(() => {
57
- console.log("Connected socket");
56
+ console.debug("Connected socket");
58
57
  this._wsJoinChannel(socket, spec);
59
58
  Utils.http.stopIndicator(this);
60
59
  });
61
60
  socket.onError(() => {
62
61
  // Start the indicator again because this can trigger even after successfully connected.
63
62
  Utils.http.startIndicator(this);
64
- console.log("Unable to connect socket");
63
+ console.debug("Unable to connect socket");
65
64
  });
66
65
  socket.connect();
67
66
  },
@@ -80,7 +79,7 @@ export default {
80
79
  const channel = socket.channel(topicName, {});
81
80
 
82
81
  for (const eventName of events) {
83
- console.log(`Registering event '${eventName}'`);
82
+ console.debug(`Registering event '${eventName}'`);
84
83
  channel.on(eventName, payload => {
85
84
  console.debug(`Received '${eventName}' event`, payload);
86
85
  Utils.ws.handleResponse(payload.onResponse, this);
@@ -90,11 +89,11 @@ export default {
90
89
  channel
91
90
  .join()
92
91
  .receive("ok", resp => {
93
- console.log(`Channel joined '${topicName}'`, resp);
92
+ console.debug(`Channel joined '${topicName}'`, resp);
94
93
  ws.channels[topicName] = channel;
95
94
  })
96
95
  .receive("error", resp => {
97
- console.log(`Unable to join channel '${topicName}'`, resp);
96
+ console.debug(`Unable to join channel '${topicName}'`, resp);
98
97
  Utils.launch.snackbar.error(
99
98
  "Unable to connect. Please try again.",
100
99
  this
@@ -103,7 +102,7 @@ export default {
103
102
  },
104
103
  _wsDisconnectSocket() {
105
104
  if (this._wsSocket) {
106
- console.log("Disconnecting socket");
105
+ console.debug("Disconnecting socket");
107
106
  // Sometimes this doesn't prevent errorneous socket from trying to reconnect. In this case,
108
107
  // the socket will keep attempting to connect.
109
108
  this._wsSocket.disconnect();
@@ -58,6 +58,7 @@ export default {
58
58
  $ready() {
59
59
  const onClick = this.spec.onClick ? { onClick: this.spec.onClick } : {};
60
60
  this.customData = Object.assign(onClick, this.spec.data);
61
+ // Fix an issue where the panel doesn't get updated
61
62
  if (this.$refs.ccomp) {
62
63
  this.$refs.ccomp.$forceUpdate();
63
64
  }
@@ -29,7 +29,6 @@
29
29
  >
30
30
  <!-- Using `item.id` as key is important to make sure the item gets updated
31
31
  when dragging ends. -->
32
- <!-- TODO: This div is causing image issue -->
33
32
  <div
34
33
  v-for="(item, index) in childViews"
35
34
  :key="item.id || index"
@@ -132,7 +131,7 @@ export default {
132
131
 
133
132
  const targetRowIndex = event.newIndex;
134
133
  if (event.from == event.to && event.oldIndex == targetRowIndex) {
135
- console.log("Reordering canceled");
134
+ console.debug("Reordering canceled");
136
135
  return;
137
136
  }
138
137
 
@@ -1,11 +1,5 @@
1
1
  <template>
2
- <v-list
3
- :two-line="twoLine"
4
- :three-line="threeLine"
5
- class="py-0"
6
- :class="$classes()"
7
- :style="$styles()"
8
- >
2
+ <v-list class="py-0" :class="$classes()" :style="$styles()">
9
3
  <div ref="topAnchor">
10
4
  <div v-if="prevPageUrl" class="py-3 px-4">
11
5
  Loading...
@@ -28,9 +22,10 @@
28
22
  >
29
23
  <div
30
24
  v-for="(row, rowIndex) in section.rows"
31
- :key="`${sectionIndex}_${rowIndex}`"
25
+ :key="row.id || `${sectionIndex}_${rowIndex}`"
32
26
  :ref="`row_${sectionIndex}_${rowIndex}`"
33
27
  :data-dragRowId="row.id"
28
+ class="row_container"
34
29
  >
35
30
  <!-- <v-divider v-if="!spec.responsiveCols && rowIndex == 0" /> -->
36
31
 
@@ -88,30 +83,6 @@ export default {
88
83
  dragSupport: null
89
84
  };
90
85
  },
91
- computed: {
92
- twoLine() {
93
- for (const section of this.sections) {
94
- const rows = section.rows || [];
95
- for (const row of rows) {
96
- if (row.subtitle) {
97
- return true;
98
- }
99
- }
100
- }
101
- return false;
102
- },
103
- threeLine() {
104
- for (const section of this.sections) {
105
- const rows = section.rows || [];
106
- for (const row of rows) {
107
- if (row.subsubtitle) {
108
- return true;
109
- }
110
- }
111
- }
112
- return false;
113
- }
114
- },
115
86
  methods: {
116
87
  $ready() {
117
88
  this.sections = this.spec.sections || [];
@@ -139,7 +110,6 @@ export default {
139
110
  return row;
140
111
  },
141
112
  action_loadNext(spec) {
142
- console.log("TODO: load new comments and append to this list", spec);
143
113
  // TODO
144
114
  // - Load next rows/items from the Rails server
145
115
  // - You can pass some parameters (e.g. currentItemId) for pagination
@@ -227,7 +197,7 @@ export default {
227
197
  const targetSectionedRowIndex = event.newIndex;
228
198
  // const sourceSectionedRowIndex = event.oldIndex;
229
199
  if (event.from == event.to && event.oldIndex == targetSectionedRowIndex) {
230
- console.log("Reordering canceled");
200
+ console.debug("Reordering canceled");
231
201
  return;
232
202
  }
233
203
 
@@ -235,7 +205,6 @@ export default {
235
205
  this.$recursiveUpdate();
236
206
 
237
207
  const targetSectionIndex = this.getSectionIndex(event.to);
238
- // const targetSection = this.getSection(targetSectionIndex);
239
208
 
240
209
  const targetAbsoluteIndex =
241
210
  this.previousSectionsRowCount(event.to) + targetSectionedRowIndex;
@@ -260,6 +229,27 @@ export default {
260
229
  });
261
230
 
262
231
  GLib.action.execute(mergedSpec, this);
232
+
233
+ this.hackLingeringHighlight();
234
+ },
235
+ hackLingeringHighlight() {
236
+ // After a row finished dragging, the row below it will automatically be
237
+ // "active&hover" for some reason, which will cause it to be incorrectly
238
+ // highlighted
239
+ // This seems to be a browser/library bug, so as a workaround, we temporarily
240
+ // apply the "disabled" class so the highlighting doesn't happen.
241
+ this.$el
242
+ .querySelectorAll(":active .row_container > .glib-clickable:hover")
243
+ .forEach(activeRow => {
244
+ activeRow.classList.add("disabled");
245
+ document.addEventListener(
246
+ "mousemove",
247
+ function() {
248
+ activeRow.classList.remove("disabled");
249
+ },
250
+ { once: true }
251
+ );
252
+ });
263
253
  },
264
254
  getSectionIndex(element) {
265
255
  return element.dataset.dragsectionindex;
@@ -1,4 +1,5 @@
1
1
  <template>
2
+ <!-- Use `display: contents` so this div doesn't intefere with the child's sizing behaviour. -->
2
3
  <div style="display: contents;">
3
4
  <!-- <common-responsive v-if="!hoverViewsSpec" :spec="spec" /> -->
4
5
  <v-menu
@@ -23,7 +23,6 @@ export default {
23
23
  // // Let the styles be handled by the child panel.
24
24
  // const outerSpec = {};
25
25
  // outerSpec["padding"] = this.spec["outerPadding"];
26
- // console.log("outerSpec", outerSpec);
27
26
  // const styles = this.$styles(outerSpec);
28
27
  //
29
28
  // const styles = this.genericStyles();
@@ -36,8 +35,6 @@ export default {
36
35
  // width: this.spec.width,
37
36
  // height: this.spec.height
38
37
  // });
39
- //
40
- // console.log("styles", styles);
41
38
 
42
39
  // Let the specified styles be handled by the child panel.
43
40
  const styles = this.$styles({
@@ -139,9 +139,6 @@ export default {
139
139
  // const createdAt = row[5]
140
140
  // const activationState = row[14] ? 'active' : 'pending'
141
141
  // count += 1
142
- // if (email.includes("'")) {
143
- // console.log("INVALID NAME", name)
144
- // }
145
142
  // str += `{ name: "${name}".to_s, email: '${email}'.to_s, created_at: '${createdAt}', activation_state: '${activationState}'},\n`
146
143
  // }
147
144
  // }
package/nav/dialog.vue CHANGED
@@ -134,7 +134,7 @@ export default {
134
134
  });
135
135
  },
136
136
  $tearDown() {
137
- console.log("Dialog destroyed");
137
+ console.debug("Dialog destroyed");
138
138
  this.stack.remove(this);
139
139
  },
140
140
  close() {
@@ -192,7 +192,6 @@ export default {
192
192
  this.model = true;
193
193
  },
194
194
  updateMainHeight() {
195
- console.debug("Setting body height");
196
195
  this.mainHeight = window.innerHeight - 140;
197
196
  }
198
197
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glib-web",
3
- "version": "2.6.3",
3
+ "version": "2.6.5",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/utils/history.js CHANGED
@@ -77,7 +77,7 @@ export default class {
77
77
  window.history.back();
78
78
  return true;
79
79
  } else {
80
- console.log("Reached beginning of navigation stack");
80
+ console.debug("Reached beginning of navigation stack");
81
81
  return false;
82
82
  }
83
83
  }
package/utils/http.js CHANGED
@@ -31,7 +31,8 @@ export default class {
31
31
  const formData = new FormData();
32
32
  var params = data || {};
33
33
  for (const key in params) {
34
- formData.append(key, params[key]);
34
+ // formData.append(key, params[key]);
35
+ this._populateFormData(formData, key, params[key]);
35
36
  }
36
37
 
37
38
  if (needCsrfToken && !Type.isString(params["authenticity_token"])) {
@@ -40,8 +41,33 @@ export default class {
40
41
  return formData;
41
42
  }
42
43
 
44
+ static _populateFormData(formData, key, value) {
45
+ if (GLib.type.isArray(value)) {
46
+ for (const item of value) {
47
+ this._populateFormData(formData, `${key}[]`, item);
48
+ }
49
+ } else if (GLib.type.isObject(value)) {
50
+ for (const innerKey in value) {
51
+ this._populateFormData(
52
+ formData,
53
+ `${key}[${innerKey}]`,
54
+ value[innerKey]
55
+ );
56
+ }
57
+ } else {
58
+ formData.append(key, value);
59
+ }
60
+ }
61
+
43
62
  static load(properties, component) {
44
- const url = new URL(properties["url"]);
63
+ const urlString = properties["url"];
64
+ let url;
65
+ try {
66
+ url = new URL(urlString);
67
+ } catch (e) {
68
+ console.warn("Invalid URL", urlString);
69
+ throw e;
70
+ }
45
71
  const domainMatched = window.location.hostname == url.hostname;
46
72
 
47
73
  // If this is an external domain, we rely on `onUnload()` instead.
@@ -134,18 +160,23 @@ export default class {
134
160
  url = Utils.url.appendParams(url, body);
135
161
  body = null;
136
162
  }
137
- console.log(`${method} ${url}`);
163
+ console.debug(`${method} ${url}`);
138
164
 
139
165
  const request = new HttpRequest();
140
166
  let response = null;
141
167
  const vm = this;
142
168
 
143
169
  const analyticsHeaders = this.analyticsHeaders(component);
170
+ const headers = Object.assign(
171
+ { Accept: "text/html" },
172
+ analyticsHeaders,
173
+ GLib.settings.headerAugmenter(url, method)
174
+ );
144
175
 
145
176
  fetch(url, {
146
177
  method: method,
147
178
  body: body,
148
- headers: Object.assign({ Accept: "text/html" }, analyticsHeaders),
179
+ headers: headers,
149
180
  // Make sure to pass cookies for same origin URLs.
150
181
  // Needed for some browsers, e.g. Edge and Android's native.
151
182
  credentials: "same-origin",
@@ -7,13 +7,13 @@ export default class {
7
7
  const ws = window.vueApp.webSocket;
8
8
  Utils.type.ifObject(payload.wsHeader, header => {
9
9
  Object.assign(ws.header, header);
10
- console.log("New header: ", ws.header);
10
+ console.debug("New header: ", ws.header);
11
11
  });
12
12
 
13
13
  const actionName = payload.action;
14
14
  if (actionName.startsWith("component/")) {
15
15
  const methodName = actionName.replace(/^component\//, "");
16
- console.log(`Executing component action: ${methodName}`);
16
+ console.debug(`Executing component action: ${methodName}`);
17
17
  component[`action_${methodName}`](payload);
18
18
  } else {
19
19
  GLib.action.execute(payload, component);
package/utils/public.js CHANGED
@@ -5,11 +5,8 @@ import Type from "./type";
5
5
  import Form from "./form";
6
6
  import Component from "./component";
7
7
  import Hash from "./hash";
8
- import Queue from "./queue";
9
8
  import Settings from "./settings";
10
9
 
11
- const _queue = new Queue();
12
-
13
10
  export default class {
14
11
  static get action() {
15
12
  return Action;
@@ -35,7 +32,4 @@ export default class {
35
32
  static get settings() {
36
33
  return Settings;
37
34
  }
38
- static get queue() {
39
- return _queue;
40
- }
41
35
  }
package/utils/settings.js CHANGED
@@ -6,6 +6,10 @@ class MutableSettings {
6
6
  this.errorHandler = err => {
7
7
  console.error(err.message);
8
8
  };
9
+ this.headerAugmenter = (_url, _method) => {
10
+ // Set a custom augmenter to add custom HTTP headers.
11
+ return {};
12
+ };
9
13
  }
10
14
  }
11
15
  const settings = new MutableSettings();
@@ -24,6 +28,10 @@ export default class ImmutableSettings {
24
28
  return settings.errorHandler;
25
29
  }
26
30
 
31
+ static get headerAugmenter() {
32
+ return settings.headerAugmenter;
33
+ }
34
+
27
35
  static get env() {
28
36
  return process.env.NODE_ENV;
29
37
  }
@@ -1,17 +0,0 @@
1
- // import Hash from "../../utils/hash";
2
-
3
- export default class {
4
- // async executeJob(spec, component) {
5
- // console.log("Waiting for next job1...");
6
- // GLib.action.execute(spec.onExecute, component);
7
- // await this.sleep(2000);
8
- // console.log("Waiting for next job2...");
9
- // }
10
- execute(spec, component) {
11
- GLib.queue.enqueue(() => {
12
- GLib.action.execute(spec.onExecute, component);
13
- // console.log("Waiting for next job0...");
14
- // this.executeJob(spec, component);
15
- });
16
- }
17
- }
package/utils/queue.js DELETED
@@ -1,104 +0,0 @@
1
- // From https://stackoverflow.com/questions/47157428/how-to-implement-a-pseudo-blocking-async-queue-in-js-ts
2
-
3
- import Vue from "vue";
4
-
5
- // const jobQueue = [];
6
- export default class {
7
- constructor() {
8
- // invariant: at least one of the arrays is empty
9
- this.resolvers = [];
10
- this.promises = [];
11
- }
12
- _add() {
13
- this.promises.push(
14
- new Promise(resolve => {
15
- this.resolvers.push(resolve);
16
- })
17
- );
18
- }
19
- enqueue(t) {
20
- // if (this.resolvers.length) this.resolvers.shift()(t);
21
- // else this.promises.push(Promise.resolve(t));
22
- if (!this.resolvers.length) this._add();
23
- this.resolvers.shift()(t);
24
- }
25
- dequeue() {
26
- if (!this.promises.length) this._add();
27
- return this.promises.shift();
28
- }
29
- // now some utilities:
30
- isEmpty() {
31
- // there are no values available
32
- return !this.promises.length; // this.length <= 0
33
- }
34
- isBlocked() {
35
- // it's waiting for values
36
- return !!this.resolvers.length; // this.length < 0
37
- }
38
- get length() {
39
- return this.promises.length - this.resolvers.length;
40
- }
41
- sleep(ms) {
42
- return new Promise(resolve => setTimeout(resolve, ms));
43
- }
44
- async start() {
45
- // TODO: This is experimental
46
- const delayBetweenJobs = 2000;
47
-
48
- for await (const job of this) {
49
- console.log("Executing job...");
50
- job();
51
- if (delayBetweenJobs) {
52
- console.log("Waiting for next job1...");
53
- await this.sleep(delayBetweenJobs);
54
- console.log("Waiting for next job2...");
55
- }
56
- }
57
- }
58
- [Symbol.asyncIterator]() {
59
- // Todo: Use AsyncIterator.from()
60
- return {
61
- next: () => this.dequeue().then(value => ({ done: false, value })),
62
- [Symbol.asyncIterator]() {
63
- return this;
64
- }
65
- };
66
- }
67
- }
68
-
69
- // export default class {
70
- // constructor() {
71
- // this.jobQueue = [];
72
- // this.timerHandle = null;
73
- // }
74
- // // static get csrfElement() {
75
- // // return document.querySelector(`meta[name="csrf-token"]`);
76
- // // }
77
-
78
- // // static getCsrf() {
79
- // // return this.csrfElement.getAttribute("content");
80
- // // }
81
-
82
- // // static setCsrf(value) {
83
- // // return this.csrfElement.setAttribute("content", value);
84
- // // }
85
-
86
- // start(interval) {
87
- // this.timerHandle = setInterval(() => {
88
- // console.log("DEQUQE1");
89
- // const oldestJob = this.jobQueue.shift();
90
- // if (oldestJob) {
91
- // console.log("DEQUQE2");
92
- // oldestJob();
93
- // }
94
- // }, interval);
95
- // }
96
-
97
- // stop() {
98
- // clearInterval(this.timerHandle);
99
- // }
100
-
101
- // enqueue(job) {
102
- // this.jobQueue.push(job);
103
- // }
104
- // }