hotstaq 0.9.21 → 0.9.22
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/build/src/HotStaq.d.ts +32 -2
- package/build/src/HotStaq.d.ts.map +1 -1
- package/build/src/HotStaq.js +103 -73
- package/build/src/HotStaq.js.map +1 -1
- package/build-web/HotStaq.js +2 -2
- package/build-web/HotStaq.min.js +297 -298
- package/package.json +1 -1
- package/src/HotStaq.ts +130 -106
package/package.json
CHANGED
package/src/HotStaq.ts
CHANGED
|
@@ -238,7 +238,7 @@ export class HotStaq implements IHotStaq
|
|
|
238
238
|
/**
|
|
239
239
|
* The current version of HotStaq.
|
|
240
240
|
*/
|
|
241
|
-
static version: string = "0.9.
|
|
241
|
+
static version: string = "0.9.22";
|
|
242
242
|
/**
|
|
243
243
|
* Indicates if this is a web build.
|
|
244
244
|
*/
|
|
@@ -297,9 +297,23 @@ export class HotStaq implements IHotStaq
|
|
|
297
297
|
*/
|
|
298
298
|
static onBeforeNavigate: (path: string) => boolean | Promise<boolean> = null;
|
|
299
299
|
/**
|
|
300
|
-
* Event fired after SPA navigation
|
|
300
|
+
* Event fired after an SPA navigation has rendered the new content,
|
|
301
|
+
* but before HotStaq executes that content's inline <script> tags.
|
|
302
|
+
*
|
|
303
|
+
* Return `false` to take ownership of running those scripts yourself —
|
|
304
|
+
* HotStaq will then skip its own execution pass. Returning `true`,
|
|
305
|
+
* `undefined`, or having no handler at all lets HotStaq execute them
|
|
306
|
+
* (the default), which is what makes inline page scripts run on SPA
|
|
307
|
+
* navigation. See `onAfterUseOutput` for the full-render equivalent.
|
|
308
|
+
*/
|
|
309
|
+
static onAfterNavigate: (path: string) => void | boolean | Promise<void | boolean> = null;
|
|
310
|
+
/**
|
|
311
|
+
* Event fired after `useOutput` has rendered a full document, but before
|
|
312
|
+
* HotStaq executes that document's inline <script> tags. Same return-value
|
|
313
|
+
* gate as `onAfterNavigate`: return `false` to run the scripts yourself;
|
|
314
|
+
* otherwise HotStaq executes them.
|
|
301
315
|
*/
|
|
302
|
-
static
|
|
316
|
+
static onAfterUseOutput: (output: string) => void | boolean | Promise<void | boolean> = null;
|
|
303
317
|
/**
|
|
304
318
|
* Override the framework's default 404 rendering.
|
|
305
319
|
*
|
|
@@ -2848,72 +2862,98 @@ export class HotStaq implements IHotStaq
|
|
|
2848
2862
|
}
|
|
2849
2863
|
|
|
2850
2864
|
/**
|
|
2851
|
-
*
|
|
2852
|
-
*
|
|
2865
|
+
* Execute the <script> elements found within `root`.
|
|
2866
|
+
*
|
|
2867
|
+
* A <script> inserted into the DOM via `innerHTML` is inert — the HTML
|
|
2868
|
+
* spec only runs a script when it is *inserted into the document with
|
|
2869
|
+
* its content already present*. So after we swap new markup in (full
|
|
2870
|
+
* render or SPA partial swap), its inline scripts never ran. For each
|
|
2871
|
+
* not-yet-executed <script>, build a fresh element, copy `type`/`src`
|
|
2872
|
+
* and — for inline scripts — set `.text` BEFORE insertion, then swap it
|
|
2873
|
+
* in so the browser runs it.
|
|
2874
|
+
*
|
|
2875
|
+
* Each executed script is tagged `data-hotstaq-executed` and skipped on
|
|
2876
|
+
* any later pass, so calling this twice (or alongside an app that runs
|
|
2877
|
+
* some scripts itself) never double-fires one.
|
|
2853
2878
|
*/
|
|
2854
|
-
static async
|
|
2879
|
+
protected static async executeInlineScripts (root: Document | HTMLElement): Promise<void>
|
|
2855
2880
|
{
|
|
2856
|
-
|
|
2857
|
-
HotStaq.onOutputReceived (output);
|
|
2881
|
+
let tmpScripts = root.getElementsByTagName ("script");
|
|
2858
2882
|
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
let htmlObj: HTMLHtmlElement = document.getElementsByTagName('html')[0];
|
|
2883
|
+
if (tmpScripts.length < 1)
|
|
2884
|
+
return;
|
|
2862
2885
|
|
|
2863
|
-
|
|
2886
|
+
// Snapshot first — the loop mutates the DOM while iterating.
|
|
2887
|
+
let scripts: HTMLScriptElement[] = [];
|
|
2864
2888
|
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
let tmpScripts = document.getElementsByTagName('script');
|
|
2868
|
-
if (tmpScripts.length > 0) {
|
|
2869
|
-
// push all of the document's script tags into an array
|
|
2870
|
-
// (to prevent dom manipulation while iterating over dom nodes)
|
|
2871
|
-
let scripts: HTMLScriptElement[] = [];
|
|
2872
|
-
for (let i = 0; i < tmpScripts.length; i++) {
|
|
2873
|
-
scripts.push(tmpScripts[i]);
|
|
2874
|
-
}
|
|
2889
|
+
for (let i = 0; i < tmpScripts.length; i++)
|
|
2890
|
+
scripts.push (tmpScripts[i]);
|
|
2875
2891
|
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2892
|
+
for (let i = 0; i < scripts.length; i++)
|
|
2893
|
+
{
|
|
2894
|
+
let orig: HTMLScriptElement = scripts[i];
|
|
2879
2895
|
|
|
2880
|
-
|
|
2881
|
-
|
|
2896
|
+
if (orig.getAttribute ("data-hotstaq-executed") != null)
|
|
2897
|
+
continue;
|
|
2882
2898
|
|
|
2883
|
-
|
|
2884
|
-
|
|
2899
|
+
let src: string = orig.getAttribute ("src");
|
|
2900
|
+
let hasSrc: boolean = (src != null) && (src !== "");
|
|
2901
|
+
let content: string = orig.textContent || "";
|
|
2885
2902
|
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
resolve2 ();
|
|
2891
|
-
};
|
|
2903
|
+
// Nothing to run — just mark it so we don't revisit it.
|
|
2904
|
+
if ((hasSrc === false) && (content === ""))
|
|
2905
|
+
{
|
|
2906
|
+
orig.setAttribute ("data-hotstaq-executed", "1");
|
|
2892
2907
|
|
|
2893
|
-
|
|
2908
|
+
continue;
|
|
2909
|
+
}
|
|
2894
2910
|
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
if (scripts[i].getAttribute ("src") !== "")
|
|
2898
|
-
{
|
|
2899
|
-
s.setAttribute ("src", scripts[i].getAttribute ("src"));
|
|
2900
|
-
hasSrc = true;
|
|
2901
|
-
}
|
|
2902
|
-
}
|
|
2911
|
+
let s: HTMLScriptElement = document.createElement ("script");
|
|
2912
|
+
let type: string = orig.getAttribute ("type");
|
|
2903
2913
|
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
if (scripts[i].getAttribute ("type") !== "")
|
|
2907
|
-
s.setAttribute ("type", scripts[i].getAttribute ("type"));
|
|
2908
|
-
}
|
|
2914
|
+
if ((type != null) && (type !== ""))
|
|
2915
|
+
s.setAttribute ("type", type);
|
|
2909
2916
|
|
|
2910
|
-
|
|
2917
|
+
if (hasSrc === true)
|
|
2918
|
+
s.setAttribute ("src", src);
|
|
2919
|
+
else
|
|
2920
|
+
// MUST be set before insertion, otherwise the script is inert.
|
|
2921
|
+
s.text = content;
|
|
2911
2922
|
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2923
|
+
s.setAttribute ("data-hotstaq-executed", "1");
|
|
2924
|
+
|
|
2925
|
+
await new Promise<void> ((resolve2, reject2) =>
|
|
2926
|
+
{
|
|
2927
|
+
s.onload = () => { resolve2 (); };
|
|
2928
|
+
s.onerror = () => { resolve2 (); };
|
|
2929
|
+
|
|
2930
|
+
if (orig.parentNode != null)
|
|
2931
|
+
orig.parentNode.replaceChild (s, orig);
|
|
2932
|
+
else
|
|
2933
|
+
document.head.appendChild (s);
|
|
2934
|
+
|
|
2935
|
+
// Inline scripts execute synchronously on insertion; only
|
|
2936
|
+
// src scripts need to wait for onload.
|
|
2937
|
+
if (hasSrc === false)
|
|
2938
|
+
resolve2 ();
|
|
2939
|
+
});
|
|
2916
2940
|
}
|
|
2941
|
+
}
|
|
2942
|
+
|
|
2943
|
+
/**
|
|
2944
|
+
* Replace the current HTML page with the output.
|
|
2945
|
+
* This is meant for web browser use only.
|
|
2946
|
+
*/
|
|
2947
|
+
static async useOutput (output: string): Promise<void>
|
|
2948
|
+
{
|
|
2949
|
+
if (HotStaq.onOutputReceived != null)
|
|
2950
|
+
HotStaq.onOutputReceived (output);
|
|
2951
|
+
|
|
2952
|
+
let parser = new DOMParser ();
|
|
2953
|
+
let child = parser.parseFromString (output, "text/html");
|
|
2954
|
+
let htmlObj: HTMLHtmlElement = document.getElementsByTagName('html')[0];
|
|
2955
|
+
|
|
2956
|
+
htmlObj.innerHTML = child.getElementsByTagName('html')[0].innerHTML;
|
|
2917
2957
|
|
|
2918
2958
|
if (HotStaq.dispatchReadyEvents === true)
|
|
2919
2959
|
{
|
|
@@ -2923,6 +2963,27 @@ export class HotStaq implements IHotStaq
|
|
|
2923
2963
|
|
|
2924
2964
|
if (HotStaq.onReadyEvent != null)
|
|
2925
2965
|
HotStaq.onReadyEvent (output);
|
|
2966
|
+
|
|
2967
|
+
// Run the rendered document's inline scripts last — and let the app
|
|
2968
|
+
// gate it. onAfterUseOutput returning false means the app will run
|
|
2969
|
+
// them itself, so HotStaq stands down. Otherwise HotStaq executes
|
|
2970
|
+
// them (the default), which is what makes inline scripts in a
|
|
2971
|
+
// useOutput-rendered page actually run.
|
|
2972
|
+
let runScripts: boolean = true;
|
|
2973
|
+
|
|
2974
|
+
if (HotStaq.onAfterUseOutput != null)
|
|
2975
|
+
{
|
|
2976
|
+
let result: any = HotStaq.onAfterUseOutput (output);
|
|
2977
|
+
|
|
2978
|
+
if (result instanceof Promise)
|
|
2979
|
+
result = await result;
|
|
2980
|
+
|
|
2981
|
+
if (result === false)
|
|
2982
|
+
runScripts = false;
|
|
2983
|
+
}
|
|
2984
|
+
|
|
2985
|
+
if (runScripts === true)
|
|
2986
|
+
await HotStaq.executeInlineScripts (document);
|
|
2926
2987
|
}
|
|
2927
2988
|
|
|
2928
2989
|
/**
|
|
@@ -3091,55 +3152,6 @@ export class HotStaq implements IHotStaq
|
|
|
3091
3152
|
// Replace only the target content.
|
|
3092
3153
|
targetEl.innerHTML = contentHTML;
|
|
3093
3154
|
|
|
3094
|
-
// Execute any script tags that were in the new content.
|
|
3095
|
-
let tmpScripts = targetEl.getElementsByTagName ('script');
|
|
3096
|
-
|
|
3097
|
-
if (tmpScripts.length > 0)
|
|
3098
|
-
{
|
|
3099
|
-
let scripts: HTMLScriptElement[] = [];
|
|
3100
|
-
|
|
3101
|
-
for (let i = 0; i < tmpScripts.length; i++)
|
|
3102
|
-
scripts.push (tmpScripts[i]);
|
|
3103
|
-
|
|
3104
|
-
for (let i = 0; i < scripts.length; i++)
|
|
3105
|
-
{
|
|
3106
|
-
let s: HTMLScriptElement = document.createElement ('script');
|
|
3107
|
-
|
|
3108
|
-
scripts[i].parentNode.appendChild (s);
|
|
3109
|
-
scripts[i].parentNode.removeChild (scripts[i]);
|
|
3110
|
-
|
|
3111
|
-
await new Promise<void> ((resolve2, reject2) =>
|
|
3112
|
-
{
|
|
3113
|
-
s.onload = () =>
|
|
3114
|
-
{
|
|
3115
|
-
resolve2 ();
|
|
3116
|
-
};
|
|
3117
|
-
|
|
3118
|
-
let hasSrc: boolean = false;
|
|
3119
|
-
|
|
3120
|
-
if (scripts[i].getAttribute ("src") != null)
|
|
3121
|
-
{
|
|
3122
|
-
if (scripts[i].getAttribute ("src") !== "")
|
|
3123
|
-
{
|
|
3124
|
-
s.setAttribute ("src", scripts[i].getAttribute ("src"));
|
|
3125
|
-
hasSrc = true;
|
|
3126
|
-
}
|
|
3127
|
-
}
|
|
3128
|
-
|
|
3129
|
-
if (scripts[i].getAttribute ("type") != null)
|
|
3130
|
-
{
|
|
3131
|
-
if (scripts[i].getAttribute ("type") !== "")
|
|
3132
|
-
s.setAttribute ("type", scripts[i].getAttribute ("type"));
|
|
3133
|
-
}
|
|
3134
|
-
|
|
3135
|
-
s.innerHTML = scripts[i].innerHTML;
|
|
3136
|
-
|
|
3137
|
-
if (hasSrc === false)
|
|
3138
|
-
resolve2 ();
|
|
3139
|
-
});
|
|
3140
|
-
}
|
|
3141
|
-
}
|
|
3142
|
-
|
|
3143
3155
|
if (HotStaq.dispatchReadyEvents === true)
|
|
3144
3156
|
{
|
|
3145
3157
|
document.dispatchEvent (new Event ("DOMContentLoaded"));
|
|
@@ -3149,14 +3161,26 @@ export class HotStaq implements IHotStaq
|
|
|
3149
3161
|
if (HotStaq.onReadyEvent != null)
|
|
3150
3162
|
HotStaq.onReadyEvent (output);
|
|
3151
3163
|
|
|
3152
|
-
// Fire onAfterNavigate hook
|
|
3164
|
+
// Fire onAfterNavigate hook, then execute the new content's inline
|
|
3165
|
+
// scripts — unless the hook returned false, in which case the app is
|
|
3166
|
+
// running them itself and HotStaq stands down. Running them after the
|
|
3167
|
+
// hook lets the app prep the DOM first; running them at all is the
|
|
3168
|
+
// fix for inline page scripts silently failing on SPA navigation.
|
|
3169
|
+
let runScripts: boolean = true;
|
|
3170
|
+
|
|
3153
3171
|
if (HotStaq.onAfterNavigate != null)
|
|
3154
3172
|
{
|
|
3155
|
-
let result = HotStaq.onAfterNavigate (path);
|
|
3173
|
+
let result: any = HotStaq.onAfterNavigate (path);
|
|
3156
3174
|
|
|
3157
3175
|
if (result instanceof Promise)
|
|
3158
|
-
await result;
|
|
3176
|
+
result = await result;
|
|
3177
|
+
|
|
3178
|
+
if (result === false)
|
|
3179
|
+
runScripts = false;
|
|
3159
3180
|
}
|
|
3181
|
+
|
|
3182
|
+
if (runScripts === true)
|
|
3183
|
+
await HotStaq.executeInlineScripts (targetEl);
|
|
3160
3184
|
}
|
|
3161
3185
|
|
|
3162
3186
|
/**
|