예제 #1
0
 internal object EvalPropertyValue(DotvvmProperty property, object value)
 {
     if (property.IsBindingProperty)
     {
         return(value);
     }
     if (value is IBinding)
     {
         DotvvmBindableObject control = this;
         // DataContext is always bound to it's parent, setting it right here is a bit faster
         if (property == DataContextProperty)
         {
             control = control.Parent;
         }
         // handle binding
         if (value is IStaticValueBinding binding)
         {
             value = binding.Evaluate(control);
         }
         else if (value is ICommandBinding command)
         {
             value = command.GetCommandDelegate(control);
         }
         else
         {
             throw new NotSupportedException($"Cannot evaluate binding {value} of type {value.GetType().Name}.");
         }
     }
     return(value);
 }
예제 #2
0
        /// <summary>
        /// Writes text iff the property contains hard-coded value OR
        /// writes knockout text binding iff the property contains binding
        /// </summary>
        /// <param name="writer">HTML output writer</param>
        /// <param name="obj">Dotvvm control which contains the <see cref="DotvvmProperty"/> with value to be written</param>
        /// <param name="property">Value of this property will be written</param>
        /// <param name="wrapperTag">Name of wrapper tag, null => knockout binding comment</param>
        public static void WriteTextOrBinding(this IHtmlWriter writer, DotvvmBindableObject obj, DotvvmProperty property, string wrapperTag = null)
        {
            var valueBinding = obj.GetValueBinding(property);

            if (valueBinding != null)
            {
                if (wrapperTag == null)
                {
                    writer.WriteKnockoutDataBindComment("text", valueBinding.GetKnockoutBindingExpression(obj));
                    writer.WriteKnockoutDataBindEndComment();
                }
                else
                {
                    writer.AddKnockoutDataBind("text", valueBinding.GetKnockoutBindingExpression(obj));
                    writer.RenderBeginTag(wrapperTag);
                    writer.RenderEndTag();
                }
            }
            else
            {
                if (wrapperTag != null)
                {
                    writer.RenderBeginTag(wrapperTag);
                }
                writer.WriteText(obj.GetValue(property).ToString());
                if (wrapperTag != null)
                {
                    writer.RenderEndTag();
                }
            }
        }
예제 #3
0
        static string GenerateConcurrencyModeHandler(string propertyName, DotvvmBindableObject obj)
        {
            var mode = (obj.GetValue(PostBack.ConcurrencyProperty) as PostbackConcurrencyMode?) ?? PostbackConcurrencyMode.Default;

            // determine concurrency queue
            string queueName     = null;
            var    queueSettings = obj.GetValueRaw(PostBack.ConcurrencyQueueSettingsProperty) as ConcurrencyQueueSettingsCollection;

            if (queueSettings != null)
            {
                queueName = queueSettings.FirstOrDefault(q => string.Equals(q.EventName, propertyName, StringComparison.OrdinalIgnoreCase))?.ConcurrencyQueue;
            }
            if (queueName == null)
            {
                queueName = obj.GetValue(PostBack.ConcurrencyQueueProperty) as string ?? "default";
            }

            // return the handler script
            if (mode == PostbackConcurrencyMode.Default && "default".Equals(queueName))
            {
                return(null);
            }
            var handlerName = $"concurrency-{mode.ToString().ToLower()}";

            if ("default".Equals(queueName))
            {
                return(JsonConvert.ToString(handlerName));
            }
            else
            {
                return($"[{JsonConvert.ToString(handlerName)},{GenerateHandlerOptions(obj, new Dictionary<string, object> { ["q"] = queueName })}]");
            }
        }
