hy-push-state
Version:
Turn static web sites into dynamic web apps
128 lines (93 loc) • 3.44 kB
Markdown
# src / mixin / script-hack.js
Copyright (c) 2018 Florian Klampfer <https://qwtel.com/>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
```js
import { Observable, from, of } from "rxjs/_esm5";
import { catchError, concatMap, tap } from "rxjs/_esm5/operators";
```
### Experimental script feature
TODO
```js
export const scriptMixin = C =>
class extends C {
```
This function removes all script tags (as query'ed by `scriptSelector`) from the response.
```js
tempRemoveScriptTags(replaceEls) {
const scripts = [];
replaceEls.forEach(docfrag =>
Array.from(docfrag.querySelectorAll(this.scriptSelector)).forEach(script => {
const pair = [script, script.previousSibling];
script.parentNode.removeChild(script);
scripts.push(pair);
})
);
return scripts;
}
```
Attempts to (synchronously) insert a `script` tag into the DOM, *before* a given `ref` element.
```js
insertScript([script, ref]) {
```
Temporarily overwrite `document.wirte` to simulate its behavior during the initial load.
This only works because scripts are inserted one-at-a-time (via `concatMap`).
```js
const originalWrite = document.write;
document.write = (...args) => {
const temp = document.createElement("div");
temp.innerHTML = args.join();
Array.from(temp.childNodes).forEach(node => {
ref.parentNode.insertBefore(node, ref.nextSibling);
});
};
```
If the script tag needs to fetch its source code, we insert it into the DOM,
but we return an observable that only completes once the script has fired its `load` event.
```js
return script.src !== ""
? Observable.create(observer => {
script.addEventListener("load", x => {
document.write = originalWrite;
observer.complete(x);
});
script.addEventListener("error", x => {
document.write = originalWrite;
observer.error(x);
});
ref.parentNode.insertBefore(script, ref.nextSibling);
})
: // Otherwise we insert it into the DOM and reset the `document.write` function.
of({}).pipe(
tap(() => {
ref.parentNode.insertBefore(script, ref.nextSibling);
document.write = originalWrite;
})
);
}
```
Takes a list of `script`--`ref` pairs, and inserts them into the DOM one-by-one.
```js
reinsertScriptTags(context) {
if (!this.scriptSelector) return of(context);
const { scripts } = context;
return from(scripts)
.pipe(
concatMap(this.insertScript.bind(this)),
catchError(error => {
throw Object.assign(context, { error });
})
)
.toPromise()
.then(() => context);
}
};
```