Esempio n. 1
0
        public Task <FluidValue> ProcessAsync(FluidValue input, FilterArguments arguments, TemplateContext ctx)
        {
            var url = input.ToStringValue();

            if (!url.Contains("?"))
            {
                url += "?";
            }

            var width  = arguments["width"].Or(arguments.At(0));
            var height = arguments["height"].Or(arguments.At(1));
            var mode   = arguments["mode"];

            if (!width.IsNil())
            {
                url += "&width=" + width.ToStringValue();
            }

            if (!height.IsNil())
            {
                url += "&height=" + height.ToStringValue();
            }

            if (!mode.IsNil())
            {
                url += "&rmode=" + mode.ToStringValue();
            }

            return(Task.FromResult <FluidValue>(new StringValue(url)));
        }
Esempio n. 2
0
        public ValueTask <FluidValue> ProcessAsync(FluidValue input, FilterArguments arguments, TemplateContext ctx)
        {
            var url = input.ToStringValue();

            var queryStringParams = new Dictionary <string, string>();

            var width  = arguments["width"].Or(arguments.At(0));
            var height = arguments["height"].Or(arguments.At(1));
            var mode   = arguments["mode"].Or(arguments.At(2));

            if (!width.IsNil())
            {
                queryStringParams.Add("width", width.ToStringValue());
            }

            if (!height.IsNil())
            {
                queryStringParams.Add("height", height.ToStringValue());
            }

            if (!mode.IsNil())
            {
                queryStringParams.Add("rmode", mode.ToStringValue());
            }

            return(new ValueTask <FluidValue>(new StringValue(QueryHelpers.AddQueryString(url, queryStringParams))));
        }
Esempio n. 3
0
        public static FluidValue IncludeFilter(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            // When a property of a JObject value is accessed, try to look into its properties
            TemplateContext.GlobalMemberAccessStrategy.Register <JObject, object>((source, name) => source[name]);

            // Convert JToken to FluidValue
            FluidValue.SetTypeMapping <JObject>(o => new ObjectValue(o));
            FluidValue.SetTypeMapping <JValue>(o => FluidValue.Create(o.Value));

            var    includePath = GlobalConfiguration.getConfiguration().source + "/" + GlobalConfiguration.getConfiguration().includes_dir + "/" + arguments.At(0).ToStringValue();
            string includeFile = WDHANFile.getFileContents(includePath);

            var includeContext = new TemplateContext();
            var includeJSON    = WDHANFile.parseFrontMatter(includePath);

            if (FluidTemplate.TryParse(includeFile, out var template))
            {
                for (int i = 1; i < arguments.Count - 1; i++)
                {
                    includeContext.SetValue(arguments.At(i).ToStringValue(), arguments.At(i));
                }
                includeContext.SetValue("include", includeJSON);
            }

            return(new StringValue(template.Render(includeContext)));
        }
Esempio n. 4
0
        // https://github.com/Shopify/liquid/commit/842986a9721de11e71387732be51951285225977
        public static FluidValue Where(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            if (input.Type != FluidValues.Array)
            {
                return(input);
            }

            // First argument is the property name to match
            var member = arguments.At(0).ToStringValue();

            // Second argument is the value to match, or 'true' if none is defined
            var targetValue = arguments.At(1).Or(BooleanValue.True).ToObjectValue();

            var list = new List <FluidValue>();

            foreach (var item in input.Enumerate())
            {
                var itemValue = item.GetValueAsync(member, context).GetAwaiter().GetResult();

                if (itemValue.ToObjectValue().Equals(targetValue))
                {
                    list.Add(item);
                }
            }

            return(new ArrayValue(list));
        }
Esempio n. 5
0
        public static FluidValue TruncateWords(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            var source = input.ToStringValue();
            var n      = Convert.ToInt32(arguments.At(0).ToNumberValue());

            var words = 0;

            for (int i = 0; i < source.Length;)
            {
                while (i < source.Length && Char.IsWhiteSpace(source[i]))
                {
                    i++;
                }
                while (i < source.Length && !Char.IsWhiteSpace(source[i]))
                {
                    i++;
                }
                words++;

                if (words == n)
                {
                    source = source.Substring(0, i);
                    break;
                }
            }

            if (arguments.Count > 1)
            {
                source += arguments.At(1).ToStringValue();
            }

            return(new StringValue(source));
        }