예제 #4
0
        /// <summary>
        /// Gets the value of a specified property.
        /// </summary>
        public virtual object GetValue(DotvvmProperty property, bool inherit = true)
        {
            var value = GetValueRaw(property, inherit);

            if (property.IsBindingProperty)
            {
                return(value);
            }
            while (value is IBinding)
            {
                DotvvmBindableObject control = this;
                if (inherit && !properties.ContainsKey(property))
                {
                    int n;
                    control = GetClosestWithPropertyValue(out n, d => d.properties != null && d.properties.ContainsKey(property));
                }
                if (value is IStaticValueBinding)
                {
                    // handle binding
                    var binding = (IStaticValueBinding)value;
                    value = binding.Evaluate(control, property);
                }
                else if (value is CommandBindingExpression)
                {
                    var binding = (CommandBindingExpression)value;
                    value = binding.GetCommandDelegate(control, property);
                }
            }
            return(value);
        }
        /// <summary>
        /// Gets the value of a specified property.
        /// </summary>
        public virtual object GetValue(DotvvmProperty property, bool inherit = true)
        {
            var value = GetValueRaw(property, inherit);

            if (property.IsBindingProperty)
            {
                return(value);
            }
            while (value is IBinding)
            {
                DotvvmBindableObject control = this;
                // DataContext is always bound to it's parent, setting it right here is a bit faster
                if (property == DataContextProperty)
                {
                    control = control.Parent;
                }
                // handle binding
                if (value is IStaticValueBinding binding)
                {
                    value = binding.Evaluate(control);
                }
                else if (value is ICommandBinding command)
                {
                    value = command.GetCommandDelegate(control);
                }
            }
            return(value);
        }
예제 #6
0
        public static string DebugString(this DotvvmBindableObject control, DotvvmConfiguration?config = null, bool multiline = true)
        {
            if (control == null)
            {
                return("null");
            }

            config = config ?? (control.TryGeyValue(Internal.RequestContextProperty) as IDotvvmRequestContext)?.Configuration;

            var type       = control.GetType();
            var properties = (from kvp in control.Properties
                              let p = kvp.Key
                                      let rawValue = kvp.Value
                                                     where p.DeclaringType != typeof(Internal)
                                                     let isAttached = !p.DeclaringType.IsAssignableFrom(type)
                                                                      orderby !isAttached, p.Name
                              let name = isAttached ? p.DeclaringType.Name + "." + p.Name : p.Name
                                         let value = rawValue == null ? "<null>" :
                                                     rawValue is ITemplate ? "<a template>" :
                                                     rawValue is DotvvmBindableObject ? $"<control {rawValue.GetType()}>" :
                                                     rawValue is IEnumerable <DotvvmBindableObject> controlCollection ? $"<{controlCollection.Count()} controls>" :
                                                     rawValue is IEnumerable <object> collection ? string.Join(", ", collection) :
                                                     rawValue.ToString()
                                                     let croppedValue = value.Length > 41 ? value.Substring(0, 40) + "…" : value
                                                                        select new { p, name, croppedValue, value, isAttached }
                              ).ToArray();

            var location = (file : control.TryGeyValue(Internal.MarkupFileNameProperty) as string, line : control.TryGeyValue(Internal.MarkupLineNumberProperty) as int? ?? -1);
            var reg      = config?.Markup.Controls.FirstOrDefault(c => c.Namespace == type.Namespace && Type.GetType(c.Namespace + "." + type.Name + ", " + c.Assembly) == type) ??
                           config?.Markup.Controls.FirstOrDefault(c => c.Namespace == type.Namespace) ??
                           config?.Markup.Controls.FirstOrDefault(c => c.Assembly == type.Assembly.GetName().Name);
            var ns      = reg?.TagPrefix ?? "?";
            var tagName = type == typeof(HtmlGenericControl) ? ((HtmlGenericControl)control).TagName : ns + ":" + type.Name;

            var dothtmlString = $"<{tagName} ";
            var prefixLength  = dothtmlString.Length;

            foreach (var p in properties)
            {
                if (multiline && p != properties[0])
                {
                    dothtmlString += "\n" + new string(' ', prefixLength);
                }
                dothtmlString += $"{p.name}={p.croppedValue}";
            }
            dothtmlString += "/>";

            var from = (location.file)
                       + (location.line >= 0 ? ":" + location.line : "");

            if (!String.IsNullOrWhiteSpace(from))
            {
                from = (multiline ? "\n" : " ") + "from " + from.Trim();
            }

            return(dothtmlString + from);
        }
예제 #7
0
        /// <summary>
        /// Returns Javascript expression that represents the property value (even if the property contains hardcoded value)
        /// </summary>
        public static string GetKnockoutBindingExpression(this DotvvmBindableObject obj, DotvvmProperty property)
        {
            var binding = obj.GetValueBinding(property);

            if (binding != null)
            {
                return(binding.GetKnockoutBindingExpression());
            }
            return(JsonConvert.SerializeObject(obj.GetValue(property)));
        }
