rui_orig/propertySet.go

883 lines
23 KiB
Go

package rui
import (
"math"
"strconv"
"strings"
)
var colorProperties = []PropertyName{
ColorTag,
BackgroundColor,
TextColor,
CaretColor,
BorderColor,
BorderLeftColor,
BorderRightColor,
BorderTopColor,
BorderBottomColor,
OutlineColor,
TextLineColor,
ColorPickerValue,
}
func isPropertyInList(tag PropertyName, list []PropertyName) bool {
for _, prop := range list {
if prop == tag {
return true
}
}
return false
}
var angleProperties = []PropertyName{
From,
}
var boolProperties = []PropertyName{
Disabled,
Focusable,
Inset,
BackfaceVisible,
ReadOnly,
EditWrap,
Spellcheck,
CloseButton,
OutsideClose,
Italic,
SmallCaps,
Strikethrough,
Overline,
Underline,
Expanded,
AvoidBreak,
NotTranslate,
Controls,
Loop,
Muted,
AnimationPaused,
Multiple,
TabCloseButton,
Repeating,
UserSelect,
ColumnSpanAll,
}
var intProperties = []PropertyName{
ZIndex,
TabSize,
HeadHeight,
FootHeight,
RowSpan,
ColumnSpan,
ColumnCount,
Order,
TabIndex,
MaxLength,
}
var floatProperties = map[PropertyName]struct{ min, max float64 }{
Opacity: {min: 0, max: 1},
NumberPickerMax: {min: -math.MaxFloat64, max: math.MaxFloat64},
NumberPickerMin: {min: -math.MaxFloat64, max: math.MaxFloat64},
NumberPickerStep: {min: -math.MaxFloat64, max: math.MaxFloat64},
NumberPickerValue: {min: -math.MaxFloat64, max: math.MaxFloat64},
ProgressBarMax: {min: 0, max: math.MaxFloat64},
ProgressBarValue: {min: 0, max: math.MaxFloat64},
VideoWidth: {min: 0, max: 10000},
VideoHeight: {min: 0, max: 10000},
}
var sizeProperties = map[PropertyName]string{
Width: string(Width),
Height: string(Height),
MinWidth: string(MinWidth),
MinHeight: string(MinHeight),
MaxWidth: string(MaxWidth),
MaxHeight: string(MaxHeight),
Left: string(Left),
Right: string(Right),
Top: string(Top),
Bottom: string(Bottom),
TextSize: "font-size",
TextIndent: string(TextIndent),
LetterSpacing: string(LetterSpacing),
WordSpacing: string(WordSpacing),
LineHeight: string(LineHeight),
TextLineThickness: "text-decoration-thickness",
ListRowGap: "row-gap",
ListColumnGap: "column-gap",
GridRowGap: string(GridRowGap),
GridColumnGap: string(GridColumnGap),
ColumnWidth: string(ColumnWidth),
ColumnGap: string(ColumnGap),
Gap: string(Gap),
Margin: string(Margin),
MarginLeft: string(MarginLeft),
MarginRight: string(MarginRight),
MarginTop: string(MarginTop),
MarginBottom: string(MarginBottom),
Padding: string(Padding),
PaddingLeft: string(PaddingLeft),
PaddingRight: string(PaddingRight),
PaddingTop: string(PaddingTop),
PaddingBottom: string(PaddingBottom),
BorderWidth: string(BorderWidth),
BorderLeftWidth: string(BorderLeftWidth),
BorderRightWidth: string(BorderRightWidth),
BorderTopWidth: string(BorderTopWidth),
BorderBottomWidth: string(BorderBottomWidth),
OutlineWidth: string(OutlineWidth),
OutlineOffset: string(OutlineOffset),
XOffset: string(XOffset),
YOffset: string(YOffset),
BlurRadius: string(BlurRadius),
SpreadRadius: string(SpreadRadius),
Perspective: string(Perspective),
PerspectiveOriginX: string(PerspectiveOriginX),
PerspectiveOriginY: string(PerspectiveOriginY),
OriginX: string(OriginX),
OriginY: string(OriginY),
OriginZ: string(OriginZ),
Radius: string(Radius),
RadiusX: string(RadiusX),
RadiusY: string(RadiusY),
RadiusTopLeft: string(RadiusTopLeft),
RadiusTopLeftX: string(RadiusTopLeftX),
RadiusTopLeftY: string(RadiusTopLeftY),
RadiusTopRight: string(RadiusTopRight),
RadiusTopRightX: string(RadiusTopRightX),
RadiusTopRightY: string(RadiusTopRightY),
RadiusBottomLeft: string(RadiusBottomLeft),
RadiusBottomLeftX: string(RadiusBottomLeftX),
RadiusBottomLeftY: string(RadiusBottomLeftY),
RadiusBottomRight: string(RadiusBottomRight),
RadiusBottomRightX: string(RadiusBottomRightX),
RadiusBottomRightY: string(RadiusBottomRightY),
ItemWidth: string(ItemWidth),
ItemHeight: string(ItemHeight),
CenterX: string(CenterX),
CenterY: string(CenterX),
}
var enumProperties = map[PropertyName]struct {
values []string
cssTag string
cssValues []string
}{
Semantics: {
[]string{"default", "article", "section", "aside", "header", "main", "footer", "navigation", "figure", "figure-caption", "button", "p", "h1", "h2", "h3", "h4", "h5", "h6", "blockquote", "code"},
"",
[]string{"div", "article", "section", "aside", "header", "main", "footer", "nav", "figure", "figcaption", "button", "p", "h1", "h2", "h3", "h4", "h5", "h6", "blockquote", "code"},
},
Visibility: {
[]string{"visible", "invisible", "gone"},
"",
[]string{"visible", "invisible", "gone"},
},
Overflow: {
[]string{"hidden", "visible", "scroll", "auto"},
string(Overflow),
[]string{"hidden", "visible", "scroll", "auto"},
},
TextAlign: {
[]string{"left", "right", "center", "justify"},
string(TextAlign),
[]string{"left", "right", "center", "justify"},
},
TextTransform: {
[]string{"none", "capitalize", "lowercase", "uppercase"},
string(TextTransform),
[]string{"none", "capitalize", "lowercase", "uppercase"},
},
TextWeight: {
[]string{"inherit", "thin", "extra-light", "light", "normal", "medium", "semi-bold", "bold", "extra-bold", "black"},
"font-weight",
[]string{"inherit", "100", "200", "300", "normal", "500", "600", "bold", "800", "900"},
},
WhiteSpace: {
[]string{"normal", "nowrap", "pre", "pre-wrap", "pre-line", "break-spaces"},
string(WhiteSpace),
[]string{"normal", "nowrap", "pre", "pre-wrap", "pre-line", "break-spaces"},
},
WordBreak: {
[]string{"normal", "break-all", "keep-all", "break-word"},
string(WordBreak),
[]string{"normal", "break-all", "keep-all", "break-word"},
},
TextOverflow: {
[]string{"clip", "ellipsis"},
string(TextOverflow),
[]string{"clip", "ellipsis"},
},
TextWrap: {
[]string{"wrap", "nowrap", "balance"},
string(TextWrap),
[]string{"wrap", "nowrap", "balance"},
},
WritingMode: {
[]string{"horizontal-top-to-bottom", "horizontal-bottom-to-top", "vertical-right-to-left", "vertical-left-to-right"},
string(WritingMode),
[]string{"horizontal-tb", "horizontal-bt", "vertical-rl", "vertical-lr"},
},
TextDirection: {
[]string{"system", "left-to-right", "right-to-left"},
"direction",
[]string{"", "ltr", "rtl"},
},
VerticalTextOrientation: {
[]string{"mixed", "upright"},
"text-orientation",
[]string{"mixed", "upright"},
},
TextLineStyle: {
[]string{"inherit", "solid", "dashed", "dotted", "double", "wavy"},
"text-decoration-style",
[]string{"inherit", "solid", "dashed", "dotted", "double", "wavy"},
},
BorderStyle: {
[]string{"none", "solid", "dashed", "dotted", "double"},
string(BorderStyle),
[]string{"none", "solid", "dashed", "dotted", "double"},
},
TopStyle: {
[]string{"none", "solid", "dashed", "dotted", "double"},
"",
[]string{"none", "solid", "dashed", "dotted", "double"},
},
RightStyle: {
[]string{"none", "solid", "dashed", "dotted", "double"},
"",
[]string{"none", "solid", "dashed", "dotted", "double"},
},
BottomStyle: {
[]string{"none", "solid", "dashed", "dotted", "double"},
"",
[]string{"none", "solid", "dashed", "dotted", "double"},
},
LeftStyle: {
[]string{"none", "solid", "dashed", "dotted", "double"},
"",
[]string{"none", "solid", "dashed", "dotted", "double"},
},
OutlineStyle: {
[]string{"none", "solid", "dashed", "dotted", "double"},
string(OutlineStyle),
[]string{"none", "solid", "dashed", "dotted", "double"},
},
Tabs: {
[]string{"top", "bottom", "left", "right", "left-list", "right-list", "hidden"},
"",
[]string{"top", "bottom", "left", "right", "left-list", "right-list", "hidden"},
},
NumberPickerType: {
[]string{"editor", "slider"},
"",
[]string{"editor", "slider"},
},
EditViewType: {
[]string{"text", "password", "email", "emails", "url", "phone", "multiline"},
"",
[]string{"text", "password", "email", "emails", "url", "phone", "multiline"},
},
Orientation: {
[]string{"up-down", "start-to-end", "bottom-up", "end-to-start"},
"",
[]string{"column", "row", "column-reverse", "row-reverse"},
},
ListWrap: {
[]string{"off", "on", "reverse"},
"",
[]string{"nowrap", "wrap", "wrap-reverse"},
},
"list-orientation": {
[]string{"vertical", "horizontal"},
"",
[]string{"vertical", "horizontal"},
},
VerticalAlign: {
[]string{"top", "bottom", "center", "stretch"},
"",
[]string{"top", "bottom", "center", "stretch"},
},
HorizontalAlign: {
[]string{"left", "right", "center", "stretch"},
"",
[]string{"left", "right", "center", "stretch"},
},
ButtonsAlign: {
[]string{"left", "right", "center", "stretch"},
"",
[]string{"left", "right", "center", "stretch"},
},
ArrowAlign: {
[]string{"left", "right", "center"},
"",
[]string{"left", "right", "center"},
},
CellVerticalAlign: {
[]string{"top", "bottom", "center", "stretch"},
"align-items",
[]string{"start", "end", "center", "stretch"},
},
CellHorizontalAlign: {
[]string{"left", "right", "center", "stretch"},
"justify-items",
[]string{"start", "end", "center", "stretch"},
},
CellVerticalSelfAlign: {
[]string{"top", "bottom", "center", "stretch"},
"align-self",
[]string{"start", "end", "center", "stretch"},
},
CellHorizontalSelfAlign: {
[]string{"left", "right", "center", "stretch"},
"justify-self",
[]string{"start", "end", "center", "stretch"},
},
GridAutoFlow: {
[]string{"row", "column", "row-dense", "column-dense"},
string(GridAutoFlow),
[]string{"row", "column", "row dense", "column dense"},
},
ImageVerticalAlign: {
[]string{"top", "bottom", "center"},
"",
[]string{"top", "bottom", "center"},
},
ImageHorizontalAlign: {
[]string{"left", "right", "center"},
"",
[]string{"left", "right", "center"},
},
ItemVerticalAlign: {
[]string{"top", "bottom", "center", "stretch"},
"",
[]string{"start", "end", "center", "stretch"},
},
ItemHorizontalAlign: {
[]string{"left", "right", "center", "stretch"},
"",
[]string{"start", "end", "center", "stretch"},
},
CheckboxVerticalAlign: {
[]string{"top", "bottom", "center"},
"",
[]string{"start", "end", "center"},
},
CheckboxHorizontalAlign: {
[]string{"left", "right", "center"},
"",
[]string{"start", "end", "center"},
},
TableVerticalAlign: {
[]string{"top", "bottom", "center", "stretch", "baseline"},
"vertical-align",
[]string{"top", "bottom", "middle", "baseline", "baseline"},
},
Cursor: {
[]string{"auto", "default", "none", "context-menu", "help", "pointer", "progress", "wait", "cell", "crosshair", "text", "vertical-text", "alias", "copy", "move", "no-drop", "not-allowed", "e-resize", "n-resize", "ne-resize", "nw-resize", "s-resize", "se-resize", "sw-resize", "w-resize", "ew-resize", "ns-resize", "nesw-resize", "nwse-resize", "col-resize", "row-resize", "all-scroll", "zoom-in", "zoom-out", "grab", "grabbing"},
string(Cursor),
[]string{"auto", "default", "none", "context-menu", "help", "pointer", "progress", "wait", "cell", "crosshair", "text", "vertical-text", "alias", "copy", "move", "no-drop", "not-allowed", "e-resize", "n-resize", "ne-resize", "nw-resize", "s-resize", "se-resize", "sw-resize", "w-resize", "ew-resize", "ns-resize", "nesw-resize", "nwse-resize", "col-resize", "row-resize", "all-scroll", "zoom-in", "zoom-out", "grab", "grabbing"},
},
Fit: {
[]string{"none", "contain", "cover", "fill", "scale-down"},
"object-fit",
[]string{"none", "contain", "cover", "fill", "scale-down"},
},
backgroundFit: {
[]string{"none", "contain", "cover"},
"",
[]string{"none", "contain", "cover"},
},
Repeat: {
[]string{"no-repeat", "repeat", "repeat-x", "repeat-y", "round", "space"},
"",
[]string{"no-repeat", "repeat", "repeat-x", "repeat-y", "round", "space"},
},
Attachment: {
[]string{"scroll", "fixed", "local"},
"",
[]string{"scroll", "fixed", "local"},
},
BackgroundClip: {
[]string{"border-box", "padding-box", "content-box"}, // "text"},
"background-clip",
[]string{"border-box", "padding-box", "content-box"}, // "text"},
},
Direction: {
[]string{"to-top", "to-right-top", "to-right", "to-right-bottom", "to-bottom", "to-left-bottom", "to-left", "to-left-top"},
"",
[]string{"to top", "to right top", "to right", "to right bottom", "to bottom", "to left bottom", "to left", "to left top"},
},
AnimationDirection: {
[]string{"normal", "reverse", "alternate", "alternate-reverse"},
"",
[]string{"normal", "reverse", "alternate", "alternate-reverse"},
},
RadialGradientShape: {
[]string{"ellipse", "circle"},
"",
[]string{"ellipse", "circle"},
},
RadialGradientRadius: {
[]string{"closest-side", "closest-corner", "farthest-side", "farthest-corner"},
"",
[]string{"closest-side", "closest-corner", "farthest-side", "farthest-corner"},
},
ItemCheckbox: {
[]string{"none", "single", "multiple"},
"",
[]string{"none", "single", "multiple"},
},
Float: {
[]string{"none", "left", "right"},
"float",
[]string{"none", "left", "right"},
},
Preload: {
[]string{"none", "metadata", "auto"},
"",
[]string{"none", "metadata", "auto"},
},
SelectionMode: {
[]string{"none", "cell", "row"},
"",
[]string{"none", "cell", "row"},
},
Resize: {
[]string{"none", "both", "horizontal", "vertical"},
"resize",
[]string{"none", "both", "horizontal", "vertical"},
},
Arrow: {
[]string{"none", "top", "right", "bottom", "left"},
"",
[]string{"none", "top", "right", "bottom", "left"},
},
MixBlendMode: {
[]string{"normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"},
string(MixBlendMode),
[]string{"normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"},
},
BackgroundBlendMode: {
[]string{"normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"},
string(BackgroundBlendMode),
[]string{"normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"},
},
ColumnFill: {
[]string{"balance", "auto"},
string(ColumnFill),
[]string{"balance", "auto"},
},
}
func notCompatibleType(tag PropertyName, value any) {
ErrorLogF(`"%T" type not compatible with "%s" property`, value, string(tag))
}
func invalidPropertyValue(tag PropertyName, value any) {
ErrorLogF(`Invalid value "%v" of "%s" property`, value, string(tag))
}
func isConstantName(text string) bool {
len := len(text)
if len <= 1 || text[0] != '@' {
return false
}
if len > 2 {
last := len - 1
if (text[1] == '`' && text[last] == '`') ||
(text[1] == '"' && text[last] == '"') ||
(text[1] == '\'' && text[last] == '\'') {
return true
}
}
return !strings.ContainsAny(text, ",;|\"'`+(){}[]<>/\\*&%! \t\n\r")
}
func isInt(value any) (int, bool) {
var n int
switch value := value.(type) {
case int:
n = value
case int8:
n = int(value)
case int16:
n = int(value)
case int32:
n = int(value)
case int64:
n = int(value)
case uint:
n = int(value)
case uint8:
n = int(value)
case uint16:
n = int(value)
case uint32:
n = int(value)
case uint64:
n = int(value)
default:
return 0, false
}
return n, true
}
func setSimpleProperty(properties Properties, tag PropertyName, value any) bool {
if value == nil {
properties.setRaw(tag, nil)
return true
} else if text, ok := value.(string); ok {
text = strings.Trim(text, " \t\n\r")
if text == "" {
properties.setRaw(tag, nil)
return true
}
if isConstantName(text) {
properties.setRaw(tag, text)
return true
}
}
return false
}
func setStringPropertyValue(properties Properties, tag PropertyName, text any) []PropertyName {
if text != "" {
properties.setRaw(tag, text)
} else if properties.getRaw(tag) != nil {
properties.setRaw(tag, nil)
} else {
return []PropertyName{}
}
return []PropertyName{tag}
}
func setArrayPropertyValue[T any](properties Properties, tag PropertyName, value []T) []PropertyName {
if len(value) > 0 {
properties.setRaw(tag, value)
} else if properties.getRaw(tag) != nil {
properties.setRaw(tag, nil)
} else {
return []PropertyName{}
}
return []PropertyName{tag}
}
func setSizeProperty(properties Properties, tag PropertyName, value any) []PropertyName {
if !setSimpleProperty(properties, tag, value) {
var size SizeUnit
switch value := value.(type) {
case string:
var ok bool
if fn := parseSizeFunc(value); fn != nil {
size.Type = SizeFunction
size.Function = fn
} else if size, ok = StringToSizeUnit(value); !ok {
invalidPropertyValue(tag, value)
return nil
}
case SizeUnit:
size = value
case SizeFunc:
size.Type = SizeFunction
size.Function = value
case float32:
size.Type = SizeInPixel
size.Value = float64(value)
case float64:
size.Type = SizeInPixel
size.Value = value
default:
if n, ok := isInt(value); ok {
size.Type = SizeInPixel
size.Value = float64(n)
} else {
notCompatibleType(tag, value)
return nil
}
}
if size.Type == Auto {
properties.setRaw(tag, nil)
} else {
properties.setRaw(tag, size)
}
}
return []PropertyName{tag}
}
func setAngleProperty(properties Properties, tag PropertyName, value any) []PropertyName {
if !setSimpleProperty(properties, tag, value) {
var angle AngleUnit
switch value := value.(type) {
case string:
var ok bool
if angle, ok = StringToAngleUnit(value); !ok {
invalidPropertyValue(tag, value)
return nil
}
case AngleUnit:
angle = value
case float32:
angle = Rad(float64(value))
case float64:
angle = Rad(value)
default:
if n, ok := isInt(value); ok {
angle = Rad(float64(n))
} else {
notCompatibleType(tag, value)
return nil
}
}
properties.setRaw(tag, angle)
}
return []PropertyName{tag}
}
func setColorProperty(properties Properties, tag PropertyName, value any) []PropertyName {
if !setSimpleProperty(properties, tag, value) {
var result Color
switch value := value.(type) {
case string:
var err error
if result, err = stringToColor(value); err != nil {
invalidPropertyValue(tag, value)
return nil
}
case Color:
result = value
default:
if color, ok := isInt(value); ok {
result = Color(color)
} else {
notCompatibleType(tag, value)
return nil
}
}
properties.setRaw(tag, result)
}
return []PropertyName{tag}
}
func setEnumProperty(properties Properties, tag PropertyName, value any, values []string) []PropertyName {
if !setSimpleProperty(properties, tag, value) {
var n int
if text, ok := value.(string); ok {
if n, ok = enumStringToInt(text, values, false); !ok {
invalidPropertyValue(tag, value)
return nil
}
} else if i, ok := isInt(value); ok {
if i < 0 || i >= len(values) {
invalidPropertyValue(tag, value)
return nil
}
n = i
} else {
notCompatibleType(tag, value)
return nil
}
properties.setRaw(tag, n)
}
return []PropertyName{tag}
}
func setBoolProperty(properties Properties, tag PropertyName, value any) []PropertyName {
if !setSimpleProperty(properties, tag, value) {
if text, ok := value.(string); ok {
switch strings.ToLower(strings.Trim(text, " \t")) {
case "true", "yes", "on", "1":
properties.setRaw(tag, true)
case "false", "no", "off", "0":
properties.setRaw(tag, false)
default:
invalidPropertyValue(tag, value)
return nil
}
} else if n, ok := isInt(value); ok {
switch n {
case 1:
properties.setRaw(tag, true)
case 0:
properties.setRaw(tag, false)
default:
invalidPropertyValue(tag, value)
return nil
}
} else if b, ok := value.(bool); ok {
properties.setRaw(tag, b)
} else {
notCompatibleType(tag, value)
return nil
}
}
return []PropertyName{tag}
}
func setIntProperty(properties Properties, tag PropertyName, value any) []PropertyName {
if !setSimpleProperty(properties, tag, value) {
if text, ok := value.(string); ok {
n, err := strconv.Atoi(strings.Trim(text, " \t"))
if err != nil {
invalidPropertyValue(tag, value)
ErrorLog(err.Error())
return nil
}
properties.setRaw(tag, n)
} else if n, ok := isInt(value); ok {
properties.setRaw(tag, n)
} else {
notCompatibleType(tag, value)
return nil
}
}
return []PropertyName{tag}
}
func setFloatProperty(properties Properties, tag PropertyName, value any, min, max float64) []PropertyName {
if !setSimpleProperty(properties, tag, value) {
f := float64(0)
switch value := value.(type) {
case string:
var err error
if f, err = strconv.ParseFloat(strings.Trim(value, " \t"), 64); err != nil {
invalidPropertyValue(tag, value)
ErrorLog(err.Error())
return nil
}
if f < min || f > max {
ErrorLogF(`"%T" out of range of "%s" property`, value, tag)
return nil
}
properties.setRaw(tag, value)
return nil
case float32:
f = float64(value)
case float64:
f = value
default:
if n, ok := isInt(value); ok {
f = float64(n)
} else {
notCompatibleType(tag, value)
return nil
}
}
if f >= min && f <= max {
properties.setRaw(tag, f)
} else {
ErrorLogF(`"%T" out of range of "%s" property`, value, tag)
return nil
}
}
return []PropertyName{tag}
}
func propertiesSet(properties Properties, tag PropertyName, value any) []PropertyName {
if _, ok := sizeProperties[tag]; ok {
return setSizeProperty(properties, tag, value)
}
if valuesData, ok := enumProperties[tag]; ok {
return setEnumProperty(properties, tag, value, valuesData.values)
}
if limits, ok := floatProperties[tag]; ok {
return setFloatProperty(properties, tag, value, limits.min, limits.max)
}
if isPropertyInList(tag, colorProperties) {
return setColorProperty(properties, tag, value)
}
if isPropertyInList(tag, angleProperties) {
return setAngleProperty(properties, tag, value)
}
if isPropertyInList(tag, boolProperties) {
return setBoolProperty(properties, tag, value)
}
if isPropertyInList(tag, intProperties) {
return setIntProperty(properties, tag, value)
}
if text, ok := value.(string); ok {
properties.setRaw(tag, text)
return []PropertyName{tag}
}
notCompatibleType(tag, value)
return nil
}
/*
func (properties *propertyList) Set(tag PropertyName, value any) bool {
tag = properties.normalize(tag)
if value == nil {
properties.remove(properties, tag)
return true
}
return properties.set(properties, tag, value) != nil
}
*/
func (data *dataProperty) Set(tag PropertyName, value any) bool {
if value == nil {
data.Remove(tag)
return true
}
tag = data.normalize(tag)
for _, supported := range data.supportedProperties {
if tag == supported {
return data.set(data, tag, value) != nil
}
}
ErrorLogF(`"%s" property is not supported`, string(tag))
return false
}