luoma 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.
Files changed (152) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +2 -0
  3. data/.rdoc_options +16 -0
  4. data/CHANGELOG.md +5 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +68 -0
  7. data/Rakefile +69 -0
  8. data/Steepfile +41 -0
  9. data/certs/jgrp.pem +27 -0
  10. data/docs/configuration.md +180 -0
  11. data/docs/custom_filters.md +3 -0
  12. data/docs/custom_tags.md +3 -0
  13. data/docs/expressions.md +3 -0
  14. data/docs/extension_types.md +41 -0
  15. data/docs/filter_reference.md +1702 -0
  16. data/docs/index.md +73 -0
  17. data/docs/luoma_for_template_authors.md +3 -0
  18. data/docs/markdown.md +111 -0
  19. data/docs/predicate_reference.md +3 -0
  20. data/docs/static_analysis.md +3 -0
  21. data/docs/tag_reference.md +352 -0
  22. data/docs/template_loaders.md +177 -0
  23. data/docs/undefined_variables.md +3 -0
  24. data/docs-requirements.txt +11 -0
  25. data/docs_/cycle.md +17 -0
  26. data/docs_/font_rendering_example.md +157 -0
  27. data/docs_/header_block_example.md +51 -0
  28. data/docs_/increment_and_decrement.md +49 -0
  29. data/docs_/logo_style_example.md +40 -0
  30. data/docs_/migration.md +11 -0
  31. data/docs_/notes.md +19 -0
  32. data/lib/luoma/cache.rb +80 -0
  33. data/lib/luoma/chain_hash.rb +54 -0
  34. data/lib/luoma/context.rb +227 -0
  35. data/lib/luoma/drop.rb +84 -0
  36. data/lib/luoma/drops/block.rb +33 -0
  37. data/lib/luoma/drops/expression.rb +29 -0
  38. data/lib/luoma/drops/html_safe.rb +36 -0
  39. data/lib/luoma/drops/range.rb +69 -0
  40. data/lib/luoma/drops/undefined.rb +119 -0
  41. data/lib/luoma/environment.rb +479 -0
  42. data/lib/luoma/errors.rb +79 -0
  43. data/lib/luoma/escape.rb +35 -0
  44. data/lib/luoma/expression.rb +1271 -0
  45. data/lib/luoma/filter.rb +97 -0
  46. data/lib/luoma/filters/array.rb +372 -0
  47. data/lib/luoma/filters/date.rb +19 -0
  48. data/lib/luoma/filters/default.rb +16 -0
  49. data/lib/luoma/filters/json.rb +14 -0
  50. data/lib/luoma/filters/math.rb +84 -0
  51. data/lib/luoma/filters/size.rb +13 -0
  52. data/lib/luoma/filters/slice.rb +52 -0
  53. data/lib/luoma/filters/sort.rb +113 -0
  54. data/lib/luoma/filters/string.rb +186 -0
  55. data/lib/luoma/fnv.rb +15 -0
  56. data/lib/luoma/lexer.rb +106 -0
  57. data/lib/luoma/lexer_legacy.rb +396 -0
  58. data/lib/luoma/lexer_unified.rb +279 -0
  59. data/lib/luoma/loader.rb +50 -0
  60. data/lib/luoma/loaders/choice_loader.rb +20 -0
  61. data/lib/luoma/loaders/file_system_loader.rb +75 -0
  62. data/lib/luoma/loaders/mixins.rb +52 -0
  63. data/lib/luoma/markup.rb +109 -0
  64. data/lib/luoma/parser.rb +252 -0
  65. data/lib/luoma/parser_unified.rb +979 -0
  66. data/lib/luoma/predicates/blank.rb +19 -0
  67. data/lib/luoma/predicates/defined.rb +10 -0
  68. data/lib/luoma/predicates/empty.rb +15 -0
  69. data/lib/luoma/predicates/type.rb +35 -0
  70. data/lib/luoma/static_analysis.rb +427 -0
  71. data/lib/luoma/tags/assign.rb +63 -0
  72. data/lib/luoma/tags/break.rb +23 -0
  73. data/lib/luoma/tags/capture.rb +43 -0
  74. data/lib/luoma/tags/case.rb +137 -0
  75. data/lib/luoma/tags/comment.rb +17 -0
  76. data/lib/luoma/tags/continue.rb +23 -0
  77. data/lib/luoma/tags/define.rb +42 -0
  78. data/lib/luoma/tags/else.rb +30 -0
  79. data/lib/luoma/tags/for.rb +108 -0
  80. data/lib/luoma/tags/if.rb +109 -0
  81. data/lib/luoma/tags/import.rb +91 -0
  82. data/lib/luoma/tags/include.rb +77 -0
  83. data/lib/luoma/tags/output.rb +20 -0
  84. data/lib/luoma/tags/raw.rb +26 -0
  85. data/lib/luoma/tags/render.rb +92 -0
  86. data/lib/luoma/tags/with.rb +55 -0
  87. data/lib/luoma/template.rb +178 -0
  88. data/lib/luoma/token.rb +84 -0
  89. data/lib/luoma/version.rb +5 -0
  90. data/lib/luoma.rb +75 -0
  91. data/sig/luoma/cache.rbs +44 -0
  92. data/sig/luoma/chain_hash.rbs +26 -0
  93. data/sig/luoma/context.rbs +115 -0
  94. data/sig/luoma/drop.rbs +44 -0
  95. data/sig/luoma/drops/block.rbs +10 -0
  96. data/sig/luoma/drops/expression.rbs +10 -0
  97. data/sig/luoma/drops/html_safe.rbs +14 -0
  98. data/sig/luoma/drops/range.rbs +15 -0
  99. data/sig/luoma/drops/undefined.rbs +27 -0
  100. data/sig/luoma/environment.rbs +212 -0
  101. data/sig/luoma/errors.rbs +67 -0
  102. data/sig/luoma/escape.rbs +15 -0
  103. data/sig/luoma/expression.rbs +514 -0
  104. data/sig/luoma/filter.rbs +66 -0
  105. data/sig/luoma/filters/array.rbs +74 -0
  106. data/sig/luoma/filters/date.rbs +9 -0
  107. data/sig/luoma/filters/default.rbs +8 -0
  108. data/sig/luoma/filters/json.rbs +7 -0
  109. data/sig/luoma/filters/math.rbs +37 -0
  110. data/sig/luoma/filters/size.rbs +6 -0
  111. data/sig/luoma/filters/slice.rbs +10 -0
  112. data/sig/luoma/filters/sort.rbs +17 -0
  113. data/sig/luoma/filters/string.rbs +103 -0
  114. data/sig/luoma/fnv.rbs +3 -0
  115. data/sig/luoma/lexer.rbs +42 -0
  116. data/sig/luoma/lexer_legacy.rbs +81 -0
  117. data/sig/luoma/lexer_unified.rbs +49 -0
  118. data/sig/luoma/loader.rbs +40 -0
  119. data/sig/luoma/loaders/choice_loader.rbs +9 -0
  120. data/sig/luoma/loaders/file_system_loader.rbs +19 -0
  121. data/sig/luoma/loaders/mixins.rbs +16 -0
  122. data/sig/luoma/markup.rbs +68 -0
  123. data/sig/luoma/parser.rbs +105 -0
  124. data/sig/luoma/parser_unified.rbs +150 -0
  125. data/sig/luoma/predicates/blank.rbs +6 -0
  126. data/sig/luoma/predicates/defined.rbs +6 -0
  127. data/sig/luoma/predicates/empty.rbs +6 -0
  128. data/sig/luoma/predicates/type.rbs +21 -0
  129. data/sig/luoma/static_analysis.rbs +141 -0
  130. data/sig/luoma/tags/assign.rbs +12 -0
  131. data/sig/luoma/tags/break.rbs +11 -0
  132. data/sig/luoma/tags/capture.rbs +14 -0
  133. data/sig/luoma/tags/case.rbs +36 -0
  134. data/sig/luoma/tags/comment.rbs +11 -0
  135. data/sig/luoma/tags/continue.rbs +11 -0
  136. data/sig/luoma/tags/define.rbs +16 -0
  137. data/sig/luoma/tags/else.rbs +17 -0
  138. data/sig/luoma/tags/for.rbs +26 -0
  139. data/sig/luoma/tags/if.rbs +45 -0
  140. data/sig/luoma/tags/import.rbs +13 -0
  141. data/sig/luoma/tags/include.rbs +13 -0
  142. data/sig/luoma/tags/output.rbs +13 -0
  143. data/sig/luoma/tags/raw.rbs +11 -0
  144. data/sig/luoma/tags/render.rbs +13 -0
  145. data/sig/luoma/tags/with.rbs +15 -0
  146. data/sig/luoma/template.rbs +117 -0
  147. data/sig/luoma/token.rbs +81 -0
  148. data/sig/luoma.rbs +11 -0
  149. data/zensical.toml +363 -0
  150. data.tar.gz.sig +0 -0
  151. metadata +233 -0
  152. metadata.gz.sig +0 -0