예제 #8
0
        /// <summary>
        /// Gets the validation target expression.
        /// </summary>
        public static string GetValidationTargetExpression(DotvvmBindableObject control)
        {
            if (!(bool)control.GetValue(Validation.EnabledProperty))
            {
                return(null);
            }

            return(control.GetValueBinding(Validation.TargetProperty)?.GetKnockoutBindingExpression(control) ??
                   RootValidationTargetExpression);
        }
예제 #9
0
        /// <summary>
        /// Returns Javascript expression that represents the property value (even if the property contains hardcoded value)
        /// </summary>
        public static string GetKnockoutBindingExpression(this DotvvmBindableObject obj, DotvvmProperty property)
        {
            var binding = obj.GetValueBinding(property);

            if (binding != null)
            {
                return(binding.GetKnockoutBindingExpression(obj));
            }
            return(JsonConvert.SerializeObject(obj.GetValue(property), DefaultViewModelSerializer.CreateDefaultSettings()));
        }
예제 #10
0
 internal static object?TryGeyValue(this DotvvmBindableObject control, DotvvmProperty property)
 {
     try
     {
         return(control.GetValue(property));
     }
     catch
     {
         return(property.DefaultValue);
     }
 }
예제 #11
0
 public DotvvmControlException(DotvvmBindableObject control, string message, Exception innerException = null)
     : base(message, innerException)
 {
     ControlType = control.GetType();
     LineNumber  = (int?)Internal.MarkupLineNumberProperty.GetValue(control);
     if (control.Parent != null)
     {
         control = control.Parent;
     }
     FileName = (string)Internal.MarkupFileNameProperty.GetValue(control);
 }
예제 #12
0
 public static IEnumerable <string> GetContextPath(DotvvmBindableObject control)
 {
     while (control != null)
     {
         var pathFragment = control.GetDataContextPathFragment();
         if (pathFragment != null)
         {
             yield return(pathFragment);
         }
         control = control.Parent;
     }
 }
예제 #13
0
        private static string TranslateRouteParameter(DotvvmBindableObject control, KeyValuePair <string, object> param, bool caseSensitive = false)
        {
            string expression = "";

            if (param.Value is IBinding)
            {
                EnsureValidBindingType(param.Value as IBinding);

                expression = (param.Value as IValueBinding)?.GetKnockoutBindingExpression(control)
                             ?? JsonConvert.SerializeObject((param.Value as IStaticValueBinding)?.Evaluate(control), DefaultViewModelSerializer.CreateDefaultSettings());
            }
            else
            {
                expression = JsonConvert.SerializeObject(param.Value, DefaultViewModelSerializer.CreateDefaultSettings());
            }
            return(JsonConvert.SerializeObject(caseSensitive ? param.Key : param.Key.ToLower()) + ": " + expression);
        }
예제 #14
0
        static string GenerateConcurrencyModeHandler(DotvvmBindableObject obj)
        {
            var mode      = (obj.GetValue(PostBack.ConcurrencyProperty) as PostbackConcurrencyMode?) ?? PostbackConcurrencyMode.Default;
            var queueName = obj.GetValueRaw(PostBack.ConcurrencyQueueProperty) ?? "default";

            if (mode == PostbackConcurrencyMode.Default && "default".Equals(queueName))
            {
                return(null);
            }
            var handlerName = $"concurrency-{mode.ToString().ToLower()}";

            if ("default".Equals(queueName))
            {
                return(JsonConvert.ToString(handlerName));
            }
            return($"[{JsonConvert.ToString(handlerName)},{GenerateHandlerOptions(obj, new Dictionary<string, object> { ["q"] = queueName })}]");
        }
예제 #15
0
        /// <summary>
        /// Gets the validation target expression.
        /// </summary>
        public static string GetValidationTargetExpression(DotvvmBindableObject control)
        {
            if (!(bool)control.GetValue(Validation.EnabledProperty))
            {
                return(null);
            }

            // find the closest control
            var validationTargetControl = control.GetClosestControlValidationTarget(out var _);

            if (validationTargetControl == null)
            {
                return("$root");
            }

            // reparent the expression to work in current DataContext
            return(validationTargetControl.GetValueBinding(Validation.TargetProperty).GetKnockoutBindingExpression(control));
        }
        internal IEnumerable <IValueBinding> GetDataContextHierarchy()
        {
            var bindings = new List <IValueBinding>();
            DotvvmBindableObject current = this;

            do
            {
                var binding = current.GetValueBinding(DataContextProperty, false);
                if (binding != null)
                {
                    bindings.Add(binding);
                }
                current = current.Parent;
            }while (current != null);

            bindings.Reverse();
            return(bindings);
        }
