This commit is contained in:
xing 2023-07-05 23:23:47 +08:00
parent 1d837d4bf8
commit d81b484916
6 changed files with 309 additions and 53 deletions

View File

@ -122,7 +122,7 @@ func CustomVideo(h *Handle, scene ...string) (ok bool) {
logs.Error(err, "get headerVideo fail", mod.HeaderVideo) logs.Error(err, "get headerVideo fail", mod.HeaderVideo)
return return
} }
scripts := []string{ scriptss := []string{
"/wp-includes/js/dist/vendor/wp-polyfill-inert.min.js", "/wp-includes/js/dist/vendor/wp-polyfill-inert.min.js",
"/wp-includes/js/dist/vendor/regenerator-runtime.min.js", "/wp-includes/js/dist/vendor/regenerator-runtime.min.js",
"/wp-includes/js/dist/vendor/wp-polyfill.min.js", "/wp-includes/js/dist/vendor/wp-polyfill.min.js",
@ -132,11 +132,11 @@ func CustomVideo(h *Handle, scene ...string) (ok bool) {
"/wp-includes/js/dist/a11y.min.js", "/wp-includes/js/dist/a11y.min.js",
"/wp-includes/js/wp-custom-header.min.js", "/wp-includes/js/wp-custom-header.min.js",
} }
scripts = slice.Map(scripts, func(t string) string { scriptss = slice.Map(scriptss, func(t string) string {
return fmt.Sprintf(`<script src="%s" id="wp-%s-js"></script> return fmt.Sprintf(`<script src="%s" id="wp-%s-js"></script>
`, t, str.Replaces(t, [][]string{{ `, t, str.Replaces(t, []string{
"/wp-includes/js/dist/vendor/", "/wp-includes/js/dist/", "/wp-includes/js/", ".min.js", ".js", "wp-", "", "/wp-includes/js/dist/vendor/", "/wp-includes/js/dist/", "/wp-includes/js/", ".min.js", ".js", "wp-", "",
}})) }))
}) })
var tr = `<script id="wp-i18n-js-after"> var tr = `<script id="wp-i18n-js-after">
@ -154,10 +154,10 @@ wp.i18n.setLocaleData( { 'text direction\u0004ltr': [ 'ltr' ] } );
c := []Components[string]{ c := []Components[string]{
NewComponent("wp-a11y-js-translations", tr, true, 10.0065, nil), NewComponent("wp-a11y-js-translations", tr, true, 10.0065, nil),
NewComponent("VideoSetting", hs, true, 10.0064, nil), NewComponent("VideoSetting", hs, true, 10.0064, nil),
NewComponent("header-script", scripts[len(scripts)-1], true, 10.0063, nil), NewComponent("header-script", scriptss[len(scriptss)-1], true, 10.0063, nil),
} }
for _, s := range scene { for _, s := range scene {
h.PushGroupFooterScript(s, "wp-custom-header", 10.0066, scripts[0:len(scripts)-2]...) h.PushGroupFooterScript(s, "wp-custom-header", 10.0066, scriptss[0:len(scriptss)-2]...)
h.PushFooterScript(s, c...) h.PushFooterScript(s, c...)
} }
ok = true ok = true

View File