Esempio n. 6
0
        public Task <FluidValue> ProcessAsync(FluidValue input, FilterArguments arguments, TemplateContext ctx)
        {
            var url = input.ToStringValue();
            var alt = arguments.At(0).Or(arguments["tag"]);
            var css = arguments.At(1).Or(arguments["class"]);

            var imgTag = $"<img src=\"{url}\"";

            if (!alt.IsNil())
            {
                imgTag += $" alt=\"{alt.ToStringValue()}\"";
            }

            if (!css.IsNil())
            {
                imgTag += $" class=\"{css.ToStringValue()}\"";
            }

            imgTag += " />";

            return(Task.FromResult <FluidValue>(new StringValue(imgTag)
            {
                Encode = false
            }));
        }
Esempio n. 7
0
        public static FluidValue Concat(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            if (input.Type != FluidValues.Array)
            {
                return(input);
            }

            if (arguments.At(0).Type != FluidValues.Array)
            {
                return(input);
            }

            var concat = new List <FluidValue>();

            foreach (var item in input.Enumerate())
            {
                concat.Add(item);
            }

            foreach (var item in arguments.At(0).Enumerate())
            {
                concat.Add(item);
            }

            return(new ArrayValue(concat));
        }
Esempio n. 8
0
        /// <summary>
        /// Date filter to support both the .net and Ruby date format
        /// </summary>
        public static FluidValue DateAdvanced(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            if (!TryGetDateTimeInput(input, context, out var value))
            {
                return(NilValue.Instance);
            }

            if (arguments.At(0).IsNil())
            {
                return(NilValue.Instance);
            }

            var format = arguments.At(0).ToStringValue();

            // Use Dotnet format to convert and fallback to the Ruby format
            try
            {
                var result = value.ToString(format, context.CultureInfo);
                if (DateTime.TryParse(result, out var _))
                {
                    return(new StringValue(result));
                }
            }
            catch (Exception) {}

            return(Date(input, arguments, context));
        }
        public async ValueTask <FluidValue> ProcessAsync(FluidValue input, FilterArguments arguments, TemplateContext ctx)
        {
            if (!ctx.AmbientValues.TryGetValue("Services", out var services))
            {
                throw new ArgumentException("Services missing while invoking 'localization_set'");
            }

            var innoFieldsService = ((IServiceProvider)services).GetRequiredService <IContentLocalizationManager>();

            var locale = arguments.At(0).ToStringValue();

            if (arguments.At(0).IsNil())
            {
                locale = ctx.CultureInfo.Name;
            }

            if (input.Type == FluidValues.Array)
            {
                // List of content item ids

                var localizationSets = input.Enumerate().Select(x => x.ToStringValue()).ToArray();

                return(FluidValue.Create(await innoFieldsService.GetItemsForSetsAsync(localizationSets, locale)));
            }
            else
            {
                var localizationSet = input.ToStringValue();

                return(FluidValue.Create(await innoFieldsService.GetContentItemAsync(localizationSet, locale)));
            }
        }
Esempio n. 10
0
        public static FluidValue Truncate(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            var text     = input.ToStringValue();
            var size     = Math.Max(0, (int)arguments.At(0).ToNumberValue());
            var ellipsis = arguments.At(1).Or(Ellipsis).ToStringValue();

            if (text == null)
            {
                return(NilValue.Empty);
            }
            else if (ellipsis.Length >= size)
            {
                return(new StringValue(ellipsis));
            }
            else if (text.Length > size - ellipsis.Length)
            {
                // PERF: using StringBuilder/StringBuilderPool is slower
                var source = text.Substring(0, size - ellipsis.Length) + ellipsis;
                return(new StringValue(source));
            }
            else
            {
                // PERF: using StringBuilder/StringBuilderPool is slower
                return(new StringValue(text + ellipsis));
            }
        }