예제 #17
0
        private static string GenerateHandlerOptions(DotvvmBindableObject handler, Dictionary <string, object> options)
        {
            JsExpression optionsExpr = new JsObjectExpression(
                options.Where(o => o.Value != null).Select(o => new JsObjectProperty(o.Key, TransformOptionValueToExpression(handler, o.Value)))
                );

            if (options.Any(o => o.Value is IValueBinding))
            {
                optionsExpr = new JsFunctionExpression(
                    new[] { new JsIdentifier("c"), new JsIdentifier("d") },
                    new JsBlockStatement(new JsReturnStatement(optionsExpr))
                    );
            }

            optionsExpr.FixParenthesis();
            var script = new JsFormattingVisitor().ApplyAction(optionsExpr.AcceptVisitor).GetParameterlessResult();

            return(script);
        }
예제 #18
0
        /// <summary>
        /// Generates a list of postback update handlers.
        /// </summary>
        private static string GetPostBackHandlersScript(DotvvmBindableObject control, string eventName)
        {
            var handlers = (List <PostBackHandler>)control.GetValue(PostBack.HandlersProperty);

            if (handlers == null)
            {
                return("null");
            }

            var effectiveHandlers = handlers.Where(h => string.IsNullOrEmpty(h.EventName) || h.EventName == eventName);
            var sb = new StringBuilder();

            sb.Append("[");
            foreach (var handler in effectiveHandlers)
            {
                if (sb.Length > 1)
                {
                    sb.Append(",");
                }
                sb.Append("{name:'");
                sb.Append(handler.ClientHandlerName);
                sb.Append("',options:function(){return {");

                var isFirst = true;
                var options = handler.GetHandlerOptionClientExpressions();
                options.Add("enabled", handler.TranslateValueOrBinding(PostBackHandler.EnabledProperty));
                foreach (var option in options)
                {
                    if (!isFirst)
                    {
                        sb.Append(',');
                    }
                    isFirst = false;

                    sb.Append(option.Key);
                    sb.Append(":");
                    sb.Append(option.Value);
                }
                sb.Append("};}}");
            }
            sb.Append("]");
            return(sb.ToString());
        }
예제 #19
0
        /// <summary>
        /// Copies the value of a property from this <see cref="DotvvmBindableObject"/> (source) to a property of another <see cref="DotvvmBindableObject"/> (target).
        /// </summary>
        /// <exception cref="DotvvmControlException">Gets thrown if copying fails and <paramref name="throwOnFailure"/> is set to true</exception>
        /// <param name="sourceProperty">The <see cref="DotvvmProperty"/> whose value will be copied</param>
        /// <param name="target">The <see cref="DotvvmBindableObject"/> that holds the value of the <paramref name="targetProperty"/></param>
        /// <param name="targetProperty">The <see cref="DotvvmProperty"/> to which <paramref name="sourceProperty"/> will be copied</param>
        /// <param name="throwOnFailure">Determines whether to throw an exception if copying fails</param>
        protected void CopyProperty(DotvvmProperty sourceProperty, DotvvmBindableObject target, DotvvmProperty targetProperty, bool throwOnFailure = false)
        {
            if (throwOnFailure && !targetProperty.MarkupOptions.AllowBinding && !targetProperty.MarkupOptions.AllowHardCodedValue)
            {
                throw new DotvvmControlException(this, $"TargetProperty: {targetProperty.FullName} doesn't allow bindings nor hard coded values");
            }

            if (targetProperty.MarkupOptions.AllowBinding && HasBinding(sourceProperty))
            {
                target.SetBinding(targetProperty, GetBinding(sourceProperty));
            }
            else if (targetProperty.MarkupOptions.AllowHardCodedValue && IsPropertySet(sourceProperty))
            {
                target.SetValue(targetProperty, GetValue(sourceProperty));
            }
            else if (throwOnFailure)
            {
                throw new DotvvmControlException(this, $"Value of {sourceProperty.FullName} couldn't be copied to targetProperty: {targetProperty.FullName}, because {targetProperty.FullName} is not set.");
            }
        }