@@ -0,0 +1,1702 @@
1
+ # Built-in filters
2
+
3
+ !!! warning
4
+
5
+ This page is a work in progress. Some of the information here is not accurate.
6
+
7
+ ## abs
8
+
9
+ ```
10
+ <number> | abs
11
+ ```
12
+
13
+ Return the absolute value of a number. Works on integers, floats and string representations of integers or floats.
14
+
15
+ ```liquid2
16
+ {{ -42 | abs }}
17
+ {{ 7.5 | abs }}
18
+ {{ '42.0' | abs }}
19
+ ```
20
+
21
+ ```plain title="output"
22
+ 42
23
+ 7.5
24
+ 42.0
25
+ ```
26
+
27
+ Given a value that can't be cast to an integer or float, the special value `Nothing` will be returned.
28
+
29
+ ```liquid2
30
+ {{ 'hello' | abs }}
31
+ {{ 'hello' | abs or 0 }}
32
+ {{ ('hello' | abs) or 99 }}
33
+ ```
34
+
35
+ ```plain title="output"
36
+
37
+ 0
38
+ ```
39
+
40
+ ## append
41
+
42
+ ```
43
+ <string> | append: <string>
44
+ ```
45
+
46
+ Return the input value concatenated with the argument value.
47
+
48
+ ```liquid2
49
+ {{ 'Hello, ' | append: 'World!' }}
50
+ ```
51
+
52
+ ```plain title="output"
53
+ Hello, World!
54
+ ```
55
+
56
+ If either the input value or argument are not a string, they will be coerced to a string before concatenation.
57
+
58
+ ```liquid2
59
+ {% assign a_number = 7.5 -%}
60
+ {{ 42 | append: a_number }}
61
+ {{ nosuchthing | append: 'World!' }}
62
+ ```
63
+
64
+ ```plain title="output"
65
+ 427.5
66
+ World!
67
+ ```
68
+
69
+ ## at_least
70
+
71
+ ```
72
+ <number> | at_least: <number>
73
+ ```
74
+
75
+ Return the maximum of the filter's input value and its argument. If either input value or argument are string representations of an integer or float, they will be cast to an integer or float prior to comparison.
76
+
77
+ ```liquid2
78
+ {{ -5.1 | at_least: 8 }}
79
+ {{ 8 | at_least: '5' }}
80
+ ```
81
+
82
+ ```plain title="output"
83
+ 8
84
+ 8
85
+ ```
86
+
87
+ If both input value and argument can not be cast to an integer or float, the special value `Nothing` will be returned instead.
88
+
89
+ ```liquid2
90
+ {{ "hello" | at_least: 2 }}
91
+ {{ "hello" | at_least: -2 }}
92
+ {{ -1 | at_least: "abc" }}
93
+ {{ ('foo' | at_least: "bar") or 42 }}
94
+ ```
95
+
96
+ ```plain title="output"
97
+ 2
98
+ -2
99
+ -1
100
+ 42
101
+ ```
102
+
103
+ ## at_most
104
+
105
+ ```
106
+ <number> | at_most: <number>
107
+ ```
108
+
109
+ Return the minimum of the filter's input value and its argument. If either input value or argument are string representations of an integer or float, they will be cast to an integer or float prior to comparison.
110
+
111
+ ```liquid2
112
+ {{ 5 | at_most: 8 }}
113
+ {{ '8' | at_most: 5 }}
114
+ ```
115
+
116
+ ```plain title="output"
117
+ 5
118
+ 5
119
+ ```
120
+
121
+ If both input value and argument can not be cast to an integer or float, the special value `Nothing` will be returned instead.
122
+
123
+ ```liquid2
124
+ {{ "hello" | at_most: 2 }}
125
+ {{ "hello" | at_most: -2 }}
126
+ {{ -1 | at_most: "abc" }}
127
+ {{ ('foo' | at_most: "bar") or 42 }}
128
+ ```
129
+
130
+ ```plain title="output"
131
+ 2
132
+ 2
133
+ -1
134
+ 42
135
+ ```
136
+
137
+ ## capitalize
138
+
139
+ ```
140
+ <string> | capitalize
141
+ ```
142
+
143
+ Return the input string with the first character in upper case and the rest lowercase.
144
+
145
+ ```liquid2
146
+ {{ 'heLLO, World!' | capitalize }}
147
+ ```
148
+
149
+ ```plain title="output"
150
+ Hello, world!
151
+ ```
152
+
153
+ If the input value is not a string, it will be converted to a string.
154
+
155
+ ```liquid2
156
+ {{ 42 | capitalize }}
157
+ ```
158
+
159
+ ```plain title="output"
160
+ 42
161
+ ```
162
+
163
+ ## ceil
164
+
165
+ ```
166
+ <number> | ceil
167
+ ```
168
+
169
+ Round the input value up to the nearest whole number. The input value will be converted to a number if it is not an integer or float.
170
+
171
+ ```liquid2
172
+ {{ 5.1 | ceil }}
173
+ {{ 5.0 | ceil }}
174
+ {{ 5 | ceil }}
175
+ {{ '5.4' | ceil }}
176
+ ```
177
+
178
+ ```plain title="output"
179
+ 6
180
+ 5
181
+ 5
182
+ 5
183
+ ```
184
+
185
+ If the input is undefined or can't be converted to a number, the special value `Nothing` is returned.
186
+
187
+ ```liquid2
188
+ {{ 'hello' | ceil }}
189
+ {{ ('hello' | ceil) or 1 }}
190
+ ```
191
+
192
+ ```plain title="output"
193
+
194
+ 1
195
+ ```
196
+
197
+ ## compact
198
+
199
+ ```
200
+ <array> | compact[: <key>]
201
+ ```
202
+
203
+ Return a new array containing items from the input array excluding `null` and `Nothing` values.
204
+
205
+ ```liquid2
206
+ {%- assign a = [1, 2, null, nosuchthing ] -%}
207
+ {{ a | compact }}
208
+ ```
209
+
210
+ ```title="Output"
211
+ [1,2]
212
+ ```
213
+
214
+ If `key` is given and it is a string, array items should be objects and the key is used to lookup a property of each object.
215
+
216
+ ```liquid2
217
+ {%- assign
218
+ items = [
219
+ { "title": "foo", "id": 1 },
220
+ { "title": null, "id": 2 },
221
+ { "title": "baz", "id": 3 },
222
+ ]
223
+ -%}
224
+
225
+ {{ items | compact: "title" }}
226
+ ```
227
+
228
+ ```title="Output"
229
+ [{"title":"foo","id":1},{"title":"baz","id":3}]
230
+ ```
231
+
232
+ If `key` is a lambda expression, the expression is evaluated for each item in the input array. If the expression evaluates to `nil` or `Nothing`, the item is excluded from the result.
233
+
234
+ ```liquid2
235
+ {%- assign
236
+ items = [
237
+ { "title": "foo", "id": 1 },
238
+ { "id": null },
239
+ { "title": null, "id": 3 },
240
+ ]
241
+ -%}
242
+
243
+ {{ items | compact: x -> (x.title or x.id) }}
244
+ ```
245
+
246
+ ```title="Output"
247
+ [{"title":"foo","id":1},{"title":null,"id":3}]
248
+ ```
249
+
250
+ ## concat
251
+
252
+ ```
253
+ <array> | concat: <array>
254
+ ```
255
+
256
+ Create a new array by joining one array-like object with another.
257
+
258
+ ```liquid2
259
+ {% assign fruits = "apples, oranges, peaches" | split: ", " %}
260
+ {% assign vegetables = "carrots, turnips, potatoes" | split: ", " %}
261
+
262
+ {% assign everything = fruits | concat: vegetables %}
263
+
264
+ {% for item in everything %}
265
+ - {{ item }}
266
+ {% endfor %}
267
+ ```
268
+
269
+ ```plain title="output"
270
+ - apples
271
+ - oranges
272
+ - peaches
273
+ - carrots
274
+ - turnips
275
+ - potatoes
276
+ ```
277
+
278
+ If the input value is not array-like, it will be converted to an array. No conversion is attempted for the argument value.
279
+
280
+ ```liquid2
281
+ {% assign fruits = "apples, oranges, peaches" | split: ", " -%}
282
+ {% assign things = "hello" | concat: fruits -%}
283
+
284
+ {% for item in things -%}
285
+ - {{ item }}
286
+ {% endfor %}
287
+ ```
288
+
289
+ ```plain title="output"
290
+ - h
291
+ - e
292
+ - l
293
+ - l
294
+ - o
295
+ - apples
296
+ - oranges
297
+ - peaches
298
+ ```
299
+
300
+ If the input is a nested array, it will be flattened before concatenation. The argument is not flattened.
301
+
302
+ ```json title="data"
303
+ {
304
+ "a": [
305
+ ["a", "x"],
306
+ ["b", ["y", ["z"]]]
307
+ ],
308
+ "b": ["c", "d"]
309
+ }
310
+ ```
311
+
312
+ ```liquid2
313
+ {{ a | concat: b | join: '#' }}
314
+ ```
315
+
316
+ ```plain title="output"
317
+ a#x#b#y#z#c#d
318
+ ```
319
+
320
+ ## date
321
+
322
+ ```
323
+ <datetime> | date: <string>
324
+ ```
325
+
326
+ Format a date and/or time according the the given format string. The input can be a string, in which case the string will be parsed as a date/time before formatting.
327
+
328
+ :::caution
329
+
330
+ LiquidScript's `date` filter can parse Unix timestamps, ISO 8601, RFC2822, SQL and HTTP header formatted date/time strings. It does not do fuzzy parsing like Ruby or Python Liquid.
331
+
332
+ :::
333
+
334
+ ```liquid2
335
+ {{ "March 14, 2016" | date: "%b %d, %y" }}
336
+ ```
337
+
338
+ ```plain title="output"
339
+ Mar 14, 16
340
+ ```
341
+
342
+ The special `'now'` or `'today'` input values can be used to get the current timestamp. `'today'` is an alias for `'now'`. Both include time information.
343
+
344
+ ```liquid2
345
+ {{ "now" | date: "%Y-%m-%d %H:%M" }}
346
+ ```
347
+
348
+ ```plain title="output"
349
+ 2021-12-02 10:17
350
+ ```
351
+
352
+ If the input is undefined, an empty string is returned.
353
+
354
+ ```liquid2
355
+ {{ nosuchthing | date: "%Y-%m-%d %H:%M" }}
356
+ ```
357
+
358
+ ```plain title="output"
359
+
360
+ ```
361
+
362
+ ## default
363
+
364
+ ```
365
+ <expression> | default[: <object>[, allow_false:<bool>]]
366
+ ```
367
+
368
+ Return a default value if the input is undefined, `nil`/`null`, `false` or empty, or return the input unchanged otherwise.
369
+
370
+ ```liquid2
371
+ {{ product_price | default: 2.99 }}
372
+
373
+ {%- assign product_price = "" %}
374
+ {{ product_price | default: 2.99 }}
375
+
376
+ {%- assign product_price = 4.99 %}
377
+ {{ product_price | default: 2.99 }}
378
+ ```
379
+
380
+ ```plain title="output"
381
+ 2.99
382
+ 2.99
383
+ 4.99
384
+ ```
385
+
386
+ If the optional `allow_false` argument is `true`, an input of `false` will not return the default. `allow_false` defaults to `false`.
387
+
388
+ ```liquid2
389
+ {% assign product_reduced = false -%}
390
+ {{ product_reduced | default: true, allow_false: true }}
391
+ ```
392
+
393
+ ```plain title="output"
394
+ false
395
+ ```
396
+
397
+ If no argument is given, the default value will be an empty string.
398
+
399
+ ```liquid2
400
+ {{ product_price | default }}
401
+ ```
402
+
403
+ ```plain title="output"
404
+
405
+ ```
406
+
407
+ Empty strings, arrays and objects all cause the default value to be returned. `0` does not.
408
+
409
+ ```liquid2
410
+ {{ "" | default: "hello" }}
411
+ {{ 0 | default: 99 }}
412
+ ```
413
+
414
+ ```plain title="output"
415
+ hello
416
+ 0
417
+ ```
418
+
419
+ ## divided_by
420
+
421
+ ```
422
+ <number> | divided_by: <number>
423
+ ```
424
+
425
+ Divide a number by another number. The result is rounded down to the nearest integer if the divisor is an integer.
426
+
427
+ ```liquid2
428
+ {{ 16 | divided_by: 4 }}
429
+ {{ 5 | divided_by: 3 }}
430
+ ```
431
+
432
+ ```plain title="output"
433
+ 4
434
+ 1
435
+ ```
436
+
437
+ If you divide by a float, the result will be a float.
438
+
439
+ ```liquid2
440
+ {{ 20 | divided_by: 7 }}
441
+ {{ 20 | divided_by: 7.0 }}
442
+ ```
443
+
444
+ ```plain title="output"
445
+ 2
446
+ 2.857142857142857
447
+ ```
448
+
449
+ If either the input or argument are not an integer or float, Liquid will try to convert them to an integer or float. If the input can't be converted, `0` will be used instead. If the argument can't be converted, an exception is raised.
450
+
451
+ ```liquid2
452
+ {{ "20" | divided_by: "7" }}
453
+ {{ "hello" | divided_by: 2 }}
454
+ ```
455
+
456
+ ```plain title="output"
457
+ 2
458
+ 0
459
+ ```
460
+
461
+ ## downcase
462
+
463
+ ```
464
+ <string> | downcase
465
+ ```
466
+
467
+ Return the input string with all characters in lowercase.
468
+
469
+ ```liquid2
470
+ {{ 'Hello, World!' | downcase }}
471
+ ```
472
+
473
+ ```plain title="output"
474
+ hello, world!
475
+ ```
476
+
477
+ If the input is not a string, Liquid will convert it to a string before forcing characters to lowercase.
478
+
479
+ ```liquid2
480
+ {{ 5 | downcase }}
481
+ ```
482
+
483
+ ```plain title="output"
484
+ 5
485
+ ```
486
+
487
+ If the input is undefined, an empty string is returned.
488
+
489
+ ## escape
490
+
491
+ ```
492
+ <string> | escape
493
+ ```
494
+
495
+ Escape special characters in a string for safe use in HTML.
496
+
497
+ This filter replaces the characters `&`, `<`, `>`, `'`, and `"` with their corresponding HTML-safe sequences:
498
+
499
+ - `&` -> `&amp;`
500
+ - `<` -> `&lt;`
501
+ - `>` -> `&gt;`
502
+ - `'` -> `&#39;`
503
+ - `"` -> `&#34;`
504
+
505
+ This helps prevent HTML injection when rendering untrusted content in HTML element bodies or attributes.
506
+
507
+ :::caution
508
+
509
+ This filter does **not** make strings safe for use in JavaScript, including in `<script>` blocks, inline event handler attributes (e.g. `onerror`), or other JavaScript contexts. For those cases, use the [`escapejs`](#escapejs) filter instead.
510
+
511
+ :::
512
+
513
+ ```liquid2
514
+ {{ "Have you read 'James & the Giant Peach'?" | escape }}
515
+ ```
516
+
517
+ ```plain title="output"
518
+ Have you read &#39;James &amp; the Giant Peach&#39;?
519
+ ```
520
+
521
+ ## escapejs
522
+
523
+ ```
524
+ <string> | escapejs
525
+ ```
526
+
527
+ Escape characters for safe use in JavaScript string literals.
528
+
529
+ This filter escapes a string for embedding inside **JavaScript string literals**, using either single or double quotes (e.g. `'...'` or `"..."`). It replaces control characters and potentially dangerous symbols with their corresponding Unicode escape sequences.
530
+
531
+ Escaped characters include:
532
+
533
+ - ASCII control characters (U+0000 to U+001F)
534
+ - Characters like quotes, angle brackets, ampersands, equals signs - Line/paragraph separators (U+2028, U+2029)
535
+
536
+ :::caution
537
+
538
+ This filter does **not** make strings safe for use in JavaScript template literals (backtick strings), or in raw JavaScript expressions. Use it only when placing data inside quoted JS strings within inline `<script>` blocks or event handlers.
539
+
540
+ **Recommended alternatives:**
541
+
542
+ - Pass data using HTML `data-*` attributes and read them in JS via `element.dataset`.
543
+ - For structured data, prefer a JSON-serialization approach using a JSON filter.
544
+
545
+ :::
546
+
547
+ ```liquid2
548
+ {% assign some_string = "<script>alert('x')</script>" %}
549
+ <img src="" onerror="{{ some_string | escapejs }}" />
550
+ ```
551
+
552
+ ```plain title="output"
553
+ <img src="" onerror="\u003Cscript\u003Ealert(\u0027x\u0027)\u003C/script\u003E" />
554
+ ```
555
+
556
+ ## escape_once
557
+
558
+ ```
559
+ <string> | escape_once
560
+ ```
561
+
562
+ Escape a string for HTML, but avoid double-escaping existing entities.
563
+
564
+ Converts characters like `&`, `<`, and `>` to their HTML-safe sequences, but leaves existing HTML entities untouched (e.g., `&amp;` stays `&amp;`).
565
+
566
+ This is useful when escaping content that may already be partially escaped.
567
+
568
+ See the [`escape`](#escape) filter for details and limitations.
569
+
570
+ ```liquid2
571
+ {{ "Have you read 'James &amp; the Giant Peach'?" | escape_once }}
572
+ ```
573
+
574
+ ```plain title="output"
575
+ Have you read &#39;James &amp; the Giant Peach&#39;?
576
+ ```
577
+
578
+ ## find
579
+
580
+ ```
581
+ <array> | find: <string>[, <object>]
582
+ ```
583
+
584
+ Return the first item in the input array that contains a property, given as the first argument, equal to the value given as the second argument. If no such item exists, `null` is returned.
585
+
586
+ In this example we select the first page in the "Programming" category.
587
+
588
+ ```json title="data"
589
+ {
590
+ "pages": [
591
+ {
592
+ "id": 1,
593
+ "title": "Introduction to Cooking",
594
+ "category": "Cooking",
595
+ "tags": ["recipes", "beginner", "cooking techniques"]
596
+ },
597
+ {
598
+ "id": 2,
599
+ "title": "Top 10 Travel Destinations in Europe",
600
+ "category": "Travel",
601
+ "tags": ["Europe", "destinations", "travel tips"]
602
+ },
603
+ {
604
+ "id": 3,
605
+ "title": "Mastering JavaScript",
606
+ "category": "Programming",
607
+ "tags": ["JavaScript", "web development", "coding"]
608
+ }
609
+ ]
610
+ }
611
+ ```
612
+
613
+ ```liquid2
614
+ {% assign page = pages | find: 'category', 'Programming' %}
615
+ {{ page.title }}
616
+ ```
617
+
618
+ ```plain title="output"
619
+ Mastering JavaScript
620
+ ```
621
+
622
+ ## find_index
623
+
624
+ Return the index of the first item in the input array that contains a property, given as the first argument, equal to the value given as the second argument. If no such item exists, `null` is returned.
625
+
626
+ In this example we find the index for the first page in the "Programming" category.
627
+
628
+ ```json title="data"
629
+ {
630
+ "pages": [
631
+ {
632
+ "id": 1,
633
+ "title": "Introduction to Cooking",
634
+ "category": "Cooking",
635
+ "tags": ["recipes", "beginner", "cooking techniques"]
636
+ },
637
+ {
638
+ "id": 2,
639
+ "title": "Top 10 Travel Destinations in Europe",
640
+ "category": "Travel",
641
+ "tags": ["Europe", "destinations", "travel tips"]
642
+ },
643
+ {
644
+ "id": 3,
645
+ "title": "Mastering JavaScript",
646
+ "category": "Programming",
647
+ "tags": ["JavaScript", "web development", "coding"]
648
+ }
649
+ ]
650
+ }
651
+ ```
652
+
653
+ ```liquid2
654
+ {% assign index = pages | find_index: 'category', 'Programming' %}
655
+ {{ pages[index].title }}
656
+ ```
657
+
658
+ ```plain title="output"
659
+ Mastering JavaScript
660
+ ```
661
+
662
+ ## first
663
+
664
+ ```
665
+ <sequence> | first
666
+ ```
667
+
668
+ Return the first item of the input sequence. The input could be array-like or a mapping, but not a string.
669
+
670
+ ```liquid2
671
+ {{ "Ground control to Major Tom." | split: " " | first }}
672
+ ```
673
+
674
+ ```plain title="output"
675
+ Ground
676
+ ```
677
+
678
+ If the input sequence is undefined, empty or not a sequence, `nil` is returned.
679
+
680
+ ## floor
681
+
682
+ ```
683
+ <number> | floor
684
+ ```
685
+
686
+ Return the input down to the nearest whole number. Liquid tries to convert the input to a number before the filter is applied.
687
+
688
+ ```liquid2
689
+ {{ 1.2 | floor }}
690
+ {{ 2.0 | floor }}
691
+ {{ 183.357 | floor }}
692
+ {{ -5.4 | floor }}
693
+ {{ "3.5" | floor }}
694
+ ```
695
+
696
+ ```plain title="output"
697
+ 1
698
+ 2
699
+ 183
700
+ -6
701
+ 3
702
+ ```
703
+
704
+ If the input can't be converted to a number, `0` is returned.
705
+
706
+ ## has
707
+
708
+ ```
709
+ <array> | has: <string>[, <object>]
710
+ ```
711
+
712
+ Return `true` if the input array contains an object with a property identified by the first argument that is equal to the object given as the second argument. `false` is returned if none of the items in the input array contain such a property/value.
713
+
714
+ In this example we test to see if any pages are in the "Programming" category.
715
+
716
+ ```json title="data"
717
+ {
718
+ "pages": [
719
+ {
720
+ "id": 1,
721
+ "title": "Introduction to Cooking",
722
+ "category": "Cooking",
723
+ "tags": ["recipes", "beginner", "cooking techniques"]
724
+ },
725
+ {
726
+ "id": 2,
727
+ "title": "Top 10 Travel Destinations in Europe",
728
+ "category": "Travel",
729
+ "tags": ["Europe", "destinations", "travel tips"]
730
+ },
731
+ {
732
+ "id": 3,
733
+ "title": "Mastering JavaScript",
734
+ "category": "Programming",
735
+ "tags": ["JavaScript", "web development", "coding"]
736
+ }
737
+ ]
738
+ }
739
+ ```
740
+
741
+ ```liquid2
742
+ {% assign has_programming_page = pages | has: 'category', 'Programming' %}
743
+ {{ has_programming_page }}
744
+ ```
745
+
746
+ ```plain title="output"
747
+ true
748
+ ```
749
+
750
+ ## join
751
+
752
+ ```
753
+ <array> | join[: <string>]
754
+ ```
755
+
756
+ Return the items in the input array as a single string, separated by the argument string. If the
757
+ input is not an array, Liquid will convert it to one. If input array items are not strings, they
758
+ will be converted to strings before joining.
759
+
760
+ ```liquid2
761
+ {% assign beatles = "John, Paul, George, Ringo" | split: ", " -%}
762
+
763
+ {{ beatles | join: " and " }}
764
+ ```
765
+
766
+ ```plain title="output"
767
+ John and Paul and George and Ringo
768
+ ```
769
+
770
+ If an argument string is not given, it defaults to a single space.
771
+
772
+ ```liquid2
773
+ {% assign beatles = "John, Paul, George, Ringo" | split: ", " -%}
774
+
775
+ {{ beatles | join }}
776
+ ```
777
+
778
+ ```plain title="output"
779
+ John Paul George Ringo
780
+ ```
781
+
782
+ ## last
783
+
784
+ ```
785
+ <array> | last
786
+ ```
787
+
788
+ Return the last item in the array-like input.
789
+
790
+ ```liquid2
791
+ {{ "Ground control to Major Tom." | split: " " | last }}
792
+ ```
793
+
794
+ ```plain title="output"
795
+ Tom.
796
+ ```
797
+
798
+ If the input is undefined, empty, string or a number, `nil` will be returned.
799
+
800
+ ## lstrip
801
+
802
+ ```
803
+ <string> | lstrip
804
+ ```
805
+
806
+ Return the input string with all leading whitespace removed. If the input is not a string, it will
807
+ be converted to a string before stripping whitespace.
808
+
809
+ ```liquid2
810
+ {{ " So much room for activities " | lstrip }}!
811
+ ```
812
+
813
+ ```plain title="output"
814
+ So much room for activities !
815
+ ```
816
+
817
+ ## map
818
+
819
+ ```
820
+ <array> | map: <string | lambda expression>
821
+ ```
822
+
823
+ Extract properties from an array of objects into a new array.
824
+
825
+ For example, if `pages` is an array of objects with a `category` property:
826
+
827
+ ```json title="data"
828
+ {
829
+ "pages": [
830
+ { "category": "business" },
831
+ { "category": "celebrities" },
832
+ { "category": "lifestyle" },
833
+ { "category": "sports" },
834
+ { "category": "technology" }
835
+ ]
836
+ }
837
+ ```
838
+
839
+ ```liquid2
840
+ {% assign categories = pages | map: "category" -%}
841
+
842
+ {% for category in categories -%}
843
+ - {{ category }}
844
+ {%- endfor %}
845
+ ```
846
+
847
+ ```plain title="output"
848
+ - business
849
+ - celebrities
850
+ - lifestyle
851
+ - sports
852
+ - technology
853
+ ```
854
+
855
+ ## minus
856
+
857
+ ```
858
+ <number> | minus: <number>
859
+ ```
860
+
861
+ Return the result of subtracting one number from another. If either the input or argument are not a number, Liquid will try to convert them to a number. If that conversion fails, `0` is used instead.
862
+
863
+ ```liquid2
864
+ {{ 4 | minus: 2 }}
865
+ {{ "16" | minus: 4 }}
866
+ {{ 183.357 | minus: 12.2 }}
867
+ {{ "hello" | minus: 10 }}
868
+ ```
869
+
870
+ ```plain title="output"
871
+ 2
872
+ 12
873
+ 171.157
874
+ -10
875
+ ```
876
+
877
+ ## modulo
878
+
879
+ ```
880
+ <number> | modulo: <number>
881
+ ```
882
+
883
+ Return the remainder from the division of the input by the argument.
884
+
885
+ ```liquid2
886
+ {{ 3 | modulo: 2 }}
887
+ {{ "24" | modulo: "7" }}
888
+ {{ 183.357 | modulo: 12 }}
889
+ ```
890
+
891
+ ```plain title="output"
892
+ 1
893
+ 3
894
+ 3.357
895
+ ```
896
+
897
+ If either the input or argument are not an integer or float, Liquid will try to convert them to an
898
+ integer or float. If the input can't be converted, `0` will be used instead. If the argument can't
899
+ be converted, an exception is raised.
900
+
901
+ ## newline_to_br
902
+
903
+ ```
904
+ <string> | newline_to_br
905
+ ```
906
+
907
+ Return the input string with `\n` and `\r\n` replaced with `<br />\n`.
908
+
909
+ ```liquid2
910
+ {% capture string_with_newlines %}
911
+ Hello
912
+ there
913
+ {% endcapture %}
914
+
915
+ {{ string_with_newlines | newline_to_br }}
916
+ ```
917
+
918
+ ```plain title="output"
919
+
920
+
921
+ <br />
922
+ Hello<br />
923
+ there<br />
924
+
925
+ ```
926
+
927
+ ## plus
928
+
929
+ ```
930
+ <number> | plus: <number>
931
+ ```
932
+
933
+ Return the result of adding one number to another. If either the input or argument are not a number, Liquid will try to convert them to a number. If that conversion fails, `0` is used instead.
934
+
935
+ ```liquid2
936
+ {{ 4 | plus: 2 }}
937
+ {{ "16" | plus: "4" }}
938
+ {{ 183.357 | plus: 12 }}
939
+ ```
940
+
941
+ ```plain title="output"
942
+ 6
943
+ 20
944
+ 195.357
945
+ ```
946
+
947
+ ## prepend
948
+
949
+ ```
950
+ <string> | prepend: <string>
951
+ ```
952
+
953
+ Return the argument concatenated with the filter input.
954
+
955
+ ```liquid2
956
+ {{ "apples, oranges, and bananas" | prepend: "Some fruit: " }}
957
+ ```
958
+
959
+ ```plain title="output"
960
+ Some fruit: apples, oranges, and bananas
961
+ ```
962
+
963
+ If either the input value or argument are not a string, they will be coerced to a string before
964
+ concatenation.
965
+
966
+ ```liquid2
967
+ {% assign a_number = 7.5 -%}
968
+ {{ 42 | prepend: a_number }}
969
+ {{ nosuchthing | prepend: 'World!' }}
970
+ ```
971
+
972
+ ```plain title="output"
973
+ 7.542
974
+ World!
975
+ ```
976
+
977
+ ## reject
978
+
979
+ ```
980
+ <array> | reject: <string>[, <object>]
981
+ ```
982
+
983
+ Return a copy of the input array including only those objects that have a property, named with the first argument, **that is not equal to** a value, given as the second argument. If a second argument is not given, only elements with the named property that are falsy will be included.
984
+
985
+ ```json title="data"
986
+ {
987
+ "products": [
988
+ { "title": "Vacuum", "type": "house", "available": true },
989
+ { "title": "Spatula", "type": "kitchen", "available": false },
990
+ { "title": "Television", "type": "lounge", "available": true },
991
+ { "title": "Garlic press", "type": "kitchen", "available": true }
992
+ ]
993
+ }
994
+ ```
995
+
996
+ ```liquid2
997
+ All products:
998
+ {% for product in products -%}
999
+ - {{ product.title }}
1000
+ {% endfor %}
1001
+
1002
+ {%- assign kitchen_products = products | reject: "type", "kitchen" -%}
1003
+
1004
+ Non kitchen products:
1005
+ {% for product in kitchen_products -%}
1006
+ - {{ product.title }}
1007
+ {% endfor %}
1008
+
1009
+ {%- assign unavailable_products = products | reject: "available" -%}
1010
+
1011
+ Unavailable products:
1012
+ {% for product in unavailable_products -%}
1013
+ - {{ product.title }}
1014
+ {% endfor %}
1015
+ ```
1016
+
1017
+ ```plain title="output"
1018
+ All products:
1019
+ - Vacuum
1020
+ - Spatula
1021
+ - Television
1022
+ - Garlic press
1023
+ Non kitchen products:
1024
+ - Vacuum
1025
+ - Television
1026
+ Unavailable products:
1027
+ - Spatula
1028
+ ```
1029
+
1030
+ ## remove
1031
+
1032
+ ```
1033
+ <string> | remove: <string>
1034
+ ```
1035
+
1036
+ Return the input with all occurrences of the argument string removed.
1037
+
1038
+ ```liquid2
1039
+ {{ "I strained to see the train through the rain" | remove: "rain" }}
1040
+ ```
1041
+
1042
+ ```plain title="output"
1043
+ I sted to see the t through the
1044
+ ```
1045
+
1046
+ If either the filter input or argument are not a string, they will be coerced to a string before
1047
+ substring removal.
1048
+
1049
+ ## remove_first
1050
+
1051
+ ```
1052
+ <string> | remove_first: <string>
1053
+ ```
1054
+
1055
+ Return a copy of the input string with the first occurrence of the argument string removed.
1056
+
1057
+ ```liquid2
1058
+ {{ "I strained to see the train through the rain" | remove_first: "rain" }}
1059
+ ```
1060
+
1061
+ ```plain title="output"
1062
+ I sted to see the train through the rain
1063
+ ```
1064
+
1065
+ If either the filter input or argument are not a string, they will be coerced to a string before substring removal.
1066
+
1067
+ ## remove_last
1068
+
1069
+ ```
1070
+ <string> | remove_last: <string>
1071
+ ```
1072
+
1073
+ Return a copy of the input string with the last occurrence of the argument string removed.
1074
+
1075
+ ```liquid2
1076
+ {{ "I strained to see the train through the rain" | remove_last: "rain" }}
1077
+ ```
1078
+
1079
+ ```plain title="output"
1080
+ I strained to see the train through the
1081
+ ```
1082
+
1083
+ If either the filter input or argument are not a string, they will be coerced to a string before substring removal.
1084
+
1085
+ ## replace
1086
+
1087
+ ```
1088
+ <string> | replace: <string>[, <string>]
1089
+ ```
1090
+
1091
+ Return the input with all occurrences of the first argument replaced with the second argument. If
1092
+ the second argument is omitted, it will default to an empty string, making `replace` behave like
1093
+ `remove`.
1094
+
1095
+ ```liquid2
1096
+ {{ "Take my protein pills and put my helmet on" | replace: "my", "your" }}
1097
+ ```
1098
+
1099
+ ```plain title="output"
1100
+ Take your protein pills and put your helmet on
1101
+ ```
1102
+
1103
+ If either the filter input or argument are not a string, they will be coerced to a string before
1104
+ replacement.
1105
+
1106
+ ## replace_first
1107
+
1108
+ ```
1109
+ <string> | replace_first: <string>[, <string>]
1110
+ ```
1111
+
1112
+ Return a copy of the input string with the first occurrence of the first argument replaced with the second argument. If the second argument is omitted, it will default to an empty string, making `replace_first` behave like `remove_first`.
1113
+
1114
+ ```liquid2
1115
+ {{ "Take my protein pills and put my helmet on" | replace_first: "my", "your" }}
1116
+ ```
1117
+
1118
+ ```plain title="output"
1119
+ Take your protein pills and put my helmet on
1120
+ ```
1121
+
1122
+ If either the filter input or argument are not a string, they will be coerced to a string before replacement.
1123
+
1124
+ ## replace_last
1125
+
1126
+ ```
1127
+ <string> | replace_last: <string>, <string>
1128
+ ```
1129
+
1130
+ Return a copy of the input string with the last occurrence of the first argument replaced with the second argument.
1131
+
1132
+ ```liquid2
1133
+ {{ "Take my protein pills and put my helmet on" | replace_first: "my", "your" }}
1134
+ ```
1135
+
1136
+ ```plain title="output"
1137
+ Take my protein pills and put your helmet on
1138
+ ```
1139
+
1140
+ If either the filter input or argument are not a string, they will be coerced to a string before replacement.
1141
+
1142
+ ## reverse
1143
+
1144
+ ```
1145
+ <array> | reverse
1146
+ ```
1147
+
1148
+ Return a copy of the input array with the items in reverse order. If the filter input is a string, `reverse` will return the string unchanged.
1149
+
1150
+ ```liquid2
1151
+ {% assign my_array = "apples, oranges, peaches, plums" | split: ", " -%}
1152
+
1153
+ {{ my_array | reverse | join: ", " }}
1154
+ ```
1155
+
1156
+ ```plain title="output"
1157
+ plums, peaches, oranges, apples
1158
+ ```
1159
+
1160
+ ## round
1161
+
1162
+ ```
1163
+ <number> | round[: <number>]
1164
+ ```
1165
+
1166
+ Return the input number rounded to the given number of decimal places. The number of digits defaults to `0`.
1167
+
1168
+ ```liquid2
1169
+ {{ 1.2 | round }}
1170
+ {{ 2.7 | round }}
1171
+ {{ 183.357 | round: 2 }}
1172
+ ```
1173
+
1174
+ ```plain title="output"
1175
+ 1
1176
+ 3
1177
+ 183.36
1178
+ ```
1179
+
1180
+ If either the filter input or its optional argument are not an integer or float, they will be converted to an integer or float before rounding.
1181
+
1182
+ ## rstrip
1183
+
1184
+ ```
1185
+ <string> | rstrip
1186
+ ```
1187
+
1188
+ Return the input string with all trailing whitespace removed. If the input is not a string, it will be converted to a string before stripping whitespace.
1189
+
1190
+ ```liquid2
1191
+ {{ " So much room for activities " | rstrip }}!
1192
+ ```
1193
+
1194
+ ```plain title="output"
1195
+ So much room for activities!
1196
+ ```
1197
+
1198
+ ## safe
1199
+
1200
+ ```
1201
+ <string> | safe
1202
+ ```
1203
+
1204
+ Return the input string marked as safe to use in an HTML or XML document. If the filter input is not a string, it will be converted to an HTML-safe string.
1205
+
1206
+ With auto-escape enabled and the following global variables:
1207
+
1208
+ ```json title="data"
1209
+ {
1210
+ "username": "Sally",
1211
+ "greeting": "</p><script>alert('XSS!');</script>"
1212
+ }
1213
+ ```
1214
+
1215
+ ```liquid2 title="template"
1216
+ <p>{{ greeting }}, {{ username }}</p>
1217
+ <p>{{ greeting | safe }}, {{ username }}</p>
1218
+ ```
1219
+
1220
+ ```html title="output"
1221
+ <p>&lt;/p&gt;&lt;script&gt;alert(&#34;XSS!&#34;);&lt;/script&gt;, Sally</p>
1222
+ <p></p><script>alert('XSS!');</script>, Sally</p>
1223
+ ```
1224
+
1225
+ ## size
1226
+
1227
+ ```
1228
+ <object> | size
1229
+ ```
1230
+
1231
+ Return the size of the input object. Works on strings, arrays and hashes.
1232
+
1233
+ ```liquid2
1234
+ {{ "Ground control to Major Tom." | size }}
1235
+ {{ "apples, oranges, peaches, plums" | split: ", " | size }}
1236
+ ```
1237
+
1238
+ ```plain title="output"
1239
+ 28
1240
+ 4
1241
+ ```
1242
+
1243
+ ## slice
1244
+
1245
+ ```
1246
+ <sequence> | slice: <int>[, <int>]
1247
+ ```
1248
+
1249
+ Return a substring or subsequence of the input string or array. The first argument is the zero-based start index. The second, optional argument is the length of the substring or sequence, which defaults to `1`.
1250
+
1251
+ ```liquid2
1252
+ {{ "Liquid" | slice: 0 }}
1253
+ {{ "Liquid" | slice: 2 }}
1254
+ {{ "Liquid" | slice: 2, 5 }}
1255
+ {% assign beatles = "John, Paul, George, Ringo" | split: ", " -%}
1256
+ {{ beatles | slice: 1, 2 | join: " " }}
1257
+ ```
1258
+
1259
+ ```plain title="output"
1260
+ L
1261
+ q
1262
+ quid
1263
+ Paul George
1264
+ ```
1265
+
1266
+ If the first argument is negative, the start index is counted from the end of the sequence.
1267
+
1268
+ ```liquid2
1269
+ {{ "Liquid" | slice: -3 }}
1270
+ {{ "Liquid" | slice: -3, 2 }}
1271
+ {% assign beatles = "John, Paul, George, Ringo" | split: ", " -%}
1272
+ {{ beatles | slice: -2, 2 | join: " " }}
1273
+ ```
1274
+
1275
+ ```plain title="output"
1276
+ u
1277
+ ui
1278
+ George Ringo
1279
+ ```
1280
+
1281
+ ## sort
1282
+
1283
+ ````
1284
+ <array> | sort[: <string>]
1285
+ ``
1286
+
1287
+ Return a copy of the input array with its elements sorted.
1288
+
1289
+ ```liquid
1290
+ {% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " -%}
1291
+ {{ my_array | sort | join: ", " }}
1292
+ ````
1293
+
1294
+ ```plain title="output"
1295
+ Sally Snake, giraffe, octopus, zebra
1296
+ ```
1297
+
1298
+ The optional argument is a sort key. If given, it should be the name of a property and the filter's input should be an array of objects.
1299
+
1300
+ ```json title="data"
1301
+ {
1302
+ "collection": {
1303
+ "products": [
1304
+ { "title": "A Shoe", "price": "9.95" },
1305
+ { "title": "A Tie", "price": "0.50" },
1306
+ { "title": "A Hat", "price": "2.50" }
1307
+ ]
1308
+ }
1309
+ }
1310
+ ```
1311
+
1312
+ ```liquid2 title="template"
1313
+ {% assign products_by_price = collection.products | sort: "price" -%}
1314
+ {% for product in products_by_price %}
1315
+ <h4>{{ product.title }}</h4>
1316
+ {% endfor %}
1317
+ ```
1318
+
1319
+ ```plain title="output"
1320
+ <h4>A Tie</h4>
1321
+ <h4>A Hat</h4>
1322
+ <h4>A Shoe</h4>
1323
+ ```
1324
+
1325
+ ## sort_natural
1326
+
1327
+ ```
1328
+ <array> | sort_natural[: <string>]
1329
+ ```
1330
+
1331
+ Return a copy of the input array with its elements sorted case-insensitively. Array items will be compared by their string representations, forced to lowercase.
1332
+
1333
+ ```liquid2
1334
+ {% assign my_array = "zebra, octopus, giraffe, Sally Snake" | split: ", " -%}
1335
+ {{ my_array | sort_natural | join: ", " }}
1336
+ ```
1337
+
1338
+ ```plain title="output"
1339
+ giraffe, octopus, Sally Snake, zebra
1340
+ ```
1341
+
1342
+ The optional argument is a sort key. If given, it should be the name of a property and the filter's input should be an array of objects. Array elements are compared using the lowercase string representation of that property.
1343
+
1344
+ ```json title="data"
1345
+ {
1346
+ "collection": {
1347
+ "products": [
1348
+ { "title": "A Shoe", "company": "Cool Shoes" },
1349
+ { "title": "A Tie", "company": "alpha Ties" },
1350
+ { "title": "A Hat", "company": "Beta Hats" }
1351
+ ]
1352
+ }
1353
+ }
1354
+ ```
1355
+
1356
+ ```liquid2 title="template"
1357
+ {% assign products_by_company = collection.products | sort_natural: "company" %}
1358
+ {% for product in products_by_company %}
1359
+ <h4>{{ product.title }}</h4>
1360
+ {% endfor %}
1361
+ ```
1362
+
1363
+ ```plain title="output"
1364
+ <h4>A Tie</h4>
1365
+ <h4>A Hat</h4>
1366
+ <h4>A Shoe</h4>
1367
+ ```
1368
+
1369
+ ## split
1370
+
1371
+ ```
1372
+ <string> | split: <string>
1373
+ ```
1374
+
1375
+ Return an array of strings that are the input string split on the filter's argument string.
1376
+
1377
+ ```liquid2
1378
+ {% assign beatles = "John, Paul, George, Ringo" | split: ", " -%}
1379
+
1380
+ {% for member in beatles %}
1381
+ {{- member }}
1382
+ {% endfor %}
1383
+ ```
1384
+
1385
+ ```plain title="output"
1386
+ John
1387
+ Paul
1388
+ George
1389
+ Ringo
1390
+ ```
1391
+
1392
+ If the argument is undefined or an empty string, the input will be split at every character.
1393
+
1394
+ ```liquid2
1395
+ {{ "Hello there" | split: nosuchthing | join: "#" }}
1396
+ ```
1397
+
1398
+ ```plain title="output"
1399
+ H#e#l#l#o# #t#h#e#r#e
1400
+ ```
1401
+
1402
+ ## squish
1403
+
1404
+ ```
1405
+ <string> | squish
1406
+ ```
1407
+
1408
+ Return the input string with all leading and trailing whitespace removed, and any other runs of whitespace replaced with a single space.
1409
+
1410
+ ```liquid2
1411
+ {{ " Hello, \n\t World! \r\n" | squish }}
1412
+ ```
1413
+
1414
+ ```plain title="output"
1415
+ Hello, World!
1416
+ ```
1417
+
1418
+ ## strip
1419
+
1420
+ ```
1421
+ <string> | strip
1422
+ ```
1423
+
1424
+ Return the input string with all leading and trailing whitespace removed. If the input is not a string, it will be converted to a string before stripping whitespace.
1425
+
1426
+ ```liquid2
1427
+ {{ " So much room for activities " | strip }}!
1428
+ ```
1429
+
1430
+ ```plain title="output"
1431
+ So much room for activities!
1432
+ ```
1433
+
1434
+ ## strip_html
1435
+
1436
+ ```
1437
+ <string> | strip_html
1438
+ ```
1439
+
1440
+ Return the input string with all HTML tags removed.
1441
+
1442
+ ```liquid2
1443
+ {{ "Have <em>you</em> read <strong>Ulysses</strong>?" | strip_html }}
1444
+ ```
1445
+
1446
+ ```plain title="output"
1447
+ Have you read Ulysses?
1448
+ ```
1449
+
1450
+ ## strip_newlines
1451
+
1452
+ ```
1453
+ <string> | strip_newlines
1454
+ ```
1455
+
1456
+ Return the input string with `\n` and `\r\n` removed.
1457
+
1458
+ ```liquid2
1459
+ {% capture string_with_newlines %}
1460
+ Hello
1461
+ there
1462
+ {% endcapture -%}
1463
+
1464
+ {{ string_with_newlines | strip_newlines }}
1465
+ ```
1466
+
1467
+ ```plain title="output"
1468
+ Hellothere
1469
+ ```
1470
+
1471
+ ## sum
1472
+
1473
+ ```
1474
+ <array> | sum[: <string>]
1475
+ ```
1476
+
1477
+ Return the sum of all numeric elements in an array.
1478
+
1479
+ ```liquid2
1480
+ {% assign array = '1,2,3' | split: ',' -%}
1481
+ {{ array | sum }}
1482
+ ```
1483
+
1484
+ ```plain title="output"
1485
+ 6
1486
+ ```
1487
+
1488
+ If the optional string argument is given, it is assumed that array items are hash/dict/mapping-like, and the argument should be the name of a property/key. The values at `array[property]` will be summed.
1489
+
1490
+ ## times
1491
+
1492
+ ```
1493
+ <number> | times: <number>
1494
+ ```
1495
+
1496
+ Return the product of the input number and the argument. If either the input or argument are not a number, Liquid will try to convert them to a number. If that conversion fails, `0` is used instead.
1497
+
1498
+ ```liquid2
1499
+ {{ 3 | times: 2 }}
1500
+ {{ "24" | times: "7" }}
1501
+ {{ 183.357 | times: 12 }}
1502
+ ```
1503
+
1504
+ ```plain title="output"
1505
+ 6
1506
+ 168
1507
+ 2200.284
1508
+ ```
1509
+
1510
+ ## truncate
1511
+
1512
+ ```
1513
+ <string> | truncate[: <integer>[, <string>]]
1514
+ ```
1515
+
1516
+ Return a truncated version of the input string. The first argument, length, defaults to `50`. The second argument defaults to an ellipsis (`...`).
1517
+
1518
+ If the length of the input string is less than the given length (first argument), the input string will be truncated to `length` minus the length of the second argument, with the second argument appended.
1519
+
1520
+ ```liquid2
1521
+ {{ "Ground control to Major Tom." | truncate: 20 }}
1522
+ {{ "Ground control to Major Tom." | truncate: 25, ", and so on" }}
1523
+ {{ "Ground control to Major Tom." | truncate: 20, "" }}
1524
+ ```
1525
+
1526
+ ```plain title="output"
1527
+ Ground control to...
1528
+ Ground control, and so on
1529
+ Ground control to Ma
1530
+ ```
1531
+
1532
+ ## truncatewords
1533
+
1534
+ ```
1535
+ <string> | truncatewords[: <integer>[, <string>]]
1536
+ ```
1537
+
1538
+ Return the input string truncated to the specified number of words, with the second argument appended. The number of words (first argument) defaults to `15`. The second argument defaults to an ellipsis (`...`).
1539
+
1540
+ If the input string already has fewer than the given number of words, it is returned unchanged.
1541
+
1542
+ ```liquid2
1543
+ {{ "Ground control to Major Tom." | truncatewords: 3 }}
1544
+ {{ "Ground control to Major Tom." | truncatewords: 3, "--" }}
1545
+ {{ "Ground control to Major Tom." | truncatewords: 3, "" }}
1546
+ ```
1547
+
1548
+ ```plain title="output"
1549
+ Ground control to...
1550
+ Ground control to--
1551
+ Ground control to
1552
+ ```
1553
+
1554
+ ## uniq
1555
+
1556
+ ```
1557
+ <array> | uniq[: <string>]
1558
+ ```
1559
+
1560
+ Return a copy of the input array with duplicate elements removed.
1561
+
1562
+ ```liquid2
1563
+ {% assign my_array = "ants, bugs, bees, bugs, ants" | split: ", " -%}
1564
+ {{ my_array | uniq | join: ", " }}
1565
+ ```
1566
+
1567
+ ```plain title="output"
1568
+ ants, bugs, bees
1569
+ ```
1570
+
1571
+ If an argument is given, it should be the name of a property and the filter's input should be an array of objects.
1572
+
1573
+ ```json title="data"
1574
+ {
1575
+ "collection": {
1576
+ "products": [
1577
+ { "title": "A Shoe", "company": "Cool Shoes" },
1578
+ { "title": "A Tie", "company": "alpha Ties" },
1579
+ { "title": "Another Tie", "company": "alpha Ties" },
1580
+ { "title": "A Hat", "company": "Beta Hats" }
1581
+ ]
1582
+ }
1583
+ }
1584
+ ```
1585
+
1586
+ ```liquid2 title="template"
1587
+ {% assign one_product_from_each_company = collections.products | uniq: "company" -%}
1588
+ {% for product in one_product_from_each_company -%}
1589
+ - product.title
1590
+ {% endfor %}
1591
+ ```
1592
+
1593
+ ```plain title="output"
1594
+ - A Shoe
1595
+ - A Tie
1596
+ - A Hat
1597
+ ```
1598
+
1599
+ ## upcase
1600
+
1601
+ ```
1602
+ <string> | upcase
1603
+ ```
1604
+
1605
+ Return the input string with all characters in uppercase.
1606
+
1607
+ ```liquid2
1608
+ {{ 'Hello, World!' | upcase }}
1609
+ ```
1610
+
1611
+ ```plain title="output"
1612
+ HELLO, WORLD!
1613
+ ```
1614
+
1615
+ ## url_decode
1616
+
1617
+ ```
1618
+ <string> | url_decode
1619
+ ```
1620
+
1621
+ Return the input string with `%xx` escapes replaced with their single-character equivalents. Also replaces `'+'` with `' '`.
1622
+
1623
+ ```liquid2
1624
+ {{ "My+email+address+is+bob%40example.com%21" | url_decode }}
1625
+ ```
1626
+
1627
+ ```plain title="output"
1628
+ My email address is bob@example.com!
1629
+ ```
1630
+
1631
+ ## url_encode
1632
+
1633
+ ```
1634
+ <string> | url_encode
1635
+ ```
1636
+
1637
+ Return the input string with URL reserved characters %-escaped. Also replaces `' '` with `'+'`.
1638
+
1639
+ ```liquid2
1640
+ {{ My email address is bob@example.com! | url_encode }}
1641
+ ```
1642
+
1643
+ ```plain title="output"
1644
+ My+email+address+is+bob%40example.com%21
1645
+ ```
1646
+
1647
+ ## where
1648
+
1649
+ ```
1650
+ <array> | where: <string>[, <object>]
1651
+ ```
1652
+
1653
+ Return a copy of the input array including only those objects that have a property, named with the first argument, equal to a value, given as the second argument. If a second argument is not given, only elements with the named property that are truthy will be included.
1654
+
1655
+ ```json title="data"
1656
+ {
1657
+ "products": [
1658
+ { "title": "Vacuum", "type": "house", "available": true },
1659
+ { "title": "Spatula", "type": "kitchen", "available": false },
1660
+ { "title": "Television", "type": "lounge", "available": true },
1661
+ { "title": "Garlic press", "type": "kitchen", "available": true }
1662
+ ]
1663
+ }
1664
+ ```
1665
+
1666
+ ```liquid2
1667
+ All products:
1668
+ {% for product in products -%}
1669
+ - {{ product.title }}
1670
+ {% endfor %}
1671
+
1672
+ {%- assign kitchen_products = products | where: "type", "kitchen" -%}
1673
+
1674
+ Kitchen products:
1675
+ {% for product in kitchen_products -%}
1676
+ - {{ product.title }}
1677
+ {% endfor %}
1678
+
1679
+ {%- assign available_products = products | where: "available" -%}
1680
+
1681
+ Available products:
1682
+ {% for product in available_products -%}
1683
+ - {{ product.title }}
1684
+ {% endfor %}
1685
+ ```
1686
+
1687
+ ```plain title="output"
1688
+ All products:
1689
+ - Vacuum
1690
+ - Spatula
1691
+ - Television
1692
+ - Garlic press
1693
+
1694
+ Kitchen products:
1695
+ - Spatula
1696
+ - Garlic press
1697
+
1698
+ Available product:
1699
+ - Vacuum
1700
+ - Television
1701
+ - Garlic press
1702
+ ```