Esempio n. 11
0
        public static FluidValue Truncate(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            if (input.IsNil())
            {
                return(StringValue.Empty);
            }

            var inputStr = input.ToStringValue();

            if (inputStr == null)
            {
                return(StringValue.Empty);
            }

            var ellipsisStr = arguments.At(1).Or(Ellipsis).ToStringValue();

            var length = Convert.ToInt32(arguments.At(0).Or(NumberValue.Create(50)).ToNumberValue());

            var l = Math.Max(0, length - ellipsisStr.Length);

            return(inputStr.Length > length
                ? new StringValue(inputStr.Substring(0, l) + ellipsisStr)
                : input
                   );
        }
Esempio n. 12
0
        public async ValueTask <FluidValue> ProcessAsync(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            if (!context.AmbientValues.TryGetValue("Services", out var servicesObj))
            {
                throw new ArgumentException("Services missing while invoking 'authorize'");
            }

            var services = servicesObj as IServiceProvider;

            var auth = services.GetRequiredService <IAuthorizationService>();
            var permissionProviders = services.GetRequiredService <IEnumerable <IPermissionProvider> >();

            var clearance      = false;
            var permissionName = arguments["permission"].Or(arguments.At(0)).ToStringValue();
            var resource       = arguments["resource"].Or(arguments.At(1)).ToObjectValue();

            Permission permission = null;

            foreach (var item in permissionProviders)
            {
                permission = item.GetPermissions().FirstOrDefault(c => c.Name == permissionName);
                if (permission != null)
                {
                    break;
                }
            }

            if (permission is Permission && input.ToObjectValue() is ClaimsPrincipal principal)
            {
                clearance = await auth.AuthorizeAsync(principal, permission, resource);
            }

            return(clearance ? BooleanValue.True : BooleanValue.False);
        }
        public async ValueTask <FluidValue> ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx)
        {
            if (!ctx.AmbientValues.TryGetValue("Services", out var services))
            {
                throw new ArgumentException("Services missing while invoking 'github_issuecomments'");
            }

            var gitHubApiService = ((IServiceProvider)services).GetRequiredService <IGitHubApiService>();

            var owner     = arguments["owner"].Or(arguments.At(0)).ToStringValue();
            var repo      = arguments["repository"].Or(arguments.At(1)).ToStringValue();
            var tokenName = arguments["tokenName"].Or(arguments.At(2)).ToStringValue();
            var prNumber  = input.ToStringValue();

            if (string.IsNullOrEmpty(owner) || string.IsNullOrEmpty(repo) || string.IsNullOrEmpty(tokenName))
            {
                throw new ArgumentException("Missing owner, repository or tokenName while invoking 'github_issuecomments'");
            }

            if (!int.TryParse(prNumber, out var parsedNumber))
            {
                throw new ArgumentException("Please provide a valid pull request number while invoking 'github_issuecomments'");
            }

            try
            {
                var client = await gitHubApiService.GetGitHubClient(tokenName);

                return(FluidValue.Create(await client.Issue.Comment.GetAllForIssue(owner, repo, parsedNumber), ctx.Options));
            }
            catch (Octokit.ApiException ex)
            {
                return(FluidValue.Create(ex.Message, ctx.Options));
            }
        }
        public FluidValue Execute(FluidValue input, FilterArguments arguments, Fluid.TemplateContext context)
        {
            //TODO check arguments
            var cellType   = arguments.At(0);
            var formatType = arguments.At(1);


            return(new OdsTableCellDataValue("123-123-123", null));
        }