예제 #20
0
        public static void AddKnockoutDataBind(this IHtmlWriter writer, string name, DotvvmBindableObject control, DotvvmProperty property, Action?nullBindingAction = null,
                                               string?valueUpdate = null, bool renderEvenInServerRenderingMode = false, bool setValueBack = false)
        {
            var expression = control.GetValueBinding(property);

            if (expression != null && (!control.RenderOnServer || renderEvenInServerRenderingMode))
            {
                writer.AddKnockoutDataBind(name, expression.GetKnockoutBindingExpression(control));
                if (valueUpdate != null)
                {
                    writer.AddKnockoutDataBind("valueUpdate", $"'{valueUpdate}'");
                }
            }
            else
            {
                nullBindingAction?.Invoke();
                if (setValueBack && expression != null)
                {
                    control.SetValue(property, expression.Evaluate(control));
                }
            }
        }
예제 #21
0
        private static string GenerateHandlerOptions(DotvvmBindableObject handler, Dictionary <string, object> options)
        {
            JsExpression optionsExpr = new JsObjectExpression(
                options.Where(o => o.Value != null).Select(o => new JsObjectProperty(o.Key, o.Value is IValueBinding b ?
                                                                                     (JsExpression) new JsIdentifierExpression(
                                                                                         JavascriptTranslator.FormatKnockoutScript(b.GetParametrizedKnockoutExpression(handler, unwrapped: true), new ParametrizedCode("c"), new ParametrizedCode("d"))) :
                                                                                     new JsLiteral(o.Value)))
                );

            if (options.Any(o => o.Value is IValueBinding))
            {
                optionsExpr = new JsFunctionExpression(
                    new[] { new JsIdentifier("c"), new JsIdentifier("d") },
                    new JsBlockStatement(new JsReturnStatement(optionsExpr))
                    );
            }

            optionsExpr.FixParenthesis();
            var script = new JsFormattingVisitor().ApplyAction(optionsExpr.AcceptVisitor).GetParameterlessResult();

            return(script);
        }
예제 #22
0
        private static JsExpression TransformOptionValueToExpression(DotvvmBindableObject handler, object optionValue)
        {
            switch (optionValue)
            {
            case IValueBinding binding:
                return(new JsIdentifierExpression(
                           JavascriptTranslator.FormatKnockoutScript(binding.GetParametrizedKnockoutExpression(handler, unwrapped: true),
                                                                     new ParametrizedCode("c"), new ParametrizedCode("d"))));

            case IStaticValueBinding staticValueBinding:
                return(new JsLiteral(staticValueBinding.Evaluate(handler)));

            case JsExpression expression:
                return(expression.Clone());

            case IBinding _:
                throw new ArgumentException("Option value can contains only IValueBinding or IStaticValueBinding. Other bindings are not supported.");

            default:
                return(new JsLiteral(optionValue));
            }
        }
예제 #23
0
        /// <summary>
        /// Gets the validation target expression.
        /// </summary>
        public static string GetValidationTargetExpression(DotvvmBindableObject control)
        {
            if (!(bool)control.GetValue(Validation.EnabledProperty))
            {
                return(null);
            }

            // find the closest control
            int dataSourceChanges;
            var validationTargetControl = control.GetClosestWithPropertyValue(out dataSourceChanges, c => c.IsPropertySet(Validation.TargetProperty, false));

            if (validationTargetControl == null)
            {
                return("$root");
            }

            // reparent the expression to work in current DataContext
            var    validationBindingExpression = validationTargetControl.GetValueBinding(Validation.TargetProperty);
            string validationExpression        = validationBindingExpression.GetKnockoutBindingExpression();

            validationExpression = string.Join("", Enumerable.Range(0, dataSourceChanges).Select(i => "$parentContext.")) + validationExpression;

            return(validationExpression);
        }
