bigpipe-util 0.2.1 → 0.2.3

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/CHANGELOG.md CHANGED
@@ -4,23 +4,29 @@ All notable changes to `bigpipe-util` will be documented in this file.
4
4
 
5
5
  Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
6
6
 
7
- ## v0.1.1 - 2022-03-27
7
+ ## v0.2.2 - 2022-08-03
8
8
 
9
- ### Added
10
- - Part of BigPipe implementation for Webpack.
9
+ ### Fixed
10
+ - calls for modules without constructor
11
+ - dialog opening when using instant closing
11
12
 
12
- ## v0.1.2 - 2022-04-14
13
+ ## v0.2.1 - 2022-06-02
13
14
 
14
15
  ### Added
15
- - Dialogs support.
16
+ - Prevent links from being double-clicked
17
+ - Support for keyboard (close by escape)
16
18
 
17
- ## v0.1.3 - 2022-04-27
19
+ ### Fixed
20
+ - Closing dialogs with [esc] in the correct order
21
+
22
+ ## v0.2.0 - 2022-05-17
18
23
 
19
24
  ### Added
20
- - Method to close only current dialog
25
+ - Shield to prevent "JSON Hijacking"
26
+ - Invalid node checking for AsyncDOM
21
27
 
22
28
  ### Fixed
23
- - Dialog z-index
29
+ - Backdrop for 2+ opened dialogs
24
30
 
25
31
  ## v0.1.4 - 2022-05-06
26
32
 
@@ -31,20 +37,20 @@ Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) princip
31
37
  ### Fixed
32
38
  - Arguments for non-method require calls
33
39
 
34
- ## v0.2.0 - 2022-05-17
40
+ ## v0.1.3 - 2022-04-27
35
41
 
36
42
  ### Added
37
- - Shield to prevent "JSON Hijacking"
38
- - Invalid node checking for AsyncDOM
43
+ - Method to close only current dialog
39
44
 
40
45
  ### Fixed
41
- - Backdrop for 2+ opened dialogs
46
+ - Dialog z-index
42
47
 
43
- ## v0.2.1 - 2022-06-02
48
+ ## v0.1.2 - 2022-04-14
44
49
 
45
50
  ### Added
46
- - Prevent links from being double-clicked
47
- - Support for keyboard (close by escape)
51
+ - Dialogs support.
48
52
 
49
- ### Fixed
50
- - Closing dialogs with [esc] in the correct order
53
+ ## v0.1.1 - 2022-03-27
54
+
55
+ ### Added
56
+ - Part of BigPipe implementation for Webpack.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- <img src="bigpipe.svg">
1
+ <img src="bigpipe.svg" alt="BigPipe logo">
2
2
 
3
3
  This library currently implements small part of [Facebook BigPipe][blog] so far, but the advantage is to efficiently insert/replace content and work with the DOM. It is also possible to easily call JavaScript modules from PHP.
4
4
 
