UNPKG

bigpipe-util

Version:

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.

207 lines (161 loc) 4.44 kB
import emptyFunction from "fbjs/lib/emptyFunction"; import AsyncResponse from "./AsyncResponse"; let requests = 0; function serialize(obj, prefix) { const str = []; for(const p in obj) { if (obj.hasOwnProperty(p)) { const k = prefix ? prefix + "[" + p + "]" : p, v = obj[p]; str.push(typeof v == "object" ? serialize(v, k) : encodeURIComponent(k) + "=" + encodeURIComponent(v)); } } return str.join("&"); } function validateResponseHandler(handler) { const valid = !handler || typeof handler === "function"; if (!valid) { console.error("AsyncRequest response handlers must be functions. Pass a function, or use bind() to build one."); } return valid; } export default class AsyncRequest { constructor(uri) { this.method = "POST"; this.uri = ""; this.relative = null; this.data = {}; this.headers = {}; this.initialHandler = emptyFunction; this.handler = null; this.finallyHandler = emptyFunction; this.errorHandler = emptyFunction; if (uri !== undefined) { this.setURI(uri); } } setMethod(method) { this.method = method.toString().toUpperCase(); return this; } getMethod() { return this.method; } setRelative(relative) { this.relative = relative; return this; } getRelative() { return this.relative; } setData(obj) { this.data = obj; return this; } getData() { return this.data; } setRequestHeader(name, value) { this.headers[name] = value; return this; } setURI(uri) { this.uri = uri; return this; } getURI() { return this.uri; } setInitialHandler(fn) { this.initialHandler = fn; return this; } getInitialHandler() { return this.initialHandler || emptyFunction; } setHandler(fn) { if (validateResponseHandler(fn)) { this.handler = fn; } return this; } setFinallyHandler(fn) { this.finallyHandler = fn; return this; } getFinallyHandler() { return this.finallyHandler || emptyFunction; } getHandler() { return this.handler || emptyFunction; } setErrorHandler(fn) { if (validateResponseHandler(fn)) { this.errorHandler = fn; } return this; } getErrorHandler() { return this.errorHandler || emptyFunction; } abort() { const transport = this.transport; if (transport) { transport.abort(); } } _unshieldResponseText(text) { const shield = "for (;;);"; const shieldLength = shield.length; if (text.length <= shieldLength) { throw new Error("Response too short on async to " + this.getURI()); } return text.substring(shieldLength); } send() { const {uri, method} = this; let { data } = this; const handler = this.getHandler(); const errorHandler = this.getErrorHandler(); const initialHandler = this.getInitialHandler(); const getFinallyHandler = this.getFinallyHandler(); initialHandler(); const request = new XMLHttpRequest(); const self = this; request.open(method, uri, true); request.onload = function () { if (this.status >= 200 && this.status < 400) { let response; try { const safeJson = self._unshieldResponseText(this.responseText); response = eval("(" + safeJson + ")"); } catch (e) { throw new Error("Failed to handle response: " + e.message + "\n" + this.responseText); } (new AsyncResponse).handle(response, self.relative); handler(response); } else { errorHandler(this); } getFinallyHandler(this); } request.setRequestHeader("X-Requested-With", "XMLHttpRequest"); requests++; if (data instanceof FormData) { data.append('__req', requests); } else { data.__req = requests; data = serialize(data); if (method === "POST") { request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); } } Object.keys(this.headers).forEach((name) => { request.setRequestHeader(name, this.headers[name]); }); request.onerror = errorHandler; request.send(data); return request; } };