예제 #24
0
        public static void WriteKnockoutDataBindComment(this IHtmlWriter writer, string name, DotvvmBindableObject control, DotvvmProperty property)
        {
            var binding = control.GetValueBinding(property);

            if (binding is null)
            {
                throw new DotvvmControlException(control, $"A value binding expression was expected in property {property}.");
            }
            writer.WriteKnockoutDataBindComment(name, binding.GetKnockoutBindingExpression(control));
        }
예제 #25
0
 public static void AddKnockoutDataBind(this IHtmlWriter writer, string name, IEnumerable <KeyValuePair <string, IValueBinding> > expressions, DotvvmBindableObject control, DotvvmProperty?property = null)
 {
     writer.AddKnockoutDataBind(name, $"{{{String.Join(",", expressions.Select(e => "'" + e.Key + "': " + e.Value.GetKnockoutBindingExpression(control)))}}}");
 }
예제 #26
0
 public static void AddKnockoutDataBind(this IHtmlWriter writer, string name, IValueBinding valueBinding, DotvvmBindableObject control)
 {
     writer.AddKnockoutDataBind(name, valueBinding.GetKnockoutBindingExpression(control));
 }
예제 #27
0
        public static string GenerateClientPostBackExpression(string propertyName, ICommandBinding expression, DotvvmBindableObject control, PostbackScriptOptions options)
        {
            var target          = (DotvvmControl?)control.GetClosestControlBindingTarget();
            var uniqueControlId = target?.GetDotvvmUniqueId();

            string getContextPath(DotvvmBindableObject?current)
            {
                var result = new List <string>();

                while (current != null)
                {
                    var pathFragment = current.GetDataContextPathFragment();
                    if (pathFragment != null)
                    {
                        result.Add(JsonConvert.ToString(pathFragment));
                    }
                    current = current.Parent;
                }
                result.Reverse();
                return("[" + string.Join(",", result) + "]");
            }

            string getHandlerScript()
            {
                if (!options.AllowPostbackHandlers)
                {
                    return("[]");
                }
                // turn validation off for static commands
                var validationPath = expression is StaticCommandBindingExpression ? null : GetValidationTargetExpression(control);

                return(GetPostBackHandlersScript(control, propertyName,
                                                 // validation handler
                                                 validationPath == null ? null :
                                                 validationPath == RootValidationTargetExpression ? "\"validate-root\"" :
                                                 validationPath == "$data" ? "\"validate-this\"" :
                                                 $"[\"validate\", {{path:{JsonConvert.ToString(validationPath)}}}]",

                                                 // use window.setTimeout
                                                 options.UseWindowSetTimeout ? "\"timeout\"" : null,

                                                 options.IsOnChange ? "\"suppressOnUpdating\"" : null,

                                                 GenerateConcurrencyModeHandler(propertyName, control)
                                                 ));
            }

            string?generatedPostbackHandlers = null;

            var adjustedExpression = expression.GetParametrizedCommandJavascript(control);
            // when the expression changes the dataContext, we need to override the default knockout context fo the command binding.
            var knockoutContext = options.KoContext ?? (
                // adjustedExpression != expression.CommandJavascript ?
                new CodeParameterAssignment(new ParametrizedCode.Builder {
                "ko.contextFor(", options.ElementAccessor.Code !, ")"
            }.Build(OperatorPrecedence.Max))
예제 #28
0
        public static string GenerateClientPostBackScript(string propertyName, ICommandBinding expression, DotvvmBindableObject control, PostbackScriptOptions options)
        {
            var expr = GenerateClientPostBackExpression(propertyName, expression, control, options);

            if (options.ReturnValue == false)
            {
                return(expr + ";event.stopPropagation();return false;");
            }
            else
            {
                return(expr);
            }
        }
예제 #29
0
 public static string GenerateClientPostBackScript(string propertyName, ICommandBinding expression, DotvvmBindableObject control, bool useWindowSetTimeout = false,
                                                   bool?returnValue = false, bool isOnChange = false, string elementAccessor = "this")
 {
     return(GenerateClientPostBackScript(propertyName, expression, control, new PostbackScriptOptions(useWindowSetTimeout, returnValue, isOnChange, elementAccessor)));
 }
예제 #30
0
 /// Sets an expected data context type
 public static void SetDataContextType(this DotvvmBindableObject obj, DataContextStack stack) => obj.SetValue(Internal.DataContextTypeProperty, stack);