Improvements to add support for script type and data-* attributes. Also added Script and Stylesheet classes to simplify Resource declarations.
This commit is contained in:
parent
3a1244bddc
commit
ca0fb05baa
|
@ -98,17 +98,17 @@ namespace Oqtane.Modules
|
|||
var inline = 0;
|
||||
foreach (Resource resource in resources)
|
||||
{
|
||||
if (string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive)
|
||||
if ((string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive) && !resource.Reload)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(resource.Url))
|
||||
{
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module, location = resource.Location.ToString().ToLower() });
|
||||
scripts.Add(new { href = url, type = resource.Type ?? "", bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", location = resource.Location.ToString().ToLower(), dataAttributes = resource.DataAttributes });
|
||||
}
|
||||
else
|
||||
{
|
||||
inline += 1;
|
||||
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
||||
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Type ?? "", resource.Content, resource.Location.ToString().ToLower());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,11 +37,8 @@
|
|||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
// obtained from https://cdnjs.com/libraries
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css",
|
||||
Integrity = "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==",
|
||||
CrossOrigin = "anonymous" },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
|
||||
new Resource { ResourceType = ResourceType.Script, Url = Constants.BootstrapScriptUrl, Integrity = Constants.BootstrapScriptIntegrity, CrossOrigin = "anonymous", Location = ResourceLocation.Body },
|
||||
new Stylesheet("https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css", "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==", "anonymous"),
|
||||
new Stylesheet(ThemePath() + "Theme.css"),
|
||||
new Script(Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous")
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -17,11 +17,9 @@ namespace Oqtane.Themes.OqtaneTheme
|
|||
Resources = new List<Resource>()
|
||||
{
|
||||
// obtained from https://cdnjs.com/libraries
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.3/cyborg/bootstrap.min.css",
|
||||
Integrity = "sha512-M+Wrv9LTvQe81gFD2ZE3xxPTN5V2n1iLCXsldIxXvfs6tP+6VihBCwCMBkkjkQUZVmEHBsowb9Vqsq1et1teEg==",
|
||||
CrossOrigin = "anonymous" },
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" },
|
||||
new Resource { ResourceType = ResourceType.Script, Url = Constants.BootstrapScriptUrl, Integrity = Constants.BootstrapScriptIntegrity, CrossOrigin = "anonymous", Location = ResourceLocation.Body }
|
||||
new Stylesheet("https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.3/cyborg/bootstrap.min.css", "sha512-M+Wrv9LTvQe81gFD2ZE3xxPTN5V2n1iLCXsldIxXvfs6tP+6VihBCwCMBkkjkQUZVmEHBsowb9Vqsq1et1teEg==", "anonymous"),
|
||||
new Stylesheet("~/Theme.css"),
|
||||
new Script(Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -62,17 +62,17 @@ namespace Oqtane.Themes
|
|||
var inline = 0;
|
||||
foreach (Resource resource in resources)
|
||||
{
|
||||
if (string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive)
|
||||
if ((string.IsNullOrEmpty(resource.RenderMode) || resource.RenderMode == RenderModes.Interactive) && !resource.Reload)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(resource.Url))
|
||||
{
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module, location = resource.Location.ToString().ToLower() });
|
||||
scripts.Add(new { href = url, type = resource.Type ?? "", bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", location = resource.Location.ToString().ToLower(), dataAttributes = resource.DataAttributes });
|
||||
}
|
||||
else
|
||||
{
|
||||
inline += 1;
|
||||
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
||||
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Type ?? "", resource.Content, resource.Location.ToString().ToLower());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,12 +117,17 @@ namespace Oqtane.UI
|
|||
}
|
||||
|
||||
public Task IncludeScript(string id, string src, string integrity, string crossorigin, string type, string content, string location)
|
||||
{
|
||||
return IncludeScript(id, src, integrity, crossorigin, type, content, location, null);
|
||||
}
|
||||
|
||||
public Task IncludeScript(string id, string src, string integrity, string crossorigin, string type, string content, string location, Dictionary<string, string> dataAttributes)
|
||||
{
|
||||
try
|
||||
{
|
||||
_jsRuntime.InvokeVoidAsync(
|
||||
"Oqtane.Interop.includeScript",
|
||||
id, src, integrity, crossorigin, type, content, location);
|
||||
id, src, integrity, crossorigin, type, content, location, dataAttributes);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch
|
||||
|
|
|
@ -176,7 +176,7 @@
|
|||
if (!string.IsNullOrEmpty(src))
|
||||
{
|
||||
src = (src.Contains("://")) ? src : PageState.Alias.BaseUrl + src;
|
||||
scripts.Add(new { href = src, bundle = "", integrity = integrity, crossorigin = crossorigin, es6module = (type == "module"), 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
|
||||
{
|
||||
|
@ -186,8 +186,8 @@
|
|||
count += 1;
|
||||
id = $"page{PageState.Page.PageId}-script{count}";
|
||||
}
|
||||
index = script.IndexOf(">") + 1;
|
||||
await interop.IncludeScript(id, "", "", "", "", script.Substring(index, script.IndexOf("</script>") - index), location.ToString().ToLower());
|
||||
var pos = script.IndexOf(">") + 1;
|
||||
await interop.IncludeScript(id, "", "", "", type, script.Substring(pos, script.IndexOf("</script>") - pos), location.ToString().ToLower(), dataAttributes);
|
||||
}
|
||||
index = content.IndexOf("<script", index + 1);
|
||||
}
|
||||
|
|
|
@ -554,11 +554,22 @@
|
|||
if (!resource.Reload)
|
||||
{
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
|
||||
|
||||
var dataAttributes = "";
|
||||
if (resource.DataAttributes != null && resource.DataAttributes.Count > 0)
|
||||
{
|
||||
foreach (var attribute in resource.DataAttributes)
|
||||
{
|
||||
dataAttributes += " " + attribute.Key + "=\"" + attribute.Value + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
return "<script src=\"" + url + "\"" +
|
||||
((resource.ES6Module) ? " type=\"module\"" : "") +
|
||||
((!string.IsNullOrEmpty(resource.Type)) ? " type=\"" + resource.Type + "\"" : "") +
|
||||
((!string.IsNullOrEmpty(resource.Integrity)) ? " integrity=\"" + resource.Integrity + "\"" : "") +
|
||||
((!string.IsNullOrEmpty(resource.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
|
||||
"></script>";
|
||||
((!string.IsNullOrEmpty(dataAttributes)) ? dataAttributes : "") +
|
||||
"></script>";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -120,13 +120,22 @@ Oqtane.Interop = {
|
|||
this.includeLink(links[i].id, links[i].rel, links[i].href, links[i].type, links[i].integrity, links[i].crossorigin, links[i].insertbefore);
|
||||
}
|
||||
},
|
||||
includeScript: function (id, src, integrity, crossorigin, type, content, location) {
|
||||
includeScript: function (id, src, integrity, crossorigin, type, content, location, dataAttributes) {
|
||||
var script;
|
||||
if (src !== "") {
|
||||
script = document.querySelector("script[src=\"" + CSS.escape(src) + "\"]");
|
||||
}
|
||||
else {
|
||||
script = document.getElementById(id);
|
||||
if (id !== "") {
|
||||
script = document.getElementById(id);
|
||||
} else {
|
||||
const scripts = document.querySelectorAll("script:not([src])");
|
||||
for (let i = 0; i < scripts.length; i++) {
|
||||
if (scripts[i].textContent.includes(content)) {
|
||||
script = scripts[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (script !== null) {
|
||||
script.remove();
|
||||
|
@ -152,37 +161,36 @@ Oqtane.Interop = {
|
|||
else {
|
||||
script.innerHTML = content;
|
||||
}
|
||||
script.async = false;
|
||||
this.addScript(script, location)
|
||||
.then(() => {
|
||||
if (src !== "") {
|
||||
console.log(src + ' loaded');
|
||||
}
|
||||
else {
|
||||
console.log(id + ' loaded');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (src !== "") {
|
||||
console.error(src + ' failed');
|
||||
}
|
||||
else {
|
||||
console.error(id + ' failed');
|
||||
}
|
||||
});
|
||||
if (dataAttributes !== null) {
|
||||
for (var key in dataAttributes) {
|
||||
script.setAttribute(key, dataAttributes[key]);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.addScript(script, location);
|
||||
} catch (error) {
|
||||
if (src !== "") {
|
||||
console.error("Failed to load external script: ${src}", error);
|
||||
} else {
|
||||
console.error("Failed to load inline script: ${content}", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
addScript: function (script, location) {
|
||||
if (location === 'head') {
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
if (location === 'body') {
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
script.async = false;
|
||||
script.defer = false;
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
script.onload = res();
|
||||
script.onerror = rej();
|
||||
script.onload = () => resolve();
|
||||
script.onerror = (error) => reject(error);
|
||||
|
||||
if (location === 'head') {
|
||||
document.head.appendChild(script);
|
||||
} else {
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
});
|
||||
},
|
||||
includeScripts: async function (scripts) {
|
||||
|
@ -222,10 +230,10 @@ Oqtane.Interop = {
|
|||
if (scripts[s].crossorigin !== '') {
|
||||
element.crossOrigin = scripts[s].crossorigin;
|
||||
}
|
||||
if (scripts[s].es6module === true) {
|
||||
element.type = "module";
|
||||
if (scripts[s].type !== '') {
|
||||
element.type = scripts[s].type;
|
||||
}
|
||||
if (typeof scripts[s].dataAttributes !== "undefined" && scripts[s].dataAttributes !== null) {
|
||||
if (scripts[s].dataAttributes !== null) {
|
||||
for (var key in scripts[s].dataAttributes) {
|
||||
element.setAttribute(key, scripts[s].dataAttributes[key]);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Models
|
||||
|
@ -27,6 +29,11 @@ namespace Oqtane.Models
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For Scripts this allows type to be specified - not applicable to Stylesheets
|
||||
/// </summary>
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Integrity checks to increase the security of resources accessed. Especially common in CDN resources.
|
||||
/// </summary>
|
||||
|
@ -52,11 +59,6 @@ namespace Oqtane.Models
|
|||
/// </summary>
|
||||
public ResourceLocation Location { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For Scripts this allows type="module" registrations - not applicable to Stylesheets
|
||||
/// </summary>
|
||||
public bool ES6Module { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allows specification of inline script - not applicable to Stylesheets
|
||||
/// </summary>
|
||||
|
@ -72,6 +74,11 @@ namespace Oqtane.Models
|
|||
/// </summary>
|
||||
public bool Reload { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cusotm data-* attributes for scripts - not applicable to Stylesheets
|
||||
/// </summary>
|
||||
public Dictionary<string, string> DataAttributes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The namespace of the component that declared the resource - only used in SiteRouter
|
||||
/// </summary>
|
||||
|
@ -82,14 +89,22 @@ namespace Oqtane.Models
|
|||
var resource = new Resource();
|
||||
resource.ResourceType = ResourceType;
|
||||
resource.Url = Url;
|
||||
resource.Type = Type;
|
||||
resource.Integrity = Integrity;
|
||||
resource.CrossOrigin = CrossOrigin;
|
||||
resource.Bundle = Bundle;
|
||||
resource.Location = Location;
|
||||
resource.ES6Module = ES6Module;
|
||||
resource.Content = Content;
|
||||
resource.RenderMode = RenderMode;
|
||||
resource.Reload = Reload;
|
||||
resource.DataAttributes = new Dictionary<string, string>();
|
||||
if (DataAttributes != null && DataAttributes.Count > 0)
|
||||
{
|
||||
foreach (var kvp in DataAttributes)
|
||||
{
|
||||
resource.DataAttributes.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
resource.Level = level;
|
||||
resource.Namespace = name;
|
||||
return resource;
|
||||
|
@ -97,5 +112,18 @@ namespace Oqtane.Models
|
|||
|
||||
[Obsolete("ResourceDeclaration is deprecated", false)]
|
||||
public ResourceDeclaration Declaration { get; set; }
|
||||
|
||||
[Obsolete("ES6Module is deprecated. Use Type property instead for scripts.", false)]
|
||||
public bool ES6Module
|
||||
{
|
||||
get => (Type == "module");
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
Type = "module";
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
53
Oqtane.Shared/Models/Script.cs
Normal file
53
Oqtane.Shared/Models/Script.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
using System.Collections.Generic;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Script inherits from Resource and offers constructors with parameters specific to Scripts
|
||||
/// </summary>
|
||||
public class Script : Resource
|
||||
{
|
||||
private void SetDefaults()
|
||||
{
|
||||
this.ResourceType = ResourceType.Script;
|
||||
this.Location = ResourceLocation.Body;
|
||||
}
|
||||
|
||||
public Script(string Src)
|
||||
{
|
||||
SetDefaults();
|
||||
this.Url = Src;
|
||||
}
|
||||
|
||||
public Script(string Content, string Type)
|
||||
{
|
||||
SetDefaults();
|
||||
this.Content = Content;
|
||||
this.Type = Type;
|
||||
}
|
||||
|
||||
public Script(string Src, string Integrity, string CrossOrigin)
|
||||
{
|
||||
SetDefaults();
|
||||
this.Url = Src;
|
||||
this.Integrity = Integrity;
|
||||
this.CrossOrigin = CrossOrigin;
|
||||
}
|
||||
|
||||
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();
|
||||
this.Url = Src;
|
||||
this.Integrity = Integrity;
|
||||
this.CrossOrigin = CrossOrigin;
|
||||
this.Type = Type;
|
||||
this.Content = Content;
|
||||
this.Location = Location;
|
||||
this.Bundle = Bundle;
|
||||
this.Reload = Reload;
|
||||
this.DataAttributes = DataAttributes;
|
||||
this.RenderMode = RenderMode;
|
||||
}
|
||||
}
|
||||
}
|
30
Oqtane.Shared/Models/Stylesheet.cs
Normal file
30
Oqtane.Shared/Models/Stylesheet.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Stylesheet inherits from Resource and offers constructors with parameters specific to Stylesheets
|
||||
/// </summary>
|
||||
public class Stylesheet : Resource
|
||||
{
|
||||
private void SetDefaults()
|
||||
{
|
||||
this.ResourceType = ResourceType.Stylesheet;
|
||||
this.Location = ResourceLocation.Head;
|
||||
}
|
||||
|
||||
public Stylesheet(string Href)
|
||||
{
|
||||
SetDefaults();
|
||||
this.Url = Href;
|
||||
}
|
||||
|
||||
public Stylesheet(string Href, string Integrity, string CrossOrigin)
|
||||
{
|
||||
SetDefaults();
|
||||
this.Url = Href;
|
||||
this.Integrity = Integrity;
|
||||
this.CrossOrigin = CrossOrigin;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user