44
55using System . Reflection ;
66using System . Text ;
7+ using System . Text . Encodings . Web ;
78using Elastic . Documentation . Extensions ;
89
910namespace Elastic . Documentation . Site ;
1011
12+ public static class UrlHelper
13+ {
14+ private static readonly KeyValuePair < string , string ? > [ ] VersionParameters = [ new ( "v" , Htmx . VersionHash ) ] ;
15+
16+ public static string AddVersionParameters ( string uri ) => AddQueryString ( uri , VersionParameters ) ;
17+
18+ /// <summary>
19+ /// Append the given query keys and values to the URI.
20+ /// </summary>
21+ /// <param name="uri">The base URI.</param>
22+ /// <param name="queryString">A collection of name value query pairs to append.</param>
23+ /// <returns>The combined result.</returns>
24+ /// <exception cref="ArgumentNullException"><paramref name="uri"/> is <c>null</c>.</exception>
25+ /// <exception cref="ArgumentNullException"><paramref name="queryString"/> is <c>null</c>.</exception>
26+ public static string AddQueryString (
27+ string uri ,
28+ IEnumerable < KeyValuePair < string , string ? > > queryString )
29+ {
30+ ArgumentNullException . ThrowIfNull ( uri ) ;
31+ ArgumentNullException . ThrowIfNull ( queryString ) ;
32+
33+ var anchorIndex = uri . IndexOf ( '#' ) ;
34+ var uriToBeAppended = uri . AsSpan ( ) ;
35+ var anchorText = ReadOnlySpan < char > . Empty ;
36+ // If there is an anchor, then the query string must be inserted before its first occurrence.
37+ if ( anchorIndex != - 1 )
38+ {
39+ anchorText = uriToBeAppended . Slice ( anchorIndex ) ;
40+ uriToBeAppended = uriToBeAppended . Slice ( 0 , anchorIndex ) ;
41+ }
42+
43+ var queryIndex = uriToBeAppended . IndexOf ( '?' ) ;
44+ var hasQuery = queryIndex != - 1 ;
45+
46+ var sb = new StringBuilder ( ) ;
47+ _ = sb . Append ( uriToBeAppended ) ;
48+ foreach ( var parameter in queryString )
49+ {
50+ if ( parameter . Value == null )
51+ continue ;
52+
53+ _ = sb . Append ( hasQuery ? '&' : '?' )
54+ . Append ( UrlEncoder . Default . Encode ( parameter . Key ) )
55+ . Append ( '=' )
56+ . Append ( UrlEncoder . Default . Encode ( parameter . Value ) ) ;
57+ hasQuery = true ;
58+ }
59+
60+ _ = sb . Append ( anchorText ) ;
61+ return sb . ToString ( ) ;
62+ }
63+ }
64+
1165public static class Htmx
1266{
1367 private static readonly string Version =
1468 Assembly . GetExecutingAssembly ( ) . GetCustomAttributes < AssemblyInformationalVersionAttribute > ( )
1569 . FirstOrDefault ( ) ? . InformationalVersion ?? "0.0.0" ;
70+
1671 public static readonly string VersionHash = ShortId . Create ( Version ) ;
1772
1873 public static string GetHxSelectOob ( bool hasSameTopLevelGroup ) => hasSameTopLevelGroup ? "#content-container,#toc-nav" : "#main-container" ;
@@ -31,7 +86,8 @@ public static string GetHxAttributes(
3186 string ? hxIndicator = HxIndicator
3287 )
3388 {
34- var hxGetUrl = targetUrl . Contains ( '?' ) ? $ "{ targetUrl } &v={ VersionHash } " : $ "{ targetUrl } ?v={ VersionHash } ";
89+ var hxGetUrl = UrlHelper . AddVersionParameters ( targetUrl ) ;
90+
3591 var attributes = new StringBuilder ( ) ;
3692 _ = attributes . Append ( $ " hx-get={ hxGetUrl } ") ;
3793 _ = attributes . Append ( $ " hx-select-oob={ hxSwapOob ?? GetHxSelectOob ( hasSameTopLevelGroup ) } ") ;
0 commit comments