/// <summary> /// Add a new QueryString segment to a UrlDetails instance if the specified value is defined (if a Missing value is provided then the source UrlDetails instance will be returned unaltered). /// The value will have ToString called on it to generate the QueryString segment value. This will throw an exception for a null source or null-or-blank-or-whitespace-only key. /// </summary> public static UrlDetails AddToQueryIfDefined<T>(this UrlDetails source, string key, Optional<T> value) { if (source == null) throw new ArgumentNullException(nameof(source)); if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException($"Null/blank {nameof(key)} specified"); return value.IsDefined ? AddToQuery(source, key, value.Value) : source; }
public bool ExecuteCallbackIfUrlMatches(UrlDetails url) { if (url == null) { throw new ArgumentNullException("url"); } if (url.Segments.Count != _segmentMatchers.Count) { return(false); } var valuesExtractedFromMatchedVariables = NonNullList <object> .Empty; foreach (var segmentAndMatcher in url.Segments.Zip(_segmentMatchers, (segment, matcher) => new { Segment = segment, Matcher = matcher })) { var matchResult = segmentAndMatcher.Matcher.Match(segmentAndMatcher.Segment); if (!matchResult.IsDefined) { return(false); } if (matchResult.Value.ValueExtractedFromVariableSegment.IsDefined) { valuesExtractedFromMatchedVariables = valuesExtractedFromMatchedVariables.Add(matchResult.Value.ValueExtractedFromVariableSegment.Value); } } if (!valuesExtractedFromMatchedVariables.Any()) { // The VariableRouteDetails class should only be used if there is at least one variable url segment to match, otherwise the StaticRouteDetails would be // more sensible. Since both VariableRouteDetails and StaticRouteDetails are private and may only be instantiated by the RouteBuilder, there is no way // that we should find ourselves here with no variable segments to process. throw new Exception("This shouldn't happen because.. "); } // Note: The number of segments that the extractedValueBuilder expects should be consistent with the number of matchedVariables (this is always the case for // VariableRouteDetails instances created by the RouteBuilder which means that it should always be the general case since VariableRouteDetails is a private // class and may not be instantiated by anything other than the RouteBuilder) TValues extractedValue; if (_extractedValueBuilder.IsDefined) { extractedValue = _extractedValueBuilder.Value(valuesExtractedFromMatchedVariables); } else { // If there's no _extractedValueBuilder then no-one cares about what's matched here, so we can set extractedValue to null (we have to use Script.Write // because we can't be sure that TValues is not a value type and we can't set it to default(TValues) because we don't have access to the TValues type // at runtime because this class is decorated with [IgnoreGeneric] extractedValue = Script.Write <TValues>("null"); } _ifMatched(extractedValue, url.QueryString); return(true); }
private void RaiseCallbacks(UrlDetails url) { if (url == null) { throw new ArgumentNullException("url"); } foreach (var callback in _navigatedCallbacks) { callback(url); } }
public Link( UrlDetails url, ReactElement text, bool caseSensitiveUrlMatching = false, Optional <NonBlankTrimmedString> name = new Optional <NonBlankTrimmedString>(), Optional <NonBlankTrimmedString> target = new Optional <NonBlankTrimmedString>(), Optional <ClassName> className = new Optional <ClassName>(), Optional <ClassName> ancestorClassName = new Optional <ClassName>(), Optional <ClassName> selectedClassName = new Optional <ClassName>(), Optional <Action <MouseEvent <Bridge.Html5.HTMLAnchorElement> > > onClick = new Optional <Action <MouseEvent <Bridge.Html5.HTMLAnchorElement> > >(), Optional <IInteractWithBrowserRouting> historyHandlerOverride = new Optional <IInteractWithBrowserRouting>()) : base(new Props(url, text, caseSensitiveUrlMatching, name, target, className, ancestorClassName, selectedClassName, onClick, historyHandlerOverride)) { }
/// <summary> /// Add a new QueryString segment to a UrlDetails instance. The value will have ToString called on it to generate the QueryString segment value. This will throw an exception for a null source, a /// null-or-blank-or-whitespace-only key or a null value. /// </summary> public static UrlDetails AddToQuery(this UrlDetails source, string key, object value) { if (source == null) throw new ArgumentNullException(nameof(source)); if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException($"Null/blank {nameof(key)} specified"); if (value == null) throw new ArgumentNullException(nameof(value)); // 2019-01-08: Unless/until https://forums.bridge.net/forum/community/help/6001 is accepted as a bug and fixed, we can't rely upon the ToString implementation of // NonBlankTrimmedString since it became annotated with [ObjectLiteral] (for deserialisation performance improvements) - ObjectLiteralToStringSupport is required return new UrlDetails( source.Segments, source.QueryString.GetValueOrDefault(new QueryString(NonNullList<QueryString.Segment>.Empty)).Add(key, ObjectLiteralToStringSupport.ToString(value)) ); }
public void NavigateTo(UrlDetails url) { if (url == null) { throw new ArgumentNullException("url"); } // 2017-12-08 DWR: Since navigations fire Dispatcher actions, it means that there will be difficulties if using a Dispatcher which does not allow the handling of one action to dispatch // another action (which is something that recommended by Facebook - to not let one action fire another because this could lead to a difficult-to-reason-about chain of events) if you // want to have a successful update send the User to a success page (because the update-successfully-applied action would cause a navigation, which would dispatch another action). // As a workaround (which, granted, feels a little dirty), we'll apply a SetTimeout to the history manipulation to "break" the potential Dispatcher action chain. This doesn't feel // SO filthy since requesting a location change and waiting for the browser to apply it and a corresponding Dispatcher method to be fired could understandably be an async process.. // though, really, we only want this for convenience. Window.SetTimeout(() => { LastNavigatedToUrl = GetCurrentLocation(); Window.History.PushState(state: null, title: null, url: url.ToString()); RaiseCallbacks(url); }); }
public bool ExecuteCallbackIfUrlMatches(UrlDetails url) { if (url == null) { throw new ArgumentNullException("url"); } if (url.Segments.Count != _segments.Count) { return(false); } if (url.Segments.Zip(_segments, (x, y) => x.Value.Equals(y.Value, StringComparison.OrdinalIgnoreCase)).Any(isMatch => !isMatch)) { return(false); } _ifMatched(url.QueryString); return(true); }
public Props( UrlDetails url, Union <ReactElement, string> text, bool caseSensitiveUrlMatching, Optional <NonBlankTrimmedString> name, Optional <NonBlankTrimmedString> target, Optional <ClassName> className, Optional <ClassName> ancestorClassName, Optional <ClassName> selectedClassName, Optional <Action <MouseEvent <Bridge.Html5.HTMLAnchorElement> > > onClick, Optional <IInteractWithBrowserRouting> historyHandlerOverride) { this.CtorSet(_ => _.Url, url); this.CtorSet(_ => _.Text, text); this.CtorSet(_ => _.CaseSensitiveUrlMatching, caseSensitiveUrlMatching); this.CtorSet(_ => _.Name, name); this.CtorSet(_ => _.Target, target); this.CtorSet(_ => _.ClassName, className); this.CtorSet(_ => _.AncestorClassName, ancestorClassName); this.CtorSet(_ => _.SelectedClassName, selectedClassName); this.CtorSet(_ => _.OnClick, onClick); this.CtorSet(_ => _.HistoryHandlerOverride, historyHandlerOverride); }
private static void MatchRoute(UrlDetails url, NonNullList <IMatchRoutes> routes, Optional <Action <UrlPathDetails> > routeNotMatched) { if (url == null) { throw new ArgumentNullException(nameof(url)); } if (routes == null) { throw new ArgumentNullException(nameof(routes)); } foreach (var route in routes) { if (route.ExecuteCallbackIfUrlMatches(url)) { return; } } if (routeNotMatched.IsDefined) { routeNotMatched.Value(url); } }