@@ -23,108 +23,56 @@ export function truncateSmart(url: string, truncateLen: number, ellipsisChars?:
2323 ellipsisLengthBeforeParsing = ellipsisChars . length ;
2424 }
2525
26- let parse_url = function ( url : string ) {
27- // Functionality inspired by PHP function of same name
28- let urlObj : UrlObject = { } ;
29- let urlSub = url ;
30- let match = urlSub . match ( / ^ ( [ a - z ] + ) : \/ \/ / i) ;
31- if ( match ) {
32- urlObj . scheme = match [ 1 ] ;
33- urlSub = urlSub . substr ( match [ 0 ] . length ) ;
34- }
35- match = urlSub . match ( / ^ ( .* ?) (? = ( \? | # | \/ | $ ) ) / i) ;
36- if ( match ) {
37- urlObj . host = match [ 1 ] ;
38- urlSub = urlSub . substr ( match [ 0 ] . length ) ;
39- }
40- match = urlSub . match ( / ^ \/ ( .* ?) (? = ( \? | # | $ ) ) / i) ;
41- if ( match ) {
42- urlObj . path = match [ 1 ] ;
43- urlSub = urlSub . substr ( match [ 0 ] . length ) ;
44- }
45- match = urlSub . match ( / ^ \? ( .* ?) (? = ( # | $ ) ) / i) ;
46- if ( match ) {
47- urlObj . query = match [ 1 ] ;
48- urlSub = urlSub . substr ( match [ 0 ] . length ) ;
49- }
50- match = urlSub . match ( / ^ # ( .* ?) $ / i) ;
51- if ( match ) {
52- urlObj . fragment = match [ 1 ] ;
53- //urlSub = urlSub.substr(match[0].length); -- not used. Uncomment if adding another block.
54- }
55- return urlObj ;
56- } ;
57-
58- let buildUrl = function ( urlObj : UrlObject ) {
59- let url = '' ;
60- if ( urlObj . scheme && urlObj . host ) {
61- url += urlObj . scheme + '://' ;
62- }
63- if ( urlObj . host ) {
64- url += urlObj . host ;
65- }
66- if ( urlObj . path ) {
67- url += '/' + urlObj . path ;
68- }
69- if ( urlObj . query ) {
70- url += '?' + urlObj . query ;
71- }
72- if ( urlObj . fragment ) {
73- url += '#' + urlObj . fragment ;
74- }
75- return url ;
76- } ;
77-
78- let buildSegment = function ( segment : string , remainingAvailableLength : number ) {
79- let remainingAvailableLengthHalf = remainingAvailableLength / 2 ,
80- startOffset = Math . ceil ( remainingAvailableLengthHalf ) ,
81- endOffset = - 1 * Math . floor ( remainingAvailableLengthHalf ) ,
82- end = '' ;
83- if ( endOffset < 0 ) {
84- end = segment . substr ( endOffset ) ;
85- }
86- return segment . substr ( 0 , startOffset ) + ellipsisChars + end ;
87- } ;
26+ // If the URL is shorter than the truncate length, return it as is
8827 if ( url . length <= truncateLen ) {
8928 return url ;
9029 }
30+
9131 let availableLength = truncateLen - ellipsisLength ;
92- let urlObj = parse_url ( url ) ;
93- // Clean up the URL
32+ let urlObj = parseUrl ( url ) ;
33+
34+ // Clean up the URL by removing any malformed query string
35+ // (e.g. "?foo=bar?ignorethis")
9436 if ( urlObj . query ) {
95- let matchQuery = urlObj . query . match ( / ^ ( .* ?) (? = ( \? | \ #) ) ( .* ?) $ / i) ;
37+ let matchQuery = urlObj . query . match ( / ^ ( .* ?) (? = ( \? | # ) ) ( .* ?) $ / i) ;
9638 if ( matchQuery ) {
9739 // Malformed URL; two or more "?". Removed any content behind the 2nd.
9840 urlObj . query = urlObj . query . substr ( 0 , matchQuery [ 1 ] . length ) ;
9941 url = buildUrl ( urlObj ) ;
10042 }
10143 }
10244 if ( url . length <= truncateLen ) {
103- return url ;
45+ return url ; // removing a malformed query string brought the URL under the truncateLength
10446 }
47+
48+ // Clean up the URL by removing 'www.' from the host if it exists
10549 if ( urlObj . host ) {
10650 urlObj . host = urlObj . host . replace ( / ^ w w w \. / , '' ) ;
10751 url = buildUrl ( urlObj ) ;
10852 }
10953 if ( url . length <= truncateLen ) {
110- return url ;
54+ return url ; // removing 'www.' brought the URL under the truncateLength
11155 }
112- // Process and build the URL
113- let str = '' ;
56+
57+ // Process and build the truncated URL, starting with the hostname
58+ let truncatedUrl = '' ;
11459 if ( urlObj . host ) {
115- str += urlObj . host ;
60+ truncatedUrl += urlObj . host ;
11661 }
117- if ( str . length >= availableLength ) {
118- if ( ( urlObj . host as string ) . length == truncateLen ) {
119- return (
120- ( urlObj . host as string ) . substr ( 0 , truncateLen - ellipsisLength ) + ellipsisChars
121- ) . substr ( 0 , availableLength + ellipsisLengthBeforeParsing ) ;
62+ if ( truncatedUrl . length >= availableLength ) {
63+ if ( urlObj . host ! . length === truncateLen ) {
64+ return ( urlObj . host ! . substr ( 0 , truncateLen - ellipsisLength ) + ellipsisChars ) . substr (
65+ 0 ,
66+ availableLength + ellipsisLengthBeforeParsing
67+ ) ;
12268 }
123- return buildSegment ( str , availableLength ) . substr (
69+ return buildSegment ( truncatedUrl , availableLength , ellipsisChars ) . substr (
12470 0 ,
12571 availableLength + ellipsisLengthBeforeParsing
12672 ) ;
12773 }
74+
75+ // If we still have available chars left, add the path and query string
12876 let pathAndQuery = '' ;
12977 if ( urlObj . path ) {
13078 pathAndQuery += '/' + urlObj . path ;
@@ -133,53 +81,133 @@ export function truncateSmart(url: string, truncateLen: number, ellipsisChars?:
13381 pathAndQuery += '?' + urlObj . query ;
13482 }
13583 if ( pathAndQuery ) {
136- if ( ( str + pathAndQuery ) . length >= availableLength ) {
137- if ( ( str + pathAndQuery ) . length == truncateLen ) {
138- return ( str + pathAndQuery ) . substr ( 0 , truncateLen ) ;
84+ if ( ( truncatedUrl + pathAndQuery ) . length >= availableLength ) {
85+ if ( ( truncatedUrl + pathAndQuery ) . length == truncateLen ) {
86+ return ( truncatedUrl + pathAndQuery ) . substr ( 0 , truncateLen ) ;
13987 }
140- let remainingAvailableLength = availableLength - str . length ;
141- return ( str + buildSegment ( pathAndQuery , remainingAvailableLength ) ) . substr (
142- 0 ,
143- availableLength + ellipsisLengthBeforeParsing
144- ) ;
88+ let remainingAvailableLength = availableLength - truncatedUrl . length ;
89+ return (
90+ truncatedUrl + buildSegment ( pathAndQuery , remainingAvailableLength , ellipsisChars )
91+ ) . substr ( 0 , availableLength + ellipsisLengthBeforeParsing ) ;
14592 } else {
146- str += pathAndQuery ;
93+ truncatedUrl += pathAndQuery ;
14794 }
14895 }
96+
97+ // If we still have available chars left, add the fragment
14998 if ( urlObj . fragment ) {
15099 let fragment = '#' + urlObj . fragment ;
151- if ( ( str + fragment ) . length >= availableLength ) {
152- if ( ( str + fragment ) . length == truncateLen ) {
153- return ( str + fragment ) . substr ( 0 , truncateLen ) ;
100+ if ( ( truncatedUrl + fragment ) . length >= availableLength ) {
101+ if ( ( truncatedUrl + fragment ) . length == truncateLen ) {
102+ return ( truncatedUrl + fragment ) . substr ( 0 , truncateLen ) ;
154103 }
155- let remainingAvailableLength2 = availableLength - str . length ;
156- return ( str + buildSegment ( fragment , remainingAvailableLength2 ) ) . substr (
157- 0 ,
158- availableLength + ellipsisLengthBeforeParsing
159- ) ;
104+ let remainingAvailableLength2 = availableLength - truncatedUrl . length ;
105+ return (
106+ truncatedUrl + buildSegment ( fragment , remainingAvailableLength2 , ellipsisChars )
107+ ) . substr ( 0 , availableLength + ellipsisLengthBeforeParsing ) ;
160108 } else {
161- str += fragment ;
109+ truncatedUrl += fragment ;
162110 }
163111 }
112+
113+ // If we still have available chars left, add the scheme
164114 if ( urlObj . scheme && urlObj . host ) {
165115 let scheme = urlObj . scheme + '://' ;
166- if ( ( str + scheme ) . length < availableLength ) {
167- return ( scheme + str ) . substr ( 0 , truncateLen ) ;
116+ if ( ( truncatedUrl + scheme ) . length < availableLength ) {
117+ return ( scheme + truncatedUrl ) . substr ( 0 , truncateLen ) ;
168118 }
169119 }
170- if ( str . length <= truncateLen ) {
171- return str ;
120+ if ( truncatedUrl . length <= truncateLen ) {
121+ return truncatedUrl ;
172122 }
123+
173124 let end = '' ;
174125 if ( availableLength > 0 ) {
175- end = str . substr ( - 1 * Math . floor ( availableLength / 2 ) ) ;
126+ end = truncatedUrl . substr ( - 1 * Math . floor ( availableLength / 2 ) ) ;
176127 }
177- return ( str . substr ( 0 , Math . ceil ( availableLength / 2 ) ) + ellipsisChars + end ) . substr (
128+ return ( truncatedUrl . substr ( 0 , Math . ceil ( availableLength / 2 ) ) + ellipsisChars + end ) . substr (
178129 0 ,
179130 availableLength + ellipsisLengthBeforeParsing
180131 ) ;
181132}
182133
134+ /**
135+ * Parses a URL into its components: scheme, host, path, query, and fragment.
136+ */
137+ function parseUrl ( url : string ) : UrlObject {
138+ // Functionality inspired by PHP function of same name
139+ let urlObj : UrlObject = { } ;
140+ let urlSub = url ;
141+
142+ // Parse scheme
143+ let match = urlSub . match ( / ^ ( [ a - z ] + ) : \/ \/ / i) ;
144+ if ( match ) {
145+ urlObj . scheme = match [ 1 ] ;
146+ urlSub = urlSub . slice ( match [ 0 ] . length ) ;
147+ }
148+
149+ // Parse host
150+ match = urlSub . match ( / ^ ( .* ?) (? = ( \? | # | \/ | $ ) ) / i) ;
151+ if ( match ) {
152+ urlObj . host = match [ 1 ] ;
153+ urlSub = urlSub . slice ( match [ 0 ] . length ) ;
154+ }
155+
156+ // Parse path
157+ match = urlSub . match ( / ^ \/ ( .* ?) (? = ( \? | # | $ ) ) / i) ;
158+ if ( match ) {
159+ urlObj . path = match [ 1 ] ;
160+ urlSub = urlSub . slice ( match [ 0 ] . length ) ;
161+ }
162+
163+ // Parse query
164+ match = urlSub . match ( / ^ \? ( .* ?) (? = ( # | $ ) ) / i) ;
165+ if ( match ) {
166+ urlObj . query = match [ 1 ] ;
167+ urlSub = urlSub . slice ( match [ 0 ] . length ) ;
168+ }
169+
170+ // Parse fragment
171+ match = urlSub . match ( / ^ # ( .* ?) $ / i) ;
172+ if ( match ) {
173+ urlObj . fragment = match [ 1 ] ;
174+ //urlSub = urlSub.slice(match[0].length); -- not used. Uncomment if adding another block.
175+ }
176+
177+ return urlObj ;
178+ }
179+
180+ function buildUrl ( urlObj : UrlObject ) : string {
181+ let url = '' ;
182+ if ( urlObj . scheme && urlObj . host ) {
183+ url += urlObj . scheme + '://' ;
184+ }
185+ if ( urlObj . host ) {
186+ url += urlObj . host ;
187+ }
188+ if ( urlObj . path ) {
189+ url += '/' + urlObj . path ;
190+ }
191+ if ( urlObj . query ) {
192+ url += '?' + urlObj . query ;
193+ }
194+ if ( urlObj . fragment ) {
195+ url += '#' + urlObj . fragment ;
196+ }
197+ return url ;
198+ }
199+
200+ function buildSegment ( segment : string , remainingAvailableLength : number , ellipsisChars : string ) {
201+ let remainingAvailableLengthHalf = remainingAvailableLength / 2 ,
202+ startOffset = Math . ceil ( remainingAvailableLengthHalf ) ,
203+ endOffset = - 1 * Math . floor ( remainingAvailableLengthHalf ) ,
204+ end = '' ;
205+ if ( endOffset < 0 ) {
206+ end = segment . substr ( endOffset ) ;
207+ }
208+ return segment . substr ( 0 , startOffset ) + ellipsisChars + end ;
209+ }
210+
183211interface UrlObject {
184212 scheme ?: string ;
185213 host ?: string ;
0 commit comments