Esempio n. 15
0
        public static ValueTask <FluidValue> AmplifyInput(FluidValue input, FilterArguments arguments, TemplateContext ctx)
        {
            var threshold    = arguments.Count > 1 ? arguments.At(1).ToNumberValue() : 100;
            var inputFactor  = input.ToNumberValue();
            var amp          = arguments.At(0).ToNumberValue();
            var resultFactor = inputFactor > threshold ? inputFactor * amp : inputFactor * (threshold - amp);

            return(NumberValue.Create(resultFactor));
        }
        public ValueTask <FluidValue> ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context)
        {
            int paramFirstIndex = 0;
            var culture         = context.CultureInfo.Name;
            var value           = "";

            var         inputObj    = input.ToObjectValue();
            ContentItem contentItem = null;

            if (inputObj is ContentItem)
            {
                contentItem = inputObj as ContentItem;
            }
            if (inputObj is JObject)
            {
                contentItem = (inputObj as JObject)?.ToObject <ContentItem>();
            }

            // if the ContentItem is passed to the filter, use the first argument as the name of the value to find
            if (contentItem != null)
            {
                var stringKey = arguments.At(0).ToStringValue();
                paramFirstIndex = 1;

                var part = contentItem.As <LocalizedTextPart>();
                if (part == null)
                {
                    return(new ValueTask <FluidValue>(input));
                    //throw new ArgumentException(" The 'localize' filter requires the LocalizedTextPart");
                }

                value = part.Data.FirstOrDefault(lt => lt.Name == stringKey)?.LocalizedItems.FirstOrDefault(li => li.Culture == culture)?.Value;
            }
            else
            {
                // try to get the ContentItem from the accessor when the name of the value is passed as the input to the filter
                value = _accessor.GetTranslation(culture, input.ToStringValue());
            }

            if (!string.IsNullOrEmpty(value) && arguments.Count > 0)
            {
                var parameters = new object[arguments.Count];
                for (var i = paramFirstIndex; i < arguments.Count; i++)
                {
                    parameters[i] = arguments.At(i).ToStringValue();
                }
                value = string.Format(value, parameters);
            }
            if (value == null)
            {
                value = string.Empty;
            }

            return(new ValueTask <FluidValue>(new StringValue(value, false)));
        }
Esempio n. 17
0
        public static FluidValue Slice(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            var source = input.ToStringValue();
            var start  = Convert.ToInt32(arguments.At(0).ToNumberValue());
            var length = Convert.ToInt32(arguments.At(1).Or(NumberValue.Create(1)).ToNumberValue());

            var len  = source.Length;
            var from = start < 0 ? Math.Max(len + start, 0) : Math.Min(start, len);

            return(new StringValue(source.Substring(from, length)));
        }
Esempio n. 18
0
        public static FluidValue Truncate(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            var source = input.ToStringValue().Substring(0, Convert.ToInt32(arguments.At(0).ToNumberValue()));

            if (arguments.Count > 1)
            {
                source += arguments.At(1).ToStringValue();
            }

            return(new StringValue(source));
        }
Esempio n. 19
0
        public ValueTask <FluidValue> ProcessAsync(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            var ret       = false;
            var claimType = arguments["type"].Or(arguments.At(0)).ToStringValue();
            var claimName = arguments["name"].Or(arguments.At(1)).ToStringValue();

            if (input.ToObjectValue() is ClaimsPrincipal principal)
            {
                ret = principal.HasClaim(claimType, claimName);
            }

            return(new ValueTask <FluidValue>(ret ? BooleanValue.True : BooleanValue.False));
        }
Esempio n. 20
0
 public static ValueTask <FluidValue> MultiplyValue(FluidValue input, FilterArguments arguments, TemplateContext ctx)
 {
     if (arguments.Count > 2)
     {
         var inValue     = input.ToNumberValue();
         var baseFactor  = arguments.At(0).ToNumberValue();
         var ampFactor   = arguments.At(1).ToNumberValue();
         var threshold   = arguments.At(2).ToNumberValue();
         var finalFactor = baseFactor * (inValue > threshold ? ampFactor : ampFactor - 1);
         return(new StringValue((input.ToNumberValue() * finalFactor).ToString()));
     }
     return(new StringValue((input.ToNumberValue() * arguments.At(0).ToNumberValue()).ToString()));
 }