@ -679,10 +679,10 @@ func __blockSelectors(m map[string]any) {
selector, ok := maps.GetStrAnyVal[string](m, "supports.__experimentalSelector") selector, ok := maps.GetStrAnyVal[string](m, "supports.__experimentalSelector")
if !ok { if !ok {
vv, _ := maps.GetStrAnyVal[string](m, "name") vv, _ := maps.GetStrAnyVal[string](m, "name")
selector = str.Join(".wp-block-", str.Replaces(vv, [][]string{ selector = str.Join(".wp-block-", str.Replaces(vv,
{"core/", ""}, []string{"core/", ""},
{"/", "-"}, []string{"/", "-"},
})) ))
} }
var features = map[string]string{} var features = map[string]string{}
maps.SetStrAnyVal(m, "supports.selector", selector) maps.SetStrAnyVal(m, "supports.selector", selector)

View File

@ -11,7 +11,9 @@ import (
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings" str "github.com/fthvgb1/wp-go/helper/strings"
"github.com/fthvgb1/wp-go/safety" "github.com/fthvgb1/wp-go/safety"
"html"
"math" "math"
"regexp"
"strconv" "strconv"
"strings" "strings"
) )
@ -188,6 +190,7 @@ const RootBlockSelector = "body"
type node struct { type node struct {
Path []string Path []string
Selector string Selector string
Name string
} }
var __validElementPseudoSelectors = map[string][]string{ var __validElementPseudoSelectors = map[string][]string{
@ -235,9 +238,9 @@ func scopeSelector(scope, selector string) string {
selectors := strings.Split(selector, ",") selectors := strings.Split(selector, ",")
var a []string var a []string
for _, outer := range scopes { for _, outer := range scopes {
outer = strings.Trim(outer, " \t\n\r\x00\x0B") outer = strings.TrimSpace(outer)
for _, inner := range selectors { for _, inner := range selectors {
inner = strings.Trim(inner, " \t\n\r\x00\x0B") inner = strings.TrimSpace(inner)
if outer != "" && inner != "" { if outer != "" && inner != "" {
a = append(a, str.Join(outer, " ", inner)) a = append(a, str.Join(outer, " ", inner))
} else if outer == "" { } else if outer == "" {
@ -341,6 +344,282 @@ type ThemeJson struct {
themeJson map[string]any themeJson map[string]any
} }
var validOrigins = []string{"default", "blocks", "theme", "custom"}
var layoutSelectorReg = regexp.MustCompile(`^[a-zA-Z0-9\-. *+>:()]*$`)
func (j ThemeJson) LayoutStyles(nodes node) string {
//todo current theme supports disable-layout-styles
var blockType map[string]any
var s strings.Builder
if nodes.Name != "" {
v, ok := maps.GetStrAnyVal[map[string]any](j.blocksMetaData, nodes.Name)
if ok {
vv, ok := maps.GetStrAnyVal[map[string]any](v, "supports.__experimentalLayout")
if ok && vv == nil {
return ""
}
blockType = vv
}
}
gap, hasBlockGapSupport := maps.GetStrAnyVal[map[string]any](j.themeJson, "settings.spacing.blockGap")
if gap == nil {
hasBlockGapSupport = false
}
_, ok := maps.GetStrAnyVal[map[string]any](j.themeJson, strings.Join(nodes.Path, "."))
if !ok {
return ""
}
layoutDefinitions, ok := maps.GetStrAnyVal[map[string]any](j.themeJson, "settings.layout.definitions")
if !ok {
return ""
}
blockGapValue := ""
if !hasBlockGapSupport {
if RootBlockSelector == nodes.Selector {
blockGapValue = "0.5em"
}
if blockType != nil {
blockGapValue, _ = maps.GetStrAnyVal[string](blockType, "supports.spacing.blockGap.__experimentalDefault")
}
} else {
//todo getPropertyValue()
}
if blockGapValue != "" {
for key, v := range layoutDefinitions {
definition, ok := v.(map[string]any)
if !ok {
continue
}
if !hasBlockGapSupport && "flex" == key {
continue
}
className := maps.GetStrAnyValWithDefaults(definition, "className", "")
spacingRules := maps.GetStrAnyValWithDefaults(definition, "spacingStyles", []any{})
if className == "" || spacingRules == nil {
continue
}
for _, rule := range spacingRules {
var declarations []declaration
spacingRule, ok := rule.(map[string]any)
if !ok {
continue
}
selector := maps.GetStrAnyValWithDefaults(spacingRule, "selector", "")
if selector == "" || !layoutSelectorReg.MatchString(selector) {
continue
}
rules := maps.GetStrAnyValWithDefaults(spacingRule, "selector", map[string]any(nil))
if rules == nil {
rules := maps.GetStrAnyValWithDefaults(spacingRule, "selector", map[string]string(nil))
if rules == nil {
continue
} else {
}
}
for property, v := range rules {
value, ok := v.(string)
if !ok || value == "" {
value = blockGapValue
}
if isSafeCssDeclaration(property, value) {
declarations = append(declarations, declaration{property, value})
}
}
format := ""
if !hasBlockGapSupport { //stylesNode
format = helper.Or(RootBlockSelector == nodes.Selector, ":where(.%2$s%3$s)", ":where(%1$s.%2$s%3$s)")
} else {
format = helper.Or(RootBlockSelector == nodes.Selector, "%s .%s%s", "%s.%s%s")
}
layoutSelector := fmt.Sprintf(format, nodes.Selector, className, selector)
s.WriteString(toRuleset(layoutSelector, declarations))
}
}
}
return s.String()
}
func isSafeCssDeclaration(name, value string) bool {
css := str.Join(name, ":", value)
css = KsesNoNull(css)
css = strings.TrimSpace(css)
css = str.Replaces(css, []string{"\n", "\r", "\t", ""})
cssArr := strings.Split(css, ";")
s := safeCSSFilterAttr(cssArr)
return "" != html.EscapeString(s)
}
var kses = regexp.MustCompile(`[\x00-\x08\x0B\x0C\x0E-\x1F]`)
var ksesop = regexp.MustCompile(`\\\\+0+`)
func KsesNoNull(s string, op ...bool) string {
s = kses.ReplaceAllString(s, "")
ops := true
if len(op) > 0 {
ops = op[0]
}
if ops {
s = ksesop.ReplaceAllString(s, "")
}
return s
}
var allowedProtocols = []string{
"http", "https", "ftp", "ftps", "mailto", "news", "irc", "irc6", "ircs", "gopher", "nntp",
"feed", "telnet", "mms", "rtsp", "sms", "svn", "tel", "fax", "xmpp", "webcal", "urn",
}
var allowCssAttr = []string{
"background", "background-color", "background-image", "background-position", "background-size", "background-attachment", "background-blend-mode",
"border", "border-radius", "border-width", "border-color", "border-style", "border-right", "border-right-color",
"border-right-style", "border-right-width", "border-bottom", "border-bottom-color", "border-bottom-left-radius",
"border-bottom-right-radius", "border-bottom-style", "border-bottom-width", "border-bottom-right-radius",
"border-bottom-left-radius", "border-left", "border-left-color", "border-left-style", "border-left-width",
"border-top", "border-top-color", "border-top-left-radius", "border-top-right-radius", "border-top-style",
"border-top-width", "border-top-left-radius", "border-top-right-radius", "border-spacing", "border-collapse",
"caption-side", "columns", "column-count", "column-fill", "column-gap", "column-rule", "column-span", "column-width",
"color", "filter", "font", "font-family", "font-size", "font-style", "font-variant", "font-weight",
"letter-spacing", "line-height", "text-align", "text-decoration", "text-indent", "text-transform", "height",
"min-height", "max-height", "width", "min-width", "max-width", "margin", "margin-right", "margin-bottom",
"margin-left", "margin-top", "margin-block-start", "margin-block-end", "margin-inline-start", "margin-inline-end",
"padding", "padding-right", "padding-bottom", "padding-left", "padding-top", "padding-block-start",
"padding-block-end", "padding-inline-start", "padding-inline-end", "flex", "flex-basis", "flex-direction",
"flex-flow", "flex-grow", "flex-shrink", "flex-wrap", "gap", "column-gap", "row-gap", "grid-template-columns",
"grid-auto-columns", "grid-column-start", "grid-column-end", "grid-column-gap", "grid-template-rows",
"grid-auto-rows", "grid-row-start", "grid-row-end", "grid-row-gap", "grid-gap", "justify-content", "justify-items",
"justify-self", "align-content", "align-items", "align-self", "clear", "cursor", "direction", "float",
"list-style-type", "object-fit", "object-position", "overflow", "vertical-align", "position", "top", "right",
"bottom", "left", "z-index", "aspect-ratio", "--*",
}
var allowCssAttrMap = slice.FilterAndToMap(allowCssAttr, func(t string) (string, struct{}, bool) {
return t, struct{}{}, true
})
var __strReg = regexp.MustCompile(`^--[a-zA-Z0-9-_]+$`)
var cssUrlDataTypes = []string{"background", "background-image", "cursor", "list-style", "list-style-image"}
var cssGradientDataTypes = []string{"background", "background-image"}
var __allowCSSReg = regexp.MustCompile(`\b(?:var|calc|min|max|minmax|clamp)(\((?:[^()]|(?!1))*\))`)
var __disallowCSSReg = regexp.MustCompile(`[\\(&=}]|/\*`)
func safeCSSFilterAttr(cssArr []string) string {
var isCustomVar, found, urlAttr, gradientAttr bool
var cssValue string
var ss strings.Builder
for _, s := range cssArr {
parts := strings.SplitN(s, ":", 2)
selector := strings.TrimSpace(parts[0])
if maps.IsExists(allowCssAttrMap, "--*") {
i := __strReg.FindStringIndex(selector)
if len(i) > 0 && i[0] > 0 {
isCustomVar = true
allowCssAttr = append(allowCssAttr, selector)
}
if maps.IsExists(allowCssAttrMap, selector) {
found = true
urlAttr = slice.IsContained(cssUrlDataTypes, selector)
gradientAttr = slice.IsContained(cssGradientDataTypes, selector)
}
if isCustomVar {
cssValue = strings.TrimSpace(parts[1])
urlAttr = cssValue[0:4] == "url("
gradientAttr = strings.Contains(cssValue, "-gradient(")
}
if found {
//todo wtf 🤮
if urlAttr {
}
if gradientAttr {
}
cssTest := __allowCSSReg.ReplaceAllString(s, "")
isAllow := !__disallowCSSReg.MatchString(cssTest)
if isAllow {
ss.WriteString(s)
ss.WriteString(";")
}
}
}
}
return strings.TrimRight(ss.String(), ";")
}
func (j ThemeJson) StyletSheet(types, origins []string, options map[string]string) string {
if origins == nil {
origins = append(validOrigins)
}
styleNodes := getStyleNodes(j)
settingsNodes := getSettingNodes(j.blocksMetaData, j.themeJson)
rootStyleKey, _ := slice.SearchFirst(styleNodes, func(n node) bool {
return n.Selector == RootBlockSelector
})
rootSettingsKey, _ := slice.SearchFirst(settingsNodes, func(n node) bool {
return n.Selector == RootBlockSelector
})
if os, ok := options["scope"]; ok {
for i := range settingsNodes {
settingsNodes[i].Selector = scopeSelector(os, settingsNodes[i].Selector)
}
for i := range styleNodes {
styleNodes[i].Selector = scopeSelector(os, styleNodes[i].Selector)
}
}
if or, ok := options["root_selector"]; ok && or != "" {
if rootSettingsKey > -1 {
settingsNodes[rootSettingsKey].Selector = or
}
if rootStyleKey > -1 && rootStyleKey < len(settingsNodes) {
settingsNodes[rootStyleKey].Selector = or
}
}
stylesSheet := ""
if slice.IsContained(types, "variables") {
stylesSheet = j.getCssVariables(settingsNodes, origins)
}
if slice.IsContained(types, "styles") {
stylesSheet = j.getCssVariables(settingsNodes, origins)
if rootStyleKey > -1 {
//todo getRootLayoutRules
stylesSheet = str.Join(stylesSheet)
} else if slice.IsContained(types, "base-layout-styles") {
rootSelector := RootBlockSelector
columnsSelector := ".wp-block-columns"
scope, ok := options["scope"]
if ok && scope != "" {
rootSelector = scopeSelector(scope, rootSelector)
columnsSelector = scopeSelector(scope, columnsSelector)
}
rs, ok := options["root_selector"]
if ok && rs != "" {
rootSelector = rs
}
baseStylesNodes := []node{
{
Path: []string{"styles"},
Selector: rootSelector,
},
{
Path: []string{"styles", "blocks", "core/columns"},
Selector: columnsSelector,
Name: "core/columns",
},
}
for _, stylesNode := range baseStylesNodes {
stylesSheet = str.Join(j.LayoutStyles(stylesNode))
}
}
}
return stylesSheet
}
var presetsMetadata = []presetMeta{ var presetsMetadata = []presetMeta{
{ {
path: []string{"color", "palette"}, path: []string{"color", "palette"},
@ -641,7 +920,7 @@ func toRuleset(selector string, declarations []declaration) string {
func getStyleNodes(t ThemeJson) []node { func getStyleNodes(t ThemeJson) []node {
var styleNodes = []node{ var styleNodes = []node{
{[]string{"styles"}, "body"}, {[]string{"styles"}, "body", ""},
} }
m := maps.GetStrAnyValWithDefaults[map[string]any](t.themeJson, "styles.elements", nil) m := maps.GetStrAnyValWithDefaults[map[string]any](t.themeJson, "styles.elements", nil)
if len(m) < 1 { if len(m) < 1 {
@ -652,7 +931,7 @@ func getStyleNodes(t ThemeJson) []node {
if !ok { if !ok {
continue continue
} }
styleNodes = append(styleNodes, node{[]string{"styles", "elements", e}, s}) styleNodes = append(styleNodes, node{[]string{"styles", "elements", e}, s, ""})
ss, ok := __validElementPseudoSelectors[e] ss, ok := __validElementPseudoSelectors[e]
if ok { if ok {
for _, sel := range ss { for _, sel := range ss {
@ -679,49 +958,26 @@ func getStyleNodes(t ThemeJson) []node {
return styleNodes return styleNodes
} }
var validOrigins = []string{"default", "theme", "custom"}
func GetStyletSheet(t ThemeJson, types, origins []string, options map[string]string) string { func GetStyletSheet(t ThemeJson, types, origins []string, options map[string]string) string {
styleNodes := getStyleNodes(t)
settingsNodes := getSettingNodes(t.blocksMetaData, t.themeJson)
if types == nil && !wpconfig.HasThemeJson() { if types == nil && !wpconfig.HasThemeJson() {
types = []string{"variables", "presets", "base-layout-styles"} types = []string{"variables", "presets", "base-layout-styles"}
} else if types == nil { } else if types == nil {
types = []string{"variables", "styles", "presets"} types = []string{"variables", "styles", "presets"}
} }
if origins == nil { styleSheet := ""
origins = validOrigins
}
rootStyleKey, _ := slice.SearchFirst(styleNodes, func(n node) bool {
return n.Selector == RootBlockSelector
})
rootSettingsKey, _ := slice.SearchFirst(settingsNodes, func(n node) bool {
return n.Selector == RootBlockSelector
})
if os, ok := options["scope"]; ok {
for i := range settingsNodes {
settingsNodes[i].Selector = scopeSelector(os, settingsNodes[i].Selector)
}
for i := range styleNodes {
styleNodes[i].Selector = scopeSelector(os, styleNodes[i].Selector)
}
}
if or, ok := options["root_selector"]; ok && or != "" {
if rootSettingsKey > -1 {
settingsNodes[rootSettingsKey].Selector = or
}
if rootStyleKey > -1 && rootStyleKey < len(settingsNodes) {
settingsNodes[rootStyleKey].Selector = or
}
}
var s string
if slice.IsContained(types, "variables") { if slice.IsContained(types, "variables") {
s = t.getCssVariables(settingsNodes, origins) origins = []string{"default", "theme", "custom"}
styleSheet = t.StyletSheet([]string{"variables"}, origins, nil)
slice.Delete(&types, slice.IndexOf(types, "variables")) slice.Delete(&types, slice.IndexOf(types, "variables"))
} }
if slice.IsContained(types, "styles") {
s = t.getCssVariables(settingsNodes, origins) if len(types) > 0 {
origins = []string{"default", "theme", "custm"}
if !wpconfig.HasThemeJson() {
origins = []string{"default"}
}
styleSheet = str.Join(styleSheet, t.StyletSheet(types, origins, nil))
} }
return s return styleSheet
} }

View File

@ -44,14 +44,14 @@ func GetStrAnyValWithDefaults[T any](m map[string]any, key string, defaults T) (
} }
// GetStrMapAnyValWithAny 使用"." 分隔层级 // GetStrMapAnyValWithAny 使用"." 分隔层级
func GetStrMapAnyValWithAny(key string, v map[string]any) (r any, o bool) { func GetStrMapAnyValWithAny(v map[string]any, key string) (r any, o bool) {
k := strings.Split(key, ".") k := strings.Split(key, ".")
if len(k) > 1 { if len(k) > 1 {
val, ok := v[k[0]] val, ok := v[k[0]]
if ok { if ok {
vx, ok := val.(map[string]any) vx, ok := val.(map[string]any)
if ok { if ok {
r, o = GetStrMapAnyValWithAny(strings.Join(k[1:], "."), vx) r, o = GetStrMapAnyValWithAny(vx, strings.Join(k[1:], "."))
} }
} }
} else { } else {

View File

@ -113,7 +113,7 @@ func TestGetStrMapAnyValWithAny(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
gotR, gotO := GetStrMapAnyValWithAny(tt.args.key, tt.args.v) gotR, gotO := GetStrMapAnyValWithAny(tt.args.v, tt.args.key)
if !reflect.DeepEqual(gotR, tt.wantR) { if !reflect.DeepEqual(gotR, tt.wantR) {
t.Errorf("GetStrMapAnyValWithAny() gotR = %v, want %v", gotR, tt.wantR) t.Errorf("GetStrMapAnyValWithAny() gotR = %v, want %v", gotR, tt.wantR)
} }

View File

@ -96,8 +96,8 @@ func Replace(s string, replace map[string]string) string {
// Replaces replace string by slice as order // Replaces replace string by slice as order
// //
// [][]string sub []string like {old1,old2,old3,....,newString} all old[x] will be replaced by lasted newString // []string sub []string like {old1,old2,old3,....,newString} all old[x] will be replaced by lasted newString
func Replaces(s string, replace [][]string) string { func Replaces(s string, replace ...[]string) string {
for _, v := range replace { for _, v := range replace {
if len(v) < 1 { if len(v) < 1 {
continue continue