UNPKG

hy-push-state

Version:

Turn static web sites into dynamic web apps

128 lines (93 loc) 3.44 kB
# 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); } }; ```