bigpipe-util 0.2.11 → 0.2.12
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 +6 -4
- package/bigpipe.svg +3 -3
- package/package.json +1 -1
- package/src/BigPipe.js +170 -0
- package/src/Pagelet.js +131 -0
- package/src/Primer.js +5 -1
- package/src/ServerJS.js +1 -1
- package/src/async/AsyncDOM.js +1 -0
- package/src/async/AsyncRequest.js +3 -3
- package/src/core/replaceTransportMarkers.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,17 +4,19 @@ 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.2.12 - 2026-01-12
|
|
8
|
+
### Added
|
|
9
|
+
- Added `Pagelet.js`, `BigPipe.js`
|
|
10
|
+
|
|
7
11
|
## v0.2.11 - 2025-12-27
|
|
12
|
+
### Added
|
|
8
13
|
- Added `incrementRequests` method to AsyncRequest.js
|
|
9
14
|
- Added submitter to form data in onSubmit handler
|
|
10
15
|
|
|
11
|
-
### Added
|
|
12
|
-
- Added `timeout` to open dialog with a delay in method `showFromModel`
|
|
13
|
-
|
|
14
16
|
## v0.2.10 - 2025-05-15
|
|
15
|
-
- Added `onAfterLoad.js`, `waitForLoad.js`
|
|
16
17
|
|
|
17
18
|
### Added
|
|
19
|
+
- Added `onAfterLoad.js`, `waitForLoad.js`
|
|
18
20
|
- Added `timeout` to open dialog with a delay in method `showFromModel`
|
|
19
21
|
|
|
20
22
|
## v0.2.8 - 2025-05-02
|
package/bigpipe.svg
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<svg width="254.23999568394254" height="64.8" viewBox="0 0 254.23999568394254 64.8" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<g transform="matrix(3.0856473041983374,0,0,3.0856473041983374,-6.171373325690784,-6.1698442238608315)" fill="#
|
|
2
|
+
<g transform="matrix(3.0856473041983374,0,0,3.0856473041983374,-6.171373325690784,-6.1698442238608315)" fill="#478be6">
|
|
3
3
|
<g xmlns="http://www.w3.org/2000/svg">
|
|
4
4
|
<path d="M2.2427,8.9287a.5.5,0,0,1,0-.8574l10-6a.4971.4971,0,0,1,.5146,0l10,6a.5.5,0,0,1,0,.8574l-10,6a.5.5,0,0,1-.5146,0Zm20,3.1426L12.5,17.917,2.7573,12.0713a.5.5,0,0,0-.5146.8574l10,6a.5.5,0,0,0,.5146,0l10-6a.5.5,0,0,0-.5146-.8574Zm0,4L12.5,21.917,2.7573,16.0713a.5.5,0,0,0-.5146.8574l10,6a.5.5,0,0,0,.5146,0l10-6a.5.5,0,0,0-.5146-.8574Z"></path>
|
|
5
5
|
</g>
|
|
6
6
|
</g>
|
|
7
|
-
<g transform="matrix(2.500000066227385,0,0,2.500000066227385,81.99999920527138,0.49999675485814166)" fill="#
|
|
7
|
+
<g transform="matrix(2.500000066227385,0,0,2.500000066227385,81.99999920527138,0.49999675485814166)" fill="#478be6">
|
|
8
8
|
<path d="M8.92 12.24 c1.48 0.4 2.88 1.72 2.88 3.66 c0 2.54 -1.6 4.1 -5.08 4.1 l-5.52 0 l0 -14 l5.22 0 c2.9 0 4.36 1.64 4.36 3.52 c0 1.48 -0.96 2.32 -1.86 2.72 z M6.14 8.56 l-2.02 0 l0 2.82 l2.02 0 c1.24 0 1.74 -0.58 1.74 -1.42 c0 -0.9 -0.58 -1.4 -1.74 -1.4 z M6.56 17.44 c1.6 0 2.32 -0.66 2.32 -1.9 c0 -1.06 -0.72 -1.8 -2.44 -1.8 l-2.32 0 l0 3.7 l2.44 0 z M16.62 6 l0 14 l-2.92 0 l0 -14 l2.92 0 z M32.2 12.559999999999999 c0.62 4.7 -2.44 7.64 -6.22 7.64 c-3.98 0 -7.26 -2.94 -7.26 -7.2 s3.36 -7.2 7.26 -7.2 c1.86 0 3.5 0.6 4.7 1.66 l-1.76 1.98 c-0.74 -0.52 -1.74 -0.9 -2.76 -0.9 c-2.54 0 -4.4 1.86 -4.4 4.46 s1.8 4.46 4.22 4.46 c1.84 0 3.12 -0.66 3.52 -2.42 l-3.3 0 l0 -2.48 l6 0 z"></path>
|
|
9
9
|
</g>
|
|
10
|
-
<g transform="matrix(2.5714285714285716,0,0,2.5714285714285716,166.91428559167045,-0.4285714285714306)" fill="#
|
|
10
|
+
<g transform="matrix(2.5714285714285716,0,0,2.5714285714285716,166.91428559167045,-0.4285714285714306)" fill="#478be6">
|
|
11
11
|
<path d="M5.46 6 c3.14 0 4.8 1.94 4.8 4.3 c0 2.42 -1.66 4.34 -4.8 4.34 l-3.68 0 l0 5.36 l-0.58 0 l0 -14 l4.26 0 z M5.54 14.04 c2.52 0 4.14 -1.46 4.14 -3.74 c0 -2.32 -1.62 -3.72 -4.14 -3.72 l-3.76 0 l0 7.46 l3.76 0 z M12.74 6 l0 14 l-0.58 0 l0 -14 l0.58 0 z M19.400000000000002 6 c3.14 0 4.8 1.94 4.8 4.3 c0 2.42 -1.66 4.34 -4.8 4.34 l-3.68 0 l0 5.36 l-0.58 0 l0 -14 l4.26 0 z M19.48 14.04 c2.52 0 4.14 -1.46 4.14 -3.74 c0 -2.32 -1.62 -3.72 -4.14 -3.72 l-3.76 0 l0 7.46 l3.76 0 z M26.680000000000003 19.42 l7.28 0 l0 0.58 l-7.68 0 l-0.18 0 l0 -14 l0.58 0 l7.1 0 l0 0.58 l-7.1 0 l0 6.1 l5.66 0 l0 0.58 l-5.66 0 l0 6.16 z"></path>
|
|
12
12
|
</g>
|
|
13
13
|
</svg>
|
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.
|
|
4
|
+
"version": "0.2.12",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bigpipe",
|
|
7
7
|
"xhr",
|
package/src/BigPipe.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
export const BIGPIPE_PHASE = Object.freeze({
|
|
2
|
+
WAITING: 0,
|
|
3
|
+
READY_FOR_JS: 1,
|
|
4
|
+
LOADING_JS: 2,
|
|
5
|
+
COMPLETE: 3
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export const RESOURCE_PHASE = Object.freeze({
|
|
9
|
+
PENDING: 0,
|
|
10
|
+
LOADING: 1,
|
|
11
|
+
LOADED: 2
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
let instance = null;
|
|
15
|
+
|
|
16
|
+
export default class BigPipe {
|
|
17
|
+
pageletResources = new Map();
|
|
18
|
+
pagelets = new Map();
|
|
19
|
+
phase = BIGPIPE_PHASE.WAITING;
|
|
20
|
+
|
|
21
|
+
constructor() {
|
|
22
|
+
if (instance) {
|
|
23
|
+
return instance;
|
|
24
|
+
}
|
|
25
|
+
instance = this;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
onPageletArrive(pageletData) {
|
|
29
|
+
const Pagelet = require("./Pagelet").default;
|
|
30
|
+
|
|
31
|
+
if (pageletData.is_last) {
|
|
32
|
+
this.setPhase(BIGPIPE_PHASE.READY_FOR_JS);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const pagelet = new Pagelet(pageletData);
|
|
36
|
+
this.registerPagelet(pagelet);
|
|
37
|
+
pagelet.start();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fileLoaded(filename) {
|
|
41
|
+
const resource = this.pageletResources.get(filename);
|
|
42
|
+
if (resource) {
|
|
43
|
+
resource.onComplete();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
registerPagelet(pagelet) {
|
|
48
|
+
this.pagelets.set(pagelet.id, pagelet);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
areAllPageletsReady() {
|
|
52
|
+
if (this.pagelets.size === 0) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return Array.from(this.pagelets.values()).every(
|
|
57
|
+
pagelet => pagelet.phase >= 3
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
pageletDisplayed() {
|
|
62
|
+
if (!this.areAllPageletsReady()) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (this.phase === BIGPIPE_PHASE.READY_FOR_JS) {
|
|
67
|
+
this.loadJSResources();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setPhase(newPhase) {
|
|
72
|
+
this.phase = newPhase;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
loadJSResources() {
|
|
76
|
+
this.setPhase(BIGPIPE_PHASE.LOADING_JS);
|
|
77
|
+
|
|
78
|
+
const jsResources = this.getResourcesByType('js');
|
|
79
|
+
|
|
80
|
+
jsResources.forEach(resource => {
|
|
81
|
+
resource.startLoading();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
this.notifyPageletsWithoutResources();
|
|
85
|
+
|
|
86
|
+
if (jsResources.length === 0) {
|
|
87
|
+
this.notifyAllPagelets();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getResourcesByType(type) {
|
|
92
|
+
return Array.from(this.pageletResources.values())
|
|
93
|
+
.filter(resource => resource.type === type);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
notifyPageletsWithoutResources() {
|
|
97
|
+
this.pagelets.forEach(pagelet => {
|
|
98
|
+
if (pagelet.jsResources.size === 0) {
|
|
99
|
+
pagelet.onJsFinished();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
notifyAllPagelets() {
|
|
105
|
+
this.pagelets.forEach(pagelet => {
|
|
106
|
+
pagelet.onJsFinished();
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
extractFilename(filePath) {
|
|
111
|
+
const match = /([^/]+)$/.exec(filePath);
|
|
112
|
+
return match ? match[1] : filePath;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
pageletResourceFactory(file, type) {
|
|
116
|
+
const name = this.extractFilename(file);
|
|
117
|
+
|
|
118
|
+
let resource = this.pageletResources.get(name);
|
|
119
|
+
if (!resource) {
|
|
120
|
+
resource = this.createResource(file, name, type);
|
|
121
|
+
this.pageletResources.set(name, resource);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return resource;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
createResource(file, name, type) {
|
|
128
|
+
const self = this;
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
file,
|
|
132
|
+
name,
|
|
133
|
+
type,
|
|
134
|
+
phase: RESOURCE_PHASE.PENDING,
|
|
135
|
+
callbacks: [],
|
|
136
|
+
|
|
137
|
+
startLoading() {
|
|
138
|
+
if (this.phase !== RESOURCE_PHASE.PENDING) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
this.phase = RESOURCE_PHASE.LOADING;
|
|
143
|
+
|
|
144
|
+
if (type === 'css') {
|
|
145
|
+
const link = document.createElement('link');
|
|
146
|
+
link.rel = 'stylesheet';
|
|
147
|
+
link.href = file;
|
|
148
|
+
link.onload = () => self.fileLoaded(name);
|
|
149
|
+
link.onerror = () => self.fileLoaded(name);
|
|
150
|
+
document.head.appendChild(link);
|
|
151
|
+
} else if (type === 'js') {
|
|
152
|
+
const script = document.createElement('script');
|
|
153
|
+
script.src = file;
|
|
154
|
+
script.onload = () => self.fileLoaded(name);
|
|
155
|
+
script.onerror = () => self.fileLoaded(name);
|
|
156
|
+
document.body.appendChild(script);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
onComplete() {
|
|
161
|
+
this.phase = RESOURCE_PHASE.LOADED;
|
|
162
|
+
this.callbacks.forEach(callback => callback(this));
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
attachToPagelet(callback) {
|
|
166
|
+
this.callbacks.push(callback);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
package/src/Pagelet.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import BigPipe, { RESOURCE_PHASE } from "./BigPipe";
|
|
2
|
+
import ServerJS from "./ServerJS";
|
|
3
|
+
import AsyncDOM from "./async/AsyncDOM";
|
|
4
|
+
|
|
5
|
+
const serverJS = new ServerJS();
|
|
6
|
+
const asyncDOM = new AsyncDOM();
|
|
7
|
+
const bigPipe = new BigPipe();
|
|
8
|
+
|
|
9
|
+
export const PAGELET_PHASE = Object.freeze({
|
|
10
|
+
ARRIVE_START: 0,
|
|
11
|
+
LOADING_CSS: 1,
|
|
12
|
+
DISPLAY_START: 2,
|
|
13
|
+
DISPLAY_END: 3,
|
|
14
|
+
ARRIVE_END: 4
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function areAllResourcesLoaded(resources) {
|
|
18
|
+
if (resources.size === 0) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
return Array.from(resources.values()).every(
|
|
22
|
+
resource => resource.phase === RESOURCE_PHASE.LOADED
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default class Pagelet {
|
|
27
|
+
cssResources = new Map();
|
|
28
|
+
|
|
29
|
+
jsResources = new Map();
|
|
30
|
+
|
|
31
|
+
id = "";
|
|
32
|
+
|
|
33
|
+
phase = PAGELET_PHASE.ARRIVE_START;
|
|
34
|
+
|
|
35
|
+
constructor(pageletData) {
|
|
36
|
+
this.bigPipe = bigPipe;
|
|
37
|
+
this.id = pageletData.id;
|
|
38
|
+
this.phase = PAGELET_PHASE.ARRIVE_START;
|
|
39
|
+
this.domops = pageletData.domops;
|
|
40
|
+
this.jsmods = pageletData.jsmods;
|
|
41
|
+
this.css = pageletData.css;
|
|
42
|
+
this.js = pageletData.js;
|
|
43
|
+
this.cssResources = new Map();
|
|
44
|
+
this.jsResources = new Map();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
isComplete() {
|
|
48
|
+
return this.phase === PAGELET_PHASE.ARRIVE_END;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
setPhase(phase) {
|
|
52
|
+
this.phase = phase;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
start() {
|
|
56
|
+
if (this.phase !== PAGELET_PHASE.ARRIVE_START) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (this.css.length > 0) {
|
|
61
|
+
this.css.forEach((cssFile) => {
|
|
62
|
+
const cssResource = this.bigPipe.pageletResourceFactory(cssFile, 'css');
|
|
63
|
+
this.attachCssResource(cssResource);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (this.js.length > 0) {
|
|
68
|
+
this.js.forEach((jsFile) => {
|
|
69
|
+
const jsResource = this.bigPipe.pageletResourceFactory(jsFile, 'js');
|
|
70
|
+
this.attachJsResource(jsResource);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (this.cssResources.size > 0) {
|
|
75
|
+
this.setPhase(PAGELET_PHASE.LOADING_CSS);
|
|
76
|
+
this.cssResources.forEach((resource) => {
|
|
77
|
+
resource.startLoading();
|
|
78
|
+
});
|
|
79
|
+
} else {
|
|
80
|
+
this.setContent();
|
|
81
|
+
this.onJsFinished();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
attachCssResource(resource) {
|
|
86
|
+
resource.attachToPagelet(() => this.onCssFinished(resource));
|
|
87
|
+
this.cssResources.set(resource.file, resource);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
attachJsResource(resource) {
|
|
91
|
+
resource.attachToPagelet(() => this.onJsFinished());
|
|
92
|
+
this.jsResources.set(resource.file, resource);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
onJsFinished() {
|
|
96
|
+
if (this.isComplete()) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!areAllResourcesLoaded(this.jsResources)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.processJsMods();
|
|
105
|
+
|
|
106
|
+
this.setPhase(PAGELET_PHASE.ARRIVE_END);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
processJsMods() {
|
|
110
|
+
serverJS.handle(this.jsmods);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
onCssFinished() {
|
|
114
|
+
if (!areAllResourcesLoaded(this.cssResources)) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this.setContent();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
setContent() {
|
|
122
|
+
this.setPhase(PAGELET_PHASE.DISPLAY_START);
|
|
123
|
+
|
|
124
|
+
if (this.domops) {
|
|
125
|
+
asyncDOM.invoke(this.domops);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.setPhase(PAGELET_PHASE.DISPLAY_END);
|
|
129
|
+
this.bigPipe.pageletDisplayed(this);
|
|
130
|
+
}
|
|
131
|
+
}
|
package/src/Primer.js
CHANGED
|
@@ -136,12 +136,16 @@ export default function Primer() {
|
|
|
136
136
|
}
|
|
137
137
|
})
|
|
138
138
|
.setErrorHandler(function () {
|
|
139
|
-
eventTarget.classList.
|
|
139
|
+
eventTarget.classList.remove("async-saving");
|
|
140
140
|
|
|
141
141
|
activeControls.forEach(function (control) {
|
|
142
142
|
control.removeAttribute("readonly");
|
|
143
143
|
});
|
|
144
144
|
|
|
145
|
+
if (submitter) {
|
|
146
|
+
submitter.disabled = false;
|
|
147
|
+
}
|
|
148
|
+
|
|
145
149
|
if (eventTarget) {
|
|
146
150
|
const loader = eventTarget.querySelector(".small-loader");
|
|
147
151
|
|
package/src/ServerJS.js
CHANGED
package/src/async/AsyncDOM.js
CHANGED
|
@@ -154,7 +154,7 @@ export default class AsyncRequest {
|
|
|
154
154
|
const handler = this.getHandler();
|
|
155
155
|
const errorHandler = this.getErrorHandler();
|
|
156
156
|
const initialHandler = this.getInitialHandler();
|
|
157
|
-
const
|
|
157
|
+
const finallyHandler = this.getFinallyHandler();
|
|
158
158
|
|
|
159
159
|
initialHandler();
|
|
160
160
|
|
|
@@ -168,7 +168,7 @@ export default class AsyncRequest {
|
|
|
168
168
|
let response;
|
|
169
169
|
try {
|
|
170
170
|
const safeJson = self._unshieldResponseText(this.responseText);
|
|
171
|
-
response =
|
|
171
|
+
response = JSON.parse(safeJson);
|
|
172
172
|
} catch (e) {
|
|
173
173
|
throw new Error("Failed to handle response: " + e.message + "\n" + this.responseText);
|
|
174
174
|
}
|
|
@@ -180,7 +180,7 @@ export default class AsyncRequest {
|
|
|
180
180
|
errorHandler(this);
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
finallyHandler(this);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
|
@@ -18,7 +18,7 @@ export default function replaceTransportMarkers(markerValue, markers, key) {
|
|
|
18
18
|
} else if (marker.__set) {
|
|
19
19
|
markers[key] = new Set(marker.__set);
|
|
20
20
|
} else {
|
|
21
|
-
for (
|
|
21
|
+
for (const value in marker) {
|
|
22
22
|
replaceTransportMarkers(markerValue, marker, value);
|
|
23
23
|
}
|
|
24
24
|
}
|