Esempio n. 21
0
        public static ValueTask <FluidValue> ReplaceFirst(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            string remove = arguments.At(0).ToStringValue();
            var    value  = input.ToStringValue();

            var index = value.IndexOf(remove);

            if (index != -1)
            {
                return(new StringValue(value.Substring(0, index) + arguments.At(1).ToStringValue() + value.Substring(index + remove.Length)));
            }

            return(input);
        }
        public async ValueTask <FluidValue> ProcessAsync(FluidValue input, FilterArguments arguments, TemplateContext ctx)
        {
            var obj = input.ToObjectValue();

            if (!(obj is ContentItem contentItem))
            {
                contentItem = null;

                if (obj is JObject jObject)
                {
                    contentItem = jObject.ToObject <ContentItem>();
                }
            }

            // If input is a 'JObject' but which not represents a 'ContentItem',
            // a 'ContentItem' is still created but with some null properties.
            if (contentItem?.ContentItemId == null)
            {
                return(NilValue.Instance);
            }

            if (!ctx.AmbientValues.TryGetValue("Services", out var services))
            {
                throw new ArgumentException("Services missing while invoking 'shape_build_display'");
            }

            var displayType         = arguments["type"].Or(arguments.At(0)).ToStringValue();
            var displayManager      = ((IServiceProvider)services).GetRequiredService <IContentItemDisplayManager>();
            var updateModelAccessor = ((IServiceProvider)services).GetRequiredService <IUpdateModelAccessor>();

            return(FluidValue.Create(await displayManager.BuildDisplayAsync(contentItem, updateModelAccessor.ModelUpdater, displayType)));
        }
Esempio n. 23
0
        public static FluidValue Json(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            var options = new JsonSerializerOptions
            {
                WriteIndented = arguments.At(0).ToBooleanValue()
            };

            switch (input.Type)
            {
            case FluidValues.Array:
                return(new StringValue(JsonSerializer.Serialize(input.Enumerate().Select(o => o.ToObjectValue()), options)));

            case FluidValues.Boolean:
                return(new StringValue(JsonSerializer.Serialize(input.ToBooleanValue(), options)));

            case FluidValues.Nil:
                return(StringValue.Create("null"));

            case FluidValues.Number:
                return(new StringValue(JsonSerializer.Serialize(input.ToNumberValue(), options)));

            case FluidValues.DateTime:
            case FluidValues.Dictionary:
            case FluidValues.Object:
                return(new StringValue(JsonSerializer.Serialize(input.ToObjectValue(), options)));

            case FluidValues.String:
                return(new StringValue(JsonSerializer.Serialize(input.ToStringValue(), options)));
            }

            throw new NotSupportedException("Unrecognized FluidValue");
        }
Esempio n. 24
0
        public static FluidValue FormatDate(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            if (!TryGetDateTimeInput(input, context, out var value))
            {
                return(NilValue.Instance);
            }

            if (arguments.At(0).IsNil())
            {
                return(NilValue.Instance);
            }

            var format = arguments.At(0).ToStringValue();

            return(new StringValue(value.ToString(format, context.CultureInfo)));
        }
Esempio n. 25
0
        public static ValueTask <FluidValue> Split(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            string[] strings;

            var stringInput = input.ToStringValue();
            var separator   = arguments.At(0).ToStringValue();

            if (separator == "")
            {
                strings = new string[stringInput.Length];

                for (var i = 0; i < stringInput.Length; i++)
                {
                    strings[i] = stringInput[i].ToString();
                }
            }
            else
            {
                strings = stringInput.Split(separator, StringSplitOptions.RemoveEmptyEntries);
            }

            var values = new FluidValue[strings.Length];

            for (var i = 0; i < strings.Length; i++)
            {
                values[i] = StringValue.Create(strings[i]);
            }

            return(new ArrayValue(values));
        }
Esempio n. 26
0
        public static Task <FluidValue> AddAlternates(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            if (input.ToObjectValue() is IShape shape)
            {
                var alternates = arguments["alternates"].Or(arguments.At(0));

                if (alternates.Type == FluidValues.String)
                {
                    var values = alternates.ToStringValue().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

                    foreach (var value in values)
                    {
                        shape.Metadata.Alternates.Add(value);
                    }
                }
                else if (alternates.Type == FluidValues.Array)
                {
                    foreach (var value in alternates.Enumerate())
                    {
                        shape.Metadata.Alternates.Add(value.ToStringValue());
                    }
                }
            }

            return(Task.FromResult(input));
        }
            public static ValueTask <FluidValue> Tab(FluidValue input, FilterArguments arguments, TemplateContext context)
            {
                var tabCount  = (int)arguments.At(0).ToNumberValue();
                var converted = ConversionUtilities.Tab(input.ToStringValue(), tabCount);

                return(new ValueTask <FluidValue>(new StringValue(converted)));
            }
