bigpipe-util 0.1.3 → 0.2.1
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 +27 -0
- package/README.md +0 -4
- package/package.json +1 -1
- package/src/Primer.js +23 -0
- package/src/ServerJS.js +0 -2
- package/src/async/AsyncDOM.js +5 -0
- package/src/async/AsyncRequest.js +22 -2
- package/src/core/Dialog.js +22 -8
package/CHANGELOG.md
CHANGED
|
@@ -21,3 +21,30 @@ Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) princip
|
|
|
21
21
|
|
|
22
22
|
### Fixed
|
|
23
23
|
- Dialog z-index
|
|
24
|
+
|
|
25
|
+
## v0.1.4 - 2022-05-06
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- Async requests counter
|
|
29
|
+
- Support for extra arguments in dialog controller
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
- Arguments for non-method require calls
|
|
33
|
+
|
|
34
|
+
## v0.2.0 - 2022-05-17
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
- Shield to prevent "JSON Hijacking"
|
|
38
|
+
- Invalid node checking for AsyncDOM
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
- Backdrop for 2+ opened dialogs
|
|
42
|
+
|
|
43
|
+
## v0.2.1 - 2022-06-02
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
- Prevent links from being double-clicked
|
|
47
|
+
- Support for keyboard (close by escape)
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
- Closing dialogs with [esc] in the correct order
|
package/README.md
CHANGED
|
@@ -106,10 +106,6 @@ if (OH_NOES_WE_NEED_TO_CANCEL_RIGHT_NOW_OR_ELSE) {
|
|
|
106
106
|
rel="dialog">Open Modal</a>
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
## Credits
|
|
110
|
-
|
|
111
|
-
- [Richard Dobroň][link-author]
|
|
112
|
-
|
|
113
109
|
## Inspiration
|
|
114
110
|
|
|
115
111
|
BigPipe is inspired by the concept behind Facebook's BigPipe. For more details
|
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.1
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bigpipe",
|
|
7
7
|
"xhr",
|
package/src/Primer.js
CHANGED
|
@@ -19,6 +19,11 @@ export default function Primer() {
|
|
|
19
19
|
let relationship = linkNodeOnClicked.rel && linkNodeOnClicked.rel.match(RELATIONSHIP_REGEX);
|
|
20
20
|
relationship = relationship && relationship[0];
|
|
21
21
|
|
|
22
|
+
if (linkNodeOnClicked.classList.contains('async-saving')) {
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
22
27
|
switch (relationship) {
|
|
23
28
|
case "async":
|
|
24
29
|
case "async-post":
|
|
@@ -26,6 +31,15 @@ export default function Primer() {
|
|
|
26
31
|
|
|
27
32
|
(new AsyncRequest(linkNodeOnClicked.getAttribute("ajaxify")))
|
|
28
33
|
.setRelative(linkNodeOnClicked)
|
|
34
|
+
.setInitialHandler(() => {
|
|
35
|
+
linkNodeOnClicked.classList.add("async-saving");
|
|
36
|
+
})
|
|
37
|
+
.setHandler(() => {
|
|
38
|
+
linkNodeOnClicked.classList.remove("async-saving");
|
|
39
|
+
})
|
|
40
|
+
.setErrorHandler(() => {
|
|
41
|
+
linkNodeOnClicked.classList.remove("async-saving");
|
|
42
|
+
})
|
|
29
43
|
.setMethod(relationship === "async-post" ? "POST" : "GET")
|
|
30
44
|
.send();
|
|
31
45
|
break;
|
|
@@ -34,6 +48,15 @@ export default function Primer() {
|
|
|
34
48
|
|
|
35
49
|
(new AsyncRequest(linkNodeOnClicked.getAttribute("ajaxify")))
|
|
36
50
|
.setRelative(linkNodeOnClicked)
|
|
51
|
+
.setInitialHandler(() => {
|
|
52
|
+
linkNodeOnClicked.classList.add("async-saving");
|
|
53
|
+
})
|
|
54
|
+
.setHandler(() => {
|
|
55
|
+
linkNodeOnClicked.classList.remove("async-saving");
|
|
56
|
+
})
|
|
57
|
+
.setErrorHandler(() => {
|
|
58
|
+
linkNodeOnClicked.classList.remove("async-saving");
|
|
59
|
+
})
|
|
37
60
|
.setMethod("POST")
|
|
38
61
|
.send();
|
|
39
62
|
break;
|
package/src/ServerJS.js
CHANGED
package/src/async/AsyncDOM.js
CHANGED
|
@@ -15,6 +15,11 @@ export default class AsyncDOM {
|
|
|
15
15
|
node = (node || document.documentElement).querySelector(selector);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
if (!node) {
|
|
19
|
+
console.error(`Selector '${selector}' does not match anything!`)
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
|
|
18
23
|
switch (type) {
|
|
19
24
|
case "eval":
|
|
20
25
|
(new Function(content)).apply(node);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import emptyFunction from "fbjs/lib/emptyFunction";
|
|
2
2
|
import AsyncResponse from "./AsyncResponse";
|
|
3
3
|
|
|
4
|
+
let requests = 0;
|
|
5
|
+
|
|
4
6
|
function serialize(obj, prefix) {
|
|
5
7
|
const str = [];
|
|
6
8
|
for(const p in obj) {
|
|
@@ -119,6 +121,17 @@ AsyncRequest.prototype.abort = function () {
|
|
|
119
121
|
}
|
|
120
122
|
};
|
|
121
123
|
|
|
124
|
+
AsyncRequest.prototype._unshieldResponseText = function (text) {
|
|
125
|
+
const shield = "for (;;);";
|
|
126
|
+
const shieldLength = shield.length;
|
|
127
|
+
|
|
128
|
+
if (text.length <= shieldLength) {
|
|
129
|
+
throw new Error("Response too short on async to " + this.getURI());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return text.substring(shieldLength);
|
|
133
|
+
};
|
|
134
|
+
|
|
122
135
|
AsyncRequest.prototype.send = function () {
|
|
123
136
|
const {uri, method} = this;
|
|
124
137
|
let { data } = this;
|
|
@@ -138,7 +151,8 @@ AsyncRequest.prototype.send = function () {
|
|
|
138
151
|
if (this.status >= 200 && this.status < 400) {
|
|
139
152
|
let response;
|
|
140
153
|
try {
|
|
141
|
-
|
|
154
|
+
const safeJson = self._unshieldResponseText(this.responseText);
|
|
155
|
+
response = eval("(" + safeJson + ")");
|
|
142
156
|
} catch (e) {
|
|
143
157
|
throw new Error("Failed to handle response: " + e.message + "\n" + this.responseText);
|
|
144
158
|
}
|
|
@@ -152,7 +166,13 @@ AsyncRequest.prototype.send = function () {
|
|
|
152
166
|
};
|
|
153
167
|
|
|
154
168
|
request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
|
155
|
-
|
|
169
|
+
|
|
170
|
+
requests++;
|
|
171
|
+
|
|
172
|
+
if (this.data instanceof FormData) {
|
|
173
|
+
this.data.append('__req', requests);
|
|
174
|
+
} else {
|
|
175
|
+
data.__req = requests;
|
|
156
176
|
data = serialize(data);
|
|
157
177
|
|
|
158
178
|
if (method === "POST") {
|
package/src/core/Dialog.js
CHANGED
|
@@ -8,6 +8,16 @@ let modalId = 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) {
|
|
13
|
+
const currentModal = stack[stack.length - 1];
|
|
14
|
+
if (currentModal.el.isEqualNode(this.el)) {
|
|
15
|
+
this.emit('dismiss', this, e, null);
|
|
16
|
+
this.hide();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
export default class Dialog {
|
|
12
22
|
close() {
|
|
13
23
|
stack.forEach((dialog) => dialog.hide())
|
|
@@ -21,7 +31,7 @@ export default class Dialog {
|
|
|
21
31
|
}
|
|
22
32
|
}
|
|
23
33
|
|
|
24
|
-
render(options) {
|
|
34
|
+
render(options, args) {
|
|
25
35
|
DOM.appendContent(document.body, this._makeDialog(options.content));
|
|
26
36
|
|
|
27
37
|
modalId++;
|
|
@@ -29,29 +39,31 @@ export default class Dialog {
|
|
|
29
39
|
const _dialog = this._show({
|
|
30
40
|
el: document.getElementById(this.id),
|
|
31
41
|
animate: false,
|
|
42
|
+
keyboard: options.keyboard ?? true,
|
|
32
43
|
backdrop: options.backdrop ?? true,
|
|
33
44
|
transition: options.transition ?? 0,
|
|
34
45
|
backdropTransition: options.backdropTransition ?? 0,
|
|
35
|
-
}, options.controller);
|
|
46
|
+
}, options.controller, args);
|
|
36
47
|
|
|
37
48
|
stack.push(_dialog);
|
|
38
49
|
}
|
|
39
50
|
|
|
40
|
-
showFromModel(model) {
|
|
51
|
+
showFromModel(model, args) {
|
|
41
52
|
const _dialog = this._show({
|
|
42
53
|
title: model.title || '',
|
|
43
54
|
content: model.body,
|
|
44
55
|
footer: model.footer || false,
|
|
45
56
|
animate: false,
|
|
57
|
+
keyboard: model.keyboard ?? true,
|
|
46
58
|
backdrop: model.backdrop ?? true,
|
|
47
59
|
transition: model.transition ?? 0,
|
|
48
60
|
backdropTransition: model.backdropTransition ?? 0,
|
|
49
|
-
}, model.controller);
|
|
61
|
+
}, model.controller, args);
|
|
50
62
|
|
|
51
63
|
stack.push(_dialog);
|
|
52
64
|
}
|
|
53
65
|
|
|
54
|
-
_show(options, controller) {
|
|
66
|
+
_show(options, controller, args) {
|
|
55
67
|
if (originalBodyPad === null) {
|
|
56
68
|
originalBodyPad = document.body.style.paddingRight;
|
|
57
69
|
}
|
|
@@ -59,7 +71,7 @@ export default class Dialog {
|
|
|
59
71
|
const _modal = new Modal(options);
|
|
60
72
|
|
|
61
73
|
if (controller) {
|
|
62
|
-
(new (window.require(controller))(_modal));
|
|
74
|
+
(new (window.require(controller))(_modal, ...args));
|
|
63
75
|
}
|
|
64
76
|
|
|
65
77
|
const self = this;
|
|
@@ -83,15 +95,17 @@ export default class Dialog {
|
|
|
83
95
|
}).show();
|
|
84
96
|
}
|
|
85
97
|
|
|
86
|
-
_fixBackdrop(
|
|
98
|
+
_fixBackdrop() {
|
|
87
99
|
const backdrops = document.querySelectorAll('.modal-backdrop');
|
|
100
|
+
let shown = false;
|
|
88
101
|
|
|
89
102
|
backdrops.forEach((backdrop, index) => {
|
|
90
|
-
if (index > 0 && index === backdrops.length - 1) {
|
|
103
|
+
if (shown || index > 0 && index === backdrops.length - 1) {
|
|
91
104
|
backdrop.style.display = 'none';
|
|
92
105
|
} else {
|
|
93
106
|
backdrop.style.zIndex = zIndex - 1;
|
|
94
107
|
backdrop.style.display = '';
|
|
108
|
+
shown = true;
|
|
95
109
|
}
|
|
96
110
|
});
|
|
97
111
|
}
|