ngx-transforms 0.0.4 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/ngx-transforms.mjs +960 -642
- package/fesm2022/ngx-transforms.mjs.map +1 -1
- package/package.json +8 -3
- package/types/ngx-transforms.d.ts +479 -326
|
@@ -60,63 +60,623 @@ import * as QRCode from 'qrcode';
|
|
|
60
60
|
* @example
|
|
61
61
|
* // With inverted colors
|
|
62
62
|
* {{ 'DARK' | asciiArt:{ inverted: true, charset: CharsetPreset.MINIMAL } }}
|
|
63
|
+
*/
|
|
64
|
+
class AsciiArtPipe {
|
|
65
|
+
generator;
|
|
66
|
+
constructor() {
|
|
67
|
+
this.generator = new AsciiGenerator({
|
|
68
|
+
charset: CharsetPreset.STANDARD,
|
|
69
|
+
width: 80,
|
|
70
|
+
optimized: true,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
transform(value, options = {}) {
|
|
74
|
+
if (!value || typeof value !== 'string') {
|
|
75
|
+
return '';
|
|
76
|
+
}
|
|
77
|
+
const maxLength = 100;
|
|
78
|
+
if (value.length > maxLength) {
|
|
79
|
+
console.warn(`AsciiArtPipe: Input truncated to ${maxLength} characters for security`);
|
|
80
|
+
value = value.substring(0, maxLength);
|
|
81
|
+
}
|
|
82
|
+
const { format = 'html', textOptions, ...config } = options;
|
|
83
|
+
try {
|
|
84
|
+
if (Object.keys(config).length > 0) {
|
|
85
|
+
this.generator.updateConfig(config);
|
|
86
|
+
}
|
|
87
|
+
const result = this.generator.convertText(value, textOptions);
|
|
88
|
+
if (format === 'text') {
|
|
89
|
+
return `<pre class="ascii-art">${this.escapeHtml(result.text)}</pre>`;
|
|
90
|
+
}
|
|
91
|
+
// Return HTML format (already safe from ts-ascii-engine)
|
|
92
|
+
return result.html;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.error('AsciiArtPipe: Error generating ASCII art', error);
|
|
96
|
+
return `<pre class="ascii-art-error">Error: Unable to generate ASCII art</pre>`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Escapes HTML special characters to prevent XSS
|
|
101
|
+
* @private
|
|
102
|
+
*/
|
|
103
|
+
escapeHtml(text) {
|
|
104
|
+
const div = document.createElement('div');
|
|
105
|
+
div.textContent = text;
|
|
106
|
+
return div.innerHTML;
|
|
107
|
+
}
|
|
108
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AsciiArtPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
109
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: AsciiArtPipe, isStandalone: true, name: "asciiArt" });
|
|
110
|
+
}
|
|
111
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AsciiArtPipe, decorators: [{
|
|
112
|
+
type: Pipe,
|
|
113
|
+
args: [{
|
|
114
|
+
name: 'asciiArt',
|
|
115
|
+
standalone: true,
|
|
116
|
+
}]
|
|
117
|
+
}], ctorParameters: () => [] });
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* CamelCasePipe: Converts text to camelCase (e.g., "hello world" → "helloWorld").
|
|
121
|
+
*
|
|
122
|
+
* @param {string} value - The input string to transform.
|
|
123
|
+
* @returns {string} The string in camelCase, or an empty string if input is invalid.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```html
|
|
127
|
+
* {{ 'hello world' | camelCase }} <!-- Outputs: helloWorld -->
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
class CamelCasePipe {
|
|
131
|
+
transform(value) {
|
|
132
|
+
if (!value || typeof value !== 'string')
|
|
133
|
+
return '';
|
|
134
|
+
return value
|
|
135
|
+
.toLowerCase()
|
|
136
|
+
.replace(/[^a-zA-Z0-9]+/g, ' ')
|
|
137
|
+
.trim()
|
|
138
|
+
.split(' ')
|
|
139
|
+
.filter(word => word.length > 0)
|
|
140
|
+
.map((word, index) => index === 0
|
|
141
|
+
? word.toLowerCase()
|
|
142
|
+
: word.charAt(0).toUpperCase() + word.slice(1))
|
|
143
|
+
.join('');
|
|
144
|
+
}
|
|
145
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CamelCasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
146
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: CamelCasePipe, isStandalone: true, name: "camelCase" });
|
|
147
|
+
}
|
|
148
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CamelCasePipe, decorators: [{
|
|
149
|
+
type: Pipe,
|
|
150
|
+
args: [{
|
|
151
|
+
name: 'camelCase',
|
|
152
|
+
standalone: true
|
|
153
|
+
}]
|
|
154
|
+
}] });
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* HighlightPipe: Highlights occurrences of a search term within a string.
|
|
158
|
+
*
|
|
159
|
+
* This Angular pipe transforms a string input by wrapping all occurrences of a specified
|
|
160
|
+
* search term with a `<span>` element that has the class "highlight".
|
|
161
|
+
* It uses the Angular `DomSanitizer` to bypass security and render the highlighted HTML.
|
|
162
|
+
*
|
|
163
|
+
* @param {string} value - The input string in which to highlight the search term.
|
|
164
|
+
* @param {string} searchTerm - The string to search for and highlight.
|
|
165
|
+
* @returns {SafeHtml} - The input string with the search term highlighted, or an empty string if input or searchTerm are falsy.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* {{ 'This is a test string' | highlight: 'test' }} // Returns 'This is a <span class="highlight">test</span> string'
|
|
169
|
+
* {{ 'This is a test TEST string' | highlight: 'test' }} // Returns 'This is a <span class="highlight">test</span> <span class="highlight">TEST</span> string'
|
|
170
|
+
* {{ 'This is a test string' | highlight: '' }} // Returns 'This is a test string'
|
|
171
|
+
* {{ null | highlight: 'test' }} // Returns ''
|
|
172
|
+
* {{ undefined | highlight: 'test' }} // Returns ''
|
|
173
|
+
*/
|
|
174
|
+
class HighlightPipe {
|
|
175
|
+
sanitizer = inject(DomSanitizer);
|
|
176
|
+
transform(value, searchTerm) {
|
|
177
|
+
if (!value || !searchTerm) {
|
|
178
|
+
return this.sanitizer.bypassSecurityTrustHtml(value || '');
|
|
179
|
+
}
|
|
180
|
+
const escapedSearch = searchTerm.replace(/[.*+?${}()|[\\]/g, '\\$&');
|
|
181
|
+
const regex = new RegExp(`(${escapedSearch})`, 'gi');
|
|
182
|
+
const highlighed = value.replace(regex, '<span class="highlight">$1</span>');
|
|
183
|
+
return this.sanitizer.bypassSecurityTrustHtml(highlighed);
|
|
184
|
+
}
|
|
185
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: HighlightPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
186
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: HighlightPipe, isStandalone: true, name: "highlight" });
|
|
187
|
+
}
|
|
188
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: HighlightPipe, decorators: [{
|
|
189
|
+
type: Pipe,
|
|
190
|
+
args: [{
|
|
191
|
+
name: 'highlight',
|
|
192
|
+
standalone: true
|
|
193
|
+
}]
|
|
194
|
+
}] });
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* InitialsPipe: Extracts initials from a name.
|
|
198
|
+
*
|
|
199
|
+
* @param {string} value - The full name.
|
|
200
|
+
*
|
|
201
|
+
* @returns {string} - The initials (e.g., 'John Doe' → 'JD').
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* {{ 'John Doe' | initials }} // Outputs: JD
|
|
205
|
+
* {{ 'Mary Jane Watson' | initials }} // Outputs: MJW
|
|
206
|
+
*/
|
|
207
|
+
class InitialsPipe {
|
|
208
|
+
transform(value) {
|
|
209
|
+
if (!value)
|
|
210
|
+
return '';
|
|
211
|
+
return value
|
|
212
|
+
.trim()
|
|
213
|
+
.split(/\s+/)
|
|
214
|
+
.map(word => word.charAt(0).toUpperCase())
|
|
215
|
+
.join('');
|
|
216
|
+
}
|
|
217
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: InitialsPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
218
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: InitialsPipe, isStandalone: true, name: "initials" });
|
|
219
|
+
}
|
|
220
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: InitialsPipe, decorators: [{
|
|
221
|
+
type: Pipe,
|
|
222
|
+
args: [{
|
|
223
|
+
name: 'initials',
|
|
224
|
+
standalone: true
|
|
225
|
+
}]
|
|
226
|
+
}] });
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* KebabCasePipe: Converts text to kebab-case (e.g., "hello world" → "hello-world").
|
|
230
|
+
*
|
|
231
|
+
* @param {string} value - The input string to transform.
|
|
232
|
+
* @returns {string} The string in kebab-case, or an empty string if input is invalid.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```html
|
|
236
|
+
* {{ 'hello world' | kebabCase }} <!-- Outputs: hello-world -->
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
class KebabCasePipe {
|
|
240
|
+
transform(value) {
|
|
241
|
+
if (!value || typeof value !== 'string')
|
|
242
|
+
return '';
|
|
243
|
+
return value
|
|
244
|
+
.trim()
|
|
245
|
+
.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2') // Add hyphen between camelCase words
|
|
246
|
+
.toLowerCase()
|
|
247
|
+
.replace(/[^a-z0-9-]+/g, '-') // Replace non-alphanumeric (except hyphen) with hyphen
|
|
248
|
+
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
|
249
|
+
}
|
|
250
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: KebabCasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
251
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: KebabCasePipe, isStandalone: true, name: "kebabCase" });
|
|
252
|
+
}
|
|
253
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: KebabCasePipe, decorators: [{
|
|
254
|
+
type: Pipe,
|
|
255
|
+
args: [{
|
|
256
|
+
name: 'kebabCase',
|
|
257
|
+
standalone: true
|
|
258
|
+
}]
|
|
259
|
+
}] });
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* MorseCodePipe: Converts text to Morse code.
|
|
263
|
+
*
|
|
264
|
+
* @param {string} value - The text to convert to Morse code.
|
|
265
|
+
*
|
|
266
|
+
* @returns {string} - The Morse code representation (e.g., 'SOS' → '... --- ...').
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* {{ 'SOS' | morseCode }} // Outputs: '... --- ...'
|
|
270
|
+
* {{ 'HELP' | morseCode }} // Outputs: '.... . .-.. .--.'
|
|
271
|
+
* <p>{{ userInput | morseCode }}</p>
|
|
272
|
+
*/
|
|
273
|
+
class MorseCodePipe {
|
|
274
|
+
morseCodeMap = {
|
|
275
|
+
'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.',
|
|
276
|
+
'G': '--.', 'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..',
|
|
277
|
+
'M': '--', 'N': '-.', 'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.',
|
|
278
|
+
'S': '...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-',
|
|
279
|
+
'Y': '-.--', 'Z': '--..', '0': '-----', '1': '.----', '2': '..---',
|
|
280
|
+
'3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...',
|
|
281
|
+
'8': '---..', '9': '----.'
|
|
282
|
+
};
|
|
283
|
+
transform(value) {
|
|
284
|
+
if (!value || typeof value !== 'string') {
|
|
285
|
+
return '';
|
|
286
|
+
}
|
|
287
|
+
return value
|
|
288
|
+
.toUpperCase()
|
|
289
|
+
.split('')
|
|
290
|
+
.map(char => this.morseCodeMap[char] || '')
|
|
291
|
+
.filter(code => code)
|
|
292
|
+
.join(' ');
|
|
293
|
+
}
|
|
294
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MorseCodePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
295
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: MorseCodePipe, isStandalone: true, name: "morseCode" });
|
|
296
|
+
}
|
|
297
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MorseCodePipe, decorators: [{
|
|
298
|
+
type: Pipe,
|
|
299
|
+
args: [{
|
|
300
|
+
name: 'morseCode',
|
|
301
|
+
standalone: true
|
|
302
|
+
}]
|
|
303
|
+
}] });
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* ReplacePipe: A custom Angular pipe that either highlights or replaces text based on a pattern.
|
|
307
|
+
*
|
|
308
|
+
* - If `isReplace` is `false`, it highlights occurrences of the pattern (if `highlightClass` is provided).
|
|
309
|
+
* - If `isReplace` is `true`, it replaces occurrences of the pattern with the replacement string, optionally highlighting the replacement.
|
|
310
|
+
*
|
|
311
|
+
* @param {string} value - The input string to transform.
|
|
312
|
+
* @param {string | RegExp} pattern - The pattern to match (string or RegExp). If an empty string, the value is returned as-is.
|
|
313
|
+
* @param {string} replacement - The string to replace matches with.
|
|
314
|
+
* @param {string} [highlightClass] - Optional CSS class for highlighting matched or replaced text (e.g., 'highlight').
|
|
315
|
+
* @param {boolean} [isReplace=true] - Whether to perform replacement (true) or only highlight matches (false).
|
|
316
|
+
*
|
|
317
|
+
* @returns {string | SafeHtml} - Returns the transformed string or SafeHtml with highlights.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* {{ 'Hello World' | replace:'World':'Universe' }}
|
|
321
|
+
* // Output: Hello Universe
|
|
322
|
+
*
|
|
323
|
+
* {{ 'test123' | replace:/\d+/g:'X':'highlight' }}
|
|
324
|
+
* // Output: test<span class="highlight">X</span>
|
|
325
|
+
*
|
|
326
|
+
* {{ 'Angular is great' | replace:'great':'awesome':'highlight':true }}
|
|
327
|
+
* // Output: Angular is <span class="highlight">awesome</span>
|
|
328
|
+
*
|
|
329
|
+
* {{ 'Angular is great' | replace:'great':'awesome':'highlight':false }}
|
|
330
|
+
* // Output: Angular is <span class="highlight">great</span>
|
|
331
|
+
*
|
|
332
|
+
* <div [innerHTML]="'Angular is great' | replace:'great':'awesome':'highlight':false"></div>
|
|
333
|
+
* // Renders: Angular is <span class="highlight">great</span>
|
|
334
|
+
*/
|
|
335
|
+
class ReplacePipe {
|
|
336
|
+
sanitizer = inject(DomSanitizer);
|
|
337
|
+
transform(value, pattern, replacement, highlightClass, isReplace = true) {
|
|
338
|
+
if (!value)
|
|
339
|
+
return '';
|
|
340
|
+
// handles empty string pattern
|
|
341
|
+
if (!pattern || (typeof pattern === 'string' && pattern.trim() === '')) {
|
|
342
|
+
return value;
|
|
343
|
+
}
|
|
344
|
+
const finalPattern = typeof pattern === 'string' ? new RegExp(pattern, 'gi') : pattern;
|
|
345
|
+
if (!highlightClass) {
|
|
346
|
+
return isReplace ? value.replace(finalPattern, replacement) : value;
|
|
347
|
+
}
|
|
348
|
+
// Sanitize the replacement to prevent XSS
|
|
349
|
+
const sanitizedReplacement = replacement.replace(/</g, '<').replace(/>/g, '>');
|
|
350
|
+
if (isReplace) {
|
|
351
|
+
const highlightedReplacement = `<span class="${highlightClass}">${sanitizedReplacement}</span>`;
|
|
352
|
+
const replaced = value.replace(finalPattern, highlightedReplacement);
|
|
353
|
+
return this.sanitizer.bypassSecurityTrustHtml(replaced);
|
|
354
|
+
}
|
|
355
|
+
const highlightedMatch = `<span class="${highlightClass}">$&</span>`;
|
|
356
|
+
const result = value.replace(finalPattern, highlightedMatch);
|
|
357
|
+
return this.sanitizer.bypassSecurityTrustHtml(result);
|
|
358
|
+
}
|
|
359
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ReplacePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
360
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: ReplacePipe, isStandalone: true, name: "replace" });
|
|
361
|
+
}
|
|
362
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ReplacePipe, decorators: [{
|
|
363
|
+
type: Pipe,
|
|
364
|
+
args: [{
|
|
365
|
+
name: 'replace',
|
|
366
|
+
standalone: true
|
|
367
|
+
}]
|
|
368
|
+
}] });
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* SnakeCasePipe: Converts text to snake_case (e.g., "hello world" → "hello_world").
|
|
372
|
+
*
|
|
373
|
+
* @param {string} value - The input string to transform.
|
|
374
|
+
* @returns {string} The string in snake_case, or an empty string if input is invalid.
|
|
375
|
+
*
|
|
376
|
+
* @example
|
|
377
|
+
* ```html
|
|
378
|
+
* {{ 'hello world' | snakeCase }} <!-- Outputs: hello_world -->
|
|
379
|
+
* ```
|
|
380
|
+
*/
|
|
381
|
+
class SnakeCasePipe {
|
|
382
|
+
transform(value) {
|
|
383
|
+
if (!value || typeof value !== 'string')
|
|
384
|
+
return '';
|
|
385
|
+
return value
|
|
386
|
+
.trim()
|
|
387
|
+
.replace(/([A-Z])/g, '_$1') // Convert camelCase to snake_case (e.g., helloWorld -> hello_World)
|
|
388
|
+
.toLowerCase() // Convert everything to lowercase
|
|
389
|
+
.replace(/[\s-]+/g, '_') // Replace spaces and hyphens with underscores
|
|
390
|
+
.replace(/[^a-z0-9_]+/g, '') // Remove all non-alphanumeric and non-underscore characters
|
|
391
|
+
.replace(/_+/g, '_') // Collapse multiple underscores
|
|
392
|
+
.replace(/^_|_$/g, ''); // Remove leading/trailing underscores
|
|
393
|
+
}
|
|
394
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: SnakeCasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
395
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: SnakeCasePipe, isStandalone: true, name: "snakeCase" });
|
|
396
|
+
}
|
|
397
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: SnakeCasePipe, decorators: [{
|
|
398
|
+
type: Pipe,
|
|
399
|
+
args: [{
|
|
400
|
+
name: 'snakeCase',
|
|
401
|
+
standalone: true
|
|
402
|
+
}]
|
|
403
|
+
}] });
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* TitleCasePipe: Capitalizes the first letter of each word in a string.
|
|
407
|
+
*
|
|
408
|
+
* @param {string} value - The input string to transform.
|
|
409
|
+
* @returns {string} The string with each word capitalized, or an empty string if input is invalid.
|
|
410
|
+
*
|
|
411
|
+
* @example
|
|
412
|
+
* ```html
|
|
413
|
+
* {{ 'hello world' | titleCase }} <!-- Outputs: Hello World -->
|
|
414
|
+
* ```
|
|
415
|
+
*/
|
|
416
|
+
class TitleCasePipe {
|
|
417
|
+
transform(value) {
|
|
418
|
+
if (!value || typeof value !== 'string')
|
|
419
|
+
return '';
|
|
420
|
+
return value
|
|
421
|
+
.split(' ')
|
|
422
|
+
.filter(word => word.length > 0)
|
|
423
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
424
|
+
.join(' ');
|
|
425
|
+
}
|
|
426
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TitleCasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
427
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: TitleCasePipe, isStandalone: true, name: "titleCase" });
|
|
428
|
+
}
|
|
429
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TitleCasePipe, decorators: [{
|
|
430
|
+
type: Pipe,
|
|
431
|
+
args: [{
|
|
432
|
+
name: 'titleCase',
|
|
433
|
+
standalone: true
|
|
434
|
+
}]
|
|
435
|
+
}] });
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* TruncatePipe: Truncates a string to a specified maximum length, optionally preserving words.
|
|
439
|
+
*
|
|
440
|
+
* This Angular pipe transforms a string input by truncating it to the given `maxLength`.
|
|
441
|
+
* It provides options to customize the ellipsis and preserve word boundaries.
|
|
442
|
+
*
|
|
443
|
+
* @param {string} value - The input string to be truncated.
|
|
444
|
+
* @param {number} [maxLength=10] - The maximum length of the truncated string. Defaults to 10.
|
|
445
|
+
* @param {string} [ellipsis='...'] - The string to append to the truncated portion. Defaults to '...'.
|
|
446
|
+
* @param {boolean} [preserveWords=false] - If true, truncates at the last space before `maxLength` to avoid cutting words. Defaults to false.
|
|
447
|
+
* @returns {string} - The truncated string. Returns an empty string if the input is null, undefined, or not a string.
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* {{ 'This is a long sentence' | truncate }} // Returns 'This is a...'
|
|
451
|
+
* {{ 'This is a long sentence' | truncate: 20 }} // Returns 'This is a long sente...'
|
|
452
|
+
* {{ 'This is a long sentence' | truncate: 15: ' [more]' }} // Returns 'This is a long [more]'
|
|
453
|
+
* {{ 'This is a long sentence' | truncate: 15: '...' : true }} // Returns 'This is a...'
|
|
454
|
+
* {{ 'This is a long sentence' | truncate: 20: '...' : true }} // Returns 'This is a long...'
|
|
455
|
+
* {{ null | truncate }} // Returns ''
|
|
456
|
+
* {{ undefined | truncate }} // Returns ''
|
|
457
|
+
*/
|
|
458
|
+
class TruncatePipe {
|
|
459
|
+
transform(value, maxLength = 10, ellipsis = '...', preserveWords = false) {
|
|
460
|
+
if (!value || typeof value !== 'string') {
|
|
461
|
+
return '';
|
|
462
|
+
}
|
|
463
|
+
if (value.length <= maxLength) {
|
|
464
|
+
return value;
|
|
465
|
+
}
|
|
466
|
+
const charsToKeep = maxLength - ellipsis.length;
|
|
467
|
+
// If maxLength is too small to even include the ellipsis, just return the ellipsis.
|
|
468
|
+
if (charsToKeep < 0) {
|
|
469
|
+
return ellipsis;
|
|
470
|
+
}
|
|
471
|
+
let truncated = value.substring(0, charsToKeep);
|
|
472
|
+
if (preserveWords) {
|
|
473
|
+
const lastSpaceIndex = truncated.lastIndexOf(' ');
|
|
474
|
+
// If a space is found and it's not the very beginning of the string
|
|
475
|
+
if (lastSpaceIndex !== -1 && lastSpaceIndex !== 0) {
|
|
476
|
+
truncated = truncated.substring(0, lastSpaceIndex);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return truncated.trim() + ellipsis;
|
|
480
|
+
}
|
|
481
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TruncatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
482
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: TruncatePipe, isStandalone: true, name: "truncate" });
|
|
483
|
+
}
|
|
484
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TruncatePipe, decorators: [{
|
|
485
|
+
type: Pipe,
|
|
486
|
+
args: [{
|
|
487
|
+
name: 'truncate',
|
|
488
|
+
standalone: true
|
|
489
|
+
}]
|
|
490
|
+
}] });
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* CreditCardMaskPipe: Masks all but the last four digits of a string, optionally controlled by a boolean flag.
|
|
494
|
+
* By default, masking is applied.
|
|
495
|
+
*
|
|
496
|
+
* @param {string} value - The input string to mask (e.g., credit card number).
|
|
497
|
+
* @param {boolean} shouldMask - (Optional) Determines if masking should be applied. Defaults to true.
|
|
498
|
+
* @returns {string} - The masked string or the original value if `shouldMask` is false or the value is too short.
|
|
499
|
+
*
|
|
500
|
+
* @example
|
|
501
|
+
* {{ '1234567890123456' | creditCardMask }} // Outputs: **** **** **** 3456
|
|
502
|
+
* {{ '1234-5678-9012-3456' | creditCardMask }} // Outputs: **** **** **** 3456
|
|
503
|
+
* {{ '1234567890123456' | creditCardMask: true }} // Outputs: **** **** **** 3456
|
|
504
|
+
* {{ '1234567890123456' | creditCardMask: false }} // Outputs: 1234567890123456
|
|
505
|
+
*/
|
|
506
|
+
class CreditCardMaskPipe {
|
|
507
|
+
transform(value, shouldMask = true) {
|
|
508
|
+
if (!value) {
|
|
509
|
+
return value;
|
|
510
|
+
}
|
|
511
|
+
if (shouldMask) {
|
|
512
|
+
const cleanedValue = value.replace(/[\s-]/g, '');
|
|
513
|
+
const cleanedLength = cleanedValue.length;
|
|
514
|
+
if (cleanedLength < 4) {
|
|
515
|
+
return value;
|
|
516
|
+
}
|
|
517
|
+
const visibleDigits = cleanedValue.slice(-4);
|
|
518
|
+
const maskedSection = '*'.repeat(cleanedLength - 4);
|
|
519
|
+
const groupedMask = maskedSection.match(/.{1,4}/g)?.join(' ') ?? '';
|
|
520
|
+
return `${groupedMask} ${visibleDigits}`.trim();
|
|
521
|
+
}
|
|
522
|
+
return value;
|
|
523
|
+
}
|
|
524
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CreditCardMaskPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
525
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: CreditCardMaskPipe, isStandalone: true, name: "creditCardMask" });
|
|
526
|
+
}
|
|
527
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CreditCardMaskPipe, decorators: [{
|
|
528
|
+
type: Pipe,
|
|
529
|
+
args: [{
|
|
530
|
+
name: 'creditCardMask',
|
|
531
|
+
standalone: true,
|
|
532
|
+
}]
|
|
533
|
+
}] });
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* EmailMaskPipe: Masks the local part of an email address, revealing only the first and last characters.
|
|
537
|
+
*
|
|
538
|
+
* This Angular pipe transforms an email address string by replacing the characters between the first and last characters of the local part (before the '@') with "***".
|
|
539
|
+
* If the local part is 2 characters or less, it masks all characters except the first.
|
|
540
|
+
*
|
|
541
|
+
* @param {string} value - The email address string to be masked.
|
|
542
|
+
* @returns {string} - The masked email address, or the original value if it's not a valid email or falsy.
|
|
543
|
+
*
|
|
544
|
+
* @example
|
|
545
|
+
* {{ 'test@example.com' | emailMask }} // Returns 't***t@example.com'
|
|
546
|
+
* {{ 'te@example.com' | emailMask }} // Returns 't***@example.com'
|
|
547
|
+
* {{ 't@example.com' | emailMask }} // Returns 't***@example.com'
|
|
548
|
+
* {{ 'example.com' | emailMask }} // Returns 'example.com'
|
|
549
|
+
* {{ null | emailMask }} // Returns ''
|
|
550
|
+
* {{ undefined | emailMask }} // Returns ''
|
|
551
|
+
*/
|
|
552
|
+
class EmailMaskPipe {
|
|
553
|
+
transform(value) {
|
|
554
|
+
if (!value || !value.includes('@')) {
|
|
555
|
+
return value || '';
|
|
556
|
+
}
|
|
557
|
+
const [local, domain] = value.split('@');
|
|
558
|
+
if (local.length <= 2) {
|
|
559
|
+
return `${local[0]}***@${domain}`;
|
|
560
|
+
}
|
|
561
|
+
const firstChar = local[0];
|
|
562
|
+
const lastChar = local[local.length - 1];
|
|
563
|
+
return `${firstChar}***${lastChar}@${domain}`;
|
|
564
|
+
}
|
|
565
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailMaskPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
566
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: EmailMaskPipe, isStandalone: true, name: "emailMask" });
|
|
567
|
+
}
|
|
568
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: EmailMaskPipe, decorators: [{
|
|
569
|
+
type: Pipe,
|
|
570
|
+
args: [{
|
|
571
|
+
name: 'emailMask',
|
|
572
|
+
standalone: true
|
|
573
|
+
}]
|
|
574
|
+
}] });
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Converts special HTML characters in a string to their corresponding HTML entities.
|
|
578
|
+
* This prevents the browser from interpreting the input as HTML, rendering it as plain text.
|
|
579
|
+
*
|
|
580
|
+
* @param {string} value - The input string containing HTML to escape.
|
|
581
|
+
* @returns {string} The string with special HTML characters escaped, or an empty string if input is invalid.
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* ```html
|
|
585
|
+
* {{ '<p>Hello</p>' | htmlEscape }} <!-- Outputs: <p>Hello</p> -->
|
|
586
|
+
* ```
|
|
587
|
+
*/
|
|
588
|
+
class HtmlEscapePipe {
|
|
589
|
+
transform(value) {
|
|
590
|
+
if (!value || typeof value !== 'string')
|
|
591
|
+
return '';
|
|
592
|
+
const escapeMap = {
|
|
593
|
+
'&': '&',
|
|
594
|
+
'<': '<',
|
|
595
|
+
'>': '>',
|
|
596
|
+
'"': '"',
|
|
597
|
+
"'": '''
|
|
598
|
+
};
|
|
599
|
+
return value.replace(/[&<>"']/g, char => escapeMap[char]);
|
|
600
|
+
}
|
|
601
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: HtmlEscapePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
602
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: HtmlEscapePipe, isStandalone: true, name: "htmlEscape" });
|
|
603
|
+
}
|
|
604
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: HtmlEscapePipe, decorators: [{
|
|
605
|
+
type: Pipe,
|
|
606
|
+
args: [{
|
|
607
|
+
name: 'htmlEscape',
|
|
608
|
+
standalone: true
|
|
609
|
+
}]
|
|
610
|
+
}] });
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Sanitizes HTML input to remove unsafe elements while allowing safe HTML to be rendered.
|
|
614
|
+
* Uses Angular's DomSanitizer to mark the output as trusted for use in [innerHTML].
|
|
615
|
+
*
|
|
616
|
+
* @param {string} value - The input string containing HTML to sanitize.
|
|
617
|
+
* @returns {SafeHtml} The sanitized HTML marked as safe, or an empty string if input is invalid.
|
|
618
|
+
*
|
|
619
|
+
* @remarks
|
|
620
|
+
* WARNING: Use with caution. Only apply to trusted input to avoid XSS risks.
|
|
621
|
+
* Ensure input is pre-validated or sourced from a secure origin (e.g., a controlled rich-text editor).
|
|
622
|
+
*
|
|
623
|
+
* @example
|
|
624
|
+
* ```html
|
|
625
|
+
* <div [innerHTML]="'<p>Hello</p><script>alert(1)</script>' | htmlSanitize"></div>
|
|
626
|
+
* <!-- Renders: <p>Hello</p> (script tag removed) -->
|
|
627
|
+
* ```
|
|
628
|
+
*/
|
|
629
|
+
class HtmlSanitizePipe {
|
|
630
|
+
sanitizer = inject(DomSanitizer);
|
|
631
|
+
transform(value) {
|
|
632
|
+
if (!value || typeof value !== 'string')
|
|
633
|
+
return this.sanitizer.bypassSecurityTrustHtml('');
|
|
634
|
+
return this.sanitizer.sanitize(0, value) || this.sanitizer.bypassSecurityTrustHtml('');
|
|
635
|
+
}
|
|
636
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: HtmlSanitizePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
637
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: HtmlSanitizePipe, isStandalone: true, name: "htmlSanitize" });
|
|
638
|
+
}
|
|
639
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: HtmlSanitizePipe, decorators: [{
|
|
640
|
+
type: Pipe,
|
|
641
|
+
args: [{
|
|
642
|
+
name: 'htmlSanitize',
|
|
643
|
+
standalone: true
|
|
644
|
+
}]
|
|
645
|
+
}] });
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* IpAddressMaskPipe: Masks the last two octets of an IPv4 address.
|
|
649
|
+
*
|
|
650
|
+
* @param {string} value - The IPv4 address (e.g., 192.168.1.1).
|
|
651
|
+
* @param {boolean} shouldMask - (Optional) Determines if masking should be applied. Defaults to true..
|
|
652
|
+
*
|
|
653
|
+
* @returns {string} - The masked IP address (e.g., 192.168.*.*).
|
|
63
654
|
*
|
|
64
|
-
* @
|
|
655
|
+
* @example
|
|
656
|
+
* {{ '192.168.1.1' | ipAddressMask }} // Outputs: 192.168.*.*
|
|
657
|
+
* {{ '10.0.0.255' | ipAddressMask }} // Outputs: 10.0.*.*
|
|
65
658
|
*/
|
|
66
|
-
class
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
charset: CharsetPreset.STANDARD,
|
|
71
|
-
width: 80,
|
|
72
|
-
optimized: true,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
transform(value, options = {}) {
|
|
76
|
-
if (!value || typeof value !== 'string') {
|
|
77
|
-
return '';
|
|
78
|
-
}
|
|
79
|
-
const maxLength = 100;
|
|
80
|
-
if (value.length > maxLength) {
|
|
81
|
-
console.warn(`AsciiArtPipe: Input truncated to ${maxLength} characters for security`);
|
|
82
|
-
value = value.substring(0, maxLength);
|
|
83
|
-
}
|
|
84
|
-
const { format = 'html', textOptions, ...config } = options;
|
|
85
|
-
try {
|
|
86
|
-
if (Object.keys(config).length > 0) {
|
|
87
|
-
this.generator.updateConfig(config);
|
|
88
|
-
}
|
|
89
|
-
const result = this.generator.convertText(value, textOptions);
|
|
90
|
-
if (format === 'text') {
|
|
91
|
-
return `<pre class="ascii-art">${this.escapeHtml(result.text)}</pre>`;
|
|
92
|
-
}
|
|
93
|
-
// Return HTML format (already safe from ts-ascii-engine)
|
|
94
|
-
return result.html;
|
|
659
|
+
class IpAddressMaskPipe {
|
|
660
|
+
transform(value, shouldMask = true) {
|
|
661
|
+
if (!value || !/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(value)) {
|
|
662
|
+
return value;
|
|
95
663
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return
|
|
664
|
+
if (shouldMask) {
|
|
665
|
+
const parts = value.split('.');
|
|
666
|
+
return `${parts[0]}.${parts[1]}.*.*`;
|
|
99
667
|
}
|
|
668
|
+
return value;
|
|
100
669
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
* @private
|
|
104
|
-
*/
|
|
105
|
-
escapeHtml(text) {
|
|
106
|
-
const div = document.createElement('div');
|
|
107
|
-
div.textContent = text;
|
|
108
|
-
return div.innerHTML;
|
|
109
|
-
}
|
|
110
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AsciiArtPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
111
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: AsciiArtPipe, isStandalone: true, name: "asciiArt" });
|
|
670
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: IpAddressMaskPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
671
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: IpAddressMaskPipe, isStandalone: true, name: "ipAddressMask" });
|
|
112
672
|
}
|
|
113
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
673
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: IpAddressMaskPipe, decorators: [{
|
|
114
674
|
type: Pipe,
|
|
115
675
|
args: [{
|
|
116
|
-
name: '
|
|
117
|
-
standalone: true
|
|
676
|
+
name: 'ipAddressMask',
|
|
677
|
+
standalone: true
|
|
118
678
|
}]
|
|
119
|
-
}]
|
|
679
|
+
}] });
|
|
120
680
|
|
|
121
681
|
/**
|
|
122
682
|
* BarcodePipe: Generates a barcode from a string value.
|
|
@@ -129,8 +689,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
129
689
|
* @example
|
|
130
690
|
* <div [innerHTML]="'123456789' | barcode:{elementType:'svg',format:'CODE128'} | async"></div>
|
|
131
691
|
* <img [src]="'123456789' | barcode:{elementType:'img'} | async" />
|
|
132
|
-
*
|
|
133
|
-
* @author Mofiro Jean
|
|
134
692
|
*/
|
|
135
693
|
class BarcodePipe {
|
|
136
694
|
sanitizer = inject(DomSanitizer);
|
|
@@ -171,45 +729,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
171
729
|
}]
|
|
172
730
|
}] });
|
|
173
731
|
|
|
174
|
-
/**
|
|
175
|
-
* CamelCasePipe: Converts text to camelCase (e.g., "hello world" → "helloWorld").
|
|
176
|
-
*
|
|
177
|
-
* @param {string} value - The input string to transform.
|
|
178
|
-
* @returns {string} The string in camelCase, or an empty string if input is invalid.
|
|
179
|
-
*
|
|
180
|
-
* @example
|
|
181
|
-
* ```html
|
|
182
|
-
* {{ 'hello world' | camelCase }} <!-- Outputs: helloWorld -->
|
|
183
|
-
* ```
|
|
184
|
-
*
|
|
185
|
-
* @author Mofiro Jean
|
|
186
|
-
*/
|
|
187
|
-
class CamelCasePipe {
|
|
188
|
-
transform(value) {
|
|
189
|
-
if (!value || typeof value !== 'string')
|
|
190
|
-
return '';
|
|
191
|
-
return value
|
|
192
|
-
.toLowerCase()
|
|
193
|
-
.replace(/[^a-zA-Z0-9]+/g, ' ')
|
|
194
|
-
.trim()
|
|
195
|
-
.split(' ')
|
|
196
|
-
.filter(word => word.length > 0)
|
|
197
|
-
.map((word, index) => index === 0
|
|
198
|
-
? word.toLowerCase()
|
|
199
|
-
: word.charAt(0).toUpperCase() + word.slice(1))
|
|
200
|
-
.join('');
|
|
201
|
-
}
|
|
202
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CamelCasePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
203
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: CamelCasePipe, isStandalone: true, name: "camelCase" });
|
|
204
|
-
}
|
|
205
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CamelCasePipe, decorators: [{
|
|
206
|
-
type: Pipe,
|
|
207
|
-
args: [{
|
|
208
|
-
name: 'camelCase',
|
|
209
|
-
standalone: true
|
|
210
|
-
}]
|
|
211
|
-
}] });
|
|
212
|
-
|
|
213
732
|
// Pre-compiled regex patterns for performance
|
|
214
733
|
const HEX_6_PATTERN = /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/;
|
|
215
734
|
const HEX_8_PATTERN = /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/;
|
|
@@ -361,8 +880,6 @@ function parseColor(value) {
|
|
|
361
880
|
* // Alpha channel support
|
|
362
881
|
* {{ '#FF000080' | colorConvert:'rgba' }} // rgba(255, 0, 0, 0.5)
|
|
363
882
|
* {{ 'rgba(255, 0, 0, 0.5)' | colorConvert:'hex' }} // #FF000080
|
|
364
|
-
*
|
|
365
|
-
* @author Mofiro Jean
|
|
366
883
|
*/
|
|
367
884
|
class ColorConvertPipe {
|
|
368
885
|
transform(value, target) {
|
|
@@ -378,172 +895,25 @@ class ColorConvertPipe {
|
|
|
378
895
|
switch (target) {
|
|
379
896
|
case 'hex':
|
|
380
897
|
if (a < 1) {
|
|
381
|
-
return `#${toHex(r)}${toHex(g)}${toHex(b)}${alphaToHex(a)}`;
|
|
382
|
-
}
|
|
383
|
-
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
384
|
-
case 'rgb':
|
|
385
|
-
return `rgb(${clampChannel(r)}, ${clampChannel(g)}, ${clampChannel(b)})`;
|
|
386
|
-
case 'rgba':
|
|
387
|
-
return `rgba(${clampChannel(r)}, ${clampChannel(g)}, ${clampChannel(b)}, ${clampAlpha(a)})`;
|
|
388
|
-
default:
|
|
389
|
-
return value;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ColorConvertPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
393
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: ColorConvertPipe, isStandalone: true, name: "colorConvert" });
|
|
394
|
-
}
|
|
395
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ColorConvertPipe, decorators: [{
|
|
396
|
-
type: Pipe,
|
|
397
|
-
args: [{
|
|
398
|
-
name: 'colorConvert',
|
|
399
|
-
standalone: true,
|
|
400
|
-
}]
|
|
401
|
-
}] });
|
|
402
|
-
|
|
403
|
-
class CountPipe {
|
|
404
|
-
transform(value) {
|
|
405
|
-
if (value === null || value === undefined) {
|
|
406
|
-
return 0;
|
|
407
|
-
}
|
|
408
|
-
if (Array.isArray(value) || typeof value === 'string') {
|
|
409
|
-
return value.length;
|
|
410
|
-
}
|
|
411
|
-
if (typeof value === 'object') {
|
|
412
|
-
return Object.keys(value).length;
|
|
413
|
-
}
|
|
414
|
-
return 0;
|
|
415
|
-
}
|
|
416
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CountPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
417
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: CountPipe, isStandalone: true, name: "count" });
|
|
418
|
-
}
|
|
419
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CountPipe, decorators: [{
|
|
420
|
-
type: Pipe,
|
|
421
|
-
args: [{
|
|
422
|
-
name: 'count',
|
|
423
|
-
standalone: true
|
|
424
|
-
}]
|
|
425
|
-
}] });
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* CreditCardMaskPipe: Masks all but the last four digits of a string, optionally controlled by a boolean flag.
|
|
429
|
-
* By default, masking is applied.
|
|
430
|
-
*
|
|
431
|
-
* @param {string} value - The input string to mask (e.g., credit card number).
|
|
432
|
-
* @param {boolean} shouldMask - (Optional) Determines if masking should be applied. Defaults to true.
|
|
433
|
-
* @returns {string} - The masked string or the original value if `shouldMask` is false or the value is too short.
|
|
434
|
-
*
|
|
435
|
-
* @example
|
|
436
|
-
* {{ '1234567890123456' | creditCardMask }} // Outputs: **** **** **** 3456
|
|
437
|
-
* {{ '1234-5678-9012-3456' | creditCardMask }} // Outputs: **** **** **** 3456
|
|
438
|
-
* {{ '1234567890123456' | creditCardMask: true }} // Outputs: **** **** **** 3456
|
|
439
|
-
* {{ '1234567890123456' | creditCardMask: false }} // Outputs: 1234567890123456
|
|
440
|
-
*
|
|
441
|
-
* @author Mofiro Jean
|
|
442
|
-
*/
|
|
443
|
-
class CreditCardMaskPipe {
|
|
444
|
-
transform(value, shouldMask = true) {
|
|
445
|
-
if (!value) {
|
|
446
|
-
return value;
|
|
447
|
-
}
|
|
448
|
-
if (shouldMask) {
|
|
449
|
-
const cleanedValue = value.replace(/[\s-]/g, '');
|
|
450
|
-
const cleanedLength = cleanedValue.length;
|
|
451
|
-
if (cleanedLength < 4) {
|
|
452
|
-
return value;
|
|
453
|
-
}
|
|
454
|
-
const visibleDigits = cleanedValue.slice(-4);
|
|
455
|
-
const maskedSection = '*'.repeat(cleanedLength - 4);
|
|
456
|
-
const groupedMask = maskedSection.match(/.{1,4}/g)?.join(' ') ?? '';
|
|
457
|
-
return `${groupedMask} ${visibleDigits}`.trim();
|
|
458
|
-
}
|
|
459
|
-
return value;
|
|
460
|
-
}
|
|
461
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CreditCardMaskPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
462
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: CreditCardMaskPipe, isStandalone: true, name: "creditCardMask" });
|
|
463
|
-
}
|
|
464
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CreditCardMaskPipe, decorators: [{
|
|
465
|
-
type: Pipe,
|
|
466
|
-
args: [{
|
|
467
|
-
name: 'creditCardMask',
|
|
468
|
-
standalone: true,
|
|
469
|
-
}]
|
|
470
|
-
}] });
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* DeviceTypePipe: Detects the device type based on the user agent string.
|
|
474
|
-
*
|
|
475
|
-
* @param {string} value - The user agent string (defaults to navigator.userAgent).
|
|
476
|
-
*
|
|
477
|
-
* @returns {'mobile' | 'tablet' | 'desktop' | 'unknown'} - The detected device type.
|
|
478
|
-
*
|
|
479
|
-
* @example
|
|
480
|
-
* {{ '' | device }} // Outputs: 'mobile' (on a mobile device)
|
|
481
|
-
* <div *ngIf="'' | device === 'desktop'">Desktop-only content</div>
|
|
482
|
-
*/
|
|
483
|
-
class DeviceTypePipe {
|
|
484
|
-
transform(value = typeof navigator !== 'undefined' ? navigator.userAgent : '') {
|
|
485
|
-
if (!value)
|
|
486
|
-
return 'unknown';
|
|
487
|
-
const userAgent = value.toLowerCase();
|
|
488
|
-
const isMobile = /mobile|android|iphone|ipod|blackberry|opera mini|iemobile|windows phone/i.test(userAgent);
|
|
489
|
-
const isTablet = /ipad|tablet|kindle|playbook|silk|nexus 7|nexus 10|android(?!.*mobile)/i.test(userAgent);
|
|
490
|
-
if (isMobile)
|
|
491
|
-
return 'mobile';
|
|
492
|
-
if (isTablet)
|
|
493
|
-
return 'tablet';
|
|
494
|
-
return 'desktop';
|
|
495
|
-
}
|
|
496
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: DeviceTypePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
497
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: DeviceTypePipe, isStandalone: true, name: "device" });
|
|
498
|
-
}
|
|
499
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: DeviceTypePipe, decorators: [{
|
|
500
|
-
type: Pipe,
|
|
501
|
-
args: [{
|
|
502
|
-
name: 'device',
|
|
503
|
-
standalone: true
|
|
504
|
-
}]
|
|
505
|
-
}] });
|
|
506
|
-
|
|
507
|
-
/**
|
|
508
|
-
* EmailMaskPipe: Masks the local part of an email address, revealing only the first and last characters.
|
|
509
|
-
*
|
|
510
|
-
* This Angular pipe transforms an email address string by replacing the characters between the first and last characters of the local part (before the '@') with "***".
|
|
511
|
-
* If the local part is 2 characters or less, it masks all characters except the first.
|
|
512
|
-
*
|
|
513
|
-
* @param {string} value - The email address string to be masked.
|
|
514
|
-
* @returns {string} - The masked email address, or the original value if it's not a valid email or falsy.
|
|
515
|
-
*
|
|
516
|
-
* @example
|
|
517
|
-
* {{ 'test@example.com' | emailMask }} // Returns 't***t@example.com'
|
|
518
|
-
* {{ 'te@example.com' | emailMask }} // Returns 't***@example.com'
|
|
519
|
-
* {{ 't@example.com' | emailMask }} // Returns 't***@example.com'
|
|
520
|
-
* {{ 'example.com' | emailMask }} // Returns 'example.com'
|
|
521
|
-
* {{ null | emailMask }} // Returns ''
|
|
522
|
-
* {{ undefined | emailMask }} // Returns ''
|
|
523
|
-
*
|
|
524
|
-
* @author Mofiro Jean
|
|
525
|
-
*/
|
|
526
|
-
class EmailMaskPipe {
|
|
527
|
-
transform(value) {
|
|
528
|
-
if (!value || !value.includes('@')) {
|
|
529
|
-
return value || '';
|
|
530
|
-
}
|
|
531
|
-
const [local, domain] = value.split('@');
|
|
532
|
-
if (local.length <= 2) {
|
|
533
|
-
return `${local[0]}***@${domain}`;
|
|
898
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}${alphaToHex(a)}`;
|
|
899
|
+
}
|
|
900
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
901
|
+
case 'rgb':
|
|
902
|
+
return `rgb(${clampChannel(r)}, ${clampChannel(g)}, ${clampChannel(b)})`;
|
|
903
|
+
case 'rgba':
|
|
904
|
+
return `rgba(${clampChannel(r)}, ${clampChannel(g)}, ${clampChannel(b)}, ${clampAlpha(a)})`;
|
|
905
|
+
default:
|
|
906
|
+
return value;
|
|
534
907
|
}
|
|
535
|
-
const firstChar = local[0];
|
|
536
|
-
const lastChar = local[local.length - 1];
|
|
537
|
-
return `${firstChar}***${lastChar}@${domain}`;
|
|
538
908
|
}
|
|
539
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
540
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type:
|
|
909
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ColorConvertPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
910
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: ColorConvertPipe, isStandalone: true, name: "colorConvert" });
|
|
541
911
|
}
|
|
542
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
912
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ColorConvertPipe, decorators: [{
|
|
543
913
|
type: Pipe,
|
|
544
914
|
args: [{
|
|
545
|
-
name: '
|
|
546
|
-
standalone: true
|
|
915
|
+
name: 'colorConvert',
|
|
916
|
+
standalone: true,
|
|
547
917
|
}]
|
|
548
918
|
}] });
|
|
549
919
|
|
|
@@ -557,8 +927,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
557
927
|
*
|
|
558
928
|
* @example
|
|
559
929
|
* <img [src]="'user@example.com' | gravatar:100" />
|
|
560
|
-
*
|
|
561
|
-
* @author Mofiro Jean
|
|
562
930
|
*/
|
|
563
931
|
class GravatarPipe {
|
|
564
932
|
transform(value, size = 80) {
|
|
@@ -579,184 +947,89 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
579
947
|
}] });
|
|
580
948
|
|
|
581
949
|
/**
|
|
582
|
-
*
|
|
950
|
+
* QrCodePipe: Generates a QR code from a string.
|
|
583
951
|
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
* It uses the Angular `DomSanitizer` to bypass security and render the highlighted HTML.
|
|
952
|
+
* @param {string} value - The string to encode.
|
|
953
|
+
* @param {QrCodeOptions} [options] - The QR code options.
|
|
587
954
|
*
|
|
588
|
-
* @
|
|
589
|
-
* @param {string} searchTerm - The string to search for and highlight.
|
|
590
|
-
* @returns {SafeHtml} - The input string with the search term highlighted, or an empty string if input or searchTerm are falsy.
|
|
955
|
+
* @returns {Promise<string>} - A promise that resolves with the QR code data URL.
|
|
591
956
|
*
|
|
592
957
|
* @example
|
|
593
|
-
*
|
|
594
|
-
* {{ 'This is a test TEST string' | highlight: 'test' }} // Returns 'This is a <span class="highlight">test</span> <span class="highlight">TEST</span> string'
|
|
595
|
-
* {{ 'This is a test string' | highlight: '' }} // Returns 'This is a test string'
|
|
596
|
-
* {{ null | highlight: 'test' }} // Returns ''
|
|
597
|
-
* {{ undefined | highlight: 'test' }} // Returns ''
|
|
598
|
-
*
|
|
599
|
-
* @author Mofiro Jean
|
|
958
|
+
* <img [src]="'Hello, World!' | qrCode | async" />
|
|
600
959
|
*/
|
|
601
|
-
class
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
return this.sanitizer.bypassSecurityTrustHtml(value || '');
|
|
960
|
+
class QrCodePipe {
|
|
961
|
+
transform(value, options) {
|
|
962
|
+
if (!value) {
|
|
963
|
+
return Promise.resolve('');
|
|
606
964
|
}
|
|
607
|
-
|
|
608
|
-
const regex = new RegExp(`(${escapedSearch})`, 'gi');
|
|
609
|
-
const highlighed = value.replace(regex, '<span class="highlight">$1</span>');
|
|
610
|
-
return this.sanitizer.bypassSecurityTrustHtml(highlighed);
|
|
611
|
-
}
|
|
612
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: HighlightPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
613
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: HighlightPipe, isStandalone: true, name: "highlight" });
|
|
614
|
-
}
|
|
615
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: HighlightPipe, decorators: [{
|
|
616
|
-
type: Pipe,
|
|
617
|
-
args: [{
|
|
618
|
-
name: 'highlight',
|
|
619
|
-
standalone: true
|
|
620
|
-
}]
|
|
621
|
-
}] });
|
|
622
|
-
|
|
623
|
-
/**
|
|
624
|
-
* Converts special HTML characters in a string to their corresponding HTML entities.
|
|
625
|
-
* This prevents the browser from interpreting the input as HTML, rendering it as plain text.
|
|
626
|
-
*
|
|
627
|
-
* @param {string} value - The input string containing HTML to escape.
|
|
628
|
-
* @returns {string} The string with special HTML characters escaped, or an empty string if input is invalid.
|
|
629
|
-
*
|
|
630
|
-
* @example
|
|
631
|
-
* ```html
|
|
632
|
-
* {{ '<p>Hello</p>' | htmlEscape }} <!-- Outputs: <p>Hello</p> -->
|
|
633
|
-
* ```
|
|
634
|
-
*/
|
|
635
|
-
class HtmlEscapePipe {
|
|
636
|
-
transform(value) {
|
|
637
|
-
if (!value || typeof value !== 'string')
|
|
638
|
-
return '';
|
|
639
|
-
const escapeMap = {
|
|
640
|
-
'&': '&',
|
|
641
|
-
'<': '<',
|
|
642
|
-
'>': '>',
|
|
643
|
-
'"': '"',
|
|
644
|
-
"'": '''
|
|
645
|
-
};
|
|
646
|
-
return value.replace(/[&<>"']/g, char => escapeMap[char]);
|
|
965
|
+
return QRCode.toDataURL(value, options);
|
|
647
966
|
}
|
|
648
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
649
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type:
|
|
967
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: QrCodePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
968
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: QrCodePipe, isStandalone: true, name: "qrCode" });
|
|
650
969
|
}
|
|
651
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
970
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: QrCodePipe, decorators: [{
|
|
652
971
|
type: Pipe,
|
|
653
972
|
args: [{
|
|
654
|
-
name: '
|
|
973
|
+
name: 'qrCode',
|
|
655
974
|
standalone: true
|
|
656
975
|
}]
|
|
657
976
|
}] });
|
|
658
977
|
|
|
659
|
-
|
|
660
|
-
* Sanitizes HTML input to remove unsafe elements while allowing safe HTML to be rendered.
|
|
661
|
-
* Uses Angular's DomSanitizer to mark the output as trusted for use in [innerHTML].
|
|
662
|
-
*
|
|
663
|
-
* @param {string} value - The input string containing HTML to sanitize.
|
|
664
|
-
* @returns {SafeHtml} The sanitized HTML marked as safe, or an empty string if input is invalid.
|
|
665
|
-
*
|
|
666
|
-
* @remarks
|
|
667
|
-
* WARNING: Use with caution. Only apply to trusted input to avoid XSS risks.
|
|
668
|
-
* Ensure input is pre-validated or sourced from a secure origin (e.g., a controlled rich-text editor).
|
|
669
|
-
*
|
|
670
|
-
* @example
|
|
671
|
-
* ```html
|
|
672
|
-
* <div [innerHTML]="'<p>Hello</p><script>alert(1)</script>' | htmlSanitize"></div>
|
|
673
|
-
* <!-- Renders: <p>Hello</p> (script tag removed) -->
|
|
674
|
-
* ```
|
|
675
|
-
*/
|
|
676
|
-
class HtmlSanitizePipe {
|
|
677
|
-
sanitizer = inject(DomSanitizer);
|
|
978
|
+
class CountPipe {
|
|
678
979
|
transform(value) {
|
|
679
|
-
if (
|
|
680
|
-
return
|
|
681
|
-
|
|
980
|
+
if (value === null || value === undefined) {
|
|
981
|
+
return 0;
|
|
982
|
+
}
|
|
983
|
+
if (Array.isArray(value) || typeof value === 'string') {
|
|
984
|
+
return value.length;
|
|
985
|
+
}
|
|
986
|
+
if (typeof value === 'object') {
|
|
987
|
+
return Object.keys(value).length;
|
|
988
|
+
}
|
|
989
|
+
return 0;
|
|
682
990
|
}
|
|
683
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
684
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type:
|
|
991
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CountPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
992
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: CountPipe, isStandalone: true, name: "count" });
|
|
685
993
|
}
|
|
686
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
994
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CountPipe, decorators: [{
|
|
687
995
|
type: Pipe,
|
|
688
996
|
args: [{
|
|
689
|
-
name: '
|
|
997
|
+
name: 'count',
|
|
690
998
|
standalone: true
|
|
691
999
|
}]
|
|
692
1000
|
}] });
|
|
693
1001
|
|
|
694
1002
|
/**
|
|
695
|
-
*
|
|
1003
|
+
* DeviceTypePipe: Detects the device type based on the user agent string.
|
|
696
1004
|
*
|
|
697
|
-
* @param {string} value - The
|
|
1005
|
+
* @param {string} value - The user agent string (defaults to navigator.userAgent).
|
|
698
1006
|
*
|
|
699
|
-
* @returns {
|
|
1007
|
+
* @returns {'mobile' | 'tablet' | 'desktop' | 'unknown'} - The detected device type.
|
|
700
1008
|
*
|
|
701
1009
|
* @example
|
|
702
|
-
* {{ '
|
|
703
|
-
*
|
|
704
|
-
*
|
|
705
|
-
* @author Mofiro Jean
|
|
1010
|
+
* {{ '' | device }} // Outputs: 'mobile' (on a mobile device)
|
|
1011
|
+
* <div *ngIf="'' | device === 'desktop'">Desktop-only content</div>
|
|
706
1012
|
*/
|
|
707
|
-
class
|
|
708
|
-
transform(value) {
|
|
1013
|
+
class DeviceTypePipe {
|
|
1014
|
+
transform(value = typeof navigator !== 'undefined' ? navigator.userAgent : '') {
|
|
709
1015
|
if (!value)
|
|
710
|
-
return '';
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
}
|
|
720
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: InitialsPipe, decorators: [{
|
|
721
|
-
type: Pipe,
|
|
722
|
-
args: [{
|
|
723
|
-
name: 'initials',
|
|
724
|
-
standalone: true
|
|
725
|
-
}]
|
|
726
|
-
}] });
|
|
727
|
-
|
|
728
|
-
/**
|
|
729
|
-
* IpAddressMaskPipe: Masks the last two octets of an IPv4 address.
|
|
730
|
-
*
|
|
731
|
-
* @param {string} value - The IPv4 address (e.g., 192.168.1.1).
|
|
732
|
-
* @param {boolean} shouldMask - (Optional) Determines if masking should be applied. Defaults to true..
|
|
733
|
-
*
|
|
734
|
-
* @returns {string} - The masked IP address (e.g., 192.168.*.*).
|
|
735
|
-
*
|
|
736
|
-
* @example
|
|
737
|
-
* {{ '192.168.1.1' | ipAddressMask }} // Outputs: 192.168.*.*
|
|
738
|
-
* {{ '10.0.0.255' | ipAddressMask }} // Outputs: 10.0.*.*
|
|
739
|
-
*
|
|
740
|
-
* @author Mofiro Jean
|
|
741
|
-
*/
|
|
742
|
-
class IpAddressMaskPipe {
|
|
743
|
-
transform(value, shouldMask = true) {
|
|
744
|
-
if (!value || !/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(value)) {
|
|
745
|
-
return value;
|
|
746
|
-
}
|
|
747
|
-
if (shouldMask) {
|
|
748
|
-
const parts = value.split('.');
|
|
749
|
-
return `${parts[0]}.${parts[1]}.*.*`;
|
|
750
|
-
}
|
|
751
|
-
return value;
|
|
1016
|
+
return 'unknown';
|
|
1017
|
+
const userAgent = value.toLowerCase();
|
|
1018
|
+
const isMobile = /mobile|android|iphone|ipod|blackberry|opera mini|iemobile|windows phone/i.test(userAgent);
|
|
1019
|
+
const isTablet = /ipad|tablet|kindle|playbook|silk|nexus 7|nexus 10|android(?!.*mobile)/i.test(userAgent);
|
|
1020
|
+
if (isMobile)
|
|
1021
|
+
return 'mobile';
|
|
1022
|
+
if (isTablet)
|
|
1023
|
+
return 'tablet';
|
|
1024
|
+
return 'desktop';
|
|
752
1025
|
}
|
|
753
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
754
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1026
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: DeviceTypePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1027
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: DeviceTypePipe, isStandalone: true, name: "device" });
|
|
755
1028
|
}
|
|
756
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1029
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: DeviceTypePipe, decorators: [{
|
|
757
1030
|
type: Pipe,
|
|
758
1031
|
args: [{
|
|
759
|
-
name: '
|
|
1032
|
+
name: 'device',
|
|
760
1033
|
standalone: true
|
|
761
1034
|
}]
|
|
762
1035
|
}] });
|
|
@@ -772,8 +1045,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
772
1045
|
* @example
|
|
773
1046
|
* {{ '{"name": "John", "age": 30}' | jsonPretty }} // Outputs: Colorful, indented JSON
|
|
774
1047
|
* <pre [innerHTML]="data | jsonPretty:4"></pre> // 4-space indentation
|
|
775
|
-
*
|
|
776
|
-
* @author Mofiro Jean
|
|
777
1048
|
*/
|
|
778
1049
|
class JsonPrettyPipe {
|
|
779
1050
|
sanitizer = inject(DomSanitizer);
|
|
@@ -841,180 +1112,162 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
841
1112
|
}] });
|
|
842
1113
|
|
|
843
1114
|
/**
|
|
844
|
-
*
|
|
1115
|
+
* TextToSpeechPipe: Converts text to speech using the Web Speech API.
|
|
845
1116
|
*
|
|
846
|
-
* @param {string} value - The
|
|
847
|
-
* @
|
|
1117
|
+
* @param {string} value - The text to convert to speech.
|
|
1118
|
+
* @param {string} [lang='en-US'] - The language (local) for speech synthesis.
|
|
848
1119
|
*
|
|
849
|
-
* @
|
|
850
|
-
* ```html
|
|
851
|
-
* {{ 'hello world' | kebabCase }} <!-- Outputs: hello-world -->
|
|
852
|
-
* ```
|
|
1120
|
+
* @returns {void} - Triggers speech synthesis (no return value).
|
|
853
1121
|
*
|
|
854
|
-
* @
|
|
1122
|
+
* @example
|
|
1123
|
+
* <div>{{ Hello World' | textToSpeech }}</div>
|
|
1124
|
+
* <div>{{ 'Bonjour' | textToSpeech:'fr-FR' }}h</div>
|
|
855
1125
|
*/
|
|
856
|
-
class
|
|
857
|
-
transform(value) {
|
|
858
|
-
if (!value || typeof
|
|
859
|
-
return
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
.toLowerCase()
|
|
864
|
-
.replace(/[^a-z0-9-]+/g, '-') // Replace non-alphanumeric (except hyphen) with hyphen
|
|
865
|
-
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
|
1126
|
+
class TextToSpeechPipe {
|
|
1127
|
+
transform(value, lang = 'en-US') {
|
|
1128
|
+
if (!value || typeof window === 'undefined' || !window.speechSynthesis)
|
|
1129
|
+
return;
|
|
1130
|
+
const uttrance = new SpeechSynthesisUtterance(value);
|
|
1131
|
+
uttrance.lang = lang;
|
|
1132
|
+
window.speechSynthesis.speak(uttrance);
|
|
866
1133
|
}
|
|
867
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
868
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1134
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TextToSpeechPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1135
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: TextToSpeechPipe, isStandalone: true, name: "textToSpeech" });
|
|
869
1136
|
}
|
|
870
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1137
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TextToSpeechPipe, decorators: [{
|
|
871
1138
|
type: Pipe,
|
|
872
1139
|
args: [{
|
|
873
|
-
name: '
|
|
1140
|
+
name: 'textToSpeech',
|
|
874
1141
|
standalone: true
|
|
875
1142
|
}]
|
|
876
1143
|
}] });
|
|
877
1144
|
|
|
878
1145
|
/**
|
|
879
|
-
|
|
1146
|
+
* TimeAgo: Converts a date into a localized time string.
|
|
880
1147
|
*
|
|
881
|
-
*
|
|
1148
|
+
* Use the in-built Intl.RelativeTimeFormat to convert a date into a localized time string.
|
|
1149
|
+
* It was chosen over moment.js because it's more lightweight and supports more locales.
|
|
1150
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat
|
|
882
1151
|
*
|
|
883
|
-
* @
|
|
1152
|
+
* @param {Date | number | string} value - The date to convert.
|
|
1153
|
+
* @param {string} [local='en'] - BCP 47 local code (e.g., 'en', 'fr', 'es').
|
|
1154
|
+
*
|
|
1155
|
+
* @returns {string} - The localized time string.
|
|
884
1156
|
*
|
|
885
1157
|
* @example
|
|
886
|
-
* {{
|
|
887
|
-
* {{
|
|
888
|
-
*
|
|
889
|
-
*
|
|
890
|
-
*
|
|
891
|
-
*/
|
|
892
|
-
class
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1158
|
+
* {{ date | timeAgo }} // '5 minutes ago'
|
|
1159
|
+
* {{ date | timeAgo:'fr' }} // 'il y a 5 minutes'
|
|
1160
|
+
*
|
|
1161
|
+
* @note Pure pipe - output won't automatically update as time passes.
|
|
1162
|
+
* Use signals or periodic change detection to re-trigger.
|
|
1163
|
+
* */
|
|
1164
|
+
class TimeAgoPipePipe {
|
|
1165
|
+
static THRESHOLDS = [
|
|
1166
|
+
[60, 1, 'second'],
|
|
1167
|
+
[3600, 60, 'minute'],
|
|
1168
|
+
[86400, 3600, 'hour'],
|
|
1169
|
+
[604800, 86400, 'day'],
|
|
1170
|
+
[2592000, 604800, 'week'],
|
|
1171
|
+
[31536000, 2592000, 'month'],
|
|
1172
|
+
[Infinity, 31536000, 'year'],
|
|
1173
|
+
];
|
|
1174
|
+
cacheLocal = '';
|
|
1175
|
+
rtf;
|
|
1176
|
+
transform(value, local = 'en') {
|
|
1177
|
+
if (value === null || value === undefined || value === "") {
|
|
1178
|
+
return "";
|
|
905
1179
|
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
.
|
|
1180
|
+
const date = new Date(value);
|
|
1181
|
+
if (isNaN(date.getTime())) {
|
|
1182
|
+
return "";
|
|
1183
|
+
}
|
|
1184
|
+
if (local !== this.cacheLocal) {
|
|
1185
|
+
this.rtf = new Intl.RelativeTimeFormat(local, { numeric: "auto" });
|
|
1186
|
+
this.cacheLocal = local;
|
|
1187
|
+
}
|
|
1188
|
+
const seconds = Math.floor((date.getTime() - Date.now()) / 1000);
|
|
1189
|
+
for (const [max, divisor, unit] of TimeAgoPipePipe.THRESHOLDS) {
|
|
1190
|
+
if (Math.abs(seconds) < max) {
|
|
1191
|
+
return this.rtf.format(Math.round(seconds / divisor), unit);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
return "";
|
|
912
1195
|
}
|
|
913
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
914
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1196
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TimeAgoPipePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1197
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: TimeAgoPipePipe, isStandalone: true, name: "timeAgo" });
|
|
915
1198
|
}
|
|
916
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1199
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TimeAgoPipePipe, decorators: [{
|
|
917
1200
|
type: Pipe,
|
|
918
1201
|
args: [{
|
|
919
|
-
name: '
|
|
1202
|
+
name: 'timeAgo',
|
|
920
1203
|
standalone: true
|
|
921
1204
|
}]
|
|
922
1205
|
}] });
|
|
923
1206
|
|
|
924
1207
|
/**
|
|
925
|
-
*
|
|
1208
|
+
* FlattenPipe: Flattens nested arrays to a specified depth.
|
|
926
1209
|
*
|
|
927
|
-
* @param {
|
|
928
|
-
* @param {
|
|
1210
|
+
* @param {unknown[]} value - The nested array to flatten.
|
|
1211
|
+
* @param {number} [depth=Infinity] - How many levels of nesting to flatten.
|
|
929
1212
|
*
|
|
930
|
-
* @returns {
|
|
1213
|
+
* @returns {unknown[]} - A new flattened array.
|
|
931
1214
|
*
|
|
932
1215
|
* @example
|
|
933
|
-
*
|
|
934
|
-
*
|
|
935
|
-
*
|
|
1216
|
+
* {{ [[1, 2], [3, 4]] | flatten }} // [1, 2, 3, 4]
|
|
1217
|
+
* {{ [[1, [2, [3]]]] | flatten:1 }} // [1, 2, [3]]
|
|
1218
|
+
* {{ [['a', 'b'], ['c']] | flatten }} // ['a', 'b', 'c']
|
|
936
1219
|
*/
|
|
937
|
-
class
|
|
938
|
-
transform(value,
|
|
939
|
-
if (!value) {
|
|
940
|
-
return
|
|
1220
|
+
class Flatten {
|
|
1221
|
+
transform(value, depth = Infinity) {
|
|
1222
|
+
if (!Array.isArray(value)) {
|
|
1223
|
+
return [];
|
|
941
1224
|
}
|
|
942
|
-
return
|
|
1225
|
+
return value.flat(depth);
|
|
943
1226
|
}
|
|
944
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
945
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1227
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: Flatten, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1228
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: Flatten, isStandalone: true, name: "flatten" });
|
|
946
1229
|
}
|
|
947
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1230
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: Flatten, decorators: [{
|
|
948
1231
|
type: Pipe,
|
|
949
1232
|
args: [{
|
|
950
|
-
name: '
|
|
1233
|
+
name: 'flatten',
|
|
951
1234
|
standalone: true
|
|
952
1235
|
}]
|
|
953
1236
|
}] });
|
|
954
1237
|
|
|
955
1238
|
/**
|
|
956
|
-
*
|
|
957
|
-
*
|
|
958
|
-
* - If `isReplace` is `false`, it highlights occurrences of the pattern (if `highlightClass` is provided).
|
|
959
|
-
* - If `isReplace` is `true`, it replaces occurrences of the pattern with the replacement string, optionally highlighting the replacement.
|
|
1239
|
+
* InitialPipe: Returns all elements except the last n.
|
|
960
1240
|
*
|
|
961
|
-
* @param {
|
|
962
|
-
* @param {
|
|
963
|
-
* @param {string} replacement - The string to replace matches with.
|
|
964
|
-
* @param {string} [highlightClass] - Optional CSS class for highlighting matched or replaced text (e.g., 'highlight').
|
|
965
|
-
* @param {boolean} [isReplace=true] - Whether to perform replacement (true) or only highlight matches (false).
|
|
1241
|
+
* @param {unknown[]} value - The array to slice.
|
|
1242
|
+
* @param {number} [n=1] - Number of elements to exclude from the end.
|
|
966
1243
|
*
|
|
967
|
-
* @returns {
|
|
1244
|
+
* @returns {unknown[]} - A new array without the last n elements.
|
|
968
1245
|
*
|
|
969
1246
|
* @example
|
|
970
|
-
* {{
|
|
971
|
-
*
|
|
972
|
-
*
|
|
973
|
-
* {{ 'test123' | replace:/\d+/g:'X':'highlight' }}
|
|
974
|
-
* // Output: test<span class="highlight">X</span>
|
|
975
|
-
*
|
|
976
|
-
* {{ 'Angular is great' | replace:'great':'awesome':'highlight':true }}
|
|
977
|
-
* // Output: Angular is <span class="highlight">awesome</span>
|
|
978
|
-
*
|
|
979
|
-
* {{ 'Angular is great' | replace:'great':'awesome':'highlight':false }}
|
|
980
|
-
* // Output: Angular is <span class="highlight">great</span>
|
|
981
|
-
*
|
|
982
|
-
* <div [innerHTML]="'Angular is great' | replace:'great':'awesome':'highlight':false"></div>
|
|
983
|
-
* // Renders: Angular is <span class="highlight">great</span>
|
|
984
|
-
*
|
|
985
|
-
* @author Mofiro Jean
|
|
1247
|
+
* {{ [1, 2, 3, 4, 5] | initial }} // [1, 2, 3, 4]
|
|
1248
|
+
* {{ [1, 2, 3, 4, 5] | initial:2 }} // [1, 2, 3]
|
|
1249
|
+
* {{ ['a', 'b', 'c'] | initial }} // ['a', 'b']
|
|
986
1250
|
*/
|
|
987
|
-
class
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
return '';
|
|
992
|
-
// handles empty string pattern
|
|
993
|
-
if (!pattern || (typeof pattern === 'string' && pattern.trim() === '')) {
|
|
994
|
-
return value;
|
|
1251
|
+
class InitialPipe {
|
|
1252
|
+
transform(value, n = 1) {
|
|
1253
|
+
if (!Array.isArray(value)) {
|
|
1254
|
+
return [];
|
|
995
1255
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
return isReplace ? value.replace(finalPattern, replacement) : value;
|
|
1256
|
+
if (n <= 0) {
|
|
1257
|
+
return [...value];
|
|
999
1258
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
if (isReplace) {
|
|
1003
|
-
const highlightedReplacement = `<span class="${highlightClass}">${sanitizedReplacement}</span>`;
|
|
1004
|
-
const replaced = value.replace(finalPattern, highlightedReplacement);
|
|
1005
|
-
return this.sanitizer.bypassSecurityTrustHtml(replaced);
|
|
1259
|
+
if (n >= value.length) {
|
|
1260
|
+
return [];
|
|
1006
1261
|
}
|
|
1007
|
-
|
|
1008
|
-
const result = value.replace(finalPattern, highlightedMatch);
|
|
1009
|
-
return this.sanitizer.bypassSecurityTrustHtml(result);
|
|
1262
|
+
return value.slice(0, -n);
|
|
1010
1263
|
}
|
|
1011
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1012
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1264
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: InitialPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1265
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: InitialPipe, isStandalone: true, name: "initial" });
|
|
1013
1266
|
}
|
|
1014
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1267
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: InitialPipe, decorators: [{
|
|
1015
1268
|
type: Pipe,
|
|
1016
1269
|
args: [{
|
|
1017
|
-
name: '
|
|
1270
|
+
name: 'initial',
|
|
1018
1271
|
standalone: true
|
|
1019
1272
|
}]
|
|
1020
1273
|
}] });
|
|
@@ -1030,15 +1283,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1030
1283
|
* {{ 'hello' | reverse }} // Outputs: 'olleh'
|
|
1031
1284
|
* {{ '12345' | reverse }} // Outputs: '54321'
|
|
1032
1285
|
* <p>{{ userInput | reverse }}</p>
|
|
1033
|
-
*
|
|
1034
|
-
* @author Mofiro Jean
|
|
1035
1286
|
*/
|
|
1036
1287
|
class ReversePipe {
|
|
1037
1288
|
transform(value) {
|
|
1038
|
-
if (
|
|
1039
|
-
return
|
|
1289
|
+
if (Array.isArray(value)) {
|
|
1290
|
+
return [...value].reverse();
|
|
1040
1291
|
}
|
|
1041
|
-
|
|
1292
|
+
if (typeof value === 'string') {
|
|
1293
|
+
return value.split('').reverse().join('');
|
|
1294
|
+
}
|
|
1295
|
+
return '';
|
|
1042
1296
|
}
|
|
1043
1297
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ReversePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1044
1298
|
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: ReversePipe, isStandalone: true, name: "reverse" });
|
|
@@ -1052,194 +1306,258 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1052
1306
|
}] });
|
|
1053
1307
|
|
|
1054
1308
|
/**
|
|
1055
|
-
*
|
|
1309
|
+
* SamplePipe: Randomly selects n items from an array.
|
|
1056
1310
|
*
|
|
1057
|
-
*
|
|
1058
|
-
*
|
|
1311
|
+
* Uses Fisher-Yates partial shuffle for unbiased selection.
|
|
1312
|
+
* Returns a single item when n=1 (default), or an array when n>1.
|
|
1313
|
+
*
|
|
1314
|
+
* @param {unknown[]} value - The array to sample from.
|
|
1315
|
+
* @param {number} [n=1] - Number of items to select.
|
|
1316
|
+
*
|
|
1317
|
+
* @returns {unknown | unknown[]} - A single random item (n=1) or array of random items (n>1).
|
|
1059
1318
|
*
|
|
1060
1319
|
* @example
|
|
1061
|
-
*
|
|
1062
|
-
* {{
|
|
1063
|
-
*
|
|
1320
|
+
* {{ [1, 2, 3, 4, 5] | sample }} // 3 (random single)
|
|
1321
|
+
* {{ [1, 2, 3, 4, 5] | sample:3 }} // [5, 1, 3] (random 3)
|
|
1322
|
+
* {{ users | sample:5 }} // 5 random users
|
|
1064
1323
|
*
|
|
1065
|
-
* @
|
|
1324
|
+
* @note Impure pipe — returns different results on each change detection cycle.
|
|
1325
|
+
* Bind the result to a signal to control when it re-samples.
|
|
1066
1326
|
*/
|
|
1067
|
-
class
|
|
1068
|
-
transform(value) {
|
|
1069
|
-
if (!value ||
|
|
1070
|
-
return
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1327
|
+
class SamplePipe {
|
|
1328
|
+
transform(value, n = 1) {
|
|
1329
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
1330
|
+
return n === 1 ? undefined : [];
|
|
1331
|
+
}
|
|
1332
|
+
if (n <= 0) {
|
|
1333
|
+
return [];
|
|
1334
|
+
}
|
|
1335
|
+
// Clamp n to array length
|
|
1336
|
+
const count = Math.min(n, value.length);
|
|
1337
|
+
// Fisher-Yates partial shuffle — only shuffle `count` positions
|
|
1338
|
+
const arr = [...value];
|
|
1339
|
+
for (let i = arr.length - 1; i > arr.length - 1 - count; i--) {
|
|
1340
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
1341
|
+
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
1342
|
+
}
|
|
1343
|
+
const result = arr.slice(arr.length - count);
|
|
1344
|
+
// Return single item for n=1, array otherwise
|
|
1345
|
+
return n === 1 ? result[0] : result;
|
|
1079
1346
|
}
|
|
1080
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1081
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1347
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: SamplePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1348
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: SamplePipe, isStandalone: true, name: "sample", pure: false });
|
|
1082
1349
|
}
|
|
1083
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1350
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: SamplePipe, decorators: [{
|
|
1084
1351
|
type: Pipe,
|
|
1085
1352
|
args: [{
|
|
1086
|
-
name: '
|
|
1087
|
-
standalone: true
|
|
1353
|
+
name: 'sample',
|
|
1354
|
+
standalone: true,
|
|
1355
|
+
pure: false,
|
|
1088
1356
|
}]
|
|
1089
1357
|
}] });
|
|
1090
1358
|
|
|
1091
1359
|
/**
|
|
1092
|
-
*
|
|
1360
|
+
* ShufflePipe: Randomly reorders elements in an array using the Fisher-Yates
|
|
1361
|
+
algorithm.
|
|
1093
1362
|
*
|
|
1094
|
-
*
|
|
1095
|
-
*
|
|
1363
|
+
* Uses the Fisher-Yates (Knuth) shuffle for unbiased randomization,
|
|
1364
|
+
* guaranteeing every permutation has equal probability.
|
|
1096
1365
|
*
|
|
1097
|
-
* @
|
|
1366
|
+
* @param {unknown[]} value - The array to shuffle.
|
|
1367
|
+
*
|
|
1368
|
+
* @returns {unknown[]} - A new array with elements in random order.
|
|
1098
1369
|
*
|
|
1099
1370
|
* @example
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1371
|
+
* {{ [1, 2, 3, 4, 5] | shuffle }} // [3, 1, 5, 2, 4]
|
|
1372
|
+
* {{ ['a', 'b', 'c'] | shuffle }} // ['c', 'a', 'b']
|
|
1102
1373
|
*
|
|
1103
|
-
* @
|
|
1374
|
+
* @note Impure pipe — runs on every change detection cycle.
|
|
1375
|
+
* Avoid using in performance-critical templates or bind the result
|
|
1376
|
+
* to a signal/variable to control when it re-shuffles.
|
|
1104
1377
|
*/
|
|
1105
|
-
class
|
|
1106
|
-
transform(value
|
|
1107
|
-
if (!value
|
|
1108
|
-
return;
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1378
|
+
class ShufflePipe {
|
|
1379
|
+
transform(value) {
|
|
1380
|
+
if (!Array.isArray(value)) {
|
|
1381
|
+
return [];
|
|
1382
|
+
}
|
|
1383
|
+
const arr = [...value];
|
|
1384
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
1385
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
1386
|
+
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
1387
|
+
}
|
|
1388
|
+
return arr;
|
|
1112
1389
|
}
|
|
1113
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1114
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1390
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ShufflePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1391
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: ShufflePipe, isStandalone: true, name: "shuffle", pure: false });
|
|
1115
1392
|
}
|
|
1116
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1393
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ShufflePipe, decorators: [{
|
|
1117
1394
|
type: Pipe,
|
|
1118
1395
|
args: [{
|
|
1119
|
-
name: '
|
|
1396
|
+
name: 'shuffle',
|
|
1397
|
+
pure: false,
|
|
1120
1398
|
standalone: true
|
|
1121
1399
|
}]
|
|
1122
1400
|
}] });
|
|
1123
1401
|
|
|
1124
1402
|
/**
|
|
1125
|
-
*
|
|
1403
|
+
* TailPipe: Returns all elements except the first n.
|
|
1126
1404
|
*
|
|
1127
|
-
* @param {
|
|
1128
|
-
* @
|
|
1405
|
+
* @param {unknown[]} value - The array to slice.
|
|
1406
|
+
* @param {number} [n=1] - Number of elements to exclude from the start.
|
|
1407
|
+
*
|
|
1408
|
+
* @returns {unknown[]} - A new array without the first n elements.
|
|
1129
1409
|
*
|
|
1130
1410
|
* @example
|
|
1131
|
-
*
|
|
1132
|
-
* {{
|
|
1133
|
-
*
|
|
1411
|
+
* {{ [1, 2, 3, 4, 5] | tail }} // [2, 3, 4, 5]
|
|
1412
|
+
* {{ [1, 2, 3, 4, 5] | tail:2 }} // [3, 4, 5]
|
|
1413
|
+
* {{ ['a', 'b', 'c'] | tail }} // ['b', 'c']
|
|
1134
1414
|
*/
|
|
1135
|
-
class
|
|
1136
|
-
transform(value) {
|
|
1137
|
-
if (!value
|
|
1138
|
-
return
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1415
|
+
class TailPipe {
|
|
1416
|
+
transform(value, n = 1) {
|
|
1417
|
+
if (!Array.isArray(value)) {
|
|
1418
|
+
return [];
|
|
1419
|
+
}
|
|
1420
|
+
// n = 0 means keep everything
|
|
1421
|
+
if (n <= 0)
|
|
1422
|
+
return [...value];
|
|
1423
|
+
// n >= length means remove everything
|
|
1424
|
+
if (n >= value.length)
|
|
1425
|
+
return [];
|
|
1426
|
+
return value.slice(n);
|
|
1144
1427
|
}
|
|
1145
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1146
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1428
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TailPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1429
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: TailPipe, isStandalone: true, name: "tail" });
|
|
1147
1430
|
}
|
|
1148
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1431
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TailPipe, decorators: [{
|
|
1149
1432
|
type: Pipe,
|
|
1150
1433
|
args: [{
|
|
1151
|
-
name: '
|
|
1152
|
-
standalone: true
|
|
1434
|
+
name: 'tail',
|
|
1153
1435
|
}]
|
|
1154
1436
|
}] });
|
|
1155
1437
|
|
|
1156
1438
|
/**
|
|
1157
|
-
*
|
|
1439
|
+
* TruthifyPipe: Removes all falsy values from an array.
|
|
1158
1440
|
*
|
|
1159
|
-
*
|
|
1160
|
-
* It provides options to customize the ellipsis and preserve word boundaries.
|
|
1441
|
+
* Falsy values: false, 0, -0, '', null, undefined, NaN
|
|
1161
1442
|
*
|
|
1162
|
-
* @param {
|
|
1163
|
-
* @param {number} [maxLength=10] - The maximum length of the truncated string. Defaults to 10.
|
|
1164
|
-
* @param {string} [ellipsis='...'] - The string to append to the truncated portion. Defaults to '...'.
|
|
1165
|
-
* @param {boolean} [preserveWords=false] - If true, truncates at the last space before `maxLength` to avoid cutting words. Defaults to false.
|
|
1166
|
-
* @returns {string} - The truncated string. Returns an empty string if the input is null, undefined, or not a string.
|
|
1443
|
+
* @param {unknown[]} value - The array to filter.
|
|
1167
1444
|
*
|
|
1168
|
-
* @
|
|
1169
|
-
* {{ 'This is a long sentence' | truncate }} // Returns 'This is a...'
|
|
1170
|
-
* {{ 'This is a long sentence' | truncate: 20 }} // Returns 'This is a long sente...'
|
|
1171
|
-
* {{ 'This is a long sentence' | truncate: 15: ' [more]' }} // Returns 'This is a long [more]'
|
|
1172
|
-
* {{ 'This is a long sentence' | truncate: 15: '...' : true }} // Returns 'This is a...'
|
|
1173
|
-
* {{ 'This is a long sentence' | truncate: 20: '...' : true }} // Returns 'This is a long...'
|
|
1174
|
-
* {{ null | truncate }} // Returns ''
|
|
1175
|
-
* {{ undefined | truncate }} // Returns ''
|
|
1445
|
+
* @returns {unknown[]} - A new array with only truthy values.
|
|
1176
1446
|
*
|
|
1177
|
-
* @
|
|
1447
|
+
* @example
|
|
1448
|
+
* {{ [0, 1, '', 'hello', null, true] | truthify }} // [1, 'hello', true]
|
|
1449
|
+
* {{ ['', 'a', '', 'b'] | truthify }} // ['a', 'b']
|
|
1178
1450
|
*/
|
|
1179
|
-
class
|
|
1180
|
-
transform(value
|
|
1181
|
-
if (!value
|
|
1182
|
-
return
|
|
1183
|
-
}
|
|
1184
|
-
if (value.length <= maxLength) {
|
|
1185
|
-
return value;
|
|
1451
|
+
class TruthifyPipe {
|
|
1452
|
+
transform(value) {
|
|
1453
|
+
if (!Array.isArray(value)) {
|
|
1454
|
+
return [];
|
|
1186
1455
|
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1456
|
+
return value.filter(Boolean);
|
|
1457
|
+
}
|
|
1458
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TruthifyPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1459
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: TruthifyPipe, isStandalone: true, name: "truthify" });
|
|
1460
|
+
}
|
|
1461
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: TruthifyPipe, decorators: [{
|
|
1462
|
+
type: Pipe,
|
|
1463
|
+
args: [{
|
|
1464
|
+
name: 'truthify',
|
|
1465
|
+
standalone: true,
|
|
1466
|
+
}]
|
|
1467
|
+
}] });
|
|
1468
|
+
|
|
1469
|
+
/**
|
|
1470
|
+
* UniquePipe: Removes duplicate values from an array.
|
|
1471
|
+
*
|
|
1472
|
+
* Supports primitives, objects by property key, and deep nested keys via dot notation.
|
|
1473
|
+
*
|
|
1474
|
+
* @param {unknown[]} value - The array to deduplicate.
|
|
1475
|
+
* @param {string} [key] - Optional property path to compare objects by (e.g., 'id', 'user.email').
|
|
1476
|
+
*
|
|
1477
|
+
* @returns {unknown[]} - A new array with duplicates removed, preserving first occurrence.
|
|
1478
|
+
*
|
|
1479
|
+
* @example
|
|
1480
|
+
* {{ [1, 2, 2, 3] | unique }} // [1, 2, 3]
|
|
1481
|
+
* {{ users | unique:'email' }} // unique by email
|
|
1482
|
+
* {{ orders | unique:'customer.email' }} // unique by nested property
|
|
1483
|
+
*/
|
|
1484
|
+
class UniquePipe {
|
|
1485
|
+
transform(value, key) {
|
|
1486
|
+
if (!Array.isArray(value)) {
|
|
1487
|
+
return [];
|
|
1191
1488
|
}
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
const lastSpaceIndex = truncated.lastIndexOf(' ');
|
|
1195
|
-
// If a space is found and it's not the very beginning of the string
|
|
1196
|
-
if (lastSpaceIndex !== -1 && lastSpaceIndex !== 0) {
|
|
1197
|
-
truncated = truncated.substring(0, lastSpaceIndex);
|
|
1198
|
-
}
|
|
1489
|
+
if (!key) {
|
|
1490
|
+
return [...new Set(value)];
|
|
1199
1491
|
}
|
|
1200
|
-
|
|
1492
|
+
const seen = new Set();
|
|
1493
|
+
return value.filter(item => {
|
|
1494
|
+
const val = this.getNestedValue(item, key);
|
|
1495
|
+
if (seen.has(val))
|
|
1496
|
+
return false;
|
|
1497
|
+
seen.add(val);
|
|
1498
|
+
return true;
|
|
1499
|
+
});
|
|
1201
1500
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1501
|
+
getNestedValue(obj, path) {
|
|
1502
|
+
return path.split('.').reduce((current, segment) => current?.[segment], obj);
|
|
1503
|
+
}
|
|
1504
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UniquePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1505
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: UniquePipe, isStandalone: true, name: "unique" });
|
|
1204
1506
|
}
|
|
1205
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type:
|
|
1507
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UniquePipe, decorators: [{
|
|
1206
1508
|
type: Pipe,
|
|
1207
1509
|
args: [{
|
|
1208
|
-
name: '
|
|
1510
|
+
name: 'unique',
|
|
1209
1511
|
standalone: true
|
|
1210
1512
|
}]
|
|
1211
1513
|
}] });
|
|
1212
1514
|
|
|
1515
|
+
// Text
|
|
1213
1516
|
const ALL_PIPES = [
|
|
1517
|
+
// Text
|
|
1214
1518
|
AsciiArtPipe,
|
|
1215
|
-
BarcodePipe,
|
|
1216
1519
|
CamelCasePipe,
|
|
1217
|
-
ColorConvertPipe,
|
|
1218
|
-
CreditCardMaskPipe,
|
|
1219
|
-
DeviceTypePipe,
|
|
1220
|
-
EmailMaskPipe,
|
|
1221
|
-
GravatarPipe,
|
|
1222
1520
|
HighlightPipe,
|
|
1223
|
-
HtmlEscapePipe,
|
|
1224
|
-
HtmlSanitizePipe,
|
|
1225
1521
|
InitialsPipe,
|
|
1226
|
-
IpAddressMaskPipe,
|
|
1227
|
-
JsonPrettyPipe,
|
|
1228
1522
|
KebabCasePipe,
|
|
1229
1523
|
MorseCodePipe,
|
|
1230
|
-
QrCodePipe,
|
|
1231
1524
|
ReplacePipe,
|
|
1232
|
-
ReversePipe,
|
|
1233
1525
|
SnakeCasePipe,
|
|
1234
|
-
TextToSpeechPipe,
|
|
1235
1526
|
TitleCasePipe,
|
|
1236
1527
|
TruncatePipe,
|
|
1237
|
-
|
|
1528
|
+
// Security & Privacy
|
|
1529
|
+
CreditCardMaskPipe,
|
|
1530
|
+
EmailMaskPipe,
|
|
1531
|
+
HtmlEscapePipe,
|
|
1532
|
+
HtmlSanitizePipe,
|
|
1533
|
+
IpAddressMaskPipe,
|
|
1534
|
+
// Media & Visual
|
|
1535
|
+
BarcodePipe,
|
|
1536
|
+
ColorConvertPipe,
|
|
1537
|
+
GravatarPipe,
|
|
1538
|
+
QrCodePipe,
|
|
1539
|
+
// Data & Utility
|
|
1540
|
+
CountPipe,
|
|
1541
|
+
DeviceTypePipe,
|
|
1542
|
+
JsonPrettyPipe,
|
|
1543
|
+
TextToSpeechPipe,
|
|
1544
|
+
TimeAgoPipePipe,
|
|
1545
|
+
// Array
|
|
1546
|
+
Flatten,
|
|
1547
|
+
InitialPipe,
|
|
1548
|
+
ReversePipe,
|
|
1549
|
+
SamplePipe,
|
|
1550
|
+
ShufflePipe,
|
|
1551
|
+
TailPipe,
|
|
1552
|
+
TruthifyPipe,
|
|
1553
|
+
UniquePipe,
|
|
1238
1554
|
];
|
|
1239
1555
|
|
|
1556
|
+
// Text
|
|
1557
|
+
|
|
1240
1558
|
/**
|
|
1241
1559
|
* Generated bundle index. Do not edit.
|
|
1242
1560
|
*/
|
|
1243
1561
|
|
|
1244
|
-
export { ALL_PIPES, AsciiArtPipe, BarcodePipe, CamelCasePipe, ColorConvertPipe, CountPipe, CreditCardMaskPipe, DeviceTypePipe, EmailMaskPipe, GravatarPipe, HighlightPipe, HtmlEscapePipe, HtmlSanitizePipe, InitialsPipe, IpAddressMaskPipe, JsonPrettyPipe, KebabCasePipe, MorseCodePipe, QrCodePipe, ReplacePipe, ReversePipe, SnakeCasePipe, TextToSpeechPipe, TitleCasePipe, TruncatePipe };
|
|
1562
|
+
export { ALL_PIPES, AsciiArtPipe, BarcodePipe, CamelCasePipe, ColorConvertPipe, CountPipe, CreditCardMaskPipe, DeviceTypePipe, EmailMaskPipe, Flatten, GravatarPipe, HighlightPipe, HtmlEscapePipe, HtmlSanitizePipe, InitialPipe, InitialsPipe, IpAddressMaskPipe, JsonPrettyPipe, KebabCasePipe, MorseCodePipe, QrCodePipe, ReplacePipe, ReversePipe, SamplePipe, ShufflePipe, SnakeCasePipe, TailPipe, TextToSpeechPipe, TimeAgoPipePipe, TitleCasePipe, TruncatePipe, TruthifyPipe, UniquePipe };
|
|
1245
1563
|
//# sourceMappingURL=ngx-transforms.mjs.map
|