script reload improvements
This commit is contained in:
parent
4630ee6e93
commit
0204ff8dd5
|
@ -70,7 +70,7 @@
|
||||||
if (!script.Contains("><") && !script.Contains("data-reload"))
|
if (!script.Contains("><") && !script.Contains("data-reload"))
|
||||||
{
|
{
|
||||||
// add data-reload attribute to inline script
|
// add data-reload attribute to inline script
|
||||||
headcontent = headcontent.Replace(script, script.Replace("<script", "<script data-reload=\"true\""));
|
headcontent = headcontent.Replace(script, script.Replace("<script", "<script data-reload=\"always\""));
|
||||||
}
|
}
|
||||||
index = headcontent.IndexOf("<script", index + 1);
|
index = headcontent.IndexOf("<script", index + 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,8 +190,6 @@
|
||||||
scripts.Add(new { href = src, type = type, bundle = "", integrity = integrity, crossorigin = crossorigin, location = location.ToString().ToLower(), dataAttributes = dataAttributes });
|
scripts.Add(new { href = src, type = type, bundle = "", integrity = integrity, crossorigin = crossorigin, location = location.ToString().ToLower(), dataAttributes = dataAttributes });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
if (dataAttributes == null || !dataAttributes.ContainsKey("data-reload") || dataAttributes["data-reload"] != "false")
|
|
||||||
{
|
{
|
||||||
if (id == "")
|
if (id == "")
|
||||||
{
|
{
|
||||||
|
@ -201,7 +199,6 @@
|
||||||
var pos = script.IndexOf(">") + 1;
|
var pos = script.IndexOf(">") + 1;
|
||||||
await interop.IncludeScript(id, "", "", "", type, script.Substring(pos, script.IndexOf("</script>") - pos), location.ToString().ToLower(), dataAttributes);
|
await interop.IncludeScript(id, "", "", "", type, script.Substring(pos, script.IndexOf("</script>") - pos), location.ToString().ToLower(), dataAttributes);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
index = content.IndexOf("<script", index + 1);
|
index = content.IndexOf("<script", index + 1);
|
||||||
}
|
}
|
||||||
if (scripts.Any())
|
if (scripts.Any())
|
||||||
|
|
|
@ -514,7 +514,7 @@
|
||||||
private void AddScript(Resource resource, Alias alias)
|
private void AddScript(Resource resource, Alias alias)
|
||||||
{
|
{
|
||||||
var script = CreateScript(resource, alias);
|
var script = CreateScript(resource, alias);
|
||||||
if (resource.Location == Shared.ResourceLocation.Head && !resource.Reload)
|
if (resource.Location == Shared.ResourceLocation.Head && resource.LoadBehavior != ResourceLoadBehavior.BlazorPageScript)
|
||||||
{
|
{
|
||||||
if (!_headResources.Contains(script))
|
if (!_headResources.Contains(script))
|
||||||
{
|
{
|
||||||
|
@ -532,11 +532,27 @@
|
||||||
|
|
||||||
private string CreateScript(Resource resource, Alias alias)
|
private string CreateScript(Resource resource, Alias alias)
|
||||||
{
|
{
|
||||||
if (!resource.Reload)
|
if (resource.LoadBehavior == ResourceLoadBehavior.BlazorPageScript)
|
||||||
|
{
|
||||||
|
return "<page-script src=\"" + resource.Url + "\"></page-script>";
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
|
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
|
||||||
|
|
||||||
var dataAttributes = "";
|
var dataAttributes = "";
|
||||||
|
if (!resource.DataAttributes.ContainsKey("data-reload"))
|
||||||
|
{
|
||||||
|
switch (resource.LoadBehavior)
|
||||||
|
{
|
||||||
|
case ResourceLoadBehavior.Once:
|
||||||
|
dataAttributes += " data-reload=\"once\"";
|
||||||
|
break;
|
||||||
|
case ResourceLoadBehavior.Always:
|
||||||
|
dataAttributes += " data-reload=\"always\"";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (resource.DataAttributes != null && resource.DataAttributes.Count > 0)
|
if (resource.DataAttributes != null && resource.DataAttributes.Count > 0)
|
||||||
{
|
{
|
||||||
foreach (var attribute in resource.DataAttributes)
|
foreach (var attribute in resource.DataAttributes)
|
||||||
|
@ -552,10 +568,6 @@
|
||||||
((!string.IsNullOrEmpty(dataAttributes)) ? dataAttributes : "") +
|
((!string.IsNullOrEmpty(dataAttributes)) ? dataAttributes : "") +
|
||||||
"></script>";
|
"></script>";
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
return "<page-script src=\"" + resource.Url + "\"></page-script>";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetLocalizationCookie(string cookieValue)
|
private void SetLocalizationCookie(string cookieValue)
|
||||||
|
|
|
@ -1,67 +1,74 @@
|
||||||
const scriptInfoBySrc = new Map();
|
const scriptKeys = new Set();
|
||||||
|
|
||||||
|
export function onUpdate() {
|
||||||
|
// determine if this is an enhanced navigation
|
||||||
|
let enhancedNavigation = scriptKeys.size !== 0;
|
||||||
|
|
||||||
|
// iterate over all script elements in document
|
||||||
|
const scripts = document.getElementsByTagName('script');
|
||||||
|
for (const script of Array.from(scripts)) {
|
||||||
|
// only process scripts that include a data-reload attribute
|
||||||
|
if (script.hasAttribute('data-reload')) {
|
||||||
|
let key = getKey(script);
|
||||||
|
|
||||||
|
if (enhancedNavigation) {
|
||||||
|
// reload the script if data-reload is always or if the script has not been loaded previously and data-reload is once
|
||||||
|
let dataReload = script.getAttribute('data-reload');
|
||||||
|
if (dataReload === 'always' || (!scriptKeys.has(key) && dataReload == 'once')) {
|
||||||
|
reloadScript(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the script key
|
||||||
|
if (!scriptKeys.has(key)) {
|
||||||
|
scriptKeys.add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getKey(script) {
|
function getKey(script) {
|
||||||
if (script.hasAttribute("src") && script.src !== "") {
|
if (script.src) {
|
||||||
return script.src;
|
return script.src;
|
||||||
|
} else if (script.id) {
|
||||||
|
return script.id;
|
||||||
} else {
|
} else {
|
||||||
return script.innerHTML;
|
return script.innerHTML;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onUpdate() {
|
function reloadScript(script) {
|
||||||
let timestamp = Date.now();
|
try {
|
||||||
let enhancedNavigation = scriptInfoBySrc.size !== 0;
|
if (isValid(script)) {
|
||||||
|
replaceScript(script);
|
||||||
// iterate over all script elements in page
|
|
||||||
const scripts = document.getElementsByTagName("script");
|
|
||||||
for (const script of Array.from(scripts)) {
|
|
||||||
let key = getKey(script);
|
|
||||||
let scriptInfo = scriptInfoBySrc.get(key);
|
|
||||||
if (!scriptInfo) {
|
|
||||||
// new script added
|
|
||||||
scriptInfo = { timestamp: timestamp };
|
|
||||||
scriptInfoBySrc.set(key, scriptInfo);
|
|
||||||
if (enhancedNavigation) {
|
|
||||||
reloadScript(script);
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (script.src) {
|
||||||
|
console.error(`Script Reload failed to load external script: ${script.src}`, error);
|
||||||
} else {
|
} else {
|
||||||
// existing script
|
console.error(`Script Reload failed to load inline script: ${script.innerHTML}`, error);
|
||||||
scriptInfo.timestamp = timestamp;
|
|
||||||
if (script.hasAttribute("data-reload") && script.getAttribute("data-reload") === "true") {
|
|
||||||
reloadScript(script);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove scripts that are no longer referenced
|
|
||||||
for (const [key, scriptInfo] of scriptInfoBySrc) {
|
|
||||||
if (scriptInfo.timestamp !== timestamp) {
|
|
||||||
scriptInfoBySrc.delete(key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadScript(script) {
|
function isValid(script) {
|
||||||
try {
|
if (script.innerHTML.includes('document.write(')) {
|
||||||
replaceScript(script);
|
console.log(`Script using document.write() not supported by Script Reload: ${script.innerHTML}`);
|
||||||
} catch (error) {
|
return false;
|
||||||
if (script.hasAttribute("src") && script.src !== "") {
|
|
||||||
console.error("Failed to load external script: ${script.src}", error);
|
|
||||||
} else {
|
|
||||||
console.error("Failed to load inline script: ${script.innerHtml}", error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceScript(script) {
|
function replaceScript(script) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var newScript = document.createElement("script");
|
var newScript = document.createElement('script');
|
||||||
|
|
||||||
// replicate attributes and content
|
// replicate attributes and content
|
||||||
for (let i = 0; i < script.attributes.length; i++) {
|
for (let i = 0; i < script.attributes.length; i++) {
|
||||||
newScript.setAttribute(script.attributes[i].name, script.attributes[i].value);
|
newScript.setAttribute(script.attributes[i].name, script.attributes[i].value);
|
||||||
}
|
}
|
||||||
newScript.innerHTML = script.innerHTML;
|
newScript.innerHTML = script.innerHTML;
|
||||||
|
newScript.removeAttribute('data-reload');
|
||||||
|
|
||||||
// dynamically injected scripts cannot be async or deferred
|
// dynamically injected scripts cannot be async or deferred
|
||||||
newScript.async = false;
|
newScript.async = false;
|
||||||
|
@ -70,11 +77,10 @@ function replaceScript(script) {
|
||||||
newScript.onload = () => resolve();
|
newScript.onload = () => resolve();
|
||||||
newScript.onerror = (error) => reject(error);
|
newScript.onerror = (error) => reject(error);
|
||||||
|
|
||||||
// remove existing script
|
// remove existing script element
|
||||||
script.remove();
|
script.remove();
|
||||||
|
|
||||||
// replace with new script to force reload in Blazor
|
// replace with new script element to force reload in Blazor
|
||||||
document.head.appendChild(newScript);
|
document.head.appendChild(newScript);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
Oqtane.Shared/Enums/ResourceLoadBehavior.cs
Normal file
10
Oqtane.Shared/Enums/ResourceLoadBehavior.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Oqtane.Shared
|
||||||
|
{
|
||||||
|
public enum ResourceLoadBehavior
|
||||||
|
{
|
||||||
|
Once,
|
||||||
|
Always,
|
||||||
|
Never,
|
||||||
|
BlazorPageScript
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
|
||||||
namespace Oqtane.Models
|
namespace Oqtane.Models
|
||||||
|
@ -13,7 +12,7 @@ namespace Oqtane.Models
|
||||||
private string _url;
|
private string _url;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A <see cref="ResourceType"/> so the Interop can properly create `script` or `link` tags
|
/// A <see cref="ResourceType"/> to define the type of resource ie. Script or Stylesheet
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ResourceType ResourceType { get; set; }
|
public ResourceType ResourceType { get; set; }
|
||||||
|
|
||||||
|
@ -45,7 +44,7 @@ namespace Oqtane.Models
|
||||||
public string CrossOrigin { get; set; }
|
public string CrossOrigin { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// For Scripts a Bundle can be used to identify dependencies and ordering in the script loading process
|
/// For Scripts a Bundle can be used to identify dependencies and ordering in the script loading process (for Interactive rendering only)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Bundle { get; set; }
|
public string Bundle { get; set; }
|
||||||
|
|
||||||
|
@ -60,7 +59,7 @@ namespace Oqtane.Models
|
||||||
public ResourceLocation Location { get; set; }
|
public ResourceLocation Location { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allows specification of inline script - not applicable to Stylesheets
|
/// For Scripts this allows for the specification of inline script - not applicable to Stylesheets
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Content { get; set; }
|
public string Content { get; set; }
|
||||||
|
|
||||||
|
@ -70,9 +69,9 @@ namespace Oqtane.Models
|
||||||
public string RenderMode { get; set; }
|
public string RenderMode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that a script should be reloaded on every page transition - not applicable to Stylesheets
|
/// Specifies how a script should be loaded in Static rendering - not applicable to Stylesheets
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Reload { get; set; }
|
public ResourceLoadBehavior LoadBehavior { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cusotm data-* attributes for scripts - not applicable to Stylesheets
|
/// Cusotm data-* attributes for scripts - not applicable to Stylesheets
|
||||||
|
@ -96,7 +95,7 @@ namespace Oqtane.Models
|
||||||
resource.Location = Location;
|
resource.Location = Location;
|
||||||
resource.Content = Content;
|
resource.Content = Content;
|
||||||
resource.RenderMode = RenderMode;
|
resource.RenderMode = RenderMode;
|
||||||
resource.Reload = Reload;
|
resource.LoadBehavior = LoadBehavior;
|
||||||
resource.DataAttributes = new Dictionary<string, string>();
|
resource.DataAttributes = new Dictionary<string, string>();
|
||||||
if (DataAttributes != null && DataAttributes.Count > 0)
|
if (DataAttributes != null && DataAttributes.Count > 0)
|
||||||
{
|
{
|
||||||
|
@ -125,5 +124,18 @@ namespace Oqtane.Models
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("Reload is deprecated. Use LoadBehavior property instead for scripts.", false)]
|
||||||
|
public bool Reload
|
||||||
|
{
|
||||||
|
get => (LoadBehavior == ResourceLoadBehavior.BlazorPageScript);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
LoadBehavior = ResourceLoadBehavior.BlazorPageScript;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
@ -27,6 +28,13 @@ namespace Oqtane.Models
|
||||||
this.Type = Type;
|
this.Type = Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Script(string Content, ResourceLoadBehavior LoadBehavior)
|
||||||
|
{
|
||||||
|
SetDefaults();
|
||||||
|
this.Content = Content;
|
||||||
|
this.LoadBehavior = LoadBehavior;
|
||||||
|
}
|
||||||
|
|
||||||
public Script(string Src, string Integrity, string CrossOrigin)
|
public Script(string Src, string Integrity, string CrossOrigin)
|
||||||
{
|
{
|
||||||
SetDefaults();
|
SetDefaults();
|
||||||
|
@ -35,6 +43,22 @@ namespace Oqtane.Models
|
||||||
this.CrossOrigin = CrossOrigin;
|
this.CrossOrigin = CrossOrigin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Script(string Src, string Integrity, string CrossOrigin, string Type, string Content, ResourceLocation Location, string Bundle, ResourceLoadBehavior LoadBehavior, Dictionary<string, string> DataAttributes, string RenderMode)
|
||||||
|
{
|
||||||
|
SetDefaults();
|
||||||
|
this.Url = Src;
|
||||||
|
this.Integrity = Integrity;
|
||||||
|
this.CrossOrigin = CrossOrigin;
|
||||||
|
this.Type = Type;
|
||||||
|
this.Content = Content;
|
||||||
|
this.Location = Location;
|
||||||
|
this.Bundle = Bundle;
|
||||||
|
this.LoadBehavior = LoadBehavior;
|
||||||
|
this.DataAttributes = DataAttributes;
|
||||||
|
this.RenderMode = RenderMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("This constructor is deprecated. Use constructor with LoadBehavior parameter instead.", false)]
|
||||||
public Script(string Src, string Integrity, string CrossOrigin, string Type, string Content, ResourceLocation Location, string Bundle, bool Reload, Dictionary<string, string> DataAttributes, string RenderMode)
|
public Script(string Src, string Integrity, string CrossOrigin, string Type, string Content, ResourceLocation Location, string Bundle, bool Reload, Dictionary<string, string> DataAttributes, string RenderMode)
|
||||||
{
|
{
|
||||||
SetDefaults();
|
SetDefaults();
|
||||||
|
@ -45,9 +69,10 @@ namespace Oqtane.Models
|
||||||
this.Content = Content;
|
this.Content = Content;
|
||||||
this.Location = Location;
|
this.Location = Location;
|
||||||
this.Bundle = Bundle;
|
this.Bundle = Bundle;
|
||||||
this.Reload = Reload;
|
this.LoadBehavior = (Reload) ? ResourceLoadBehavior.BlazorPageScript : ResourceLoadBehavior.Once;
|
||||||
this.DataAttributes = DataAttributes;
|
this.DataAttributes = DataAttributes;
|
||||||
this.RenderMode = RenderMode;
|
this.RenderMode = RenderMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user