@@ -8,7 +8,6 @@ Try the app with [live demo](http://bigpipe.xf.cz).
8
8
 
9
9
  ## Requirements
10
10
  * PHP 7.1 or higher
11
- * Node 8, 10+.
12
11
  * Webpack
13
12
 
14
13
  ## Installation
@@ -58,7 +57,7 @@ export default class ServerJS extends ServerJSImpl {
58
57
  ```javascript
59
58
  import AsyncRequest from 'bigpipe-util/src/AsyncRequest';
60
59
 
61
- const request = AsyncRequest('/ajax/remove.php')
60
+ const request = (new AsyncRequest('/ajax/remove.php'))
62
61
  // or .setURI('/ajax/remove.php')
63
62
  .setMethod('POST')
64
63
  .setData({
@@ -73,6 +72,9 @@ const request = AsyncRequest('/ajax/remove.php')
73
72
  .setErrorHandler((xhr) => {
74
73
  // A function to be called if the request fails
75
74
  })
75
+ .setFinallyHandler((xhr) => {
76
+ // after request callback function
77
+ })
76
78
  .send();
77
79
 
78
80
  if (OH_NOES_WE_NEED_TO_CANCEL_RIGHT_NOW_OR_ELSE) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bigpipe-util",
3
3
  "description": "This library currently implements small part of Facebook BigPipe so far, but the advantage is to efficiently insert/replace content and work with the DOM. It is also possible to easily call JavaScript modules from PHP.",
4
- "version": "0.2.1",
4
+ "version": "0.2.3",
5
5
  "keywords": [
6
6
  "bigpipe",
7
7
  "xhr",
package/src/ServerJS.js CHANGED
@@ -21,7 +21,8 @@ export default class ServerJS {
21
21
  replaceTransportMarkers(this._relativeTo, marker);
22
22
  }
23
23
 
24
- const context = new (window.require(modulePath));
24
+ const factory = window.require(modulePath);
25
+ const context = typeof factory === 'function' ? new factory : factory;
25
26
 
26
27
  if (!context[method]) {
27
28
  throw new TypeError(`Module ${modulePath} has no method "${method}"`);
@@ -26,166 +26,181 @@ function validateResponseHandler(handler) {
26
26
  return valid;
27
27
  }
28
28
 
29
- export default function AsyncRequest(uri) {
30
- this.method = "POST";
31
- this.uri = "";
32
- this.relative = null;
33
- this.data = {};
34
- this.headers = {};
35
- this.initialHandler = emptyFunction;
36
- this.handler = null;
37
- this.errorHandler = emptyFunction;
38
-
39
- if (uri !== undefined) {
40
- this.setURI(uri);
29
+ export default class AsyncRequest {
30
+ constructor(uri) {
31
+ this.method = "POST";
32
+ this.uri = "";
33
+ this.relative = null;
34
+ this.data = {};
35
+ this.headers = {};
36
+ this.initialHandler = emptyFunction;
37
+ this.handler = null;
38
+ this.finallyHandler = emptyFunction;
39
+ this.errorHandler = emptyFunction;
40
+
41
+ if (uri !== undefined) {
42
+ this.setURI(uri);
43
+ }
41
44
  }
42
- }
43
45
 
44
- AsyncRequest.prototype.setMethod = function (method) {
45
- this.method = method.toString().toUpperCase();
46
- return this;
47
- };
46
+ setMethod(method) {
47
+ this.method = method.toString().toUpperCase();
48
+ return this;
49
+ }
48
50
 
49
- AsyncRequest.prototype.getMethod = function () {
50
- return this.method;
51
- };
51
+ getMethod() {
52
+ return this.method;
53
+ }
52
54
 
53
- AsyncRequest.prototype.setRelative = function (relative) {
54
- this.relative = relative;
55
- return this;
56
- };
55
+ setRelative(relative) {
56
+ this.relative = relative;
57
+ return this;
58
+ }
57
59
 
58
- AsyncRequest.prototype.getRelative = function () {
59
- return this.relative;
60
- };
60
+ getRelative() {
61
+ return this.relative;
62
+ }
61
63
 
62
- AsyncRequest.prototype.setData = function (obj) {
63
- this.data = obj;
64
- return this;
65
- };
64
+ setData(obj) {
65
+ this.data = obj;
66
+ return this;
67
+ }
66
68
 
67
- AsyncRequest.prototype.getData = function () {
68
- return this.data;
69
- };
69
+ getData() {
70
+ return this.data;
71
+ }
70
72
 
71
- AsyncRequest.prototype.setRequestHeader = function (name, value) {
72
- this.headers[name] = value;
73
- return this;
74
- };
73
+ setRequestHeader(name, value) {
74
+ this.headers[name] = value;
75
+ return this;
76
+ }
75
77
 
76
- AsyncRequest.prototype.setURI = function (uri) {
77
- this.uri = uri;
78
+ setURI(uri) {
79
+ this.uri = uri;
78
80
 
79
- return this;
80
- };
81
- AsyncRequest.prototype.getURI = function () {
82
- return this.uri;
83
- };
81
+ return this;
82
+ }
83
+ getURI() {
84
+ return this.uri;
85
+ }
84
86
 
85
- AsyncRequest.prototype.setInitialHandler = function (fn) {
86
- this.initialHandler = fn;
87
- return this;
88
- };
87
+ setInitialHandler(fn) {
88
+ this.initialHandler = fn;
89
+ return this;
90
+ }
89
91
 
90
- AsyncRequest.prototype.getInitialHandler = function () {
91
- return this.initialHandler || emptyFunction;
92
- };
92
+ getInitialHandler() {
93
+ return this.initialHandler || emptyFunction;
94
+ }
93
95
 
94
- AsyncRequest.prototype.setHandler = function (fn) {
95
- if (validateResponseHandler(fn)) {
96
- this.handler = fn;
96
+ setHandler(fn) {
97
+ if (validateResponseHandler(fn)) {
98
+ this.handler = fn;
99
+ }
100
+ return this;
97
101
  }
98
- return this;
99
- };
100
102
 
101
- AsyncRequest.prototype.getHandler = function () {
102
- return this.handler || emptyFunction;
103
- };
103
+ setFinallyHandler(fn) {
104
+ this.finallyHandler = fn;
105
+ return this;
106
+ }
104
107
 
105
- AsyncRequest.prototype.setErrorHandler = function (fn) {
106
- if (validateResponseHandler(fn)) {
107
- this.errorHandler = fn;
108
+ getFinallyHandler() {
109
+ return this.finallyHandler || emptyFunction;
108
110
  }
109
- return this;
110
- };
111
111
 
112
- AsyncRequest.prototype.getErrorHandler = function () {
113
- return this.errorHandler || emptyFunction;
114
- };
112
+ getHandler() {
113
+ return this.handler || emptyFunction;
114
+ }
115
115
 
116
- AsyncRequest.prototype.abort = function () {
117
- const transport = this.transport;
116
+ setErrorHandler(fn) {
117
+ if (validateResponseHandler(fn)) {
118
+ this.errorHandler = fn;
119
+ }
120
+ return this;
121
+ }
118
122
 
119
- if (transport) {
120
- transport.abort();
123
+ getErrorHandler() {
124
+ return this.errorHandler || emptyFunction;
121
125
  }
122
- };
123
126
 
124
- AsyncRequest.prototype._unshieldResponseText = function (text) {
125
- const shield = "for (;;);";
126
- const shieldLength = shield.length;
127
+ abort() {
128
+ const transport = this.transport;
127
129
 
128
- if (text.length <= shieldLength) {
129
- throw new Error("Response too short on async to " + this.getURI());
130
+ if (transport) {
131
+ transport.abort();
132
+ }
130
133
  }
131
134
 
132
- return text.substring(shieldLength);
133
- };
135
+ _unshieldResponseText(text) {
136
+ const shield = "for (;;);";
137
+ const shieldLength = shield.length;
134
138
 
135
- AsyncRequest.prototype.send = function () {
136
- const {uri, method} = this;
137
- let { data } = this;
139
+ if (text.length <= shieldLength) {
140
+ throw new Error("Response too short on async to " + this.getURI());
141
+ }
138
142
 
139
- const handler = this.getHandler();
140
- const errorHandler = this.getErrorHandler();
141
- const initialHandler = this.getInitialHandler();
143
+ return text.substring(shieldLength);
144
+ }
142
145
 
143
- initialHandler();
146
+ send() {
147
+ const {uri, method} = this;
148
+ let { data } = this;
144
149
 
145
- const request = new XMLHttpRequest();
146
- const self = this;
150
+ const handler = this.getHandler();
151
+ const errorHandler = this.getErrorHandler();
152
+ const initialHandler = this.getInitialHandler();
153
+ const getFinallyHandler = this.getFinallyHandler();
147
154
 
148
- request.open(method, uri, true);
155
+ initialHandler();
149
156
 
150
- request.onload = function () {
151
- if (this.status >= 200 && this.status < 400) {
152
- let response;
153
- try {
154
- const safeJson = self._unshieldResponseText(this.responseText);
155
- response = eval("(" + safeJson + ")");
156
- } catch (e) {
157
- throw new Error("Failed to handle response: " + e.message + "\n" + this.responseText);
158
- }
157
+ const request = new XMLHttpRequest();
158
+ const self = this;
159
159
 
160
- (new AsyncResponse).handle(response, self.relative);
160
+ request.open(method, uri, true);
161
161
 
162
- handler(response);
163
- } else {
164
- errorHandler(this);
162
+ request.onload = function () {
163
+ if (this.status >= 200 && this.status < 400) {
164
+ let response;
165
+ try {
166
+ const safeJson = self._unshieldResponseText(this.responseText);
167
+ response = eval("(" + safeJson + ")");
168
+ } catch (e) {
169
+ throw new Error("Failed to handle response: " + e.message + "\n" + this.responseText);
170
+ }
171
+
172
+ (new AsyncResponse).handle(response, self.relative);
173
+
174
+ handler(response);
175
+ } else {
176
+ errorHandler(this);
177
+ }
178
+
179
+ getFinallyHandler(this);
165
180
  }
166
- };
167
181
 
168
- request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
182
+ request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
169
183
 
170
- requests++;
184
+ requests++;
171
185
 
172
- if (this.data instanceof FormData) {
173
- this.data.append('__req', requests);
174
- } else {
175
- data.__req = requests;
176
- data = serialize(data);
186
+ if (data instanceof FormData) {
187
+ data.append('__req', requests);
188
+ } else {
189
+ data.__req = requests;
190
+ data = serialize(data);
177
191
 
178
- if (method === "POST") {
179
- request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
192
+ if (method === "POST") {
193
+ request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
194
+ }
180
195
  }
181
- }
182
196
 
183
- Object.keys(this.headers).forEach((name) => {
184
- request.setRequestHeader(name, this.headers[name]);
185
- });
197
+ Object.keys(this.headers).forEach((name) => {
198
+ request.setRequestHeader(name, this.headers[name]);
199
+ });
186
200
 
187
- request.onerror = errorHandler;
188
- request.send(data);
201
+ request.onerror = errorHandler;
202
+ request.send(data);
189
203
 
190
- return request;
204
+ return request;
205
+ }
191
206
  };
@@ -3,16 +3,16 @@ import DOM from "./DOM";
3
3
 
4
4
  const DEFAULT_Z_INDEX = 1040;
5
5
 
6
- const stack = [];
7
- let modalId = 1;
6
+ let stack = [];
7
+ let dialogId = 1;
8
8
  let zIndex;
9
9
  let originalBodyPad = null;
10
10
 
11
- Modal.prototype._handleKeydownEvent = function(e) {
12
- if (e.which === 27 && this._options.keyboard) {
11
+ Modal.prototype._handleKeydownEvent = function(event) {
12
+ if (event.which === 27 && this._options.keyboard) {
13
13
  const currentModal = stack[stack.length - 1];
14
14
  if (currentModal.el.isEqualNode(this.el)) {
15
- this.emit('dismiss', this, e, null);
15
+ this.emit('dismiss', this, event, null);
16
16
  this.hide();
17
17
  }
18
18
  }
@@ -34,9 +34,9 @@ export default class Dialog {
34
34
  render(options, args) {
35
35
  DOM.appendContent(document.body, this._makeDialog(options.content));
36
36
 
37
- modalId++;
37
+ dialogId++;
38
38
 
39
- const _dialog = this._show({
39
+ const dialog = this._show({
40
40
  el: document.getElementById(this.id),
41
41
  animate: false,
42
42
  keyboard: options.keyboard ?? true,
@@ -45,11 +45,13 @@ export default class Dialog {
45
45
  backdropTransition: options.backdropTransition ?? 0,
46
46
  }, options.controller, args);
47
47
 
48
- stack.push(_dialog);
48
+ stack.push(dialog);
49
+
50
+ return dialog;
49
51
  }
50
52
 
51
53
  showFromModel(model, args) {
52
- const _dialog = this._show({
54
+ const dialog = this._show({
53
55
  title: model.title || '',
54
56
  content: model.body,
55
57
  footer: model.footer || false,
@@ -60,7 +62,9 @@ export default class Dialog {
60
62
  backdropTransition: model.backdropTransition ?? 0,
61
63
  }, model.controller, args);
62
64
 
63
- stack.push(_dialog);
65
+ stack.push(dialog);
66
+
67
+ return dialog;
64
68
  }
65
69
 
66
70
  _show(options, controller, args) {
@@ -71,7 +75,13 @@ export default class Dialog {
71
75
  const _modal = new Modal(options);
72
76
 
73
77
  if (controller) {
74
- (new (window.require(controller))(_modal, ...args));
78
+ if (typeof controller === 'string') {
79
+ controller = new (window.require(controller));
80
+ }
81
+
82
+ if (typeof controller === 'function') {
83
+ controller(_modal, ...args);
84
+ }
75
85
  }
76
86
 
77
87
  const self = this;
@@ -81,7 +91,9 @@ export default class Dialog {
81
91
  el.style.zIndex = zIndex;
82
92
  setTimeout(() => document.querySelector('.modal-backdrop').style.zIndex = zIndex - 1);
83
93
  }).on('showBackdrop', this._fixBackdrop).on('hidden', function ({el}) {
84
- stack.pop();
94
+ stack = stack.filter((modal) => {
95
+ return ! modal.el.isEqualNode(el);
96
+ });
85
97
 
86
98
  if (stack.length) {
87
99
  document.body.classList.add('modal-open');
@@ -111,7 +123,7 @@ export default class Dialog {
111
123
  }
112
124
 
113
125
  _makeDialog(content) {
114
- this.id = `js_${modalId.toString(16)}`;
126
+ this.id = `js_${dialogId.toString(16)}`;
115
127
  return `<div id="${this.id}" class="modal fade" tabindex="-1" role="dialog">
116
128
  ${content}
117
129
  </div>`;