Esempio n. 28
0
        public ValueTask <FluidValue> ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx)
        {
            var inputObj  = input.ToObjectValue();
            var whitelist = arguments.At(0)?.ToStringValue()?.Split(',');

            if (inputObj is Arguments shortcodeArgs && whitelist?.Length > 0)
            {
                using var sb = ZString.CreateStringBuilder();
                sb.Append(" ");

                foreach (var arg in shortcodeArgs)
                {
                    // todo: remove this when OC supports - in shortcode arguments
                    // temporary code to support arguments that contain a - since OC currently does not support
                    var key = arg.Key.Replace('_', '-');
                    if (whitelist.Contains(key))
                    {
                        sb.Append($"{key}=\"{arg.Value}\" ");
                    }
                    else if (whitelist.Contains(arg.Value))
                    {
                        // for boolean arguments that are valid for vue.js
                        sb.Append($"{arg.Value} ");
                    }
                }
                return(new StringValue(sb.ToString()));
            }
            return(StringValue.Empty);
        }
Esempio n. 29
0
        public async Task <FluidValue> ProcessAsync(FluidValue input, FilterArguments arguments, TemplateContext ctx)
        {
            var latest = arguments["latest"].ToBooleanValue();
            var mode   = arguments["mode"].Or(arguments.At(0));

            if (mode.IsNil() || mode.ToStringValue() == "id")
            {
                var contentItemId = input.ToStringValue();
                var contentItem   = await _contentManager.GetAsync(contentItemId, latest?VersionOptions.Latest : VersionOptions.Published);

                return(FluidValue.Create(contentItem));
            }
            if (mode.ToStringValue() == "alias")
            {
                var contentItemId = await _contentAliasManager.GetContentItemIdAsync(input.ToStringValue());

                if (contentItemId != null)
                {
                    var contentItem = await _contentManager.GetAsync(contentItemId, latest?VersionOptions.Latest : VersionOptions.Published);

                    return(FluidValue.Create(contentItem));
                }
            }
            else if (mode.ToStringValue() == "version")
            {
                var contentItemVersionId = input.ToStringValue();
                var contentItem          = await _contentManager.GetVersionAsync(contentItemVersionId);

                return(FluidValue.Create(contentItem));
            }

            return(FluidValue.Create(null));
        }
Esempio n. 30
0
        public static ValueTask <FluidValue> ColorExtract(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            var      value = input.ToStringValue();
            RgbColor rgbColor;
            HslColor hslColor;

            if (HexColor.TryParse(value, out HexColor hexColor))
            {
                rgbColor = (RgbColor)hexColor;
                hslColor = (HslColor)hexColor;
            }
            else if (RgbColor.TryParse(value, out rgbColor))
            {
                hslColor = (HslColor)rgbColor;
            }
            else if (HslColor.TryParse(value, out hslColor))
            {
                rgbColor = (RgbColor)hslColor;
            }
            else
            {
                return(NilValue.Empty);
            }

            return(arguments.At(0).ToStringValue() switch
            {
                "alpha" => new StringValue(rgbColor.A.ToString()),
                "red" => new StringValue(rgbColor.R.ToString()),
                "green" => new StringValue(rgbColor.G.ToString()),
                "blue" => new StringValue(rgbColor.B.ToString()),
                "hue" => new StringValue(hslColor.H.ToString()),
                "saturation" => new StringValue(Convert.ToInt32(hslColor.S * 100.0).ToString()),
                "lightness" => new StringValue(Convert.ToInt32(hslColor.L * 100.0).ToString()),
                _ => NilValue.Empty,
            });